BenjiMencer/claudebox
GitHub: BenjiMencer/claudebox
Claudebox 是一个在 Docker 中运行 Claude Code 的沙箱环境,通过网络隔离、fail-closed 自检和 prompt injection 扫描,保障 AI 编程 agent 安全地访问 Web。
Stars: 0 | Forks: 0
# 在 Docker 中运行受锁定的 Claude Code (macOS)
在容器中运行 Claude Code,该容器唯一允许的网络目标是
web-scanner 服务。所有 Web 访问都通过 scanner skill 进行;其他所有
网络访问在网络层被阻断。一个“失败即关闭”(fail-closed)的自检机制会拒绝
启动 agent,前提是如果防护墙未能生效。
本项目旨在被直接克隆并按原样运行 —— 你唯一需要提供的是
一个可达的 scanner 服务及其 token。
## 前置条件
- Docker Desktop for Mac。
- 一个运行中且可通过固定 IP 访问的 web-scanner 服务(此处默认假设为:
`100.123.181.11`,端口 `8080` 用于兼容 Anthropic 的 API,端口 `8099`
用于 scanner 本身)。如果你的服务位于其他地址,请按照
如下所示覆盖 `SCANNER_IP` /
`ANTHROPIC_BASE_URL` / `SCANNER_BASE_URL`。
- 由该服务签发的 `SCANNER_TOKEN`。
## 快速开始
```
export SCANNER_TOKEN=""
./run.sh
```
`run.sh` 会在首次运行时(以及每当 Dockerfile 发生更改时)构建镜像,
然后启动容器,并将当前目录以读写方式挂载到
容器内的 `/home/ccagent/work` —— 这正是 Claude Code 将看到
并进行编辑的目录。请在你希望 agent 处理的项目的根目录下运行它。
将额外的参数直接传递给 `claude` CLI,例如:
```
./run.sh --resume
```
## 推荐:用于日常使用的 shell 函数
`add_to_zshrc.txt` 包含一个 `claude-local` zsh 函数,你可以将其粘贴到
`~/.zshrc` 中。请注意,它顶部还包含一些无关的 shell 便利设置(`$EDITOR`、
`AUTO_CD`、历史记录设置)—— 如果你已经设置了
自己的配置,请将其裁剪掉,只保留 `claude-local` 函数本身。它能完成
`run.sh` 所做的一切,此外还会:
- 从该仓库旁边的 `.env` 文件中读取 `SCANNER_TOKEN`,而
无需你在每次会话中都 `export` 它,
- 如果 Docker Desktop 未运行,则自动启动它,
- 首次调用时自动构建镜像(使用 `claude-local --rebuild`
来强制进行后续重建),
- 可以在*任意*目录下工作 —— 它始终挂载你当前的工作
目录,而不是这个仓库。
设置 —— 将 `add_to_zshrc.txt` 的内容追加到 `~/.zshrc` 中(或者手动粘贴
`claude-local` 函数),然后在此仓库旁边创建一个 `.env` 文件:
```
cat add_to_zshrc.txt >> ~/.zshrc
echo 'SCANNER_TOKEN=' > ~/claude-docker/.env
source ~/.zshrc
```
默认情况下,该函数期望此仓库位于 `~/claude-docker` —— 如果你将其克隆到了
其他位置,请编辑该函数顶部附近的 `claude_docker_dir` 行。然后,从任意项目目录:
```
claude-local
```
## 自行验证(务必执行此操作 —— 不要盲目信任)
容器名为 `cc-agent-`(每个项目目录对应一个,因此
你可以同时运行多个而不会发生名称冲突 —— 请参阅下文的“运行多个
容器”)。使用 `docker ps` 找到确切的名称,然后执行:
```
NAME=cc-agent-yourdir # from `docker ps`
# internet 应被阻止:
docker exec -it "$NAME" curl -s --max-time 5 http://1.1.1.1; echo "exit=$?"
# scanner 应正常工作:
docker exec -it "$NAME" curl -s --max-time 5 http://100.123.181.11:8099/health; echo
# agent user 不应能够刷新规则:
docker exec -it -u ccagent "$NAME" sudo iptables -F 2>&1 || echo "denied — good"
```
## 运行多个容器
`run.sh` 和 `claude-local` 都会根据当前
目录的 basename 命名容器(例如 `cc-agent-myproject`),因此同时
在多个项目目录下运行 agent 是可行的 —— 每个目录都有其各自的
容器。在*同一个*目录下运行两次仍然会拒绝
启动第二个实例(见下文),因为两个 agent 同时编辑相同的挂载
文件是不安全的。
如果具有该名称的容器已经存在 —— 通常是由于终端崩溃、Mac 休眠
或 Docker Desktop 重启留下的旧容器
(因为 `--rm` 仅在正常退出时进行清理)—— 如果该容器已停止,
脚本会自动回收它;如果它仍在运行,则会告诉你如何附加
或移除它。
## 文件
- `Dockerfile` — 构建 agent 镜像(Claude Code + scanner skill + 指南)。
- `entrypoint.sh` — 在容器启动时以 root 身份运行:设置出站策略 (egress policy),运行
`selftest.sh`,然后将权限降级为非特权用户 `ccagent`
并执行 `claude`。
- `selftest.sh` — 在允许 agent 启动之前,执行“失败即关闭”检查,确保互联网被阻断且
scanner 可达。
- `run.sh` — 使用正确的 flag 进行构建和运行,并挂载当前目录。
- `add_to_zshrc.txt` — 可选的 `claude-local` shell 函数,便于在任何
项目目录下进行日常使用。
- `CLAUDE.md` — 项目指令,告知 agent 仅将 scanner 用于 Web
访问,并将扫描到的页面内容视为不受信任的数据。
- `skills/web-scanner/` — scanner skill(从
`SCANNER_TOKEN` 读取其 token,从不进行存储)。
- `settings.json` / `claude.json` — 内置于镜像中的 Claude Code 配置
(模型、token 限制,以及对内置 `WebSearch`/`WebFetch`
工具的拒绝规则,从而确保 scanner skill 是通往 Web 的唯一路径)。
## 必需的容器 flag —— 切勿丢弃
`run.sh` 和 `claude-local` 函数在运行容器时都会使用:
```
--cap-drop=ALL --cap-add=NET_ADMIN --cap-add=SETUID --cap-add=SETGID --security-opt no-new-privileges
```
这四项都很重要:
- `NET_ADMIN` — 在启动时需要一次,供 `entrypoint.sh` 安装
`iptables` 出站规则。
- `SETUID` / `SETGID` — 在启动时需要一次,供 `gosu` 从 root
降级到非特权用户 `ccagent`。如果没有这两项,即使进程的 UID 是 0,
内核也会拒绝权限降级,因为
`--cap-drop=ALL` 已经剥离了 setuid/setgid 检查所需的
capability —— 容器将完全无法启动 agent。
- `no-new-privileges` — 一旦我们以 `ccagent` 身份运行,此项就会阻止 agent
通过提权恢复为 root,以刷新出站规则或重新添加
capabilities。
如果你要自定义运行命令,请保留全部这四项,否则隔离环境
要么无法启动(缺少 SETUID/SETGID),要么无法保持稳固(缺少 NET_ADMIN /
no-new-privileges)。
## 现实的局限性 —— 请阅读此内容
这是 Docker Desktop for **macOS** 所能提供的最佳隔离环境,并且这是
**纵深防御,而非绝对的保证**。有两个结构性原因:
1. **出站规则是在容器内部设置的。** 它们在
启动时被安装,随后 agent 将以非特权模式运行,带有 `no-new-privileges` 且
没有 `NET_ADMIN`,因此 *agent* 无法刷新它们。但规则存在于
容器自身的网络命名空间中,而不是由你管理的宿主机内核中。在
Mac 上,“宿主机”是 Docker Desktop 隐藏的 Linux 虚拟机,你不管理它
—— 因此在容器之下没有一层*由你控制*的机制来捕获
绕过行为。如果容器镜像允许提权,容器内的 root 可以移除这些
规则。
2. **scanner IP 在设计上就是被允许的通道。** 防火墙控制着流量
*去往何处*,而不是其中的*内容*。scanner 自身的注入
拦截和 SSRF 防护与这道防护墙同等关键 —— 保持
它们的健康有效,并保留 `CLAUDE.md` 中关于遵守
`contains_suspected_injection` 标志的规则。
**如果你需要一个真正的、由内核强制执行的边界**,请在 Linux 宿主机上
运行此环境,并将 `nftables`/`iptables` 规则放置在宿主机的 `DOCKER-USER`
链中(与容器的桥接接口相匹配)。在那里,规则位于
你所拥有的内核中的容器之下,即使是容器内的 root 也无法
触及它们。
`selftest.sh` 中的自检功能将“我希望防护墙能守住”变成了“除非防护墙起效,否则
agent 将不会启动”。这是这里最重要的一环;切勿
移除它。
标签:AI代理容器化, Cutter, Docker, macOS工具, 安全防御评估, 网络隔离, 请求拦截