Claude Code 沙箱环境深度解析:DevContainer、bubblewrap 与 iptables 配置实战
1. 为什么 Claude Code 需要两层沙箱Claude Code 和普通聊天式 AI 最大的区别是它能直接读写文件、执行 Shell 命令。你让它「清理一下构建产物」它可能真的去跑rm -rf你让它「拉一下依赖」它可能真的去访问外网。这种能力是效率的来源也是风险的来源。沙箱环境要解决的核心问题就一句话在不打断正常开发流程的前提下把 AI 的执行权限关进一个可控的边界里。Claude Code 的做法是分两层各自管不同粒度的事。第一层是进程级沙箱作用范围只有 BashTool 执行的命令。Linux/WSL 下用 bubblewrapbwrap做 namespace 隔离macOS 下用系统自带的 sandbox-exec。它管的是「这条命令能碰哪些文件、能连哪些域名」。第二层是容器级沙箱作用范围是整个 DevContainer 的所有出站流量。用 Docker 容器 iptables ipset 做网络白名单即使第一层被绕过容器防火墙还能兜底。这两层相互独立、互为补充。本文聚焦它们在 DevContainer 里的落地配置给出可以直接复制的devcontainer.json、init-firewall.sh骨架以及验证沙箱是否真的生效的检查动作。适合需要在受控网络下运行 AI 编码工具的开发者尤其是企业内网、CI/CD 或对出站流量有合规要求的场景。2. 前置准备TaoToken 接入与基础环境在配置沙箱之前先把模型接入这条链路打通。Claude Code 需要一个兼容 Anthropic 协议的 API 端点TaoToken 提供的就是这个能力官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。你需要先拿到一个 API Key。登录后进入控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 后面会写进容器的环境变量里注意不要提交到 Git 仓库。环境变量建议这样设置ANTHROPIC_BASE_URL指向 TaoToken 的 API 地址ANTHROPIC_API_KEY填你刚创建的 Keyexport ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的Key如果你还没决定用哪个模型可以先去模型对话页面试一下响应速度和效果确认没问题再写进配置。长期做编码和 Agent 任务的话Coding Plan 的额度模型更适合高频调用具体可以看 coding-plan 页面。这里有个容易踩的坑沙箱开启后容器内的出站流量会被 iptables 限制。如果你把ANTHROPIC_BASE_URL指向的域名没有加进白名单Claude Code 会直接连不上模型表现为请求超时或连接被拒。所以第 4 节的白名单里必须包含 TaoToken 的 API 域名。3. 可复制配置devcontainer.json 与防火墙脚本3.1 devcontainer.json 骨架devcontainer.json是容器的蓝图关键点有三个加NET_ADMIN和NET_RAW能力、以非 root 用户运行、启动后执行防火墙脚本并等待其完成。{ name: Claude Code Sandbox, image: mcr.microsoft.com/devcontainers/base:ubuntu, runArgs: [ --cap-addNET_ADMIN, --cap-addNET_RAW ], remoteUser: node, mounts: [ sourceclaude-code-bashhistory-${devcontainerId},target/commandhistory,typevolume, sourceclaude-code-config-${devcontainerId},target/home/node/.claude,typevolume ], workspaceMount: source${localWorkspaceFolder},target/workspace,typebind,consistencydelegated, workspaceFolder: /workspace, postStartCommand: sudo /usr/local/bin/init-firewall.sh, waitFor: postStartCommand, containerEnv: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: ${localEnv:ANTHROPIC_API_KEY} } }waitFor: postStartCommand是安全关键。它保证防火墙完全生效之前VS Code 不会把工作区开放给你避免出现「防火墙还没起来AI 已经开始跑命令」的窗口期。--cap-addNET_ADMIN是配置 iptables/ipset 必需的NET_RAW用于发送 ICMP 拒绝包。这两个能力只给容器不给宿主机。3.2 init-firewall.sh 骨架这个脚本是容器级沙箱的核心执行顺序不能乱。先清理旧规则再保护 Docker 内部 DNS然后建白名单集合最后设默认 DROP 策略。#!/bin/bash set -euo pipefail # Phase 1: 清理与 Docker DNS 保护 DOCKER_DNS_RULES$(iptables-save | grep 127.0.0.11 || true) iptables -F iptables -X iptables -t nat -F iptables -t nat -X if [ -n $DOCKER_DNS_RULES ]; then echo $DOCKER_DNS_RULES | iptables-restore -n fi # Phase 2: 基础服务放行 iptables -A OUTPUT -p udp --dport 53 -j ACCEPT iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT # Phase 3: 白名单 IP 集合构建 ipset create allowed-domains hash:net -exist ipset flush allowed-domains # GitHub 动态 IP 段 for ip in $(curl -s https://api.github.com/meta | jq -r .web[], .api[], .git[]); do ipset add allowed-domains $ip -exist done # 固定域名解析后加入 for domain in api.anthropic.com registry.npmjs.org taotoken.net; do for ip in $(dig short $domain | grep -E ^[0-9.]$); do ipset add allowed-domains $ip -exist done done # Phase 4: 策略执行 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT DROP iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited # Phase 5: 验证 echo 防火墙规则已生效注意taotoken.net必须出现在固定域名列表里否则容器内 Claude Code 连不上模型。api.github.com/meta返回的是动态 IP 段每次启动重新拉取避免 GitHub 换 IP 后白名单失效。3.3 进程级沙箱配置容器级管网络进程级管命令。在~/.claude/settings.json里配置sandbox字段{ sandbox: { enabled: true, autoAllowBashIfSandboxed: true, allowUnsandboxedCommands: false, excludedCommands: [], network: { allowedDomains: [*.github.com, *.npmjs.org, *.anthropic.com, taotoken.net], allowAllUnixSockets: false, allowLocalBinding: false }, failIfUnavailable: true } }failIfUnavailable: true很重要。默认情况下如果 bwrap 缺失沙箱会静默降级为无沙箱运行你以为有保护其实没有。设成 true 后依赖缺失直接报错退出不会偷偷放行。autoAllowBashIfSandboxed: true让沙箱内的命令自动执行跳过权限弹窗适合 CI/CD。但危险路径rm -rf /、rm -rf $HOME仍会被二次检查拦截不会因为自动放行就绕过。4. 验证沙箱是否真的生效配置写完不代表生效必须做检查动作。下面这几步我建议每次改完配置都跑一遍。第一步确认容器内防火墙默认策略是 DROPsudo iptables -L OUTPUT -n --line-numbers | head -5你应该看到Chain OUTPUT (policy DROP)以及后面跟着的 ACCEPT 规则和最后的 REJECT。第二步测试白名单外的域名应该失败curl -s --max-time 5 https://example.com echo FAIL: 未拦截 || echo OK: 已拦截预期结果是OK: 已拦截。如果返回了内容说明防火墙没生效。第三步测试白名单内的域名应该成功curl -s --max-time 5 https://api.github.com/zen echo OK: 白名单放行第四步确认进程级沙箱已启用。在 Claude Code 里执行一条命令观察是否有沙箱启动日志。或者直接检查 bwrap 是否存在which bwrap bwrap --version如果failIfUnavailable: true且 bwrap 缺失Claude Code 启动时会直接报错这本身就是一种验证。第五步验证 TaoToken 连通性。在容器内跑curl -s --max-time 10 https://taotoken.net/api/v1/models \ -H Authorization: Bearer $ANTHROPIC_API_KEY | head -c 200能返回模型列表就说明白名单和 Key 都配对了。如果超时回去检查taotoken.net有没有加进 ipset。5. 本篇常见错排查报错一iptables: Permission denied原因是没有给容器NET_ADMIN能力。检查devcontainer.json的runArgs里有没有--cap-addNET_ADMIN。注意这个能力必须在容器创建时给运行中加不了改完要重建容器。报错二Claude Code 连不上模型请求超时最常见的原因是taotoken.net没进白名单。iptables 默认 DROP 后未匹配的流量会被 REJECT表现为连接被拒或超时。把域名加进init-firewall.sh的固定域名列表重建容器。报错三ipset: set allowed-domains already exists脚本重复执行时会出现。用-exist参数可以忽略或者先ipset destroy allowed-domains再创建。上面的骨架已经用了-exist正常不会报。报错四沙箱静默降级命令实际在无沙箱环境执行旧版本 Claude Code 在 bwrap 缺失时会静默降级。解决办法是设sandbox.failIfUnavailable: true让它在依赖缺失时直接报错。同时确认sandbox.bwrapPath指向正确的二进制路径非标准安装路径的环境需要手动指定。报错五excludedCommands里的命令绕过了所有限制这是设计行为不是 bug。excludedCommands中的命令以完全无限制方式执行只应该豁免你完全信任的工具。如果你把git加进去那 git 的所有网络和文件操作都不受沙箱约束。生产环境建议保持为空。报错六Docker DNS 在iptables -F后失效iptables -F会清掉 Docker 内部的 DNS NAT 规则导致容器无法解析域名。脚本 Phase 1 里先保存127.0.0.11相关规则清空后再恢复就是为了避免这个问题。如果你自己改脚本别把这步删了。6. 把沙箱接进你的日常流程沙箱配好之后日常使用其实无感。命令在沙箱内自动执行网络访问被限制在白名单内你该写代码写代码该让 AI 干活让 AI 干活。真正需要你操心的是白名单的维护——每引入一个新的依赖源或 API 端点就要同步更新init-firewall.sh和settings.json的allowedDomains。如果你还在调试接入阶段建议先用模型对话页面确认 TaoToken 的响应正常再进容器配置。API Key 的管理在 API Keys 页面接入细节可以对照接入文档。长期跑编码和 Agent 任务的话Coding Plan 的额度模型比按次调用更划算适合把 Claude Code 当成日常工具而不是偶尔试试。最后提醒一句沙箱不是万能的。Read/Write/Edit 工具、WebSearch/WebFetch、MCP Server 进程、Hooks 脚本都不在 BashTool 沙箱的保护范围内。要限制这些得用permissions.deny规则单独处理。沙箱解决的是「AI 执行 Shell 命令」这一层的风险其他工具的风险要另外评估。