用 Docker 沙箱隔离编程智能体
文章介绍以轻量虚拟机为基础的 Docker Sandboxes,通过限定共享文件、网络和凭据,为编程智能体提供可丢弃的执行环境。内容涵盖与普通容器的区别、在 Go 项目中使用 sbx CLI,以及通过 Go 程序调用云端沙箱和相关限制。
AI 深度解读
让 AI 编程代理自己装依赖、跑测试、构建镜像,效率更高,也把本机文件、网络和凭证暴露给了它。Docker Sandboxes 的思路是:给代理一台可以丢弃的微型虚拟机,再限定它能接触什么。
作者用 Go 项目演示了本地 sbx CLI 和云端 REST API。对前端和全栈开发者,值得看的是其中的权限设计:怎样让代理少问确认,同时把代码修改、Docker 操作和密钥使用限制在可审查的范围内。
普通容器为什么还不够
容器用命名空间(namespaces)限制进程能看到什么,用 cgroups 限制资源用量,但仍与宿主机共享内核。容器里的内核漏洞可能影响宿主机,这与独立虚拟机的隔离边界不同。
另一个问题来自 Docker 自身。代理需要构建镜像、启动测试数据库,常见做法是挂载宿主机的 /var/run/docker.sock。能操作这个 socket,就可能启动特权容器并挂载宿主机根目录,权限接近宿主机 root。
网络和凭证也不能只靠「放进容器」解决。--network none 会阻断正常开发所需的访问;直接联网又可能触及内网服务或云元数据端点。把 API key 塞进环境变量,则让容器内的程序有机会读取它。
Docker Sandboxes 用微型虚拟机(microVM)作为主要隔离边界。每个沙箱拥有独立内核、文件系统和 Docker Engine,代理可以使用 sudo,运行 docker compose up,但控制的是沙箱自己的 Docker。代价是镜像和层缓存不共享,每个沙箱都要拉取所需内容。
代理在沙箱里权限很大,安全性取决于哪些能力被允许跨出沙箱。
按作者描述,出站 TCP 流量经过代理并检查目标地址;UDP 关闭,ICMP 被阻断,DNS 也执行相同策略。Balanced 预设允许常见开发服务,Locked Down 要求逐个放行。
凭证同样经过宿主机代理:代理程序先发送带占位值的请求,宿主机确认目标获准后,再换入真实密钥。真实 key 不进入虚拟机,因此打印环境变量不会直接拿到它。不过,这套机制保护的是密钥内容;获准发起哪些请求,仍要靠网络策略约束。
本地使用时,先决定代码能不能直接改
原文给出的安装条件包括:macOS Sonoma 14 或更高版本且使用 Apple silicon;Ubuntu 24.04 或更高版本并具备 KVM,用户加入 kvm 组;Windows 11 启用 Windows Hypervisor Platform。
对应安装命令分别是 brew trust docker/tap、brew install docker/tap/sbx,Ubuntu 的 curl -fsSL https://get.docker.com | sudo SBX=1 sh 和 sudo usermod -aG kvm $USER,以及 Windows 的 winget install -h Docker.sbx。这些是原文提供的条件,不能据此推断其他系统也受支持。
执行 sbx login 后,在项目目录运行 sbx run --name my-service claude 即可启动。作者称首次要拉取代理镜像,之后启动只需数秒,但没有提供基准测试。本地 CLI 和本地计算免费,包括商业使用;模型服务费用并未因此消失。
真正要先选的是工作区模式:
- Direct mode 默认把宿主机目录挂到沙箱内相同的绝对路径,修改立即双向可见。
- Clone mode 把源仓库只读挂到
/run/sandbox/source,代理在内部私有克隆中工作。 - No workspace 不挂载宿主机目录,文件随沙箱删除。
虚拟机隔离并不等于项目文件不会被改坏,Direct mode 明确允许代理修改真实文件。
较大的重构适合用 sbx run --clone --name refactor claude .。宿主机会增加 sandbox-refactor remote,再通过 git fetch sandbox-refactor、git branch -r --list 'sandbox-refactor/*' 和 git diff main sandbox-refactor/<branch> 审查结果。执行 sbx rm refactor 也会移除这个 remote。
环境配置则可以复用:用 sbx exec my-service -- go version 检查工具链,用 sbx exec -it my-service bash 进入环境,配置后执行 sbx template save my-service go-agent:v1,下次用 sbx run -t go-agent:v1 --name another-service claude 创建。服务端口通过 sbx ports my-service --publish 8080:8080 发布。
Go 依赖下载需要 proxy.golang.org 和 sum.golang.org,可分别用 sbx policy allow network 放行。私有模块还涉及 GOPRIVATE、Git 主机规则和凭证。作者建议精确允许目标,避免用 *.googleapis.com 这样的宽泛通配符解决下载失败。
放进 CI 和应用后,生命周期才是重点
下面这段原文代码展示了 CI 如何创建沙箱、交给代理修测试,再独立验证结果:
echo "$DOCKER_PAT" | sbx login --username "$DOCKER_ID" --password-stdin
sbx secret import --all
sbx create --name ci-task --clone claude .
sbx exec ci-task -- claude --dangerously-skip-permissions -p "Fix the failing tests in ./internal/store and make sure go test ./... passes"
sbx exec ci-task -- go test ./...
git fetch sandbox-ci-task
sbx rm --force ci-task
重点看 --dangerously-skip-permissions:它取消代理自身的确认,依赖外部 microVM、网络策略和 Clone mode 限定行动范围。sbx secret import --all 也应审查,不能照抄后就假定只导入了任务必需的凭证。
sbx exec 透传命令退出码,因此测试失败可以让 CI 步骤失败;125 表示命令无法运行,例如沙箱不存在,127 表示找不到可执行文件。无终端时,需要确认的命令应使用 --force。这段示例没有展示失败后的兜底清理,落地流水线还需要补上。
Cloud Sandboxes 面向应用按任务执行代码,需要 Docker Agentic Platform 订阅,计算按量付费。官方提供 TypeScript SDK;作者展示了用 Go net/http 调 REST API 的实现,但没有给生产负载数据。
云端分成两层:管理 API 位于 https://connect.docker.com/sandboxes,负责创建、查询和删除;沙箱自己的 endpoint 负责执行命令和文件操作。PAT 必须勾选 Allow Cloud Sandboxes access,先向 https://hub.docker.com/v2/auth/token 换取 access token,不能直接拿 PAT 调管理 API。
创建请求必须同时指定 cpus 和 memoryMib,例如 1 CPU 配 1 GiB 会被拒绝。原文建议沿用 SDK 的合法规格,但所给内容没有列出规格表。创建返回 202 Accepted,状态为 creating;示例每 2 秒查询一次,等待 running 或 failed。
沙箱 endpoint 使用单独凭证,有效期最多 5 分钟。下面这段代码展示了凭证申请范围:
func (c *Client) Connect(ctx context.Context, sbx *Sandbox) (*Session, error) {
body := map[string]any{
"permissions": []string{"sandboxesExec", "sandboxesFilesWrite"},
}
var cred struct {
Token string `json:"token"`
ExpireTime time.Time `json:"expireTime"`
}
target := apiBaseURL + "/v1/" + sbx.Name + "/endpoint-credentials"
if err := c.do(ctx, http.MethodPost, target, c.token, body, &cred); err != nil {
return nil, fmt.Errorf("endpoint credential: %w", err)
}
return &Session{
client: c,
endpoint: strings.TrimSuffix(sbx.Core.Endpoint.URI, "/"),
token: cred.Token,
}, nil
}
要看 permissions 这一行:示例只申请 sandboxesExec 和 sandboxesFilesWrite。不过,允许执行命令后,程序仍可能通过命令读取文件,不能把未申请 sandboxesFilesRead 理解成文件内容绝对不可读。另外,代码解析了 ExpireTime 却没有保存,长期服务需要补充续期管理。
文件写入使用 PUT /v1/files/content,mode=420 对应 0644;命令执行使用阻塞式 POST /v1/processes/exec。输出中的 stdout、stderr 采用 base64,Go 的 []byte 字段可由 encoding/json 解码。所给原文在这一段末尾截断,后续限制和完整示例没有提供。
我的判断
这篇最有用的地方,是把代理权限拆成了机器、工作区、网络和凭证几个独立问题。相比只套普通容器,独立内核和独立 Docker Engine 更适合需要安装工具、构建镜像的任务;相比每一步都让人确认,它把控制点前移到了环境配置和结果审查。
但「删除沙箱就没事」有前提。Direct mode 的文件修改已经落到宿主机;获准联网的请求也可能已经产生外部影响。销毁环境不能撤回这些动作。
成本和适用范围同样缺少对照:独立缓存会增加拉取与存储开销,云端还要处理轮询、短期凭证和失败清理。作者没有给出与普通容器、其他虚拟机方案的性能或成本对比,「启动数秒」「适合放心放权」应当视为作者描述和设计判断。
能拿来做什么
- 日常让 Claude Code、Codex 修改较大范围代码时,优先试用 Clone mode,把接收改动落实为 fetch、diff 和挑选提交。
- 给前端或 Node 项目配置网络策略时,先梳理包源、代码托管和模型服务的实际目标,再逐项放行;原文的 Go 域名清单不能直接套用。
- 在 CI 中尝试自动修测试时,保留独立测试步骤,并补齐失败清理和凭证筛选,避免只凭代理声称完成就接收结果。
- 如果应用需要执行生成代码,可以参考其两层 API 设计:管理权限留在服务端,任务侧使用短期沙箱凭证,并明确处理创建超时、命令失败与删除。