摘要

  • DNS Push 不是给 TTL 延期,而是把“何时仍可视为当前”的依据,从持续倒计时换成服务器在活跃 DSO 订阅中交付变更的义务。
  • TLS session resumption 只缩短新连接的密码学握手。旧连接一关,DSO 与其全部订阅随之结束;新会话必须重新 SUBSCRIBE,并重新取得初始状态。
  • 可审计的“当前”必须同时指向发现结果、TLS 身份、DSO 生命周期、NAME/TYPE/CLASS、订阅响应、PUSH 增删、TTL 冻结/恢复和独立服务探测。一个绿色连接灯不可能代替整条证据链。

绿灯回来时,谁还欠客户端一次通知

先看一个明确属于演示的场景。某控制器订阅一个 SRV RRset,成功后收到初始 PUSH:服务端点 TTL 为 120 秒。订阅持续十分钟,客户端没有递减这 120 秒。随后服务所有者撤回端点,恰好在同一时间,路径上的中间设备切断长连接。

客户端很快凭 TLS ticket 恢复安全连接,监控重新变绿。可是新连接上没有任何新的 SUBSCRIBE 被接受。旧服务器曾经承担的义务——把端点删除告知这个客户端——已经随着旧 DSO 会话结束。如果客户端仍让旧记录的 TTL 停在那里,它实际上把两分钟的发布声明改造成了无限期本地授权。

这类错误常被一句“会话已恢复”遮住。RFC 8765 对边界写得很清楚:TLS 连接结束就终止 DSO;TLS 恢复后,DNS Push 服务器没有订阅状态,客户端必须重新创建所需订阅。恢复的是握手成本,不是应用状态,更不是服务器欠下的变更交付责任。

缓存为何有资格停表

普通 DNS 缓存的责任链很短。发布者给出 TTL,缓存从中扣减,归零后不再把记录当作新鲜数据。再次查询可以重新取得答案。记录变化频繁时,缩短轮询间隔虽能降低发现延迟,却会让客户端、递归解析器与权威服务器持续处理大量“没有变化”的请求。

DNS Push 用状态换掉重复查询。客户端针对一个精确的 NAME、TYPE、CLASS 发出 SUBSCRIBE。服务器逐条接受或拒绝。订阅建立时,只要答案集合非空,成功响应之后就应立即发送初始 PUSH;后续增加与删除在 TLS/TCP 有序字节流上异步抵达。

这里出现一个非同寻常的缓存规则。客户端保存新增记录携带的 TTL,但只要相关订阅仍活跃,就不递减它。理由不是 TTL 消失,而是服务器承诺:TTL 若变,会产生更新;记录若消失,也会产生删除。换言之,缓存把新鲜度判断托管给了一项仍在履行的会话义务。

这个权力有精确终点。UNSUBSCRIBE 结束一条订阅,关闭 DSO 会话结束全部订阅。此时保存的 TTL 重新开始老化,归零即删除。实现如果说不出在哪一刻停表、又在哪一刻恢复计时,就说不清为何仍可使用那条记录。

从发现到订阅,中间有四道边界

客户端通常先向配置的递归解析器发起 DNS over TLS 连接,默认端口是 853。若解析器接受请求,它可能代表下游维持上游订阅并转发结果;若不能,则可沿 _dns-push-tls._tcp.<zone> 发现相应服务。发现答案、TTL、解析器与观察点都应进入证据记录。

RFC 8765 要求 Strict Privacy,但 TLS 只保护选定对端的通道。若 SRV 发现被篡改,客户端仍可能与错误目标建立一个密码学完全有效的安全连接。因此,审计必须分开保存发现的 DNSSEC 状态、目标名、SNI、证书或 TLSA 验证结果与实际对端 IP/端口。“TLS 有效”不能吞掉发现层的不确定性。

连接建立后,一次 DSO Keepalive 或 SUBSCRIBE 本身可以建立 DSO。每个 SUBSCRIBE 有非零 MESSAGE ID,并只承载一个 NAME、TYPE、CLASS;响应的 RCODE 决定这一条订阅是否存在。一个服务器可以支持 DSO 却不支持 Push,也可能支持 Push 却不对该名称有权威,或因状态容量不足而拒绝。

所以,四个事实必须分别陈述:TCP 通了;TLS 身份与保密满足所选规则;DSO 会话存在;具体 RRset 订阅已接受。只有最后一个事实能够授权缓存冻结相应 TTL。

PUSH 没有跨连接的连续编号

PUSH 是服务器单向发送的 DSO 消息,MESSAGE ID 固定为零,客户端不回应用层响应。新增记录使用从零到 0x7FFFFFFF 的 TTL。删除单条 RR 使用 0xFFFFFFFF。集体删除使用 0xFFFFFFFE,RDATA 为空,TYPE 与 CLASS 决定删除范围。

客户端应用变更前,必须证明它匹配本会话至少一条活跃订阅。这样,UNSUBSCRIBE 正在外发而旧 PUSH 已在路上的竞态,可以通过“已无匹配订阅”安全忽略。也正因如此,单独留下一个 RR 变更日志没有意义:它必须连到当时存在的会话与订阅。

TCP 能证明同一条连接内的字节顺序,却不能给两条连接之间补一枚游标。PUSH 的零 MESSAGE ID 不是序列号;一个 PUSH 还可能合并多条订阅的变更。连接断开后,不能凭“旧流最后一条”和“新流第一条”声称没有缺口。正确恢复是重新订阅并取得新的初始状态。

空答案集合也不能靠猜。规范只要求在订阅成功且当前集合非空时立即送初始 PUSH。成功响应之后没有 PUSH,可以表示“订阅已接受、当前为空”。监控必须保留成功响应和空状态,不能把沉默自动标成失败,也不能虚构一条空记录。

Keepalive 只证明连接仍能互相看见

DSO 同时使用 inactivity timeout 与 keepalive interval。Keepalive 维持 NAT、firewall 的路径状态,也让双方周期性知道彼此仍可通信。订阅活跃时,会话不因长时间没有 RR 变化而被当作闲置,但 keepalive 仍然有效。

它证明的仅是有限可达性,不是内容完整性。它不能证明递归解析器的上游订阅仍在,不能证明所有删除都抵达,不能证明客户端正确应用增删,更不能证明 SRV 指向的服务仍能工作。DNS 当前与服务健康是两套独立事实。

RECONFIRM 的边界同样重要。面对 Discovery Proxy,客户端发现某个目标似乎已失效时,可以要求代理重新做 multicast DNS 查询;代理确认消失后,再向相关订阅者推送删除。对其他类型的服务器,RECONFIRM 的动作未定义,NOERROR 也不意味着服务器真的重新核验。把它显示成“已确认有效”,就是把一个怀疑信号错误升级成真值证明。

TLS 恢复不能继承 DNS 债务

TLS resumption 对长期连接很有价值:它可以减少往返与计算。但旧连接关闭后,旧 DSO 会话与订阅已经清零。新连接即使使用相同 ticket,也必须按新会话处理。客户端应记录完整恢复序列:旧会话终止;TTL 重新老化;新 TCP/TLS 建立;标明 full 或 resumed handshake;新 DSO 建立;每个所需 RRset 重新 SUBSCRIBE;响应分类;非空状态取得新初始 PUSH;随后才再次冻结 TTL。

SUBSCRIBE 允许放入 TLS early data,但 0-RTT 没有跨连接的通用 non-replay 保证,也不具备相同的前向保密。重复包可能被当作新的订阅请求。由于状态通常短小而暂时,协议允许这种优化;但早期请求不能充当“恰好执行一次”的授权回执。实际会话上的成功响应仍是生效依据。

若 Push 无法建立,客户端可以退回普通轮询。RFC 8765 建议,同一 NAME、TYPE、CLASS 的查询间隔至少取 900 秒与“答案 TTL 加两秒”两者中的较小值,并在每次轮询前再尝试恢复 Push。这保护服务器负载,却不保证零延迟。界面应明确显示 fallback 与最后一次观测时间,不能沿用“实时订阅”的绿灯。

一条“当前”结论需要哪些账

第一本账是发现:查询名、答案、TTL、DNSSEC 结果、递归解析器和 vantage。第二本账是安全通道:目标名、SNI、证书或 TLSA、对端地址、full/resumed handshake。第三本账是 DSO:开始与结束、keepalive、inactivity、Retry Delay 和关闭原因。

第四本账属于订阅:MESSAGE ID、标准化 NAME、TYPE、CLASS、请求字节、RCODE 与接受时间。第五本账属于变更:同一流内顺序、原始字节或受控哈希、增加/单删/集删、RR、保存 TTL、匹配订阅与缓存动作。第六本账记录业务后果:直查权威看到什么,真实端点是否可达,应用据此做了什么。

只有这些账能够回答一次事故中最关键的问题:数据是谁发布的,谁承诺更新,承诺何时终止,客户端为何仍相信,以及这个信念是否真的影响流量。

把共同规范留薄,把本地责任写清

Heng Lu 的 Minimum Initial Specification、Localized Future Decision 与 Voluntary Adoption 在这里不是抽象口号。共同层应统一 DSO/TLV、订阅标识、增删编码、计时、安全和关闭语义。它不必统一全球服务器容量、客户端 UI、日志保留时长或服务探测阈值。

后续决定应留给承担成本的人。服务端决定 admission budget,客户端决定何时值得保持实时订阅,安全团队决定认证与 0-RTT,SRE 决定 canary、fallback 与 rollback,隐私团队决定证据保留边界。RFC 和 IANA codepoint 只是可协调的符号;只有部署、测试与线上字节才能证明采用。

Running-code primacy 也不是“代码跑了就正确”。它要求用实际执行压过图纸想象。若代码在订阅消失后仍冻结 TTL,运行结果正是治理失效的证据。正确的控制面应保持狭窄:发布者控制 RRset,发现指向服务,TLS 保护通道,DSO 划定会话,SUBSCRIBE 创造有限义务,缓存仅在义务存在时停表,应用另行验证服务。

来源