lolspider / notes
Clipping · 9 分钟

Agent 的 Sandbox 到底是怎么实现的?

从 Codex、Claude Code、Gemini CLI 到 OpenHands很多人讨论 AI Agent 安全时,关注的是:“模型会不会执行危险命令?”但真正重要的问题其实是:即使 Agent 决定执行危险命令,底层系统是否允许它成功?这就是 Sandbox(沙箱)存在的...

x.com @Smartpigai

📝 摘要

本文系统梳理了 AI Agent 沙箱(Sandbox)的实现原理,先厘清了三个常被混淆的概念——Sandbox(技术访问边界)、Permission/Approval(执行前审批)和 Tool Policy(工具调用限制),指出成熟 Agent 系统需三层协同。随后深入分析了四种主流实现:Codex(本地用系统原生沙箱 Seatbelt/Bubblewrap,云端用隔离容器+两阶段信任模型)、Claude Code(默认仅隔离 Bash,可通过 sandbox-runtime/Dev Container/VM 扩展为全进程隔离)、Gemini CLI(可替换 Provider 架构支持 Seatbelt/Docker/gVisor/LXC 等多种后端+工具级沙箱)、OpenHands(将沙箱作为 Agent Runtime 基础设施)。文章总结了四条实现路线的优劣,并给出了按场景选择沙箱强度的决策指南和八条评估沙箱的关键问题。

核心概念: 真正的 Sandbox 不是系统提示或确认框,而是由操作系统/容器运行时/网络代理/虚拟机强制执行的边界——模型可以提出任何操作,但执行环境只允许它触碰被授权的资源。

关键技术栈 / 关键要点:

  1. 三层安全模型 — Tool Policy 决定能调用什么,Approval 决定何时暂停,Sandbox 决定命令最终能触碰什么
  2. Codex 两阶段信任模型 — Setup 阶段可联网+使用 Secrets,Agent 阶段默认离线并移除 Secrets,减少长期凭据暴露
  3. Claude Code 分层隔离 — 默认 Bash-only 沙箱,需 sandbox-runtime/Dev Container 才能包住 MCP、Hooks 等全进程
  4. Gemini CLI Provider 可替换 — 沙箱强度取决于选择的 Provider(Seatbelt vs Docker vs gVisor),"开启了 Sandbox"≠强隔离
  5. OpenHands 沙箱即运行时 — Agent Server 运行在容器内部,Sandbox 是整个 Agent Runtime 的基础设施
  6. 评估沙箱的八条关键问题 — 隔离粒度、文件边界、网络默认态、MCP/Hooks 是否在边界内、Secrets 可见性、越权批准机制、任务独立销毁、容器暴露面

关键结论 / 实践建议: Agent 越强,安全越不能依赖"模型不犯错",而应假设模型一定犯错、第三方内容可能包含 Prompt Injection,然后确保错误无法越过执行边界。可信代码用 OS Sandbox,无人值守用临时容器+短命凭据,陌生代码用 gVisor/MicroVM,操作生产环境用权限代理签发短期单用途凭据。


从 Codex、Claude Code、Gemini CLI 到 OpenHands

很多人讨论 AI Agent 安全时,关注的是:

“模型会不会执行危险命令?”

但真正重要的问题其实是:

即使 Agent 决定执行危险命令,底层系统是否允许它成功?

这就是 Sandbox(沙箱)存在的意义。

Sandbox 不是一句“请不要删除文件”的系统提示,也不只是执行命令前弹出一个确认框。真正的 Sandbox 是由操作系统、容器运行时、网络代理或虚拟机强制执行的边界。

模型可以提出任何操作,但执行环境只允许它触碰被授权的资源。

先区分三个经常被混淆的概念

1. Sandbox

回答的是:

这条命令在技术上能够访问什么?

例如:

  • 能读哪些文件
  • 能写哪些目录
  • 能不能访问网络
  • 能不能启动子进程
  • 能不能看到密钥和环境变量

2. Permission / Approval

回答的是:

执行之前,要不要询问用户?

确认框本身不是安全边界。如果用户批准后,命令就在宿主机上获得完整权限,那么它依然不是真正的隔离。

3. Tool Policy

回答的是:

Agent 能不能调用某个工具?

例如禁止 Shell、允许 Git、限制 MCP 工具或者只允许访问特定域名。

它通常由 Agent 框架执行,安全强度取决于框架本身是否可能被绕过。

一个成熟的 Agent 系统,通常会同时使用这三层:

Tool Policy 决定能调用什么,Approval 决定何时暂停,Sandbox 决定命令最终能触碰什么。

一、Codex:本地使用系统原生沙箱,云端使用隔离容器

Codex 本地运行时采用的是“宿主机原生隔离”路线。

不同系统使用不同机制:

  • macOS:Seatbelt Sandbox
  • Linux 和 WSL2:Bubblewrap
  • Windows:受限 Token、ACL 等原生隔离机制

默认的 workspace-write 模式允许 Agent 读取文件、修改当前工作区并执行项目命令,但不能随意写入工作区之外,命令网络访问默认也处于关闭状态。

如果 Agent 需要访问工作区之外的目录或使用网络,就进入独立的 Approval 流程。

因此,Codex 的设计重点不是“每条命令都询问用户”,而是:

让工作区内的低风险操作自动执行,把越界行为交给审批。

Codex Cloud 则是另一种实现。

每个任务运行在 OpenAI 管理的隔离容器中,不会继承用户电脑上的文件、进程和本地登录状态。它还采用两阶段运行方式:

  1. Setup 阶段可以联网安装依赖,并使用配置的 Secrets。
  2. Agent 阶段默认离线,Setup Secrets 会在进入这一阶段前被移除。

这个设计很值得注意:它把“准备构建环境”和“模型自主执行代码”拆成了两个不同的信任阶段,从而减少长期凭据暴露给 Agent 的机会。

参考:Codex Sandbox、Agent approvals & security

二、Claude Code:默认只隔离 Bash,也可以包住整个进程

Claude Code 的实现有一个非常容易被忽略的细节:

内置 Sandbox 默认主要保护 Bash 命令及其子进程,并不天然包住整个 Claude Code。

它在:

  • macOS 使用 Seatbelt
  • Linux、WSL2 使用 Bubblewrap
  • 网络访问通过代理和域名规则进行限制

Sandbox 中的 Bash 默认只能写当前工作目录和临时目录。因为限制由操作系统执行,所以 Bash 启动的 npm、Python、Git 和其他子进程也会继承相同边界。

但是,Claude Code 内置的 Read、Edit、WebFetch 工具仍然运行在主进程中,由 Permission Rules 控制;MCP Server 和 Hooks 也可能作为宿主机上的独立进程运行。

所以,只打开 /sandbox,并不代表整个 Claude Code 会话都被关进了一个完整容器。

Anthropic 为此提供了几种更强的方案:

  • @anthropic-ai/sandbox-runtime:用 Seatbelt 或 Bubblewrap 包住整个 Claude Code 进程
  • Dev Container
  • 自定义 Docker/OCI 容器
  • 独立虚拟机或 MicroVM

这是一种“分层组合”的路线:日常开发只隔离高风险 Shell,完全无人值守时再把整个 Agent、MCP 和 Hooks 放入更大的隔离边界。

参考:Claude Code sandbox environments、Sandboxed Bash

三、Gemini CLI:把 Sandbox 做成可替换的 Provider

Gemini CLI 的特点是选择非常多。

目前可以配置:

  • macOS Seatbelt
  • Docker
  • Podman
  • Windows Native Sandbox
  • gVisor / runsc
  • LXC / LXD

使用 Docker 或 Podman 时,Gemini CLI 会启动一个容器,并把当前项目目录挂载到容器中的相同绝对路径。Agent 可以正常修改项目,但与宿主机其他部分隔离。

如果选择 gVisor,容器的系统调用还会经过用户态内核处理。相比普通容器,它可以进一步减少容器直接接触宿主机内核攻击面的机会。

Gemini CLI 还支持 Tool-level Sandboxing:只隔离 shell_exec、write_file 等具体工具,而不是把整个 CLI 放进 Sandbox。

当命令因为目录或网络限制失败时,它可以发起一次 Sandbox Expansion Request,让用户只为当前命令临时扩大权限。

这种设计的优势是灵活,缺点也是灵活:

“Gemini CLI 开启了 Sandbox”并不能直接说明它的隔离强度。

Seatbelt 的宽松配置、普通 Docker 和 gVisor 的安全边界并不相同。真正需要看的,是当前选择的 Provider、挂载目录、网络配置和扩权规则。

参考:Gemini CLI Sandboxing

四、OpenHands:把 Sandbox 当成 Agent 的运行时

OpenHands 更接近一个完整的 Agent 执行平台。

它支持三类 Sandbox Provider:

  • Docker Sandbox:推荐方案
  • Process Sandbox:直接作为本地进程运行,速度快但没有容器隔离
  • Remote Sandbox:在远端环境中运行

Docker 模式下,Agent Server 会运行在容器内部。OpenHands 后端通过 API 把“执行命令、修改文件”等 Action 发送给容器里的执行服务,再接收执行结果。

也就是说,Sandbox 不只是给某条 Shell 命令套一层限制,而是整个 Agent Runtime 的基础设施。

这种架构比较适合:

  • 多用户并发
  • 远程 Agent
  • 批量评测
  • 可复现构建环境
  • 每个任务使用独立执行空间

但 Docker 并不自动等于绝对安全。

如果把 Docker Socket、宿主机敏感目录或云凭据挂载进容器,Agent 仍然可能跨越原本的边界。普通容器通常还与宿主机共享内核,因此高风险场景可能需要 gVisor、Sysbox、MicroVM 或独立虚拟机作为更外层的隔离。

参考:OpenHands Sandbox Providers、Runtime Architecture

四种实现路线,本质上分别在优化什么?

系统原生 Sandbox

代表:Codex Local、Claude Code Bash Sandbox。

优点是启动快、资源开销低、和本地开发环境结合自然。

适合可信项目中的日常开发。

工具级 Sandbox

代表:Gemini CLI Tool Sandboxing、Claude Code 的 Bash-only 隔离。

只约束最危险的工具,用户体验更顺滑,但必须确认文件工具、MCP、Hooks 和浏览器是否属于同一个安全边界。

容器 Runtime

代表:Codex Cloud、OpenHands Docker、Gemini Docker。

环境一致,容易销毁和并行扩展,也适合无人值守任务。但安全性高度取决于挂载、Capabilities、网络出口、容器用户和凭据管理。

VM、MicroVM 或用户态内核

代表:gVisor、Firecracker、独立云主机等部署方式。

隔离更强,适合运行未知仓库和不可信代码,代价是启动延迟、资源消耗和运维复杂度更高。

如何选择?

如果是可信代码库的日常开发:

OS Sandbox + 仅工作区可写 + 默认禁止命令联网。

如果需要 Agent 长时间无人值守:

临时容器 + 网络 Allowlist + 短期凭据 + 完成后销毁环境。

如果要运行陌生仓库、安装未知依赖:

gVisor、MicroVM 或独立 VM,不要只依赖权限弹窗。

如果 Agent 可以操作云平台、数据库或生产环境:

不要把长期管理员凭据直接交给 Agent。使用权限代理签发短期、单用途凭据,并在执行层限制目标、操作类型和有效时间。

最后一个判断标准

评价一个 Agent 的 Sandbox,不要只问:

“它是不是用了 Docker?”

而要问下面这些问题:

  1. 隔离的是单条命令,还是整个 Agent 进程?
  2. 文件读取和写入边界分别是什么?
  3. 网络默认关闭,还是默认开放?
  4. MCP、Hooks、浏览器和插件是否在同一边界内?
  5. Agent 能看到哪些 Secrets?
  6. 越权请求由谁批准,批准持续多久?
  7. Sandbox 是否每个任务独立,并在完成后销毁?
  8. 容器是否暴露了 Docker Socket、宿主机目录或生产身份?

Agent 越强,安全的核心就越不能依赖“模型不要犯错”。

真正可靠的设计是:

假设模型一定会犯错、第三方内容可能包含 Prompt Injection、依赖可能带有恶意代码,然后确保错误仍然无法越过执行边界。

未来 Agent 的竞争,不只是谁写代码更快。

还包括谁能在拥有更大自主权的同时,把每一次行动限制在更小、更清晰、可验证的边界内。