逐跳追踪 DNS 查询,找到真正的来源
下发一个解析器地址,只决定查询发往哪里,并不决定解析器看到哪个来源。把客户端、转发器和接收服务器一路追下来,再把路径写清楚。
给客户端下发解析器地址,只决定查询发往哪里,并不决定解析器会看到哪个来源。路由、转发和地址转换在两端之间轮流插手。一份面向来源问题的操作手册会逐跳跟随查询,并记录每个观察点实际看到了什么。顺序很重要,因为在观察之前下的结论,只是格式更漂亮的猜测。
先写下整体安排,再去收集证据
有四个位置能告诉你 DNS 来源的信息,而且它们说的并不相同。客户端内核知道自己会用哪个源地址,转发器知道自己向上游发了什么,做转换的设备知道自己改写了什么,接收方解析器知道实际到达的是什么。在收集任何材料之前,先写下这四个位置里你真正能接触到哪几个。
| Observation point | What it can prove | What it cannot prove | How to read it |
|---|---|---|---|
| client kernel | which source address it would use for that resolver | anything a device in between rewrote | ip route get against the resolver address |
| forwarder | that it sent an upstream query, and from which source | what the client originally asked for | its own log and its cache state |
| translating device | that a source was rewritten, and to what | whether the query was legitimate | NAT policy and its counters |
| receiving resolver | the address that actually arrived | which client behind that address asked | a scoped capture on port 53 |
也把你的预期写下来,包括你认为正在使用的解析器。分裂路由可能把查询送到完全不同的地方,缓存也可能完全不经过上游就给出应答,所以只留在脑子里的假设撑不过第一次意外。把声明的设计和实测的路径放在同一份记录的两个独立栏目里;两者对不上时,你就找到了值得追下去的问题。
确认查询究竟是哪个进程发出的
从客户端开始:记下解析器地址、查询名和时间,再确认是哪个进程发出的。下面第一行会指出持有 UDP 套接字的进程,这正是这个小标题承诺的检查。如果那个进程是浏览器辅助进程或容器运行时,而不是系统解析器,那么后面的整条链路描述的就是没人问过的路径。
ss -tunp 'dport = :53'
dig @10.20.30.53 host.corp.example +noall +answer
ip route get 10.20.30.53
在多网卡主机上,先弄清内核为这个目的地会选择哪个源地址,再去别处找答案。这是路径完全由你掌控的最后一个点,也是你要和解析器报告的值作比较的那个值。对不上,说明中间有设备改写了报文,或者发出的是两个不同的查询。
测试名要留在内部,因为把内部名交给公共解析器,既不能证明内部路径的任何事情,又公开了一个本不必公开的名字。开了安全 DNS 的浏览器根本不会出现在那份套接字列表里,而这个缺席本身就是答案。
转发器会再发一次独立的查询
转发器把一次交互变成两次。下游请求到达后,转发器会用它自己选定的地址向上游发出自己的查询。决定上游解析器看到什么的是它的配置,而不是客户端。
dnsmasq 手册正因如此值得一读:它写明了转发器如何选择上游服务器、如何绑定监听侧,而这套安排里没有任何一环把客户端来源带上去。所以解析器日志里出现网关地址是正常现象,不是故障证据。缺席同样容易被误读,因为缓存命中不会产生任何上游查询,看起来和从未到达的查询一模一样。在得出这两个结论中的任何一个之前,先读转发器的配置和它的缓存状态。两个问题,两个该看的地方。
到接收方解析器上读来源
唯一能定论的观察发生在服务器一侧。凡是从客户端接口列表推断出来的东西,在解析器报告实际到达者之前都只是假设。
tcpdump -ni any port 53 and host 10.20.30.53
在解析器主机上抓包,抓包要短、要有范围,并存放在策略允许的地方。如果观察到的来源是转换后的地址,下一个问题就是哪台设备做了转换、它的策略是不是预期的那一份。抓包回答的是到达了什么,并不解释策略为什么允许。记录里要写明观察点和跳数,因为关于网关的隐私结论针对的是那一跳,而不是整条路径。
把结论写成路径,而不是一个地址
记录客户端、解析器、任何转发器、每一跳观察到的来源,以及管辖它的策略。这样一份记录能在不再抓包的情况下回答日后的大部分问题。把它画成链条,因为地址清单会掩盖哪一跳产出了哪一个。
把日期、客户端版本和配置版本附在图上,后来的读者才能判断它是否仍然成立。把传输来源和服务处理的其他内容分开,因为看到一个网关地址,并不能说明还保留了别的什么。解析器的位置同样不能说明是哪个客户端来问的。把这些结论分开,才是让判断经得起复核的关键。