
说到"给 Agent 加沙箱",多数人想的是"让它跑在容器里"。但沙箱只解决了一个边界,还有两个常被漏掉。
完整的隔离要分三层:
三层里,出事故概率最高的是第三层——因为前两层是基础设施团队在管,而凭据往往是业务代码自己拼的。
四档,成本递增:
档位 | 隔离强度 | 适用 |
|---|---|---|
进程级(子进程 + 限制用户) | 弱 | 只跑自己写的代码 |
容器级 | 中 | 跑模板化、可控的代码 |
microVM / gVisor 类 | 较强 | 跑模型生成的任意代码 |
独立主机 / 独立账号 | 强 | 跑不可信第三方代码 |
判断标准只有一句:这段代码是谁写的? 如果是你自己写的规则脚本,进程级隔离够;如果内容是模型现场生成的,至少要到 microVM 档——因为模型生成的代码,你无法预见它会不会去 curl 一个内网地址。
最常犯的错是:用容器隔离就以为安全了,但容器共享内核,且默认网络是通的。
不管哪一档,这三条都得有:
一、网络默认关闭或白名单。 不是"禁止访问内网",而是默认就没有出口,需要时才开具体目标。差别在于默认值——白名单模式下漏配只是功能不可用,黑名单模式下漏配就是漏洞。
二、文件系统只读 + 一个临时可写目录。 挂载点要显式列举,别继承宿主目录。
三、资源上限四项:CPU、内存、执行时长、输出大小。 最后一项最容易被忘——一段死循环打印的代码,会把你的日志系统和磁盘打满,这比 CPU 打满更难恢复。
这一条在工具层治理里提过,但它值得重复:模型的上下文会被写进日志、被回放、被评测数据集带走。凭据一旦进去,等于永久泄露到多个存储里。
正确形态是代理:
模型输出:{ tool: "query_order", args: { order_id: "..." } }
↓
执行层:按 (任务, 用户, 工具) 取一个短期凭据
↓
代理:带凭据调用真实接口,返回结果摘要模型只说"要做什么",凭据在执行层注入。模型全程不知道 token 长什么样。
代理不是简单转发,它要收窄四件事:
作用域:这个工具只需要 order:read,就不要给 order:*。按工具声明的最小 scope 发放。
时间:短期 token,有效期按任务时长算,一分钟就够就不要给一小时。长任务可以设置"续期"而不是"预发长有效期"。
主体:代表谁?是代表发起用户(impidersonate),还是代表系统账号?前者权限小但审计清晰,后者方便但等于给了服务账号的全部权限。默认应该代表用户。
次数:高危操作一次性凭据,用完即废。这样即使 token 泄露,也已经被消耗掉了。
四条里,作用域和主体是设计阶段定的,时间和次数是运行时能加的——后者更容易落地,建议先做。
"密钥不出域"很多人理解成"密钥不写进代码、不进上下文"。这不够,还有一条:沙箱里的进程也不能把数据带出去。
要堵的路径不止 HTTP:
169.254.169.254 这类链路本地地址是经典目标,能拿到实例凭据。沙箱里要显式禁止访问/proc:容器里能读到的环境变量往往比你以为的多,包括上层注入的配置这三条的共同点是:它们不是应用层能拦的,必须在网络与运行时层面做。
凭据代理天然是审计点,因为所有外部调用都从它过。要记的最小集合是:
时间、主体(哪个用户)、任务 ID、工具名、凭据的 scope、调用结果。
有了这份记录,"某个账号凌晨调了 300 次删除接口"这类事才能被发现。反过来,如果凭据是业务代码各自持有、各自直连,这条路就断了——你只能看到接口日志,看不到"是谁的 Agent 干的"。
不建议自建沙箱的内核级隔离。
自己写 seccomp 规则、自己调 namespace、自己管 cgroup,投入极大且很容易漏。这块已经有成熟的 runtime 可用,把工程资源投进去的边际收益很低。
真正该自己做的,是凭据收窄和出网控制——这两块没有通用方案,且恰好是出事概率最高的地方。把精力按这个顺序分配,安全收益大得多。
沙箱与凭据代理,做的是同一件事的两个面:把 Agent 的能力关进一个可枚举的盒子里。
盒子边界要能回答三个问题——它能执行什么、能看到什么、能代表谁。三个都答得出来,这套东西才算隔离;只答得出第一个,那只是换了个地方跑代码。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。