Senko Rašić
和许多开发者一样,我发现自己越来越多地使用人工智能代理来辅助软件开发。
我目前使用命令行界面 Claude Code 以及 Opus 4.5(截至撰写本文时 Anthropic 的顶级型号)。我用它来将粗略的任务需求提炼成详细的开发计划,然后执行该计划。
默认情况下,Claude Code 每次都会询问是否可以读取和写入文件以及运行软件。这种默认配置虽然合理,但时间长了确实会让人觉得烦躁。更糟糕的是,它频繁地打断我,以至于我一边照看它一边无法同时做其他事情。
还有一种--dangerously-skip-permissions(又称“YOLO”)模式,它会在未经询问的情况下运行任何程序。这可能很危险(尽管我知道有些人就是这么运行的,而且他们的开发机器至今还没出问题)。
沙盒
标准解决方案是将代理程序置于沙箱中——要么在远程机器上(exe.dev、sprites.dev、daytona.io),要么通过 Docker 或其他虚拟化机制在本地运行。
Linux 上的一个轻量级替代方案是bubblewrap,它利用 Linux 内核特性(如 cgroups 和用户命名空间)来限制(囚禁)进程。
事实证明,Bubblewrap 是构建轻量级 AI 代理沙箱的好方案。以下是我个人对这种方案的需求:
- 模仿我常用的Linux开发机配置(我不想管理多个开发环境)
- 除当前项目所需信息外,几乎无法获取其他信息。
- 仅对当前项目拥有写入权限
- 可以直接操作项目的文件/文件夹,因此我可以轻松地从我的 IDE 中查看或修改相同的文件,或者自己运行代码。
- 网络访问——既用于连接到人工智能提供商和搜索互联网,也用于启动一个我可以连接的服务器。
Bubblewrap 和 Docker 并非强化的安全隔离机制,但我并不在意。我并不太担心以下风险:
- 利用零日 Linux 内核漏洞逃脱
- 隐蔽侧信道通信
- 从当前项目中窃取数据(包括项目特定的访问密钥)
- 搞砸了代码库(代码通过
gitGitHub 或其他地方进行管理和备份)。
最后一点比较棘手,即使是完全远程的沙箱也无法防范这种情况。理论上,我们可以使用透明的 API 代理,在 AI 代理毫不知情的情况下注入正确的访问密钥,但目前实现起来非常复杂。
另一种方法是通过创建项目特定的 API 密钥来控制潜在的损害,这样即使这些密钥泄露,至少影响范围也会降到最低。
实际应用
这是我的 BubbleWrap 沙盒脚本的样子:
#!/usr/bin/bashexec 3<$HOME/.claude.jsonexec /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/这是我的自定义nodejs安装位置- 我更改了主机名,以便轻松区分主机和沙箱。
我可能会根据需要对脚本进行一些调整,但这对我来说是一个相当不错的起点。
如何自定义
如果你想将此方法应用到其他 AI 代理或你的系统中,我的建议是修改脚本使其运行bash,然后手动运行你的代理,看看哪里出了问题,并进行相应的调整。
一个很有用的命令是strace,它可以跟踪文件访问系统调用,以便查看需要执行哪些操作:
strace -e trace=open,openat,stat,statx,access -o /tmp/strace.log codex
通过检查日志,您可以发现需要哪些文件,并根据需要进行绑定。
blog.senko.net
