安装djbdns
系统需求:
djbdns只能在UNIX系统下工作。一般说来,所有宣称是UNIX
Clone的系统,应该都可以正常执行。但是有一个例外:djbdns在SCO UnixWare上目前没有办法运作。
djbdns
建议使用daemontools来启动、监视以及控制整个DNS
service的运作。通常,我们都会顺便把daemontools给一起安装上去,不过daemontools并不在这篇文章的讨论范围里,故此略过,
直接以最基本的方式来启动dnscache程序。
下载djbdns套件:djbdns套件目前最新的公开发行版是djbdns-1.05.tar.gz,读者可
以从下列之网址取得:
解开djbdns套件:
gunzip djbdns-1.05.tar
tar
-xf djbdns-1.05.tar
cd djbdns-1.05
编译及安装djbdns套件:
make;make install
请注意:安装时,必须使用root的身份执行。安装完成后,可以在/usr/local/bin的目录下看到如dnscache,tinydns等的程
序。一旦完成安装动作之后,下一步即可正式开始设定Cache DNS Server。
虽说是Cache
Server,但是因为使用的对象不同,在设定上也会有不同的考虑与做法。一般而言,我们在这里把Cache
Server很简单地用二分法分成「Local Cache Server」及「External Cache Server」。
此所谓Local Cache Server只有本机可以使用,其他外界的机器无法使用。典型的范例是使用在Proxy Server或是Mail
Server上,因为这些服务对于DNS的查询相当频繁,因此,自己维护一份Cache,可以有效地减低对外界DNS Cache
Server查询的Latency;而External Cache Server则刚好和Internal Cache
Server不同,他的使用对象则是可以开放给局域网络上面任何有需要DNS服务的机器。当然,DNS Cache
Server也不是全然没有设防的,通常,祇有你开放的网段可以使用这台Cache
Server,所以并不是全世界都可以使用他(相对地,Content Server就完全不同,因为DNS Content Server就像是Web
Server一样,做网页就是要给人家浏览参观,因此,如果不开放让人家查询您网域内的名称数据,那也就不需要DNS Service了)。
由于Local Cache
Server祇提供本机使用,其一个最没有安全考量的设定即是将监听(Listening)的接口(Interface)放在lo0之上。每一台有网络功
能的UNIX系统都会有一个lo0(Loopback),而且IP地址为127.0.0.1。所有127.0.0.1的封包绝对不会从外面收到,也不会对
外发出127.0.0.1的封包,所以在网络联机上面来看,这是绝对安全的一种做法,所以我们让dnscache监听lo0
127.0.0.1的Port 53(UDP/TCP)。
External Cache
Server则因为必须让局域网络上的其他机器可以联机查询,所以必须确定选择一个对外联机的网络接口,以及一个确保有路由可以出去的IP地址。
除了在网络接口与IP地址的选择之外,对于执行dnscache程序的身份也是一个很重要的考虑原因。BIND在长久以后,才终于实现可以做到
「change root」、「change user」;而djbdns里所有的程式预设就已经可以很简单地做到「change
root」、「change
user」的功能。因此,对于程序执行的身份上,您可以选择一个对系统存取权限最小的账号来执行,或甚至专门为这些程序新建一些账号,并将这些账号以最小
权限来作设定,如此一来,就算是程序遭到攻破,对系统所造成的安全影响也会减至最小。在这里我们选定系统里的一个预设帐号「nobody」来执行这些程
序;官方网站上则建议新建一个低权限的账号专门给djbdns使用。
接着介绍本文的主角dnscache。dnscache是一个局域网络快取服务器,它接受本地客户端,像是web浏览器或邮件传送程序所送过来的递归性质
的DNS查询要求。它会搜集远程DNS服务器所响应的DNS数据,然后将数据储存起来等待在稍后可能送来的查询使用。dnscache的设定主要是由
dnscache-conf来为我们代劳,不同于BIND在配置文件里有一堆设定项目让你眼花撩乱,dnscache-conf可以帮助你完成绝大部分设
定工作。首先来了解dnscache-conf的参数下法:
dnscache-conf acct logacct Directory ip
dnscache-conf nobody nobody /usr/local/djb/dnscache 127.0.0.1
dnscache-conf会建立一个服务目录Directory用来执行dnscache的服务。在这里的目录名称就是/usr/local/djb
/dnscache。目录名称Direcotry必须由根目录(“/”)开头,同时不允许含有任何特殊字符。dnscach-conf会设定
dnscache在执行时改变根工作环境(change
root)到/usr/local/djb/dnscache/root下,并且会将程序的执行身份改变成是账号nobody的uid和gid。
dnscache-conf会设定dnscache,同时监听ip地址的UDP及TCP的53
port。地址ip是可以省略的参数,如果ip没有指定,dnscache-conf会将dnscache
设定成监听127.0.0.1这个地
址。dnscache-conf另外会建立/usr/local/djb/dnscache/root/ip/127.0.0.1这个档案,所以
dnscache才会知道要接受来自127.0.0.1的查询请求。
dnscach-conf会在/usr/local/djb/dnscache/seed这个档案中
填进长度128
bytes不特定且是秘密的数据(简单地说就是随机数),并且用这个档案作为初始dnscache随机数生成器。如果在您的系统中有良好的随机数生成来
源,就可用它来产生长度128
bytes的随机数数据,取代/usr/local/djb/dnscache/seed中dnscache-conf所产生的数据。
dnscach-conf另外会产生一个存放执行记录的目录/usr/local/djb/dnscache/log/main。这些执行记录档案的拥有
者是账号logacct(nobody)。负责处理执行记录的是multilog程序,其执行身份会是nobody(multilog程序属于
daemontools套件一部份,不在djbdns套件里)。
dnscache程序说明
dnscache在执行时会使用$UID以及$GID环境变量中所指定的数据转变执行身份,同时他也会依据$ROOT环境变量中的目录名称进行变更根工作
目录。dnscache会监听环境变量$IP的UDP及TCP port 53。
一般来说$IP通常是
127.0.0.1,但是他也有可能是一个外部IP地址。dnscache会接受来自ip地址
1.2.3.4的查询封包,如果他检查到有任意一个下面档案存在:ip/1.2.3.4或ip/1.2.3或ip/1.2或ip/1。
dnscache会经由$IPSEND所指定IP地址的高位端口口(high
port)对外送出查询封包。一般而言,$IPSEND通常是0.0.0.0,也就是该机器的主要ip地址(第一张适配卡的第一个地址)。
dnscache会从标准输入接口读取一个长度最多128 bytes
的随机数初始值,然后用它来初始查询过程中使用到的随机数生成器。dnscache会从servers/@这个档案中读取一份的Root
Server,每一个Root
Server都是用十进位和逗号(,)隔开IP地址。它也会扫瞄整个servers目录,检视还有没有其他以域名命名的档案,如果有的话,那么,它也会读
取这些档案,然后将该域名的相关数据直接到档案中指定的IP地址进行查询。
也就是说,如果有servers/moon.af.mil这个档案,则dnscache会将anything.moon.af.mil的查询动作交给档案
中列出的DNS
server来查询,同时它也不会理会其他服务器,像是RootServer的DNS服务器,送来有关anything.moon.af.mil的任何资
料。
在djbdns
1.03版以上(含),如果$FORWARDONLY环境变量有被设定的话,dnscache会将档案servers/@中所列出的ip地址当成是其他的
快取服务器而不是Root Server。它会将所有接受到的查询要求,直接查询这些快取服务器,因此,它不会像以往一样对Root
Server或是其他DNS server提出查询请求。
dnscache会维护一个固定大小并小于256kbytes的列表,用来同时持续追踪最多200个UDP以及20个TCP查询联机。它同时对每一个进
行中的查询要求动态地分配内存,这些内存通常都不大,但是偶而可能会有大一点的情形发生。如果在查询过程中它发现没有记忆体可以用了,它即会放弃整个查询
要求。
dnscache会向作业统要求保留一块128k
bytes大小的缓冲存储器,这块内存是用来存放突然大量冒出来的UDP查询。在djbdns
1.03版以上(含),如果dnscache已经同时在处理200个UDP查询请求,而此时又有新的UDP查询要求发生,dnscache会将旧的查询请
求丢弃掉;TCP查询也是一样的,如果dnscache已经同时在处理20个TCP查询请求,而此时又有新的TCP查询要求发生,dnscache亦会将
旧的查询请求丢弃掉。
dnscache利用环境变量$CACHESIZE所指定的数值,建立一个固定大小的快取缓冲区。通常这个缓冲区的5%是用来存放一个哈希表,剩余的部份
则是用来存放快取数据(其中包括一个长度8 bytes可以表示到公元2038年的时间失效戳记):
A型态:22 bytes加上每一个地址长度4 bytes,还有另外加上该 域名的长度。
NS,PTR或CNAME型态:22 bytes加上该域名的长度以及所有 对应数据的长度。
MX型态:22 bytes加上每一个MX 2 bytes,再加上所有相关网域名称的长度。
其他的数据型态:22 bytes加上每笔记录2
bytes,再加上对应的数据字符串长度以及该名称的长度。
不存在的域名或服务器发生错误时的查询记录:22 bytes加上该域名长度。
每一个总长度超过8192 bytes的记录都不会被执行快取。
dnscache如果用光快取缓冲区时并不会结束自己,它会将旧的快取记录删除,以将空间空出来。
dnscache的解析和快取政策
dnscache乃是根据一份被设定好的Root Server进行查询作业;BIND的作法则不同,BIND
在启动时读取一个提示档案(hint file),然后根据提示档案中的地址来找出正确的Root Server的地址;
dnscache不会传递或是存放不是服务器管辖范围内的查询响应,这些数据可能是「有毒的(是恶意变造的)」。
比如说,对于foo.com这笔记录,祇有从Root
Server,dom的server或foo.dom
server查询来的才会被回应及快取。dnscache不会放过自己的快取缓冲区资料不用,而使用在查询过程中获得的额外线索数据(glue)。除此之
外,dnscache也不会使用不是该服务器管辖范围内提供的线索资料、TTL为0的线索数据、或是违反其他快取原则的线索数据。
dnscache的快取资料最多保留一个星期,它会把TTL值超过2147483647解释成0来使用。dnscache也不会快取SOA记录,但是它会
使用SOA的TTL值(大于一个小时以上)当成是快取失效时限,用来存放没有纪录的查询响应或是不存在的域名。
dnscache对DNS客户端的响应
dnscache对于客户端的响应通常比BIND的响应要来得小,这是因为响应时通常没有包含威权记录数据或是一些额外的记录数据。威权数据指的是像数据
源服务器的NS记录,以及用来处理反面答案的SOA记录数据。所谓的额外数据则包括像是伴随着NS和MX所依附的A记录数据。当回应的封包因为超过UDP
封包大小限制而被强制切除时,dnscache会将得到的封包整个丢弃不用。
DNS的运作范例与判读dnscache的log档案
DNS的运作原理看似简单,其背后却是隐藏了很多步骤。对于一个使用者而言,这些步骤不是他们关心的,他们要的祇是一个正确的答案;但是对于一个管理
DNS系统的人,了解整个DNS系统的运作却是一件很重要的事。
尤其特别的是,DNS运作里牵涉了如下三种DNS相关的程序类别:Resolver、Cache
server、以及Content
server,并且在整个查询过程中,会接触至少超过两台以上的机器,而这些机器却祇有很少部份是在您的掌控之下。因此,在debug
DNS问题时,也相对变得复杂。就以下的范例而言,「dig」是一个应用程序,也
就是后面所说的客户端;resolver则是系统里的/usr
/lib/libresolv.so(有些系统为/usr/lib/libc)里所提供的查询函数。cache server则是listen
127.0.0.2 port 53(UDP/TCP)的dnscache程序;而contentserver则是查询过程中所使用到的其他DNS
server,如:198.41.0.4、196.7.0.139、131.193.178.181等。
以下使用一个「干净的」dnscache服务器来作范例查询,同时藉由dnscache所产生的log档案,来为读者解说一个标准的DNS查询过程(请注
意,此一查询范例是一个相当标准的查询范例,仅供参考。在真实世界里,则可能无法如此顺利,有时遇到设定不良的DNS伺服器的时候,整个查询过程可能就变
的复杂许多)。
dnscache的log档案以一行为单位,笔者为了让读者可以容易读出这些log所代表的意义,已经事前将讯息解译成比较容易读懂的格式,同时作了一些
排版的动作并加上笔者的说明于其后:
我们查询的指令为:dig –t a cr.yp.to @127.0.0.1。以下是查询结果:
> dig -t a cr.yp.to @127.0.0.1
; <<>> DiG
8.3 <<>> -t cr.yp.to @127.0.0.1
; (1 server found)
;;
res options: init recurs defnam dnsrch
;; got answer:
;;
->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6
;;
flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;;
QUERY SECTION:
;; cr.yp.to, type = A, class = IN
;; ANSWER SECTION:
cr.yp.to.
21h23m24s IN A 131.193.178.181
;; Total query time: 0 msec
;; FROM:
proxy.sinica.edu.tw to SERVER: 127.0.0.1
;; WHEN: Fri Jan 25 06:02:14
2002
;; MSG SIZE sent: 26 rcvd: 42
接下来,是我们在一个干净的dnscache下产生的查询过程纪录:
a) 01-25 03:25:24
starting
此为dnscache程序启动的讯息。
b) 01-25 03:25:36
query 1 127.0.0.1:2678:6 a
cr.yp.to.
当收到第一号查询请求,客户端所使用的IP/Port分别为127.0.0.1/2678,同时定义一个查询编号6。
客户端希望知道cr.yp.to领域名A型态所对应的数据。
c) 01-25 03:25:36
tx 0 a cr.yp.to. .
198.41.0.4
128.9.0.107
192.5.5.241
202.12.27.33
192.203.230.10
128.8.10.90
192.36.148.17
198.32.64.12
193.0.14.129
128.63.2.53
192.33.4.12
192.112.36.4
198.41.0.10
此为dnscache根据预设的Root Server List对上述第一台Root
Server,即198.41.0.4送出查询请求。查询内容为cr.yp.to的A型态为何?同时记录此次查询的gluelessness
level为0。
d) 01-25 03:25:37
rr 198.41.0.4 172800 a
colo.to. 206.14.214.156
rr 198.41.0.4 172800 a tonic.to.
206.184.59.10
rr 198.41.0.4 172800 a ns.ripe.net. 193.0.0.193
rr 198.41.0.4 172800 a dns.msen.com. 148.59.19.11
rr 198.41.0.4
172800 a munnari.oz.au. 128.250.1.21
rr 198.41.0.4 172800 a
ns1.iafrica.com. 196.7.0.139
rr 198.41.0.4 172800 a
auth02.ns.uu.net. 198.6.1.82
rr 198.41.0.4 172800 ns to.
auth02.ns.uu.net.
rr 198.41.0.4 172800 ns to. colo.to.
rr
198.41.0.4 172800 ns to. dns.msen.com.
rr 198.41.0.4 172800 ns
to. munnari.oz.au.
rr 198.41.0.4 172800 ns to. ns.ripe.net.
rr 198.41.0.4 172800 ns to. ns1.iafrica.com.
rr 198.41.0.4
172800 ns to. tonic.to.
此为dnscache收到Root Server 198.41.0.4响应的数据。
198.41.0.4没有cr.yp.to的A型态数据,但是告诉我们有关to的领域名数据可以在上面ns纪录对应的7台机器里面找到,同时
传回这7台机器的IP地址(glue)。另外由198.41.0.4指定这14笔纪录的存活时间(TTL,
Time-To-Live)时间为172800,即经
过172800秒以后,这些数据就失去效力,我们不可再使用这些资料。
e) 01-25 03:25:37
stats 1 400 1 0
此为
dnscache记录目前的状态,自程序启动到目前为止收到1次查询请求,并且将所收到的14笔响应数据连同之前查询过的数据,
共计400 bytes送进cache内存里面。在后续的查询过程里,如果还有需要这些数据的话,dnscache将不会再向外查询,而直接
使用cache里面所记录的数据,前提为该笔数据的TTL时间尚在有效的范围之内(TTL由送进cache的那一刻开始往下递减,当减至
0以下时,该笔数据就不具效力,dnscache必须再向外重新查询)。
目前还有1个udp与0个tcp查询还未处理完毕。
f) 01-25 03:25:37
cached a auth02.ns.uu.net.
cached a colo.to.
cached a dns.msen.com.
cached a
munnari.oz.au.
cached a ns.ripe.net.
cached a
ns1.iafrica.com.
cached a tonic.to.
dnscache要进行下一轮回的查询,发现查询所需要的数据里有7笔记录在cache里面可以找得到,因此直接使用这些资料。
g) 01-25 03:25:37
tx 0 a cr.yp.to. to.
196.7.0.139
206.14.214.156
206.184.59.10
198.6.1.82
148.59.19.11
193.0.0.193
128.250.1.21
由于dnscache还没有找到正确的答案,所以根据上次所响应的
数据,对第一台管理to领域的DNS Server,即196.7.0.139送出
查询请求。查询内容为:「请告诉我cr.yp.to的A型态为何?」,同时记录此次查询的gluelessness level为0。
h) 01-25 03:25:37
rr 196.7.0.139 086400 a
a.ns.yp.to. 131.193.178.181
rr 196.7.0.139 086400 a b.ns.yp.to.
131.193.178.181
rr 196.7.0.139 086400 ns yp.to. a.ns.yp.to.
rr 196.7.0.139 086400 ns yp.to. b.ns.yp.to.
此为dnscache收到来自196.7.0.139响应的数据。在196.7.0.139
没有cr.yp.to的A型态数据,但是告诉我们有关yp.to的领域名称数据可以在上面ns所对应的2台机器里找到,同时传回这2台
机器的IP地址(glue)。同时,这4笔纪录的TTL时间为86400秒。
i) 01-25 03:25:37
stats 1 529 1 0
dnscache自程序启动到目前为止,收到1次查询请求,并且将查询过程中所得到的数据共有529
bytes送进了cache内存里面。
dnscache将数据存放在内存里,因此,如果程序结束或是系统重新启动以后,这些数据都不会存在了,必须重新再进行查
询。目前还有1个udp与0个tcp查询还未处理完毕。
j) 01-25 03:25:37
cached a a.ns.yp.to.
cached a b.ns.yp.to.
dnscache要进行下一轮回的查询,发现查询所需要的数据里有2笔记录在cache里可以找得到,因此直接使用这些资料。
k) 01-25 03:25:37
tx 0 a cr.yp.to. yp.to.
131.193.178.181
131.193.178.181
到目前为止,dnscache仍未找到正确的答案,因此,根据最近一次响应的数据,对第一台管理yp.to领域的DNS
Server,即131.193.178.181送出同样的查询请求,查询内容依然为:
「cr.yp.to的A型态为何?」。同时记录此次查询的gluelessness level为0。
l) 01-25 03:25:38
rr 131.193.178.181 086400 a
cr.yp.to. 131.193.178.181
rr 131.193.178.181 259200 a
a.ns.yp.to. 131.193.178.181
rr 131.193.178.181 259200 a
b.ns.yp.to. 131.193.178.181
rr 131.193.178.181 259200 ns yp.to.
a.ns.yp.to.
rr 131.193.178.181 259200 ns yp.to. b.ns.yp.to.
dnscache收到来自131.193.178.181响应的数据,131.193.178.181回应cr.yp.to的A型态数据为
131.193.178.181,该笔数据的存活时间TTL为86400秒。
同时告诉我们有关yp.to的领域名资料可以在上面ns对应的2台机器里面找到,并且传回这2台机器的IP地址(glue)。最后这4笔记录的TTL时间
和第一笔不一样,为259200秒。
m) 01-25 03:25:38
stats 1 694 1 0
dnscache记录目前的状态。自程序启动到目前为止收到1次查询请求,并且将所查询所得的数据,共计694
bytes送进cache记忆体里。目前尚有1个udp与0个tcp查询未处理完毕。
n) 01-25 03:25:38
sent 1
dnscache终于找到了正确的答案,所以将1号查询的答案送回给查询的客户端。