给 Claude Code 加沙箱:与其每次求批准,不如约束环境
会写代码、会跑代码的 agent,在你不用盯着它时最有用。但 agent 跑的每条命令也都是风险:一个手滑的
rm、一个意料之外的网络调用、一段读了它不该读的密钥的脚本。
我们管理这类风险的常规办法,是让用户批准动作——但批准这件事不可扩展,而且会把人训练成「不假思索点 yes」。
沙箱(sandboxing)给出另一条路:与其为每个动作求权限,不如约束环境本身,让即便一个不受限的 agent 也造不成真正的破坏。这篇讲我们怎么把 OS 级沙箱带进 Claude Code。
两道边界:文件系统与网络
一个有用的沙箱要管两件事:agent 能碰什么,以及 agent 能够到什么。
| 边界 | 管什么 | 效果 |
|---|---|---|
| 文件系统隔离 | 进程能读写哪些文件和目录 | agent 在项目目录里自由干活,但读不到你的 SSH 密钥、也写不到工作区之外 |
| 网络隔离 | 进程能跟哪些主机通信 | agent 够得到它需要的包管理仓库,但没法把数据导出到任意服务器 |
这两道边界合起来,能兜住绝大多数可能出岔子的情况。一条命令既逃不出项目目录、又没法往家里打电话,它能造成的破坏就被狠狠地限制住了。
为什么这比批准弹窗强
批准弹窗是为每个潜在危险动作都塞一个人进环里。听着安全,其实有两个问题:
- 不可扩展 —— 一个真在干活的 agent 会产生太多太多弹窗。
- 更糟的是会「习惯化」(habituation) —— 当你被要求批准第一百条无害命令时,你已经不读了,反射性地就批了。那个本该保护你的弹窗,变成了噪音。
沙箱把这个模型翻了过来。因为环境本身是安全的,agent 可以在里面完全不弹窗地自由跑。你把人的判断留给那个真正需要跨边界的罕见动作,而不是花在常规操作上。
我们怎么做的
沙箱的底层原语各操作系统不同,所以我们是在各平台的原生设施上搭的:
| 平台 | 用的机制 |
|---|---|
| macOS | Seatbelt(sandbox-exec)定义文件系统与网络策略 |
| Linux | namespaces + seccomp-bpf 过滤器实现等价隔离 |
我们把这些打包成了一个开源库,所以同一套沙箱原语在 Claude Code 之外也能用。设计目标是:开沙箱这件事,不该让 agent 或用户去操心机制——你声明什么被允许,操作系统负责强制执行。
网络规则靠代理来管
文件系统规则相对好表达,就是一串路径白名单。网络规则更刁钻:你通常不想要「全部放行」或「全部禁止」,你要的是「这些主机,行;其余一切,不行」。
为此,沙箱里的网络流量会被路由经过一个代理(proxy),由它强制执行一份主机白名单。发往已批准域名(比如你的包管理仓库)的请求放过去;发往未知主机的请求被拦下。这样你就拿到了对出站流量(egress)的细粒度控制,而不必提前把每个 IP 地址都预测出来。
沙箱与权限一起用
沙箱不是要取代权限系统——而是补充它。有了沙箱,信任模型就变了:
- 在沙箱边界之内,动作天然就是安全的,Claude Code 可以不弹窗直接推进。
- 会跨出边界的动作——往工作区外写、够一个不在白名单上的主机——才是值得弹窗的那些。
正是这一点,让 agent 能长段自主地干活,又不牺牲安全。沙箱处理常规,人处理例外。
总结
沙箱运行时是开源的,已经集成进 Claude Code。如果你自己造 agent,也可以用同一个库给它们一个安全的行动环境。
我们认为这是代理式编码(agentic coding)未来的关键一环:不是要求 agent 完美可信,而是搭出一种它不必完美可信也无妨的环境。