00dev-org/sandboxeed

GitHub: 00dev-org/sandboxeed

一个基于 Docker 的轻量级沙箱工具,通过 Squid 代理实现网络隔离,为 AI 编程 Agent 提供统一、安全的运行环境。

Stars: 1 | Forks: 0

# sandboxeed 一个基于 Docker 的沙箱,通过 Squid 代理实现过滤的互联网访问。 为您所有的 AI 编程 Agent 提供统一的环境。 它解决了流行 Agent 工具中的常见问题: - Claude 创建空 dotfile 导致 git 崩溃 命令:https://github.com/anthropic-experimental/sandbox-runtime/issues/139 - 启用沙箱时 Codex 无网络访问 - OpenCode 或 Copilot CLI 没有内置沙箱 - 使用不同 AI 工具时沙箱体验不一致 ## 架构 ``` HOST +--------------------------------------------------------------------------+ | | | $ sandboxeed claude | | | | | | manages container lifecycle | | | | | +-----+----------------- internal network ----------------------------+ | | | | | | | +-----------------------------+ +-----------------------------+ | | | | | sandbox | | squid proxy | | | | | | |-->| | | | | | | AI agent | | [+] github.com | | | | | | (claude / codex / | | [+] api.openai.com | | | | | | gemini / opencode / ...) | | [-] everything else -> 403 | | | | | | | +---------------+-------------+ | | | | | /workspace <- ./ | | | | | | | (host dir, always mounted) | | egress network | | | | +-----------------------------+ | | | | | | | | | | +-----------------------------+ | | | | | | DinD (optional) | | | | | | | Podman-in-Docker | | | | | | | unix:///var/sock/... | | | | | | +-----------------------------+ | | | | +------------------------------------------------------+--------------+ | | | | +---------------------------------------------------------+----------------+ | v +-----------------+ | internet | | (allowed only) | +-----------------+ ``` 沙箱容器**没有直接的互联网访问**——所有出站流量都 被强制通过 Squid 代理,该代理仅将连接转发到您配置的 `domains`。每次运行会创建两个 Docker 网络:一个由沙箱、代理和可选 DinD sidecar 共享的仅内部网络,以及一个仅允许代理连接到互联网的出口网络。 ## 系统要求 - Linux/macOS - Docker 或 Podman(通过兼容 Docker 的 `docker` CLI) - Go 1.25+(从源码构建) 当 `docker` 指向兼容 Podman 的 CLI 时支持 Podman。在 macOS 上,sandboxeed 在关闭时使用 Podman 特有的强制容器移除路径,以避免缓慢的代理拆除。 ## 安装 ``` go build -o sandboxeed . ``` 或者如果可用,使用预构建的二进制文件。 ## 快速开始 1. 将您的 Agent 状态复制到 sandboxeed 管理的路径中,这样不安全的沙箱配置(自动批准、绕过权限)可以与您安全的主机配置保持分离: ``` mkdir -p ~/.sandboxeed cp -a ~/.claude ~/.sandboxeed/.claude cp -a ~/.claude.json ~/.sandboxeed/.claude.json ``` 2. 复制适用于您 Agent 的示例配置: ``` cp examples/claude/Dockerfile.sandbox ./Dockerfile.sandbox cp examples/claude/sandboxeed.yaml ./sandboxeed.yaml ``` 3. 构建沙箱镜像: ``` sandboxeed --build ``` 4. 运行您的 Agent: ``` sandboxeed claude ``` 查看 [Agent 示例](#agent-examples) 获取所有支持的 Agent。 ## 用法 ``` # 在 sandbox 中启动交互式 shell sandboxeed # 运行 AI agent 工具 sandboxeed claude # 在 sandbox 内运行任意命令 sandboxeed node script.js # 构建 sandbox 镜像,然后运行命令 sandboxeed --build claude # 仅构建 sandbox 镜像(无命令) sandboxeed --build # 为此运行跳过 Docker-in-Docker(如果已启用) sandboxeed --no-docker claude # 打印当前版本 sandboxeed --version # 移除残留的 sandboxeed Docker 资源 sandboxeed --cleanup ``` ### 标志 | 标志 | 描述 | |---------------|------------------------------------------------------------------------------------------| | `--build` | 构建 Docker 镜像(总是 `--no-cache`);如果提供了命令,则在构建后运行它 | | `--no-docker` | 即使 `sandboxeed.yaml` 中设置了 `docker: true`,也跳过 Docker-in-Docker | | `--read-only` | 将所有卷(包括项目目录)在沙箱内挂载为只读 | | `--unsafe` | 以特权模式运行 DinD(不安全,仅用于测试) | ### 内置命令 - `--help` - 显示用法摘要(也可以使用 `-h`) - `--version` - 打印应用版本 - `--inspect` - 打印有效的合并沙箱配置而不启动容器 - `--cleanup` - 列出 sandboxeed 管理的容器、网络和卷,并在确认后移除它们;镜像不会被移除 Sandboxeed 选项仅在沙箱命令启动前解析。第一个位置参数总是沙箱命令,其后的每个参数都属于沙箱命令。当命令以 `-` 或 `--` 开头时,使用 `--` 强制将下一个参数视为沙箱命令。如果未给出命令,将打开一个交互式 `bash` shell。 ### 自动构建 当在 `sandboxeed.yaml` 中设置了 `build.dockerfile` 且配置的镜像在本地不存在时,sandboxeed 会在启动沙箱前自动构建它。使用 `--build` 可强制重新构建。 ## 启动序列 当您运行 `sandboxeed` 时: 1. 如果 `github.com` 或 `.github.com` 在您的 `domains` 列表中,将从 GitHub API 获取 GitHub 当前的 SSH 主机密钥,并将其注入到沙箱的 `/etc/ssh/` 中,同时注入一个 SSH 配置,该配置通过 `corkscrew` 经由代理路由 `github.com`。 2. 一个 Squid 代理容器启动,其中包含您配置的域名的允许列表。 3. 沙箱容器启动并连接到内部 Docker 网络——所有出站流量都通过代理,所有不在您域名列表中的内容都会被阻止。 4. 当沙箱退出时,sandboxeed 会移除代理、沙箱、任何 DinD sidecar 以及所有每次运行的网络和卷。 每次运行都使用唯一命名的容器、网络和卷,因此崩溃或中断的运行不会阻塞下一次运行,并且多个沙箱可以从同一目录并发运行。 ## 配置 配置按以下顺序加载: 1. 内置默认值 2. `~/.sandboxeed.yaml` 中的可选用户配置 3. `./sandboxeed.yaml` 中的可选项目配置 后面的层覆盖前面的层。环境变量按变量名合并,卷挂载按容器目标路径合并,域名去重。 对于可复用的用户管理沙箱镜像,`sandbox.image` 和 `sandbox.build.dockerfile` 可以放在 `~/.sandboxeed.yaml` 中。项目配置可以原样继承该对,使用自己的构建设置覆盖两者,或仅覆盖 `image` 以使用不同的预构建镜像。 当前目录总是挂载到沙箱内的 `/workspace`。您配置的任何卷都在该默认值之上合并。 在您的项目目录中放置一个 `sandboxeed.yaml`: ``` sandbox: build: dockerfile: Dockerfile.sandbox # Dockerfile for --build and auto-build image: my-custom-image:latest # Image to run (required when this file is present) volumes: - ~/.gitconfig:/root/.gitconfig:ro environment: - MY_VAR=value working_dir: /workspace # default: /workspace docker: true # enable Docker-in-Docker support memory: 2g # sandbox container memory limit cpus: "2" # sandbox container CPU limit pids: 512 # sandbox container PID limit domains: # whitelisted outbound domains - github.com - api.example.com - .docker.io # matches docker.io and all subdomains ``` ### 配置字段 | 字段 | 描述 | |--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `build.dockerfile` | Dockerfile 的路径。供 `--build` 使用,以及当本地找不到配置的镜像标签时用于自动构建。当使用 `--build` 但未指定显式路径时,默认为 `Dockerfile`。 | | `image` | 当存在 `sandboxeed.yaml` 时为必填项。要运行的 Docker 镜像(以及使用 `--build` 时要标记的镜像)。 | | `volumes` | 在默认的 `.:/workspace` 挂载之后添加的额外卷挂载。支持 `~`、`./` 和 `../` 路径前缀。当多个条目指向同一容器路径时,最后一个配置层胜出。 | | `environment` | 在代理默认值(`HTTP_PROXY`、`HTTPS_PROXY`、`NO_PROXY`)之后添加的额外环境变量。当同一变量出现多次时,最后一个配置层胜出。 | | `working_dir` | 容器内的工作目录。默认值:`/workspace`。 | | `docker` | 设置为 `true` 以启动 Docker-in-Docker sidecar(参见 [Docker-in-Docker](#docker-in-docker))。 | | `memory` | 沙箱容器的内存限制,传递给 Docker 作为 `--memory`。在项目配置和用户配置中均支持。 | | `cpus` | 沙箱容器的 CPU 限制,传递给 Docker 作为 `--cpus`。在项目配置和用户配置中均支持。 | | `pids` | 沙箱容器的 PID 限制,传递给 Docker 作为 `--pids-limit`。在项目配置和用户配置中均支持。 | | `domains` | 允许沙箱访问的域名。所有其他出站流量都会被阻止。用户和项目域名会合并并去重。 | ### 用户配置 在 `~/.sandboxeed.yaml` 中保留个人默认值。那里仅支持以下字段: - `sandbox.build.dockerfile` - `sandbox.image` - `sandbox.volumes` - `sandbox.environment` - `sandbox.domains` - `sandbox.memory` - `sandbox.cpus` - `sandbox.pids` 用户或项目配置中的 `sandbox.build.dockerfile` 要求同一配置层中有 `sandbox.image`,以便构建始终针对可预测的镜像名称。 `working_dir` 和 `docker` 等字段属于项目配置。 如果它们出现在用户配置中,sandboxeed 将报错。 使用它可以避免将与用户相关的更改提交到项目仓库,并避免跨多个项目重复。 例如:为您使用的 AI Agent 挂载配置,设置通用环境变量,或将常用域名列入白名单。 ### 分离沙箱和主机配置 **最佳实践:** 从 `~/.sandboxeed/` 挂载 sandboxeed 管理的 Agent 状态副本,而不是 直接挂载您真实的主机路径。 沙箱配置故意不安全——批准提示被禁用,权限被 绕过,工具以完全访问权限运行。如果您将真实的 `~/.claude` 或 `~/.codex` 挂载到 沙箱,那些宽松的设置会被写入您的主机配置。下次您在沙箱外运行 Agent 时,它会继承不安全的沙箱设置。另一方面, 沙箱也可以读取您的主机 API 密钥、会话历史,或注入稍后在主机上执行的内容(MCP 服务器条目、shell 钩子、自定义斜杠命令)。 在 `~/.sandboxeed/` 中保留单独的副本意味着沙箱和主机配置独立演进。 每个 Agent 设置一次: ``` mkdir -p ~/.sandboxeed # Claude Code cp -a ~/.claude ~/.sandboxeed/.claude cp -a ~/.claude.json ~/.sandboxeed/.claude.json # Codex CLI cp -a ~/.codex ~/.sandboxeed/.codex # Gemini CLI cp -a ~/.gemini ~/.sandboxeed/.gemini # OpenCode mkdir -p ~/.sandboxeed/.config ~/.sandboxeed/.local/share cp -a ~/.config/opencode ~/.sandboxeed/.config/opencode cp -a ~/.local/share/opencode ~/.sandboxeed/.local/share/opencode ``` 然后在您的配置中从 `~/.sandboxeed/` 挂载。每当您想从主机刷新沙箱状态时重新复制(例如,更改 API 密钥或更新设置后)。 用户配置: ``` # ~/.sandboxeed.yaml sandbox: build: dockerfile: ~/.sandboxeed/Dockerfile image: localhost/sandboxeed:latest volumes: - ~/.sandboxeed/.claude:/home/node/.claude - ~/.sandboxeed/.claude.json:/home/node/.claude.json - ~/.sandboxeed/.codex:/home/node/.codex - ~/.gitconfig:/home/node/.gitconfig:ro environment: - DISABLE_AUTOUPDATER=1 memory: 2g cpus: "2" pids: 512 domains: - .anthropic.com - .claude.ai - .claude.com - .openai.com - .chatgpt.com ``` 项目配置: ``` # ./sandboxeed.yaml sandbox: build: dockerfile: Dockerfile.sandbox image: my-custom-image:latest environment: - ENV=DEV # overrides the user config value domains: - .github.com # combined with api.openai.com from user config ``` 在上面的示例中,最终的允许域名列表包括来自用户配置的 `.anthropic.com`、`.claude.ai`、`.claude.com`、`.openai.com`、`.chatgpt.com` 和来自项目配置的 `.github.com`。`ENV` 变量仅由项目配置设置;用户级卷和 `DISABLE_AUTOUPDATER` 保持不变。 ### Agent 示例 作为即用型起点,从 [`examples/`](examples/README.md) 复制适用于您 Agent 的文件: | 文件 | 范围 | 描述 | |-------------------------------------------------|--------|--------------------------------------------------------------------| | [`.sandboxeed.yaml`](examples/.sandboxeed.yaml) | 全局 | 用户配置(`~/.sandboxeed.yaml`),包含共享卷和域名 | | Agent | 项目文件 | |---------------------------------|----------------------------------------------------------| | [Claude Code](examples/claude/) | `Dockerfile.sandbox`, `sandboxeed.yaml`, `settings.json` | | [Codex CLI](examples/codex/) | `Dockerfile.sandbox`, `sandboxeed.yaml`, `config.toml` | | [Gemini CLI](examples/gemini/) | `Dockerfile.sandbox`, `sandboxeed.yaml` | | [OpenCode](examples/opencode/) | `Dockerfile.sandbox`, `sandboxeed.yaml` | 将 `.sandboxeed.yaml` 复制到 `~/.sandboxeed.yaml` 作为全局默认值。将您的 Agent 状态复制到 `~/.sandboxeed/`,如 [分离沙箱和主机配置](#separating-sandbox-and-host-config) 中所述,以便沙箱挂载隔离的副本而不是您真实的主机路径。 每个 Agent 的 `Dockerfile.sandbox` 和 `sandboxeed.yaml` 放在您的项目根目录中。 这些是与项目无关的启动器。您通常需要为实际项目添加语言运行时、包管理器、SDK 或额外的允许域名。 ### 无配置文件 如果未找到 `sandboxeed.yaml`,sandboxeed 将运行一个 `bash:latest` 容器,具有: - 当前目录挂载在 `/workspace` - 设置了代理环境变量(`HTTP_PROXY`、`HTTPS_PROXY`、`NO_PROXY`) - 无出站互联网访问(所有流量被代理阻止) ## Dockerfile 要求 sandboxeed 在运行时向沙箱注入几项内容——您的 Dockerfile 不需要设置这些: | 内容 | 位置 | 备注 | |-------------------|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | SSH 客户端配置 | `/etc/ssh/ssh_config` | 仅当 `github.com` 或 `.github.com` 在 `domains` 中时。通过 `corkscrew` 经由代理路由 `github.com`;覆盖该路径下的任何现有文件 | | GitHub 主机密钥 | `/etc/ssh/ssh_known_hosts` | 仅当 `github.com` 或 `.github.com` 在 `domains` 中时。启动时从 GitHub API 获取;`StrictHostKeyChecking yes` | | 代理环境变量 | `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` | 每次运行设置 | | Docker socket | `DOCKER_HOST=unix:///var/sock/podman.sock` | 仅当 `docker: true` 时;通过卷从 Podman-in-Docker sidecar 共享 | | 项目挂载 | `.:/workspace` | 当前目录,总是挂载 | **您的 Dockerfile 必须提供:** - **`corkscrew`** - 当 `github.com` 或 `.github.com` 在 `domains` 中时为必选项。用于通过 HTTP 代理隧道传输 SSH;没有它,通过 SSH 的 `git clone` 将失败。 - **`docker` CLI** - 当设置 `docker: true` 时为必选项。DinD sidecar 提供守护进程,但镜像中必须存在客户端。 **推荐模式:** - 创建 `/workspace` 并将其 `chown` 给运行时用户,以便默认卷挂载可写- 以 root 身份安装您的工具和运行时,然后在 `CMD` 之前使用 `USER` 切换到非 root 用户。注入到 `/etc/ssh/` 的文件只需要可读,因此运行时不需要 root 访问权限。 - 如果工具不支持 `HTTP_PROXY`/`HTTPS_PROXY`,请在 Dockerfile 中显式配置其代理设置(例如,npm 的 `proxy` 配置或工具特定的配置文件)。 ## Docker-in-Docker 当配置中设置 `docker: true` 时,sandboxeed 会在内部网络上启动一个 Podman-in-Docker sidecar 容器。沙箱容器通过 `DOCKER_HOST=unix:///var/sock/podman.sock` 自动配置为使用它,Podman socket 通过 Docker 卷在 sidecar 和沙箱之间共享。 与传统的 `docker:dind` 不同,Podman sidecar **不**需要 `--privileged`。它以最小的一组能力运行(用于 fuse-overlayfs 存储的 `SYS_ADMIN`、`seccomp=unconfined` 和 `apparmor=unconfined`)。这阻止了最常见的容器逃逸向量——sidecar 无法访问主机块设备或主机设备 cgroup。 对于需要完全特权访问的测试场景(例如,创建自定义容器网络),使用 `--unsafe` 标志以特权模式运行 DinD sidecar。 启动 DinD sidecar 通常会给沙箱启动时间增加约 10 秒。要跳过单次运行的开销,即使设置了 `docker: true`,也请使用 `sandboxeed --no-docker ...`。 使用 Docker-in-Docker 时,请确保您的 `domains` 列表包含您需要拉取的注册表(例如,Docker Hub 镜像层的 `.docker.io`、`.cloudflarestorage.com`)。 ## 网络隔离 沙箱容器**没有直接的互联网访问**。所有流量都通过 Squid 代理路由,该代理仅允许连接到 `domains` 下列出的域名。空的 `domains` 列表会阻止所有出站流量。 每次运行创建两个 Docker 网络: - `-internal-` - 连接沙箱、代理和可选的 DinD sidecar(内部,无外部路由) - `-egress-` - 将代理连接到外部世界 当启用 Docker-in-Docker 时,sandboxeed 为 DinD sidecar 内的 `/var/lib/containers`(Podman 存储)和 `/var/sock`(共享 API socket)创建每次运行带标签的卷,以便可以可靠地清理它们。 ## 安全注意事项 sandboxeed 减少了运行不受信任或半受信任代码的攻击面,但它**不是完整的安全边界**。请记住以下几点: - **卷挂载会暴露主机文件。** `volumes` 下列出的任何路径都可以在沙箱内直接访问。挂载到容器中的敏感文件(SSH 密钥、API token、配置文件)可以被读取或泄露到允许的域名。尽可能使用 `:ro` 挂载,避免挂载不需要的凭证。如果您挂载 Git SSH 密钥,请使用具有您能强制执行的最窄仓库或项目范围的专用密钥,而不是更广泛的个人密钥。 示例配置从 `~/.sandboxeed/`(sandboxeed 管理的 Agent 状态副本)挂载,而不是您真实的主机路径。这至关重要,因为沙箱配置使用宽松的自动批准设置,这在主机上是危险的;直接挂载您的真实配置会导致那些不安全的设置泄露回去。有关设置说明,请参阅 [分离沙箱和主机配置](#separating-sandbox-and-host-config)。 您也可以使用 `--read-only` 运行,以强制所有挂载(包括项目目录)为只读模式。 - **示例配置故意是宽松的。** `examples/` 中的文件禁用了批准提示和安全检查,以便在一次性环境中使用方便。 **不要在沙箱外使用它们。** - **域名过滤并非万无一失。** Squid 代理按域名过滤,而不是按 IP。DNS rebinding、通过允许的域名隧道传输(例如,通过受损的 CDN)或通过 DNS 本身的泄露都无法被阻止。 - **Docker-in-Docker 能力。** Podman-in-Docker sidecar 以 `SYS_ADMIN`、`seccomp=unconfined` 和 `apparmor=unconfined` 运行——足以支持 fuse-overlayfs,但不足以访问主机设备。使用 `--unsafe` 时,sidecar 以完全特权运行,这授予主机内核访问权限,应仅用于测试。 - **无系统调用过滤。** 沙箱容器以 Docker 的默认 seccomp 配置文件运行,但没有额外限制。它不等同于像 gVisor 或 Firecracker 这样的 VM 级隔离边界。 - **代理绕过。** 如果沙箱容器获得操纵其自身网络命名空间或路由的能力(例如,通过 `CAP_NET_ADMIN`),它可能完全绕过代理。默认的 Docker 能力集不授予此权限,但自定义镜像或运行时标志可能会。
标签:AI 编程助手, Claude, Codex, CVE检测, Docker, EVTX分析, NIDS, Sandbox, SOC Prime, Squid 代理, 人工智能, 代理过滤, 安全防御评估, 容器化, 开发工具, 日志审计, 本地开发环境, 沙箱, 环境一致性, 用户模式Hook绕过, 网络安全, 网络隔离, 自动化运维, 请求拦截, 轻量级虚拟化, 隐私保护, 隔离执行