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工具, 安全防御评估, 网络隔离, 请求拦截