遏制框架及其盲点
遏制原语——沙箱、虚拟机、系统调用过滤器——非常擅长回答代码可以在哪里运行。它们定义了执行环境:哪些二进制文件可以执行、哪些文件可以读取、哪些内核调用被允许。对于任何运行生成代码或任意工具链的 Agent 来说,这都是必要的一层。然而,它并不是外部触及范围的模型。
一个位于完美遏制沙箱内的 Agent 仍然可以向数千个不同目的地发出数千个出站请求。沙箱并不关心进程是与一个主机通信还是与一万个主机通信;它关心的是进程不会逃逸出盒子。我们不断遇到的故障模式不是逃逸,而是触及范围。
考虑一个被遏制的 Agent,它通过检索文档中的提示注入载荷被攻陷。该 Agent 无法突破系统调用过滤器。它无法读取主机文件系统。然而,它可以利用被授予的网络访问权限和被授予的凭证来枚举内部端点、以小片段方式外泄上下文,或探测第三方 API,直到某个 API 返回有用数据。遏制依然成立。爆炸半径仍然很大。
围绕 Agent 安全的行业讨论在很大程度上继承了容器安全的威胁模型,其中边界是主机。对于 Agent 而言,边界是 Agent 能够影响的外部身份、端点和数据流的集合。该集合不受沙箱约束。它受出口策略约束,而没有成本模型的出口策略只不过是一份允许主机的列表。
出口作为预算资源
我们将出口视为一种预算资源,其维度按每次出站操作进行扣减。我们发现有用的维度包括:

- 不同目的地。 联系到的唯一网络端点数量,而不是调用次数。对同一主机的一万次调用与对一万个主机的十次调用具有不同的风险特征。目的地计数必须跨 DNS、IP 和端口进行归一化,并且必须将重定向视为新目的地。
- 出站字节数。 离开进程的载荷量,包括头部、查询字符串和请求体。这可以捕获低于调用次数阈值的慢速外泄。
- 凭证范围。 出口可以出示的身份和令牌集合。允许宽泛令牌访问狭窄目的地的预算仍然是宽泛预算。
- 时间窗口。 预算有效的间隔。每秒刷新的预算不是预算。
执行应位于网络边界,而不是 Agent 框架中。框架级检查是建议性的:它们依赖于 Agent 的配合、工具包装器被正确使用,以及没有代码路径绕过包装器。网络边界——代理、服务网格边车、出口接口上的 eBPF 钩子——无论由哪个库发起,都能看到每个数据包。Agent 框架可以请求预算;边界负责执行它。
边界还必须发出终止状态。当预算耗尽时,运行不会静默降级或重试。它会以定义的恢复路径终止:操作员收到通知,运行被标记为预算耗尽,人类可以授权一次具有明确新预算的新运行。这使得耗尽成为一等结果,而不是被吞掉的错误。
设计规则
每个离开进程的工具调用都必须扣减预算。 这包括看起来是内部的调用、使用缓存 DNS 条目的调用,以及由依赖项而非 Agent 自身代码发出的调用。如果字节离开了进程边界,它就是出口。扣减与请求是原子的;如果扣减失败,请求就不会离开。
预算是按运行和按身份的,并且不会结转。 一次运行是一个具有定义开始和结束的单一逻辑任务。预算属于该运行,而不属于 Agent 进程、用户会话或部署。身份是单独限定的:代表一个用户行动的 Agent 不会继承另一个用户的预算。未使用的预算不会结转到下一次运行,因为结转会产生积累触及范围的激励。
记录扣减、目的地类别和决策,而不仅仅是请求。 仅记录 URL 的日志行不足以进行事后分析。我们记录预算扣减量、目的地类别(例如:内部服务、第三方 API、公共网络、未知)、剩余预算,以及允许或拒绝请求的策略决
](/media/server-rack-closeup-3b0bfb-b5562f.jpg)
我们使用过的一种实现是在 Agent 沙箱和网络之间放置一个正向代理。代理持有一个按运行标识符和身份键控的每次运行预算表。在每个请求上,代理解析目的地、对其进行分类、根据请求头和请求体计算字节成本,并尝试在所有维度上对运行的预算进行原子递减。如果任何维度将变为负数,代理会向 Agent 返回拒绝,并向控制平面发出预算耗尽事件。控制平面将运行转换为终止状态并通知操作员。
Agent 永远看不到预算表。它看到的是拒绝,必须将其作为不可重试的条件处理。这是有意为之:Agent 不应能够推理其剩余触及范围,因为能够推理其剩余触及范围的 Agent 可以围绕它进行规划。
诚实的权衡
预算执行会增加延迟和操作面。每个出站请求都会产生一次代理跳转、一个分类步骤和一次状态更新。对于对单个已知端点进行高频调用的 Agent,这是收益甚微的开销;预算实际上是静态的。对于分散到许多目的地的 Agent,开销正是重点。
更困难的权衡是误报。需要联系新目的地类别的合法运行将触及预算并终止。操作员必须能够快速授予新预算,并且授予必须与原始预算一样严格地记录。如果恢复路径很慢,团队将扩大默认预算以避免中断,控制就变得装饰性。预算的好坏取决于恢复路径。
遏制仍然是必要的。但它并不充分。一个能够在沙箱内触及一万个目的地的 Agent,对于它能够触及的数据而言,在任何有意义的层面上都不算被遏制。边界定义了它在哪里运行。预算定义了它能触及多远。两者都必须存在,并且第二个必须在 Agent 无法争辩的地方执行。
封面照片:Brett Sayles / Pexels。
