AI 编码代理不只是生成代码。它通常还能执行 Shell、安装依赖、读取项目文件,并调用测试或部署工具。这种能力提高了开发效率,也扩大了攻击面:一旦代理下载到恶意依赖、执行受污染的安装脚本,或者被仓库中的提示注入误导,当前进程能够读取的凭据就可能被复制或外传。
真正的问题不是代理是否“值得信任”,而是它运行时拥有多少权限。更稳妥的做法是把代理视为可能执行不可信代码的自动化进程,并通过 Docker Sandbox 把主机密钥、个人配置和高权限接口放到容器边界之外。
为什么一次依赖安装就可能泄露密钥
供应链攻击不需要直接攻破编码代理。恶意代码可以藏在包安装脚本、测试工具、构建插件或仓库脚本中。代理为了完成任务,可能主动运行:
npm install
npm test
pip install -r requirements.txt
make build
这些命令继承代理进程能够访问的环境变量和文件。如果代理直接运行在开发者主机上,风险对象可能包括:
AWS_ACCESS_KEY_ID、GITHUB_TOKEN等环境变量;~/.ssh、~/.aws、~/.config中的凭据;- 云平台 CLI 的登录缓存;
- Docker Socket,也就是接近主机 root 权限的控制接口;
- Kubernetes 配置和生产环境上下文。
即使代理没有主动查看密钥,它启动的子进程通常也会继承环境变量。恶意安装脚本只需读取进程环境,并通过网络请求发送出去。因此,“不要让代理输出密钥”并不是完整防线;关键是让密钥从一开始就不在代理的可达范围内。
Sandbox 应当切断哪些通道
一个有效的代理沙箱至少要约束四类能力。
文件系统:只挂载当前工作区,不挂载用户主目录、SSH 目录、云平台配置或宿主机根目录。
环境变量:使用明确的允许列表,不要把宿主机环境整体转交给容器。特别要检查 .env 文件和 Compose 的自动加载行为。
网络:在不需要下载依赖时关闭网络;确实需要联网时,使用代理、域名允许列表或独立的依赖准备阶段。
宿主机控制接口:不要挂载 /var/run/docker.sock。容器内的 root 并不等于宿主机 root,但 Docker Socket 往往能让容器创建高权限挂载,从而绕过原有隔离。
一个可改造的最小 Docker Sandbox
下面的示例假设项目位于当前目录,编码代理可以通过 agent-cli 启动。运行前需要把镜像中的代理安装命令和入口命令替换成实际工具。
创建 Dockerfile.agent:
FROM node:22-bookworm-slim
RUN useradd --create-home --uid 10001 agent
WORKDIR /workspace
# 按实际产品替换;固定版本,避免每次构建获得不同代码。
RUN npm install --global agent-cli@1.2.3
USER agent
ENTRYPOINT ["agent-cli"]
构建镜像:
docker build --pull -f Dockerfile.agent -t local/coding-agent:1.2.3 .
在 Linux 主机上,可以使用下面的命令启动受限容器:
docker run --rm -it \
--name coding-agent-sandbox \
--network none \
--cap-drop ALL \
--security-opt no-new-privileges \
--pids-limit 256 \
--memory 4g \
--cpus 2 \
--tmpfs /tmp:rw,noexec,nosuid,size=512m \
--mount type=bind,src="$PWD",dst=/workspace,rw \
--workdir /workspace \
--env HOME=/home/agent \
local/coding-agent:1.2.3
这个命令没有传入宿主机环境变量,没有挂载用户主目录,也没有暴露 Docker Socket。--network none 进一步阻止受污染脚本向外发送数据。工作区仍然是可写的,因此应在独立 Git 分支或临时副本中运行,并在合并前审查差异。
可以先验证隔离是否符合预期:
docker run --rm \
--network none \
--cap-drop ALL \
--security-opt no-new-privileges \
--mount type=bind,src="$PWD",dst=/workspace,rw \
--env HOME=/home/agent \
local/coding-agent:1.2.3 \
sh -lc 'env | sort; test ! -e /var/run/docker.sock; test ! -e "$HOME/.ssh"'
如果代理镜像的入口点不允许执行 sh,可在验证命令中增加 --entrypoint sh。
联网安装依赖时,分成两个信任阶段
完全关闭网络并不总是现实。代理可能需要安装包或查询文档。可以把工作流拆成两个阶段:由受控流程准备依赖,再让代理在离线沙箱中修改和测试代码。
以 Node.js 为例,可以先由人工检查锁文件,然后在独立容器中执行确定性安装:
docker run --rm \
--mount type=bind,src="$PWD",dst=/workspace,rw \
--workdir /workspace \
--env npm_config_ignore_scripts=true \
node:22-bookworm-slim \
npm ci
npm_config_ignore_scripts=true 会跳过依赖安装脚本,可以降低风险,但某些原生模块可能因此无法完成安装。不要把它理解为万能开关:包本身仍会在测试或运行阶段执行。更严格的环境还应使用内部制品仓库、锁定依赖哈希、扫描新增包,并限制出口网络。
如果任务确实需要某项凭据,应创建短期、最小权限、可撤销的专用令牌,只注入单次容器:
read -rsp 'Temporary token: ' AGENT_TOKEN
printf '\n'
docker run --rm -it \
--network agent-egress \
--cap-drop ALL \
--security-opt no-new-privileges \
--env AGENT_TOKEN="$AGENT_TOKEN" \
--mount type=bind,src="$PWD",dst=/workspace,rw \
local/coding-agent:1.2.3
unset AGENT_TOKEN
这种方式仍然意味着容器内代码可以读取令牌。它只能缩小令牌的权限和有效期,不能让令牌对恶意进程不可见。高价值密钥更适合保留在容器外,由受控代理服务代为执行少量、经过授权的操作。
上线前的检查清单
采用编码代理时,可以从以下边界开始:
- 默认不继承宿主机环境变量,只允许显式注入;
- 只挂载任务所需目录,绝不挂载整个主目录;
- 不暴露 Docker Socket、Kubernetes 配置和生产云凭据;
- 默认关闭网络,需要联网时限制目标和时间窗口;
- 固定代理镜像与依赖版本,并审查锁文件变化;
- 使用非 root 用户、删除 Linux capabilities,并启用
no-new-privileges; - 在临时分支或一次性工作区运行,完成后审查
git diff; - 对必须使用的令牌设置最小权限、短有效期和独立审计记录。
Docker Sandbox 不是绝对安全边界,尤其不能替代及时修补的容器运行时、依赖审查和凭据轮换。但它能落实一个重要原则:编码代理可以拥有完成任务所需的代码和工具,却不应顺便拥有开发者的全部数字身份。