Bash 沙箱实现网络隔离方法详解(解析 Claude Code 依赖的核心工具链)

Claude Code 在 Linux/WSL2 下的网络隔离由 Bubblewrap 命名空间、socat 中继与 seccomp 系统调用过滤三件套共同实现:先把沙箱关进独立网络命名空间,再通过 Unix 域套接字 + socat 桥接一个代理出口,最后用 seccomp 拦截非代理套接字防止绕过。下面按工具链、流量路径、配置项三块拆开讲。

一、隔离要拦什么

Agent 在本机执行 Bash 命令,最危险的两件事是”读不该读”与”把不该发的东西发出去”。网络隔离的职责是后者:阻止未授权的外联请求、阻断恶意脚本回传、避免提权后的反弹连接。沙箱因此把网络一刀切——默认完全无网络,再通过代理开”白名单”窗口。

工具链各司其职:

  • Bubblewrap:用命名空间从内核层切断所有网络设备;
  • socat:在隔离空间和宿主机之间建桥,桥本身只走 Unix 域套接字;
  • seccomp:禁止沙箱进程再创建任何 Unix 域套接字,杜绝绕桥;
  • HTTP/SOCKS5 代理:按域名白名单和黑名单过滤出站流量。

二、Bubblewrap 的网络命名空间

Bubblewrap(bwrap)通过 clone() 系统调用给子进程创建一套全新的命名空间,相关的几个标志里,CLONE_NEWNET 是网络隔离的关键——它把子进程丢进一个只含回环设备的网络栈,没有真实网卡、没有外部 IP,所有 TCP/UDP 流量在网络层就发不出去。

实际命令长这样:

bwrap \
  --new-session \
  --unshare-pid \
  --unshare-net \
  --proc /proc \
  --dev /dev \
  --ro-bind /usr /usr \
  --bind "$CWD" "$CWD" \
  --tmpfs /home \
  -- bash -c "your-command"

--unshare-net 是网络隔离的开关;--unshare-pid 与 --new-session 配合切断对宿主机进程与会话的反向操控。bwrap 与 Docker 的最大区别是不需要 root 守护进程,普通用户就能通过 user namespace 拿到创建其他命名空间的能力。

三、用 socat 把代理”桥”进来

光切网络只能让命令”上不了网”,Agent 还需要访问 npm、git clone、容器镜像仓库这些合法流量。沙箱的做法是开一个很小的口子:宿主机的 HTTP/SOCKS5 代理通过 Unix 域套接字被 bind mount 进沙箱,沙箱内的进程用 HTTP_PROXY、HTTPS_PROXY 把流量交给代理。

沙箱内                                    宿主机
curl → HTTP_PROXY=localhost:3128         socat UNIX-LISTEN:/tmp/claude-http.sock
       ↓                                       ↓
socat TCP-LISTEN:3128 ←────────────────→ HTTP 代理 (域名白名单过滤)
       ↓                                         ↓
Unix socket  (bind mount)                      真实网络

三件事要并存:

  • 沙箱内 socat 监听 localhost:3128/localhost:1080,把 TCP 流量桥到 Unix 域套接字;
  • 沙箱外 socat 把同一 Unix 套接字桥到宿主机的代理端口;
  • 代理进程在宿主机侧按白名单/黑名单做最终决策,匹配失败的请求被拒。

整个数据通路里,TCP/IP 流量只在沙箱外的代理之后才出现;沙箱内只有 Unix 域套接字流量。这就是用最少的”开口”把受控网络”桥”进来的核心思路。

四、seccomp 兜底防止逃逸

--unshare-net 拦了 IP,bwrap 自身不做 syscall 过滤,因此需要 seccomp 收口。Claude Code 的策略是禁止沙箱进程自己创建 Unix 域套接字——这样进程即便想偷偷开 IPC 通道也开不了。

seccomp 通过 BPF 程序在内核拦截系统调用,对 socket(AF_UNIX, ...) 显式返回 EPERM。两个细节决定了有效性:

  • 加载 BPF 之前必须先 prctl(PR_SET_NO_NEW_PRIVS),否则 setuid 二进制能重获特权;
  • io_uring_setup、io_uring_enter、io_uring_register 三个 iouring 调用同样要拦,iouring 能在内核态绕过 seccomp 直接建 socket。

官方把 seccomp 过滤器以预编译 C 二进制(apply-seccomp)形式发布,配合 libseccomp 在沙箱启动前装载。运行时还能让出与 bwrap 的依赖检查:/sandbox 的 Dependencies 标签会列出 ripgrep、bubblewrap、socat、seccomp 是否齐全。

五、代理层的白名单与黑名单

沙箱内的进程拿不到原始 IP 流量,所有请求都得过代理。代理在请求头里取 Host,按配置比对:

命中策略 行为 典型配置
精确白名单 直接放行 github.com、pypi.org
通配符白名单 子域一并放行 *.amazonaws.com
黑名单 命中即拒,记日志 metrics.example.com
未知域名 默认拒绝,弹一次确认 用户在 UI 临时放行

白名单并不孤立——WebFetch 工具的权限规则会反向生成代理配置,写在权限里的 WebFetch(domain: example.com) 会自动加进代理白名单。这种”工具权限 ↔ 网络白名单”的双向同步是 Claude Code 减少权限弹窗的关键。

六、macOS 路径差异

macOS 用 Seatbelt 而不是 bwrap。Seatbelt 的策略语言 SBPL 写一份声明式 profile,描述允许读哪些路径、允许连接哪些端口;内核 TrustedBSD 模块强制执行。Claude Code 在 macOS 上用 sandbox-exec 加载动态生成的 profile,把网络限制为只能连代理的 localhost 端口——强制层在内核里,比 Linux 的”环境变量引导 + seccomp 兜底”更严,但代价是 macOS 的网络栈在内核层就被锁住。

差异主要在两点:

  • 沙箱内进程能不能创建任意 socket:macOS 强制不允许,Linux 默认允许但被 seccomp 阻止;
  • 代理怎么被发现:macOS 写死在 profile 里,Linux 走环境变量。

七、典型配置长什么样

/sandbox 面板的 Config 标签展示解析后的配置,实际写在 settings.json:

{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "filesystem": {
      "allowWrite": ["./node_modules", "./dist"],
      "denyRead": ["~/.ssh", "~/.aws/credentials"]
    },
    "network": {
      "allowedDomains": [
        "github.com",
        "registry.npmjs.org",
        "pypi.org"
      ],
      "allowUnixSockets": ["/var/run/docker.sock"]
    }
  }
}

两个常被忽视的注意点:

  • allowUnixSockets 一定要精确路径。/var/run/docker.sock 一旦开放就等于把 root 权限交出去,OpenClaw 案例里那个把根目录暴露出去的 Web Server 就是 Docker socket 滥用;
  • allowedDomains 里写 github.com 依然有数据外泄风险(公网 Gist、issue 评论),处理敏感数据时建议走内部镜像。

八、自检与故障排查

沙箱启用后,调试时分三步定位:

  1. which bwrap、which socat:依赖必须能查到;
  2. grep CONFIG_SECCOMP /boot/config-$(uname -r):内核要带 CONFIG_SECCOMP=y;
  3. 在沙箱里跑 curl https://example.com,用 journalctl -f 看代理日志,没命中白名单就会被拒。

WSL1 走的是 syscall 翻译层,没有 user namespace,沙箱会直接报错”需要 WSL2″。在 WSL2 上运行 Windows 可执行文件也会被沙箱拦——cmd.exe 之类通过 Unix 套接字访问 Windows 主机,沙箱会阻止这条通路;需要时把这些命令加进 excludedCommands,让它们走沙箱外。

九、整套链路的小结

把工具链拼回一张图:

Bash 命令
   ↓
bwrap (--unshare-net, --unshare-pid, --new-session)
   ↓ (无网络,仅 Unix 域套接字)
socat TCP-LISTEN:3128 → Unix socket (bind mount)
   ↓
宿主机 socat → HTTP/SOCKS5 代理
   ↓
域名白名单/黑名单 → 真实网络
   ↓ (同时)
seccomp BPF: 禁 socket(AF_UNIX) / 禁 io_uring

每一层都对应一个真实威胁:bwrap 切断 IP,Unix socket 桥接受控流量,代理做策略决策,seccomp 防止逃逸。任何一层缺失都会让整条防线变成”开口的笼子”。

到这里,沙箱网络隔离的”切、桥、拦、审”四步就完整了;想给 Claude 接入新的出站目的地时,先想清楚”该放在白名单还是临时放行”,再去碰代理配置。

常见问题(FAQ)

Q1:能不能完全关掉网络隔离让沙箱走宿主网络?

可以,但要把 allowUnsandboxedCommands 设为 false 同时把相应命令加进 excludedCommands,否则 Claude 会自己用 dangerouslyDisableSandbox 退出沙箱执行。

Q2:seccomp 报错 Operation not permitted 怎么办?

通常是内核版本过低或缺少 CONFIG_SECCOMP_FILTER;容器内运行时需要 --cap-add=SYS_ADMIN 或特权模式,否则 BPF 装载会被拒绝。

Q3:为什么允许了 github.com 还是访问失败?

白名单只匹配主机名,git over SSH 不走 HTTP 代理,需要把 allowUnixSockets 指向 SSH agent 套接字,或显式在白名单加 *.github.com 让通配符覆盖 SSH 端口的 22 端口策略。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
拓扑大师的头像拓扑大师普通用户

相关推荐

返回顶部