详解在 Linux 环境下用沙箱技术安全运行 AI Agent 的方法。对部署自主 Agent、防止恶意行为是必读的安全实践。
和许多开发者一样,我发现自己越来越多地使用 AI Agent 来协助软件开发。
我目前使用 Claude Code 的命令行界面,搭配 Opus 4.5(撰写时 Anthropic 的顶级模型)。我用它来将粗略的任务要求转化为详细的开发计划,然后实现这个计划。
默认情况下,Claude Code 每次都会询问是否可以读写文件和运行软件。这是明智的默认配置,但经过一段时间后会变得烦人。更糟的是,它会足够频繁地中断我,使得我无法在照顾它的同时并行做很多事情。
也有一个 --dangerously-skip-permissions(又名"YOLO")模式,它会愉快地运行任何东西而不询问。这可能存在风险(尽管我认识一些以这种方式运行它的人,至今都还没有毁掉他们的开发机器)。
标准解决方案是对 Agent 进行沙箱隔离 —— 要么在远程机器上(exe.dev、sprites.dev、daytona.io),要么在本地通过 Docker 或其他虚拟化机制。
Linux 上的一个轻量级替代方案是 bubblewrap,它使用 Linux 内核特性(如 cgroup 和用户命名空间)来限制(隔离)进程。
事实证明,bubblewrap 是轻量级沙箱隔离 AI Agent 的一个不错方案。以下是我个人对这样一个解决方案的需求:
模拟我常规的 Linux 开发机设置(我不想管理多个开发环境)
对当前项目以外的信息的访问权限最小化/无访问权限
仅对当前项目具有写入访问权限
可以直接操作项目的文件/文件夹,这样我可以轻松地从我的 IDE 检查或修改相同的文件,或自己运行代码
网络访问 —— 既要连接到 AI 提供商和搜索互联网,也要能够启动我可以连接到的服务器
Bubblewrap 和 Docker 不是加固的安全隔离机制,但我对此可以接受。我并不真正担心以下风险:
通过零日 Linux 内核漏洞进行逃逸
隐蔽的旁信道通信
从当前项目泄露数据(包括项目特定的访问密钥)
破坏代码库(代码由 git 管理并在 GitHub 或其他地方备份)
最后一点比较棘手,但即使是完整的远程沙箱也无法防止这种情况。理论上,我们可以拥有透明的 API 代理,在不让 AI Agent 意识到的情况下注入适当的访问密钥,但现在要正确设置这个真的很不平凡。
一个替代方案是通过创建项目特定的 API 密钥来遏制潜在的损失,这样至少如果这些密钥被泄露,影响区域会最小化。
以下是我的 bubblewrap 沙箱脚本看起来的样子:
#!/usr/bin/bash
exec 3<$HOME/.claude.json
exec /usr/bin/bwrap \
--tmpfs /tmp \
--dev /dev \
--proc /proc \
--hostname bubblewrap --unshare-uts \
--ro-bind /bin /bin \
--ro-bind /lib /lib \
--ro-bind /lib32 /lib32 \
--ro-bind /lib64 /lib64 \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/local/bin /usr/local/bin \
--ro-bind /usr/local/lib /usr/local/lib \
--ro-bind /opt/node/node-v22.11.0-linux-x64/ /opt/node/node-v22.11.0-linux-x64/ \
--ro-bind /etc/alternatives /etc/alternatives \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/profile.d /etc/profile.d \
--ro-bind /etc/bash_completion.d /etc/bash_completion.d \
--ro-bind /etc/ssl/certs /etc/ssl/certs \
--ro-bind /etc/ld.so.cache /etc/ld.so.cache \
--ro-bind /etc/ld.so.conf /etc/ld.so.conf \
--ro-bind /etc/ld.so.conf.d /etc/ld.so.conf.d \
--ro-bind /etc/localtime /etc/localtime \
--ro-bind /usr/share/terminfo /usr/share/terminfo \
--ro-bind /usr/share/ca-certificates /usr/share/ca-certificates \
--ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \
--ro-bind /etc/hosts /etc/hosts \
--ro-bind /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf \
--ro-bind /usr/share/zoneinfo /usr/share/zoneinfo \
--ro-bind $HOME/.bashrc $HOME/.bashrc \
--ro-bind $HOME/.profile $HOME/.profile \
--ro-bind $HOME/.gitconfig $HOME/.gitconfig \
--ro-bind $HOME/.local $HOME/.local \
--bind $HOME/.claude $HOME/.claude \
--bind $HOME/.cache $HOME/.cache \
--file 3 $HOME/.claude.json \
--bind "$PWD" "$PWD" \
claude --dangerously-skip-permissions $@
如果这看起来相当特殊,那是因为它确实如此。我没有使用一些通用规则,而是用 bwrap 进行了实验,直到找到了我的系统所需的最小配置。
一些有趣的细节:
/tmp、/proc 和 /dev 由 bwrap 自动处理
我将文件和目录绑定挂载(即暴露),路径与本地机器相同,因此文件位置、项目路径等没有区别
我不暴露整个 /etc,只暴露最少需要的部分
$HOME/.claude.json 的内容被注入到沙箱中,所以对其进行的任何更改都不会保存到真实的文件中
$HOME/.claude/ 目录被映射为读写,所以 Claude 可以保存和修改其中的文件(比如会话数据)
/opt/node/node-v22.11.0-linux-x64/ 是我的自定义 Node.js 安装位置
我更改了主机名,这样可以轻松区分主机和沙箱
我可能会根据需要调整脚本,但这对我来说是一个相当不错的起点。
如果你想将其改编为另一个 AI Agent 或适配你的系统,我的建议是调整脚本以运行 bash,然后手动运行你的 Agent,查看什么破坏了并相应地调整。
一个有用的命令是 strace,它可以追踪文件访问系统调用,所以你可以看到需要什么:
strace -e trace=open,openat,stat,statx,access -o /tmp/strace.log codex
检查日志,你可以发现需要哪些文件并相应地绑定它们。