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 评论),处理敏感数据时建议走内部镜像。
八、自检与故障排查
沙箱启用后,调试时分三步定位:
which bwrap、which socat:依赖必须能查到;grep CONFIG_SECCOMP /boot/config-$(uname -r):内核要带CONFIG_SECCOMP=y;- 在沙箱里跑
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 端口策略。