TT Lab
开始
学习 学习路径 课程

证书已经续期,浏览器却还显示旧证书

权威服务器变了,缓存还在给旧答案

在 TT Lab 中继续学习

目标

用 Python 启动权威 DNS 服务器和缓存服务器,用 dig 读取 TTL、CNAME 和否定应答,然后亲手重现“zone 已经改了,缓存却仍然给出旧答案”。

为什么重要

DNS 故障大多是“谁在记着什么、记了多久”的问题。/etc/hosts 比 DNS 先响应,缓存会按权威服务器给出的 TTL 返回旧答案,就连不存在的名称也会按 SOA 规定的时间被记住。本实验会亲手接触这三个层次。素材服务器 /opt/app/minidns.py 是用标准库做的小型 DNS,权威模式每次查询都会重新读取 zone 文件,缓存模式则一边递减 TTL 一边应答。

步骤

  1. 创建 zone 文件 /root/dns/zone.txt。其中必须有 lab.internal. 的 SOA(最后一个字段 minimum 为 60)、www.lab.internal. A 10.0.0.10 TTL 120,以及 api.lab.internal. CNAME www.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。
  2. 在 /etc/hosts 中添加 10.0.0.99 www.lab.internal,并把 getent hosts www.lab.internal 的结果以及 /etc/nsswitch.conf 中的 hosts: 这一行保存到 /root/dns/02-hosts.txt。
  3. 用 python3 /opt/app/minidns.py cache --upstream 127.0.0.1:5300 --port 5301 启动缓存服务器,以间隔几秒的方式向缓存(5301)查询两次 www.lab.internal,并把两次 answer 都保存到 /root/dns/03-ttl.txt。
  4. 向缓存查询 api.lab.internal 的 A,并把 answer 保存到 /root/dns/04-cname.txt。
  5. 用 +comments +authority 以间隔几秒的方式向缓存查询两次不存在的名称 nope.lab.internal,保存到 /root/dns/05-nx.txt,并把否定应答留在缓存中的秒数以一个数字写入 /root/dns/05-negttl.txt。
  6. 先向缓存查询一次 www 使其填充,再把 zone 文件中的 www.lab.internal. 改为 10.0.0.11,并增大 SOA serial。然后在 120 秒内依次向权威服务器(5300)和缓存(5301)查询,把两个 answer 保存到 /root/dns/06-stale.txt。
  7. 结束缓存服务器后重新启动,把 www 的 answer 保存到 /root/dns/07-after.txt。如果无法清空缓存,请把旧答案最多还能保留多少秒以一个数字写入 /root/dns/07-window.txt。
  8. 用 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。
  9. 在 /root/dns/09-report.md 中进行整理。必须包含 /etc/hosts 为什么排在前面、迁移时最坏情况下的传播时间(秒)、否定缓存时间(秒),以及 ndots 产生的额外查询。

参考

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 步日志中看到的查询顺序。