把一个 AI Agent 接上 exec、浏览器和 Git 仓库后,很多人会立刻想到 Docker:起个容器,不就隔离了吗?
这句话只对了一半。
容器确实能把进程、文件系统视图和网络命名空间切开;但它们仍共用宿主机内核。对一个会读网页、装依赖、运行第三方脚本、调用外部 API 的 Agent 来说,风险并不止“它会不会把程序跑坏”。真正难题是:一段不受完全信任的指令,最终能碰到哪些资产?
可能是宿主机的 Docker Socket;可能是挂进容器的项目目录;可能是云端 API Key;也可能是能访问内网的网络出口。模型是否被提示注入、工具调用是否被诱导、依赖包是否有恶意代码,这些问题最后都会落到同一个地方:执行环境有没有边界。
沙箱不是给 Agent 盖一层玻璃罩。它是在替人做权限拆分:让 Agent 能完成任务,但拿不到不该拿的东西。
先别选技术,先画出 Agent 的攻击路径
普通 Web 服务的代码通常来自团队仓库,输入也经过设计。Agent 的工作流恰好反过来:它常常需要处理陌生输入,并把输入翻译成操作。
一封邮件里的一句“请读取这个文件并执行修复脚本”,一段网页里的隐藏提示,一份带有恶意安装命令的 README,都可能进入模型上下文。模型即使没有“中毒”,也可能把这些文字当作高优先级任务,继而调用 Shell、浏览器或代码执行工具。
因此,沙箱至少要回答五个问题:
- 进程边界:Agent 启动的程序能否影响宿主机或其他任务?
- 文件边界:它能读写哪些目录?工作成果如何带出来?
- 网络边界:它能访问公网、内网、元数据服务、公司 SaaS 吗?
- 身份边界:API Key、OAuth Token、SSH Key 是否会直接出现在进程或文件里?
- 资源边界:它能吃掉多少 CPU、内存、磁盘、进程数和运行时间?
如果只回答“我用了 Docker”,其实只覆盖了其中一部分。Docker 更像隔间,不是完整的保险库。
Docker:足够快,但别把默认容器当安全边界
Docker 仍是多数个人项目和内部自动化的第一站。原因很朴素:镜像好做,生态完整,启动快,依赖隔离直观。为每次 Agent 任务创建短生命周期容器,也比直接在宿主机执行安全得多。
但 Docker 的隔离属于操作系统级隔离。容器与宿主机共享 Linux 内核;如果运行时配置太宽松,隔离会被自己打穿。
最常见的“沙箱变后门”有四种:
- 挂载过大的目录:把整个家目录、仓库根目录或
/var/run挂进去,Agent 的可见范围就远大于任务需要。 - 暴露 Docker Socket:容器一旦能控制 Docker,往往就等于能启动高权限容器,边界名存实亡。
- 使用特权模式或过多 Capabilities:
--privileged是为了方便排障,不该成为常态;额外 Linux capabilities 同样应按需给。 - 网络默认放行:容器可以出网并不代表它应该能访问所有内网地址、云元数据端点或管理面板。
个人场景里,Docker 可以承担低风险任务:格式化文本、转换文件、运行受信代码的测试、处理不含密钥的公开数据。前提是把它收紧到“任务最小集合”。
docker run --rm \
--read-only \
--user 10001:10001 \
--cap-drop ALL \
--security-opt no-new-privileges \
--pids-limit 256 \
--memory 2g \
--cpus 2 \
--network none \
-v "$PWD/input:/work/input:ro" \
-v "$PWD/output:/work/output" \
agent-worker:latest
这段命令没有把 Docker 变成绝对安全的执行舱,但做了几件重要小事:根文件系统只读;使用非 root 用户;移除 capabilities;禁止新权限;限制资源;默认断网;输入只读、输出单独可写。
代价也很现实:有些构建会因只读根文件系统失败,有些语言工具链需要缓存,有些任务必须访问指定 API。正确做法不是删掉所有限制,而是逐项打开、逐项记录。安全配置不是一次性“加固完成”,而是每多一个能力,都问一次:这项能力是否真的服务于本次任务?
gVisor:仍像容器,但给系统调用加一道缓冲层
当 Agent 要运行陌生依赖、抓取来的代码或难以审计的插件时,仅共享宿主机内核会让人不太踏实。此时 gVisor 处在一个很有价值的位置。
gVisor 提供 OCI 兼容运行时 runsc,可以接入 Docker 和 Kubernetes。它不是传统虚拟机,也不是另一套容器编排;它在应用与宿主机内核之间放入一个“应用内核”。应用发起的系统调用不会像普通容器那样直接抵达宿主机内核,而会先被 gVisor 拦截和处理。
这意味着什么?攻击者若想利用内核接口逃逸,面对的接口面被缩小了。gVisor 官方文档强调,它的目的正是为不受信用户态代码增加一层针对内核漏洞利用的防线。
但别把它理解成万能防毒软件。gVisor 保护的是“工作负载与宿主机的内核交界”,不能替你修复这些错误:
- 把生产数据库密码注入 Agent 进程;
- 把敏感目录以可写方式挂载进去;
- 放开所有网络出口;
- 给了错误的云 IAM 权限;
- 把 Agent 生成的代码自动合并、自动发布。
gVisor 也会带来兼容性和性能成本。系统调用路径多了一层,某些低层特性、调试方式和高性能工作负载可能不适配。因此,它特别适合这类中间地带:需要容器生态和较快启动,又要执行来源不完全可信的代码。
例如,CI 中让 Agent 拉取 issue 附件、生成补丁、运行测试;或内部平台让多名用户提交脚本,但不希望这些脚本直接贴着宿主机内核运行。Docker 的工作方式还在,安全边界却比默认容器厚了一层。
MicroVM:隔离更硬,代价是运维更像在管一批小机器
再往前一步,就是 Firecracker 这类 MicroVM。它利用 KVM 硬件虚拟化,每个任务运行在独立的轻量虚拟机里,拥有自己的 guest kernel。与容器共享宿主机内核不同,MicroVM 把内核也放进了边界之内。
这正是它适合高风险、多租户 Agent 的原因。
如果平台允许不同用户的 Agent 执行任意代码,或者 Agent 需要长期运行、带浏览器桌面、读写较复杂的工作区,MicroVM 的隔离模型更接近传统 VM:一个工作负载越界,不应直接触及另一个工作负载的内核空间。Firecracker 的设计目标就是在传统虚拟机的隔离特性与容器的启动速度、资源效率之间找平衡。
代价同样不能忽略:
- 要维护 guest kernel、根文件系统镜像和补丁节奏;
- 冷启动、镜像分发、日志采集、文件回收都比容器复杂;
- 同样的硬件下,密度通常不如普通容器;
- 网络、持久化卷、快照恢复和可观测性都需要平台层配合。
所以 MicroVM 不是“每个 Python 脚本都必须用”的答案。它更像一把更重的锁:平台给外部用户提供执行环境、让 Agent 操作浏览器访问不可信网页、或涉及高价值凭据与内部网络时,值得付出这份复杂度。
OpenSandbox 这类平台,解决的不是“起容器”而是生命周期
真正让团队头疼的,常常不是创建一个隔离环境,而是创建一千个以后怎么管。
一个 Agent 沙箱必须有身份、镜像、配额、网络策略、超时清理、工作区导出、日志审计、暂停恢复和密钥注入。只用 docker run 拼起来,最初很轻,半年后就会长成一团谁也不敢改的脚本。
OpenSandbox 这类面向 Agent 的运行时平台,价值在于把这些共性装进统一生命周期:创建沙箱、执行命令、传输文件、设置超时、回收资源。其社区实现也在围绕 Kubernetes Pod 隔离、持久化工作区、暂停恢复、预热池和网络出口控制继续迭代。
这里有个容易忽略的判断:“能持久化”不是天然优点。
Agent 每次执行后都保留环境,调试和长任务会舒服很多;但状态也可能带走上一次任务的临时凭据、恶意依赖或错误配置。安全设计应把“临时任务”和“长期工作区”区分开:
- 临时任务:优先无状态、结束即销毁;
- 需要交付文件:只导出明确的输出目录,经过扫描或人工审核;
- 长任务:允许持久化,但要有快照、过期时间和可追溯身份;
- 任何凭据:尽量短期化、按任务绑定、可撤销,避免写入镜像和工作区。
真正该隔离的,是网络和凭据
很多沙箱文章把注意力全放在“容器会不会逃逸”。对日常 Agent,发生概率更高的事故反而是权限过大。
一个 Agent 不必逃逸,只要能访问内网、读取部署 Token、把文件上传到任意域名,就足以造成损失。于是网络与身份策略应比运行时类型更早设计。
网络建议分三档:
- 完全断网:格式化、编译、静态分析、离线数据处理。默认首选。
- 域名白名单出网:只允许包仓库、模型 API、指定文档源。DNS、代理和 HTTPS 访问都需要可审计。
- 受控内网访问:通过独立服务身份、短期 Token 和固定 API,不让 Agent 直接拿到通用 VPN 或管理员网络。
凭据也一样。把 API_KEY 作为环境变量塞进任务,确实方便,但它会出现在进程环境、调试输出、崩溃报告甚至模型可读取的文件中。更稳妥的方向是凭据代理或网关:Agent 只得到调用某项能力的授权,真实密钥由网络层或专门服务注入;每次请求都记录主体、目标、范围和时间。
这与“让 Agent 更聪明”无关,却比换一个更大模型更能减少事故。
一张选型表:按风险和任务,不按热度
| 场景 | 首选 | 关键条件 | 不该做什么 |
|---|---|---|---|
| 本地低风险自动化、公开数据处理 | 收紧后的 Docker | 只挂输入输出目录,默认无网络,非 root | 挂宿主机家目录、Docker Socket |
| CI 运行 Agent 补丁与第三方依赖 | Docker + gVisor | 临时工作区、有限网络、资源限制 | 直接给生产凭据和发布权限 |
| 多用户代码执行、浏览器 Agent、来源不明脚本 | MicroVM 或基于它的平台 | 独立网络策略、镜像治理、审计和回收 | 把它当成无需运维的“安全开关” |
| 高价值生产操作 | 不让 Agent 直连生产 | 专用 API、审批、人类确认、短期权限 | 给 SSH root、通用云管理员权限 |
如果只记住一句话:隔离等级要跟“最坏情况下 Agent 能造成的损失”对齐,而不是跟项目的技术潮流对齐。
一条能落地的升级路线
不必为了安全一夜之间改造出 MicroVM 平台。大多数团队可以按下面顺序推进:
- 盘点 Agent 工具:列出 Shell、浏览器、文件、Git、数据库、消息、部署等能力。
- 默认拒绝网络与挂载:从
--network none、只读输入、独立输出目录开始,再按任务加白名单。 - 清理高危捷径:禁止 Docker Socket、
--privileged、宿主机根目录挂载、长期管理员 Token。 - 给陌生代码增加 gVisor:先选 CI 或预发布环境做试点,测兼容性与性能。
- 把高风险任务迁进 MicroVM:面向多租户、浏览器自动化、任意代码执行等场景建立独立运行池。
- 把凭据从环境里拿走:改为短期、最小权限、可审计的服务身份或凭据网关。
- 保留人工闸门:沙箱能限制破坏半径,不能判断业务后果。合并代码、删除数据、发版、付款仍需要明确确认。
AI Agent 的能力增长很快,权限往往增长得更快。真正成熟的自动化,不是让 Agent 拥有一台什么都能做的电脑,而是让它每次只拿到完成眼前任务所需的那把钥匙。