摘要

  • NSID 能证明某个 DNS 应答者在这一次响应中附带了特定字节,却不能自行证明这些字节就是主机名、物理机器、地理位置、软件版本或永久身份。
  • 可执行的归因至少需要四张相互衔接的收据:原始请求与响应、观测点与时间、运营者当时有效的标签映射,以及该证据被允许控制的运维单元。

一个服务地址可以通向许多实例

把 IP 地址当作服务器名字,曾经是很实用的排障捷径。分布式 DNS 改变了这个前提。Anycast 让多个节点宣布同一个服务地址,路由系统按观测者所在网络和当时的路径状态选择其中一个节点;负载均衡器也能让多个进程或主机共用同一个入口。

地址并没有失去意义。它仍然准确记录客户端把查询发往哪个服务。它失去的是唯一指认某台机器的能力。RFC 4786 用“汇聚区”描述会被路由到某一 anycast 节点的拓扑范围。这个范围依赖观测位置,也会随着路由变化而移动,不能被理解成稳定的地理边界。

RFC 3258 把这个问题具体落到权威 DNS。发送到同一个共享地址的两次独立查询,不保证落在同一台服务器上。另发一个 ping、traceroute 或 TCP 连接,甚至更不能证明它抵达了刚才处理 DNS 查询的进程。这些观测可以各自正确,却没有共同的被观测对象。

Suzanne Woolf 与 David Conrad 在 2007 年的 RFC 4892 中处理的正是这条证据断裂。该文档属于 Informational,并未定义一套完成的线协议。它提出的问题很窄也很实际:当多个名字服务器合法共用地址时,怎样把错误、陈旧或异常缓慢的回答缩小到某个应答实例,同时避免泄露运营者不愿公开的维护拓扑?

因此,事故报告必须把两句话分开。“我从这个观测点访问该服务地址,收到这份响应”由交易记录直接支持。“这台物理机器给出了响应”则多出了一层拓扑映射。前一句不能自动替后一句作证。

第二次查询为什么会拿错收据

BIND 早已形成一种方便的约定。对 HOSTNAME.BIND. 发起 CHAOS 类 TXT 查询,可以得到管理员配置的主机名;后来使用的 ID.SERVER. 弱化了对单一实现的绑定。它们都走 DNS 协议,通常受到与普通 DNS 流量相近的防火墙和路由政策约束,运营者也能决定是否暴露真实维护地址。

问题在于,标签来自第二次交易。异常回答返回以后,anycast 路由可能已经收敛到另一节点,负载均衡器可能选中另一个后端,故障实例也可能撤出。随后得到的标签对第二次查询是真实的,却未必属于第一次响应。

这种误归因很有迷惑性,因为每一块材料都看起来可信:服务 IP 相同,标签确实由这个服务地址返回,路由探测也能走到合理的位置。可是,共用地址恰恰是多个实例得以隐藏在同一入口后的设计。用地址相同来合并两次交易,等于用待解决的问题作为答案。

RFC 4892 因此要求,实例识别信息应当能够随正常业务响应一起返回。专门的识别查询仍可保留,但不能与“同一响应内的关联”视为等价。把标签和异常回答装进同一封装,是因果边界,不只是少发一次查询。

RFC 4892 真正提出了什么

Woolf 与 Conrad 没有把现有约定换个名字后直接标准化。他们从失败场景反推设计约束。

识别机制必须位于 DNS 内,不能依赖特定服务器实现,并应能附着在普通查询的响应上。它需要易于实现、启用和关闭,也应允许访问控制。运营者可以区分实例,但不应被迫公开内部主机名、机架信息或用于管理的单播地址。原有 CHAOS 类与伪域名的额外负担,也不应成为新机制的前提。

文档还刻意把“能识别”与“已认证”分开。一个看起来像主机名的字符串不会因此获得可信来源。DNSSEC 的任务是按其验证模型保护签名的 DNS 数据,它不会自动为应答者附带的所有通道元数据背书。更好的机制可以容纳认证,但不能把可读性误写成完整性。

RFC 4892 没有申请 IANA 分配,它留下的是需求清单。两个月后,Rob Austein 撰写的 RFC 5001 定义了 NSID 选项,并在致谢中列出 Woolf。本文必须保持这条作者边界:把后续标准的作者身份扩张给前一份需求文档的作者,正好会重演“标签范围被便利性放大”的问题。

NSID 究竟证明了多少

解析器在 EDNS 查询里放入一个空的 NSID 选项。理解该选项并选择响应的服务器,会在同一份回答里带回 NSID 字节。于是,观测者可以可靠地说:这次交易的应答者附带了这个值。第二次查询造成的时间竞态被消除了。

但语义歧义没有消失。RFC 5001 把 NSID 负载定义为不透明字节串,故意不在协议中规定其语法与含义。运营者可以放入真实主机名、单播地址、持久随机数、动态值、加密块,或任意八位组。界面应以十六进制展示,比较也应基于原始字节,因为准确保存比把它美化成文字更重要。

所以,一个值可以对应物理机,也可以对应进程、容器、负载均衡池、站点或 anycast 节点。它可能跨重启保持,也可能每次重启改变;可能全网唯一,也可能因为配置错误而在所有实例上相同;可能只对内部人员有意义。协议提供了信封,运营者才定义信封里的语义。

NSID 还是逐跳而非传递的。终端客户端向递归解析器请求 NSID,若对方返回值,识别的是接收该请求的递归解析器,而不是它在上游询问的权威服务器。递归解析器可以在自己的上游查询里另行请求 NSID,但那是另一跳、另一张收据。EDNS 本身的逐跳属性也支持这条边界。

NSID 也不会自动得到认证。RFC 5001 把它视为 DNSSEC 通常保护范围之外的通道信号;需要完整性时,应采用 TSIG 等明确的通道安全机制。即使负载看起来像签名或密文,静态值仍可能被重放。密码学外观不能补出新鲜度、物理位置或管理责任。

四张收据把标签变成证据

第一张是响应关联收据。保存原始查询、原始响应和未经改写的 NSID 字节。若最初没有请求 NSID,报告应承认实例尚未确定。随后发出的 ID.SERVER. 可以提供线索,却不能倒过来给第一次响应指定作者。

第二张是观测点与路径收据。记录观测源、目标服务地址、传输方式和时间。Anycast 观测只能说明某个客户端在某个时刻抵达什么。另一个城市或网络得出不同值,不会让前一个结果失效;它可能只是处在不同汇聚区。

第三张是运营者映射收据。不透明值需要一张带版本的表,说明它在什么时间区间映射到哪个运维单元。没有生效时间与退役时间,重建后重复使用标签就会制造虚假的连续性。没有这张表,标签最多是聚合同类响应的键,不是资产名称。

第四张是行动范围收据。映射必须说明目标究竟是进程、容器、主机、后端池、站点还是 anycast 节点,并标出谁有权操作。只有这样,团队才能判断证据足以支持查日志、摘除单个后端、重启服务,还是需要更多材料才能撤销整站路由。

这套模型没有削弱 NSID,反而保留了它最擅长的工作:把异常响应分组、找出离群应答者、引导团队查看正确日志。它拒绝的只是一个协议从未作出的承诺——一串字节天然就是机器身份证。

来源