摘要

  • RFC 9432 把成员区域及其属性清单放进一个普通 DNS 区域,使消费者能够自动新增、移除或调整权威服务;成员区域的实际数据仍通过独立的区域传送取得。
  • “版本 2 结构正确”“传送对端已认证”“这个成员可被本地接纳”“该目录拥有待删除状态”“运行中的服务器确实得到预期结果”是五个不同命题,任何一个都不能替代其余四个。
  • 安全消费者应在目录损坏时保留最后有效状态,以独立规则限制可接纳成员,记录目录与成员标签的来源关系,拦截异常的大规模变更,隔离可恢复状态,并从配置一直验证到外部权威应答。

删除动作从未出现

设想一个二级 DNS 服务商,用同一套目录为数十万客户区域编排多个集群。上游库存系统因权限过期而返回空结果。生成器没有报错:它产出包含 SOA、NS 和 version=2 的新目录,递增序列号,通过带认证的 IXFR 传到消费者。

目录里没有任何成员。

从线路上看,没有恶意插入,没有格式破坏,也没有一条显式的删除命令。新状态只是省略了旧成员。消费者若把“新清单”直接等同于“授权后的完整现实”,便会把所有旧成员的缺席解释成移除。

RFC 9432 对这一风险没有含糊其辞:若脚本生成空目录,数百万个成员区域可能在数秒内从二级服务器上被删除,相关域名随即离线。自动化缩短正常变更所需时间,也以同样比例缩短错误扩散时间。

因此,审计不能只证明“我收到了合法的新目录”。它还要证明:为何这次缺席是完整库存而不是采集失败;谁有权让缺席生效;消费者凭什么相信这批删除属于目录的权限范围。

目录传的是配置,不是成员数据

Catalog Zone 本身是一个普通 DNS 区域。成员名称作为 PTR 记录出现在 zones 之下,每个成员节点使用唯一标签,并可带有 group、coo 或扩展属性。消费者为这个区域启用目录解释后,便能据此建立或调整本地配置。

成员区域的 A、AAAA、MX、NS、DNSSEC 等权威内容并不在目录里。成员被接纳后,消费者还要从相应主服务器执行普通区域传送。目录回答的是“本机应管理哪些区域、应用哪些已约定属性”;成员传送回答的是“这些区域究竟要提供什么数据”。

这两条链会分别失败。目录可能成功新增成员,而成员 AXFR 一直失败;某个节点可能已经移除成员,其他节点仍在应答旧副本;成员 NOTIFY 甚至可能早于目录里的新增记录到达。

所以,目录序列号一致只证明控制对象已收敛,不能证明服务已收敛。证据必须继续穿过消费者决策、运行配置、成员传送、加载结果和外部查询。

五本账,不能合并成一个绿灯

第一本是结构账。版本 2 要求唯一且受支持的 version TXT。一个成员 PTR RRset 只能有一个值,不同标签不能指向同一成员。已知属性若形状非法,目录即为损坏;实现不支持的记录则应被忽略,而不是猜测含义。

第二本是传送来源账。TSIG 可认证 DNS 事务,TLS 上的区域传送可进一步保护通道与机密性。这能说明观察到的消息来自哪个已配置对端、途中是否遭到意外修改,却看不到生产者内部库存为何变空。

第三本是本地准入账。RFC 9432 明确指出,启用目录后,服务哪些区域的管理控制会完全转移给目录生产者;紧接着,它建议消费者用正则表达式或另一个数据库限定可接纳成员。密码学认证解决“谁发来的”,准入规则解决“这个发送者可在这里配置什么”。

第四本是状态归属账。目录只能删除由它最初配置的区域及关联状态。消费者必须知道每项状态由哪个目录、哪个成员标签建立,不能只看当前区域名称。

第五本是运行结果账。数据库有配置,不等于进程已加载;进程已加载,不等于所有节点都应答;应答存在,也不等于版本和属性正确。运行代码与数据包才是执行事实。

把五本账压缩成“Catalog OK”,等于让最早的一层借用了最后一层的权威。

损坏目录有保护,空目录未必有

RFC 9432 为结构损坏定义了清晰的失败方式。缺少版本、版本不受支持、成员重复、已知属性非法,都意味着消费者不得把对象当成目录处理。如果一个原本有效的目录后来损坏,消费者不得因此移除或重新配置既有成员;服务器重启后也应继续提供最后有效目录所建立的区域。

这条规则保护的是“无法解释的输入”。它并不保护“能够解释、但含义异常的输入”。空的版本 2 目录完全可能合法;一次删除九成成员的变更也可能没有任何结构错误。

因此,消费者需要语义差异检查。它应规范化比较新增、移除、标签变化、组和扩展属性,统计比例和客户分区,核对允许的后缀与维护窗口,并与独立库存交叉验证。意外空目录、跨分区删除或超阈值变更应进入隔离区,而不是自动落地。

这是本地拒绝,不是对标准的违抗。薄的共同规则负责互操作,后续是否执行由承受后果的一方决定,正是 Heng Lu 所说的 Localized Future Decision。

谁创建状态,谁才可能拥有删除权

成员从某个目录中消失时,RFC 9432 规定:如果该区域最初不是由这个目录配置,消费者不得删除区域和关联状态。只有来源精确匹配时,目录移除才触及区域数据、DNSSEC 密钥等状态。规范还提醒运营方,可以临时归档以便纠错。

这要求一套来源台账。每个成员至少要关联:来源目录、成员标签、首次接纳序列号、本地配置档、生成的状态存储、后来属性变化和当前所有权。只保存“区域当前存在”无法回答谁有权让它消失。

归档也不能停留在口号。哪些文件、日志、计时器、密钥和产品元数据被保留?保留多久?谁能恢复?恢复后的状态如何与缺席期间主服务器发生的变化对齐?永久保存密钥会扩大泄露面,立即清除又会让一次错误传送变成不可逆损失。

回滚更不只是重新传一次旧目录。某些实现若已清除成员状态,重新加入 PTR 得到的是“新建区域”,不一定是原区域、原密钥和原历史。

一个不表达含义的标签,却能决定状态去向

成员节点的唯一标签被设计成不携带业务含义,真正的区域名称在 PTR 目标中。但标签变化具有明确操作语义:消费者把它视为先移除、再新增,并重置关联状态。代码审查里一个看似无意义的标识变化,可能让密钥和日志被清空。

coo(Change of Ownership)进一步放大了标签的作用。旧目录先指向新目录;消费者要等新目录也出现该成员,并再次确认旧目录的 coo 仍然存在,才进行迁移。这是两侧状态共同成立的交接,不是新生产者单方面声明。

若新目录保留同一成员标签,关联状态可随区域一并被接管。对有意的平滑迁移,这很有用;对不该移交的密钥或私有元数据,这就是越界。若旧生产者不希望状态被继承,就要在增加 coo 前或同时更改标签并触发重置。

“迁移区域”因此不是完整决策。领导层必须分别说明:区域数据、日志、计时器、DNSSEC 密钥和产品私有状态,哪些连续,哪些清空,哪些归档。

group 与 ext 不会自动变成全球命令

group 让生产者提示某些成员接受不同处理,但字符串本身没有预定义意义。消费者把自己理解的值映射到本地配置档,对未知值必须忽略,对多个值如何组合也由实现决定。

ext 下的自定义属性更明确地属于实现私有空间,不承诺通用互操作。IANA 的版本 2 登记列出 zones、version、coo、group 与 *.ext,其作用是协调名称范围,不是赋予私有属性普遍执行力。

这体现了 Minimum Initial Specification:共同层只保留必要、可本地验证的语法和故障规则。生产者与消费者可以自愿约定更多含义,但其他参与者无需接受。发布不是现实,实际实现、验证、部署和使用才是采用。

三种实现,三种实际后果

BIND 当前文档说明,目录更新会使成员被新增、移除或重新配置;它支持版本 1 和 2、标签触发的状态重置以及 coo。最小更新间隔可以延缓执行,却不能判断一批删除是否真实。

Knot DNS 把组值映射到本地配置档,并明确写到:成员被移出目录时,其区域文件、日志、计时器和 DNSSEC 密钥会立即清除,区域文件另有文档所列例外。目录更新还可能引起更广范围的区域重新加载。目录差异因此可能同时成为存储事件和进程事件。

PowerDNS 文档记录了版本 2 的生产者、消费者能力,以及 coo、多组值和用于指示状态重置的唯一值。它支持的后端与属性并不等同于其他产品。

所以,“三者都支持 RFC 9432”不是风险结论。混合环境要逐版本测试损坏目录、空目录、名称冲突、未知组、成员移除、标签变化、不完整迁移、重启和恢复。Running-Code Primacy 要求证明二进制实际做了什么,而不是把抽象标准投射成统一行为。

把权力还原为可重建的证据链

对每个已接纳序列号,保存内容哈希、规范化差异、认证对端、传送方式、结构判定、准入策略版本、每个成员的来源目录与标签、冲突或忽略原因、各消费者应用结果、成员传送与加载状态,以及外部权威查询。

对删除增加状态清单、归档标识、清除时间和恢复截止点。对 coo 增加新旧目录两侧观察、标签连续或重置、状态移交决定和旧目录退出时点。密钥与敏感目录全文不应进入普通日志。

最终,调查者应当无需猜测就能回答:生产者发布了什么;通道认证了谁;消费者接纳了什么;目录实际拥有哪项状态;软件执行了什么;用户最后解析到了什么。

来源