AI 编程代理的 DNS 变更:让审核更安全
AI 编程代理执行 DNS 变更时,应将创建、编辑和删除分开,让审核者评估 DNS 影响、状态漂移、缓存和回滚风险。

AI 编程代理执行 DNS 变更时,需要比普通基础设施编辑更严格的操作形式。代理绝不能把「更新 DNS」作为一个审批项。它应将创建、编辑和删除分别列为不同操作,因为它们需要不同的证据、不同的审核问题和不同的失败处理方式。
只有当代理替换邮件记录、在不兼容的数据旁边添加 CNAME,或在迁移期间删除最后一条地址记录时,这种区分才不会显得繁琐。DNS 往往让细小的文本改动看起来无害,但影响会扩散到解析器、客户端、委派区域、证书检查、邮件投递和服务发现。审核者必须先看到准确的操作动词,才能判断影响范围。
DNS 记录动词代表不同类型的风险
创建 DNS 记录,会向区域中加入一条新的声明。审核者需要确认名称由谁负责、目标是否正确、新数据是否与现有数据冲突,以及是否有人可能利用该记录完成域名验证或劫持流量。
编辑记录,会改变已有的声明。这个操作需要并排显示旧值和新值。将一个地址改成另一个地址,可能会转移应用流量。修改 TXT 记录,可能会改变发件人认证、所有权验证,或外部服务读取的配置。旧状态能帮助审核者区分有意替换和代理基于过时信息采取的行动。
删除记录,意味着现在正确的状态是不存在这条记录。它的失败范围最广,因为安全结果往往取决于 DNS 之外的因素。也许新端点需要一段时间才能接收流量。也许另一个团队仍在使用验证 TXT 记录。也许这条记录属于代理无法从代码库推断出的故障转移配置。
把这些操作当成一种通用写入,会让审核者失去警觉。当每张卡片都写着同样的内容时,审核者会一路点击通过,直到太晚才发现其中一张卡片删除了整个 RRset。控制机制必须让这种危险差异无法被忽略。
DNS 术语也需要谨慎处理。DNS 所有者名称可以对应一个 RRset,也就是名称和类型相同的一组记录。许多服务商 API 暴露的是单个「记录」对象,但更新行为可能会替换整个 RRset。如果代理想删除两个 A 值中的一个,而服务商却替换整个集合,就可能意外删除两个值。在确定某个操作的含义前,要先了解服务商实际使用的对象模型。
让每个请求声明一个不可逆的操作
DNS 操作应只包含一个动词:create、edit 或 delete。不要在审批边界接受名为 apply、sync 或 upsert 的模糊方法。这些名称对程序员很方便,却会增加运维人员的判断成本。
请求还必须说明它操作的是一个服务商记录对象,还是整个 RRset。如果 API 无法安全地编辑单个成员,操作就应明确说明它会替换整个 RRset,并列出所有保留的值。对这一点保持沉默,会让一次看似很小的地址修改变成中断。
可以采用下面这种请求结构,既保留意图,也为执行器提供足够的状态来拒绝漂移:
{
"action": "edit",
"zone": "example.net",
"owner": "api.example.net.",
"type": "A",
"expected_rrset": {
"ttl": 300,
"values": ["198.51.100.24"]
},
"proposed_rrset": {
"ttl": 300,
"values": ["198.51.100.81"]
},
"reason": "Move the API endpoint after the service owner confirmed health checks",
"verification": [
"authoritative lookup returns 198.51.100.81",
"HTTPS health check succeeds at the new endpoint"
]
}
对于创建操作,expected_rrset 应说明 RRset 不存在,或者在服务商模型有要求时,写出允许的现有状态。对于删除操作,proposed_rrset 应明确写成不存在。不要用空字符串表示缺失,也不要省略字段。含义不清的载荷会产生含义不清的日志。
执行器应在写入前立即读取当前区域状态,将观测结果与 expected_rrset 比较,任何不一致都应停止。这是一种比较并交换的纪律,即使 DNS 服务商没有提供比较并交换端点,也应如此执行。
RFC 2136《域名系统中的动态更新》直接在协议中体现了这一思想。它的前置条件部分允许客户端声明某些 RRset 在服务器接受更新前必须存在或必须不存在。多数托管 DNS API 不会暴露 RFC 2136 的线路消息,但操作层面的结论仍然成立:基于未经验证的旧读取结果进行写入并不安全。
代理可以重试读取操作,但不应在写入失败后把预期状态改成它当前看到的状态再重试。状态不一致意味着其他操作者修改了 DNS,代理的计划已经过时,或者服务商以规划器没有预料到的方式规范化了数据。应将这种情况交给人工审核者处理。
创建操作需要所有权和冲突证据
新记录在获批前,需要证明该名称属于预期的服务。代码库路径、工单引用或自然语言请求都可以提示所有权,但它们无法证明新的公网名称确实可以使用。
代理提出创建建议前,应检查准确的所有者名称在各种记录类型下的情况。CNAME 尤其需要注意。RFC 1034 规定,如果某个节点存在 CNAME RR,则该节点不应有其他数据。只检查现有 CNAME 的规划器,可能会在已经存在地址、邮件或 TXT 数据的名称上提出 CNAME。服务商可能拒绝写入,也可能按照特定规则替换该名称下的数据。
创建审核应以清晰的语言展示四项内容:
- 完整的所有者名称、区域、记录类型、值和 TTL。
- 同一所有者名称下的当前记录,包括代理不会修改的类型。
- 谁请求了该名称,以及哪个服务会使用它。
- 适用于该记录类型的目标检查。
目标检查取决于数据类型。对于 A 或 AAAA 记录,代理可以在存在部署清单时确认地址属于预期的部署清单。对于 CNAME,应解析目标并确认目标名称完整。对于 MX,应与邮件负责人确认邮件主机名和优先级。对于供外部验证器使用的 TXT,应保留准确的原始值,不要凭猜测清理空格或调整引号。
不要让代理因为新请求的子域位于熟悉的域名之下,就推断它无害。login、auth、mta、vpn 和 admin 的后果显而易见,但任意名称也可能很重要。公网 DNS 名称一旦被依赖,就会成为一个接口。
创建操作与发布部署也不同,因为回滚不一定意味着删除。如果新记录支持验证流程,流程完成后删除它可能会破坏续期或未来的验证。提案应说明记录是否临时存在、由谁决定何时过期,以及请求者是否接受日后删除。如果没有人负责回答这些问题,就不要擅自设置过期日期。
编辑操作必须说明要替换的状态
只有当提案明确标出将要替换的准确状态时,编辑才是安全的。「让应用指向新主机」只是意图,不是可执行的 DNS 操作。
每个编辑请求都应包含旧 RRset,包括代理计划保留的值。假设一个 A RRset 有两个地址:
Current: app.example.net. 60 IN A 198.51.100.10
app.example.net. 60 IN A 198.51.100.11
Proposed: app.example.net. 60 IN A 198.51.100.11
app.example.net. 60 IN A 198.51.100.12
这是一次受控轮换。审核者可以看到操作保留了一个服务地址,同时替换了另一个。如果服务商按整体替换 RRset,那么只发送 198.51.100.12 就是把删除和创建伪装成编辑。
TTL 变更也属于同一张审核卡片。团队经常在迁移前降低 TTL,迁移后再提高 TTL。这两种变更都有后果。较低的 TTL 会增加解析器查询频率,也可能更快暴露配置错误。较高的 TTL 可能让流量在错误编辑后更长时间停留在错误端点。任何一种变更都不应藏在普通记录更新中。
代理还应区分数据编辑和格式差异。服务商可能会规范化所有者名称、添加末尾的点、拆分 TXT 字符串或重新排列值。规划器如果直接比较 API 返回的原始文本,就会产生多余的审批卡片或虚假的冲突。比较前应先规范表示形式,但要保留审核者需要读取的语义数据。
不要因为代理在版本控制中找到了匹配字符串,就直接批准编辑。版本控制中的内容可能是期望状态,但紧急 DNS 变更后,它可能已经失去相关性。当前权威状态仍然是写入条件。代码库可以解释意图,DNS 才能告诉你客户端实际会收到什么。
删除操作需要说明为什么现在可以不存在
删除请求应回答为什么记录现在可以消失,而不只是说明代理为什么觉得它不重要。旧记录看似多余,直到有人发现它仍然支持旧版回调、邮件系统、证书验证器或委派服务。
安全的做法是将流量迁移与清理分开。先添加或编辑替代数据,然后验证预期的权威响应和依赖服务。只有服务负责人确认新行为后,代理才应请求删除旧数据。即使服务商可以将两者合并,也要让删除请求保持独立。
删除请求应包含以下事实:
- 要删除的准确 RRset 或服务商记录对象。
- 确认不再依赖该数据的服务或团队。
- 替代操作及其验证结果,如果存在替代操作。
- 删除后的计划验证。
- 删除是否会改变整个 RRset。
区域顶级记录需要格外谨慎。不同服务商对区域顶级的处理方式不同,尤其是提供模拟 CNAME 行为的别名或合成记录时。代理绝不能在没有熟悉该服务商语义的审核者参与下,把期望的 CNAME 转换成服务商专用的顶级功能。DNS 中熟悉的词语,并不保证行为也熟悉。
删除后恢复会受到否定缓存影响。RFC 2308 说明了解析器如何缓存否定答案,包括 NXDOMAIN 和无数据响应。删除记录后再恢复时,一些客户端仍可能在否定缓存过期前继续看到之前的缺失状态。这不表示禁止删除,而是说明回滚计划必须包含这样一段时间:权威 DNS 已经修复,但用户仍然收到缓存的失败结果。
如果服务商拒绝删除单个值,删除操作不应默默扩大为其他操作。执行器应报告服务商要求替换整个 RRset,并请求新的明确操作。多一次审批,比解释为什么一条所谓未使用的地址记录消失了要便宜得多。
审核卡片需要超出区域差异的上下文
原始差异能提供数据,却经常隐藏后果。审核卡片应先用一句简短的话说明操作和预期行为,例如:「删除 verify.example.net 的过时 TXT 验证记录;请求者已确认外部验证完成。」然后再显示准确记录。
无需打开其他视图,卡片就应展示以下字段:操作动词、区域、所有者名称、类型、旧状态、建议状态、TTL、请求者、代理进程身份、原因和验证计划。如果写入可能影响整个 RRset,应在第一行说明。
使用直接的措辞。「替换两个 A 值」比「协调记录集」更好。「移除这条 MX 记录」比「应用期望配置」更好。使用操作动词而不是抽象表达,审核者会更快作出判断。
有用的卡片还应告诉审核者什么情况足以拒绝操作。对于 CNAME 创建,要显示该所有者名称下的冲突数据。对于编辑,要显示当前状态是否不同于预期状态。对于删除,要显示代理是否缺少服务负责人的确认。审批界面应公开不确定性,而不是把它藏在执行日志中。
不要通过提供大段自由文本的例外框,把审核者变成手动策略引擎。如果代理请求的操作不符合常规结构,就要求它补充缺失证据并准备一项新的操作。让审核者从聊天记录中重新推断 DNS 语义,迟早会导致错误获批。
一个完整的审核示例
假设代理需要将 api.example.net 移到替代地址。一张糟糕的卡片只写着:「更新 API 的 DNS。」审核者无法看出服务商会替换一个包含两个成员的 RRset。
一项可审核的编辑操作应写成:「将 api.example.net 的 A RRset 中的 198.51.100.24 替换为 198.51.100.81;保留 198.51.100.25;TTL 保持为 300。」随后展示完整的当前和建议 RRset,写出服务负责人,并说明代理将在写入后查询权威名称服务器并运行获批的健康检查。
如果审核者希望执行没有重叠期的切换,就应拒绝这张卡片并明确提出该意图。代理不应因为部署文件中出现了新地址,就自行判断不需要重叠。
DNS 缓存让时机成为审批的一部分
权威 DNS 和递归 DNS 回答的是不同的运维问题。权威服务器告诉你区域当前发布的数据。递归解析器可以在收到的 TTL 过期前返回旧答案。浏览器、操作系统、应用运行时、负载均衡器或内部解析器还可能增加另一层缓存。
因此,审批应明确期望的验证点。「权威服务器返回新答案」验证的是写入。「公共解析器返回新答案」验证的是部分传播。「应用在新目标上提供流量」验证的是用户真正需要的结果。这些是不同的检查。
使用能够显示答案来源的命令。将名称和地址替换为区域实际使用的权威服务器:
dig @ns1.example.net api.example.net A +noall +answer
; expected answer shape
api.example.net. 300 IN A 198.51.100.81
dig @1.1.1.1 api.example.net A +noall +answer
; resolver answer can retain an older value with a lower remaining TTL
api.example.net. 117 IN A 198.51.100.24
第一条查询检查一台权威服务器上的已发布状态。第二条查询检查一台递归解析器当前返回的内容。两者都不能证明所有客户端都已经切换。应将两项结果都记录在操作日志中,让事故响应人员能够区分服务商写入失败和预期的缓存行为。
不要把 TTL 当成全球一致性的倒计时。解析器缓存答案时会接收一个 TTL,因此不同解析器会在不同时间开始倒计时。一些客户端还可能独立缓存连接或应用配置。迁移计划需要服务级验证,而不是承诺等待若干秒后就一定完成。
DNS 委派变更需要更高程度的谨慎。编辑 NS 记录、胶水记录、DS 记录或其他与委派有关的数据,可能影响子区域之外的解析。代理应将这些操作归类为单独的变更类型,并要求同时负责父区域与子区域关系的审核者参与。不要把它们纳入普通主机记录流程。
宽泛的 DNS 凭据不是捷径
给代理一个可以编辑所有区域的服务商凭据,看起来很高效,因为它无需等待集成变更就能完成任务。但这不是正确的权限边界。提示词注入、错误的代码库指令或混乱的计划,都可能因此获得远超目标服务所需的权限。
将每条执行路径限制在所需的区域和操作范围内。如果服务商支持,应将读取权限与写入权限分开。建立代码库或服务身份与可请求区域之间的映射。在请求到达服务商凭据之前,就拒绝超出映射范围的区域请求。
权限边界应限制操作本身,而不是只把令牌藏起来。如果代理可以要求中间层发送任意服务商 API 请求,而中间层接受任意路径、方法、请求头和请求体,那么代理仍然拥有广泛控制权。操作层应负责 DNS 服务商调用的结构,并拒绝架构之外的字段。
这正是通用 upsert 端点带来问题的地方。它让代理可以用一个请求体把创建、编辑和删除合并起来,而且常常还会使用审核者看不到的服务商默认值。应定义独立的执行器方法,让每个方法验证操作所需的证据。删除方法应拒绝包含替代状态的载荷。编辑方法应拒绝缺少预期状态的请求。
对于使用 Sallyport 的团队,DNS 凭据应留在代理进程之外。应用可以执行 HTTP 调用,同时将凭据保存在加密保险库中,让代理只接收结果而不是凭据。这能解决凭据暴露问题,但不能判断 DNS 操作建议是否合理,判断工作仍由操作结构和审批证据完成。
过时的计划可能删除错误的线上记录
一种常见故障始于代理在一次漫长的编程任务开始时读取 DNS。它看到 cdn.example.net 只有一个 CNAME 目标,于是计划在部署后替换该目标。代理工作期间,值班工程师为了事故处理而更新目标,临时转移了流量。
代理稍后完成并发送旧的替换请求。如果服务商调用使用通用更新,或使用无条件的先删除后创建流程,就会覆盖值班工程师的变更。相对于代理数小时前读取的状态,这个请求可能看起来正确;相对于线上区域,它却是错误的。
写入前重新读取可以防止这类故障。执行器读取 RRset,将其与操作中的 expected_rrset 比较,如果值班工程师设置的目标不同,就拒绝写入。拒绝记录应保留两个状态,并告诉审核者记录已被其他操作者修改。不要尝试合并。
合并 DNS 数据需要服务知识。两个地址可能代表有意的容量配置、临时重叠、区域拆分或紧急路由。代理不能仅凭部署文件中只出现一个地址,就安全地推断另一个地址应该被删除。
部分失败后也适用同一规则。如果服务商报告超时,执行器必须在重试前读取服务商的权威状态。写入可能已经成功,只是客户端没有收到响应。盲目重试会在某些 API 上创建重复值,在另一些 API 上产生不需要的替换行为。
日志必须保留审核者真正批准的内容
DNS 审计记录不能只有一条说明记录发生变化的服务商活动事件。应保存建议操作、执行前观测到的状态、审批决定、发起请求的进程、服务商响应和验证结果。保留足够的信息,才能还原执行器是否遵循了获批请求。
防篡改日志还能提供一个有价值的特性:调查事故的人可以检查历史是否在事后被修改。当 DNS 答案已经从缓存中过期,人们开始依赖记忆或截图时,这一点尤其重要。即使没有原始代理对话,证据也应保持可理解。
将运行记录与调用记录分开。运行记录回答哪个代理进程在何时获得权限,以及权限何时结束。调用记录回答它请求了哪一项准确的 DNS 操作、是否有人批准,以及结果如何。立即撤销一次运行可以阻止之后的调用,但不能撤销服务商已经接受的 DNS 写入。审计轨迹应清楚记录这条边界。
在审批卡片和执行记录中都使用操作 ID。如果操作者看到错误结果,应能通过操作 ID 找到准确获批的删除或编辑,而不必在无法区分的代理输出流中搜索。该 ID 应连接请求、状态比较、服务商响应和验证命令。
第一项实际改动很小:在审批边界禁止通用 DNS 写入。强制每个请求归入创建、编辑或删除之一,要求编辑和删除提供完整的预期 RRset,并在执行前拒绝状态漂移。仅这一项约束,就能让代理提出的 DNS 工作足够清晰,使人可以发现真正重要的错误。
常见问题
可以让 AI 代理修改生产 DNS 吗?
不应该。代理可以准备记录变更方案并收集证据,但任何可能改变公网流量、邮件投递、身份检查或服务归属的操作,都应由人工审批。影响较小的沙箱区域可以采用更精简的流程,但必须与生产环境的委派关系隔离。
代理什么时候可以删除 DNS 记录?
只有在目标状态确实是删除,并且请求明确写出要删除的完整所有者名称和记录类型时,才应使用 delete 操作。如果需要先迁移流量,应先创建或编辑替代记录,验证解析结果,等待适用的缓存窗口过去,再单独提交删除操作。
编辑 DNS 记录和删除 DNS 记录有什么区别?
编辑操作会把已知的 RRset 替换为明确写出的目标状态。删除操作则会移除 RRset 或其中一条记录,不提供替代状态。把两者都当成普通更新,会掩盖审核者究竟是在批准一次迁移,还是在批准一次中断。
降低 TTL 会让 DNS 变更更安全吗?
TTL 不会让记录变更变得安全。它只告诉递归解析器可以保留答案多长时间,而权威服务器可能会立即显示新状态。较短的 TTL 可以减少一种延迟,但无法防止目标地址错误或委派关系损坏。
审核 DNS 变更请求时应检查什么?
检查完整的所有者名称、记录类型、当前值、建议值、TTL、区域、环境和变更原因。对于应用使用的记录,还要检查目标是否能解析、服务所有者是否批准,以及该名称下是否存在冲突记录。
如何限制 AI 代理的 DNS 权限?
不要给代理一个可以管理所有区域的宽泛服务商令牌。应提供受约束的操作路径,要求声明区域和精确的记录操作,并在写入前重新读取当前 RRset。凭据应留在代理进程之外。
DNS 更新的前置条件是什么?
RFC 2136 为 DNS UPDATE 消息定义了前置条件,只有满足这些条件,服务器才会应用变更。即使服务商只提供 HTTP API,也应采用同样的思路:在写入前立即将观测到的状态与预期状态比较,发现不同就停止。
AI 代理可以安全地添加 CNAME 记录吗?
CNAME 所在的所有者名称通常不能同时存放其他普通 DNS 数据,因此新建 CNAME 可能与现有的 A、AAAA、MX、TXT 或其他类型记录冲突。操作规划器应在提出 CNAME 前读取该名称下的全部记录,而不是只查找现有的 CNAME。
DNS 差异内容足够用于审批吗?
原始区域差异可以提供帮助,但通常不会显示意图、记录范围、缓存影响和依赖检查。审核者需要差异内容,还需要一段清晰说明,表明代理将创建、替换还是删除 RRset,以及服务行为应发生什么变化。
DNS 自动化应保留什么审计记录?
保存建议执行的操作、写入前观测到的状态、操作者身份、审批决定、服务商响应和验证结果。防篡改记录很重要,因为随着缓存变化,DNS 事故会变得难以还原,团队也可能忘记自己批准了什么。