权威服务器变了,缓存还在给旧答案
目标
用 Python 启动权威 DNS 服务器和缓存服务器,用 dig 读取 TTL、CNAME 和否定应答,然后亲手重现“zone 已经改了,缓存却仍然给出旧答案”。
为什么重要
DNS 故障大多是“谁在记着什么、记了多久”的问题。/etc/hosts 比 DNS 先响应,缓存会按权威服务器给出的 TTL 返回旧答案,就连不存在的名称也会按 SOA 规定的时间被记住。本实验会亲手接触这三个层次。素材服务器 /opt/app/minidns.py 是用标准库做的小型 DNS,权威模式每次查询都会重新读取 zone 文件,缓存模式则一边递减 TTL 一边应答。
步骤
- 创建 zone 文件
/root/dns/zone.txt。其中必须有lab.internal.的 SOA(最后一个字段 minimum 为60)、www.lab.internal.A10.0.0.10TTL120,以及api.lab.internal.CNAMEwww.lab.internal.。用python3 /opt/app/minidns.py auth --zone /root/dns/zone.txt --port 5300启动权威服务器(日志写入/root/dns/auth.log),并把dig @127.0.0.1 -p 5300 www.lab.internal A +noall +answer的结果保存到/root/dns/01-auth.txt。 - 在
/etc/hosts中添加10.0.0.99 www.lab.internal,并把getent hosts www.lab.internal的结果以及/etc/nsswitch.conf中的hosts:这一行保存到/root/dns/02-hosts.txt。 - 用
python3 /opt/app/minidns.py cache --upstream 127.0.0.1:5300 --port 5301启动缓存服务器,以间隔几秒的方式向缓存(5301)查询两次www.lab.internal,并把两次 answer 都保存到/root/dns/03-ttl.txt。 - 向缓存查询
api.lab.internal的 A,并把 answer 保存到/root/dns/04-cname.txt。 - 用
+comments +authority以间隔几秒的方式向缓存查询两次不存在的名称nope.lab.internal,保存到/root/dns/05-nx.txt,并把否定应答留在缓存中的秒数以一个数字写入/root/dns/05-negttl.txt。 - 先向缓存查询一次
www使其填充,再把 zone 文件中的www.lab.internal.改为10.0.0.11,并增大 SOA serial。然后在 120 秒内依次向权威服务器(5300)和缓存(5301)查询,把两个 answer 保存到/root/dns/06-stale.txt。 - 结束缓存服务器后重新启动,把
www的 answer 保存到/root/dns/07-after.txt。如果无法清空缓存,请把旧答案最多还能保留多少秒以一个数字写入/root/dns/07-window.txt。 - 用
dig @127.0.0.1 -p 5300 +search +domain=lab.internal +ndots=5 api.lab A查询短名称,然后从权威服务器日志(/root/dns/auth.log)中,提取包含api.lab的 QUERY 行,保存到/root/dns/08-search.txt。 - 在
/root/dns/09-report.md中进行整理。必须包含/etc/hosts为什么排在前面、迁移时最坏情况下的传播时间(秒)、否定缓存时间(秒),以及ndots产生的额外查询。
参考
dig的+noall +answer只显示 answer 节,+comments +authority显示状态行和 authority 节。- 权威服务器每次查询都会重新读取 zone 文件。修改之后不需要重新启动。缓存服务器必须重新启动才会清空。
- 第 6 步中的“缓存给出旧答案”,必须在填充缓存之后的 TTL(120 秒)之内观察才能看到。如果时间已过,请重新填充缓存再做一遍。
- 常见错误:如果不是向缓存(5301),而是向权威服务器(5300)查询两次,TTL 不会递减。权威服务器始终原样给出 zone 中的 TTL。
zone 文件与权威服务器
创建 /root/dns/zone.txt,在 5300 上启动权威服务器,并把 www.lab.internal 的 answer 保存到 /root/dns/01-auth.txt。
zone 文件中每行是 이름 TTL 타입 값(占位符依次为名称、类型、值)。名称请写成末尾带点的绝对名称。SOA 的值是 주서버 관리자 serial refresh retry expire minimum(占位符依次为主服务器、管理员)七个项目,最后一个 minimum 是否定应答的缓存时间。服务器用 nohup ... > /root/dns/auth.log 2>&1 & 放到后台。
/etc/hosts 优先
在 /etc/hosts 中加入 10.0.0.99 www.lab.internal,并把 getent hosts www.lab.internal 的结果以及 nsswitch 中的 hosts: 这一行保存到 /root/dns/02-hosts.txt。
getent 通过与程序相同的路径(nsswitch)解析名称。如果 hosts: 这一行中 files 在 dns 之前,就是 /etc/hosts 优先。用 grep '^hosts:' /etc/nsswitch.conf 可以只提取那一行。
缓存一边递减 TTL 一边应答
在 5301 上启动缓存服务器,以间隔几秒的方式查询两次 www.lab.internal,并把两个 answer 保存到 /root/dns/03-ttl.txt。
缓存模式是 minidns.py cache --upstream 127.0.0.1:5300 --port 5301。第一次查询会向上游询问并保存,从第二次开始会递减剩余 TTL 后返回。第二次的结果请用 >> 追加。
CNAME 链在一个答案中返回
向缓存查询 api.lab.internal 的 A,并把 answer 保存到 /root/dns/04-cname.txt。
解析器遇到 CNAME 后,会一直追到链末尾的 A,并把两条记录放进同一个 answer。如果只看到一行,说明查询的类型是 CNAME——请以 A 来查询。
不存在的名称也会被记住
用 +comments +authority 以间隔几秒的方式向缓存查询两次 nope.lab.internal,保存到 /root/dns/05-nx.txt,并把否定应答留在缓存中的秒数写入 /root/dns/05-negttl.txt。
NXDOMAIN 的答案中没有 answer,SOA 位于 authority 节。该 SOA 的 TTL 就是这个否定应答的缓存时间,按 RFC 2308,它是 SOA minimum 与 SOA 自身 TTL 中较小的值。请一并留下查询两次时该 TTL 递减的记录。
zone 已经改了,缓存仍是旧答案
先向缓存查询 www 使其填充,再把 zone 中的 www.lab.internal. 改为 10.0.0.11,并增大 serial。在 120 秒内依次向权威服务器和缓存查询,把两个 answer 保存到 /root/dns/06-stale.txt。
权威服务器会直接读取文件,所以给出新地址;缓存则给出保存下来的旧答案,直到 TTL 用完。顺序很重要——先填充缓存,再修改,然后立刻向两处查询。serial 用 sed 只增加一位即可。
能清空缓存,或清空不了
结束缓存服务器后重新启动,把 www 的 answer 保存到 /root/dns/07-after.txt,并把无法清空缓存时旧答案最多可能保留的秒数写入 /root/dns/07-window.txt。
这个缓存只保存在内存中,所以结束进程就会清空(pkill -f 'minidns.py cache')。别人的缓存(客户的 ISP)是无法清空的,这时需要等待的时间就是修改之前那条记录的 TTL。
ndots 产生的额外查询
用 +search +domain=lab.internal +ndots=5 api.lab 查询之后,把权威服务器日志中包含 api.lab 的 QUERY 行保存到 /root/dns/08-search.txt。
点数少于 ndots 的名称,会先附加搜索域。所以日志中先出现 api.lab.lab.internal. 查询,失败之后才出现 api.lab. 查询。用 grep 'QUERY api\.lab' /root/dns/auth.log 提取。
整理报告
在 /root/dns/09-report.md 中整理 /etc/hosts 的优先级、最坏情况下的传播时间(秒)、否定缓存时间(秒),以及 ndots 产生的额外查询。
请写本实验中亲自测得的数字。传播时间取自第 7 步,否定缓存取自第 5 步的值。ndots 只需如实写下第 8 步日志中看到的查询顺序。