10.3 DNS NOTIFY(区域变更通知)
习惯上,BIND中的slave通过轮询(polling)机制来决定何时需要进行区域传输。轮询间隔时间又被称为更新间隔时间(refresh interval)。而区域SOA记录中的其他参数,则可以用来配置轮询机制的其他参数。
但是使用这种轮询机制,在slave检测到并且从其master名称服务器传回新的区域数据,需要等待一段更新间隔时间。对使用动态更新的环境而言,这种延迟所带来的后果是灾难性的。当区域信息变更时,如果primary名称服务器能够主动通知slave服务器,岂不更好?毕竟,primary名称服务器能感知数据发生了改变:有人重载(reload)了数据,或其收到并处理了一条动态更新。因此,primary可以在处理完重载或更新之后立即发送通知,而不必等待更新间隔时间的到来1。
RFC 1996提出了一种机制,允许primary名称服务器通知slave,区域数据已经变更。BIND 8和9已实现了这一功能,并称之为DNS NOTIFY。
DNS NOTIFY的工作原理如下:当primary名称服务器注意到区域的序号已经改变时,便会给该区域所有slave服务器发送一条特殊的通告。primary名称服务器通过检索区域NS记录,排除出现在区域SOA记录MNAME字段中的名称服务器和本地主机域名,从而确定区域中所有的slave服务器。
名称服务器会在何时发送变更通知?重启primary名称服务器,会导致其通知所有slave当前全部区域的序号,因为primary无从得知其区域数据文件在启动之前是否被编辑过。以新的序号重载一个或多个区域,会导致名称服务器通知区域的slave。并且动态更新会使区域的序号增大,这也会导致名称服务器发送变更通知。
这个特殊的NOTIFY通告,可以通过检查DNS包头中的opcode来加以识别。一般查询要求的opcode多为QUERY。而NOTIFY消息,包括通告与应答,具有一个特殊的opcode,即NOTIFY。除此之外,NOTIFY消息看起来和对区域SOA记录所做查询的响应很像:因为两者都包含序号被改变的区域SOA记录,并且authoritative answer(权威应答,简称aa)位也被置位了。
当slave从它的master名称服务器收到区域的NOTIFY通告时,它会发送NOTIFY响应。该响应用来告知master,它已经收到NOTIFY通告,于是master就会停止向它发送该区域的NOTIFY通告。随后,就像该区域的更新间隔时间到期一样,slave会执行以下动作:向master名称服务器查询其声明有变动的区域SOA记录。如果发现序号更大,则slave就会执行区域数据传输。
为什么slave不直接相信master发送的区域变更消息呢?因为攻击者可能通过伪造NOTIFY通告来欺骗slave,目的在于制造许多不必要的区域数据传输,导致master瘫痪,从而实现对master名称服务器的拒绝服务攻击。
如果slave实际完成了区域数据传输,RFC 1996还提到:slave应该向该区域中的其他权威名称服务器发送自己的NOTIFY通告。这样做的原因是:只靠primary master可能无法通知到区域中所有的slave服务器,因为很可能某些slave无法直接和primary master通信(所以才会使用另一台slave作为它们的master)。虽然BIND 及其后续版本,以及所有BIND 9名称服务器都实现了该功能,但是BIND 8的早期版本并不支持该功能。对于旧版的BIND 8,除非有明确的配置,否则slave不会发送NOTIFY消息。
下面来看它实际是如何工作的。在网络上,toystory.movie.edu是区域movie.edu的primary名称服务器,而wormhole.movie.edu和zardoz.movie.edu则是slave,如图10-1所示。
https://yqfile.alicdn.com/7e533dcef25e5a482eb14a069bc2c299f1e3a7fc.png" >
当在toystory.movie.edu上编辑、重载或动态更新区域movie.edu时,toystory.movie.edu将给wormhole.movie.edu和zardoz.movie.edu发送NOTIFY通告。这两个slave都会向toystory.movie.edu发送应答,表示已经收到通知。它们接着检查区域movie.edu的序号是否增加,如果增加了,便会执行区域数据传输。如果wormhole.movie.edu和zardoz.movie.edu所运行的是BIND (或其后续版本)或者BIND 9的名称服务器,当传输完新的区域数据后,它们还会彼此发送NOTIFY通告,以便通知其他slave服务器区域数据已经改变。不过对区域movie.edu而言,wormhole.movie.edu并不是zardoz.movie.edu的master名称服务器,反之亦然,所以它们彼此都会忽略对方的NOTIFY通告。
BIND名称服务器会把与NOTIFY有关的信息记录在syslog中。下面是在重载区域movie.edu后,toystory.movie.edu的日志记录:
其中,第一条信息表示toystory.movie.edu发送NOTIFY通告,用来通知两个slave(2NS),区域movie.edu现在的序号是2000010958。另外两条信息表示这两个slave名称服务器已经确认收到了该通知。
BIND 9名称服务器仅会记录:
再来看一个更复杂的例子。在图10-2中,a是区域的primary master名称服务器,也是b的master服务器,而b是c的master服务器,并且b有两个网络接口。
在这个例子中,区域数据更新后,a会通知b和c。随后b会向a查询区域的序号是否增加,如果增加则启动区域数据传输。然而,c会忽略a的NOTIFY通告,因为a不是c所配置的master名称服务器(b才是)。如果b运行的是BIND (及其后续版本)或者BIND 9,或者明确配置成通知c,那么在b完成区域数据传输后,它还会给c发送NOTIFY通告,这会提醒c去检查b所持有的区域序号。如果c运行的也是BIND 8.2.3(及其后续版本)或者BIND 9,它也会在完成区域数据传输后,给b发送NOTIFY通告,当然,b会把它忽略掉。
同时请注意,如果c有可能会从b的另外一个网络接口收到NOTIFY通告,c就必须把b的两个网络接口的IP地址,都配置到其zone语句的masters子语句中,否则c会忽略来自另一个未知接口的NOTIFY通告。
https://yqfile.alicdn.com/720d904b29fd267ea7edbd130053f14f5505770b.png" >
至于BIND 4版本的slave名称服务器,以及其他不支持NOTIFY功能的名称服务器,则会回应NOTIMP(Not Implemented,不支持指定的操作)的错误信息。请注意,Microsoft的DNS服务器支持DNS NOTIFY功能。
在BIND 8和9中,虽然DNS NOTIFY是默认开启的,但是可以用如下子语句从全局上完全关闭该功能:
https://yqfile.alicdn.com/8e30b8e7829fcaaf55de1c20887c452e5418ef4e.png" >
你还可以针对特定的区域开启或关闭NOTIFY功能。例如,如果知道区域fx.movie.edu中所有的slave名称服务器都使用BIND 4版本,因此它们都不能识别NOTIFY通告,那么zone语句可以配置为:
这样就可以避免给fx.movie.edu中的slave发送无用的NOTIFY通告。针对特定区域的NOTIFY配置,将会覆盖针对全局的配置。可惜BIND 8和9都不允许针对特定的服务器关闭NOTIFY功能。
除了区域的NS记录中指定的服务器,BIND 8和9甚至允许将其他服务器增加到“NOTIFY列表”中。例如,可能存在一个或多个未注册的slave名称服务器(本书第8章介绍过),不过又想让它们尽快取得区域数据更新。或者可能存在一个运行较旧版本的BIND 8名称服务器,它虽然是区域的slave,但同时又是另一个slave的master服务器,同样需要传送NOTIFY消息给该slave。
要将服务器加入NOTIFY列表,可在zone语句中使用also-notify子语句:
在BIND 及其后续版本的名称服务器中,还可以把also-notify作为options的子语句。当NOTIFY功能开启时,该处所做的配置将会应用到所有区域上(只要它们没有配置自己的also-notify子语句)。
从BIND 和9.1.0开始,配置notify子语句时可以同时指定explicit这个参数;这将禁止把NOTIFY消息转发给所有名称服务器,除非在also-notify列表中另有指定。例如,下面两条子语句将名称服务器配置为只给IP地址为192.249.249.20的slave发送NOTIFY消息:
https://yqfile.alicdn.com/9bfe715f9be9c0330d8fc2efe4a4859916854a0d.png" >
还可以使用allow-notify子语句告诉名称服务器,可以接受来自区域中master以外的其他名称服务器发送的NOTIFY消息:
https://yqfile.alicdn.com/766d5be66687d30596295fcee72b2f327a5c3955.png" >
如果作为options的子语句,则allow-notify的配置会影响所有slave区域。如果作为特定zone的子语句,则allow-notify的配置会覆盖掉该区域所有全局allow-notify配置。