- Windows 宿主机模式基于 WSL2 实现沙箱执行:只读/批准/自动审核识别、 工作区可写+区外只读挂载、网络档位(受限/开放/禁止)、敏感目录屏蔽 - 新增 scripts/setup-wsl-sandbox.ps1 一键准备 WSL 沙箱发行版 - 执行环境提示词按平台拆分骨架:prompts/execution_mode/macos.txt (原 execution_mode.txt,mac/dockerweb 沿用)与 windows.txt (骨架+动态注入环境与路径、当前规则、网络档位、切换一致性说明) - Windows 执行环境切换通知改为完整命令写法说明(双向),mac 保持原样 - 附调研与 PoC 验证报告:wsl2-sandbox-research.md、 windows-sandbox-research.md、wsl2-sandbox-poc-report.md
6.3 KiB
6.3 KiB
WSL2 + bwrap 沙箱概念验证(PoC)实验报告
日期:2026-07-30 · 实验机:Windows 11 (26200) · WSL2 内核 5.15.167.4 结论先行:路线可行,隔离强度达到调研预期,可以进入源码改造。 前提条件:必须使用专用发行版并关闭 Windows 互操作(interop),否则存在沙箱逃逸(见 E8)。
1. 实验环境搭建(已验证的部署流程)
:: 1. 下载 Alpine minirootfs(~3.5MB)
curl -L -o alpine-minirootfs.tar.gz https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-minirootfs-3.21.3-x86_64.tar.gz
:: 2. 导入为专用沙箱发行版(非交互,可编程)
wsl --import astrion-sandbox <安装目录> alpine-minirootfs.tar.gz --version 2
:: 3. 发行版内配置(详见 §3 的发现)
:: - /etc/wsl.conf: [interop] enabled=false / appendWindowsPath=false;[network] generateResolvConf=false
:: - /etc/resolv.conf: 固定公共 DNS(223.5.5.5 / 119.29.29.29)
:: - /etc/apk/repositories: 换国内镜像(mirrors.aliyun.com)
:: - apk add bubblewrap bash ncurses-libs python3 git
2. 实验结果总表(24 项,23 PASS / 1 INFO)
沙箱命令模板(与项目现有 _build_linux_common_plan 同构):
只读模式: bwrap --die-with-parent --new-session --unshare-all --share-net \
--ro-bind / / --ro-bind <ws> <ws> --chdir <ws> \
--proc /proc --dev /dev --tmpfs /tmp -- bash -lc <cmd>
工作区可写: 同上,工作区改为 --bind <ws> <ws>
网络全禁/仅回环: 去掉 --share-net(--unshare-all 自带的 unshare-net,lo 自动可用)
| 分组 | 实验 | 结果 | 说明 |
|---|---|---|---|
| E0 基础 | 发行版执行 / bwrap 0.11 / user namespace | ✅ | WSL2 满足 bwrap 全部前提 |
| E0 路径 | E:\... → /mnt/e/... 转换 + 工作目录 |
✅ | 机械转换即可,含空格路径正常 |
| E0 编码 | WSL_UTF8=1 解决 wsl.exe UTF-16 乱码 |
✅ | 必须设置 |
| E0 输出 | localhost 代理警告仅在 stderr | ✅ | 可在执行器层过滤 |
| E1 只读 | 工作区内写入 → Read-only file system |
✅ | 报错关键词与 mac 语义一致,审批关键词匹配可直接复用 |
| E1 只读 | 系统/工作区读取正常 | ✅ | |
| E1 只读 | 写 Linux 系统目录被拒 | ✅ | |
| E1 只读 | 写工作区外 Windows 盘(9P 子挂载)被拒 | ✅ | 关键项:ro-bind / 对 9P 挂载同样生效 |
| E2 可写 | 工作区内写入真实落盘 Windows | ✅ | |
| E2 可写 | 工作区外 Windows 盘写入被拒 | ✅ | |
| E2 可写 | Linux 系统目录写入被拒;tmpfs /tmp 可写 | ✅ | 对齐 macOS /tmp 语义 |
| E3 禁读 | 未掩蔽时敏感文件可读(风险确认) | ✅ | 前置确认 |
| E3 禁读 | --ro-bind /dev/null <file> 掩蔽单文件 |
✅ | |
| E3 禁读 | --tmpfs <dir> 掩蔽整目录 |
✅ | 可实现 macOS deny-read 等价 |
| E4 网络 | share-net 外网可达 | ✅ | |
| E4 网络 | unshare-net 外网不可达 | ✅ | |
| E4 网络 | unshare-net 下 lo 可用、127.0.0.1 实际通信 | ✅ | 等价 macOS restricted(仅回环)语义 |
| E5 综合 | 只读模式 rm -rf 工作区被拦截 |
✅ | |
| E5 综合 | 可写沙箱 rm -rf /mnt/c/Windows/... 全部 Read-only |
✅ | |
| E6 交互 | bwrap 内 bash -i stdio 逐条往返 |
✅ | 持久终端方案成立 |
| E7 性能 | 单次 wsl.exe 调用 ~950-1000ms | ℹ️ | 后续需长驻进程复用优化 |
| E8 工具链 | git init/add/commit 全流程、python3 写文件 | ✅ | 工作区内正常,工作区外 OSError 拒绝 |
3. 关键发现(实验中新暴露、调研未覆盖的)
E8-逃逸(最严重):必须关闭 Windows interop
- 未关闭时:只读 bwrap 沙箱内执行
cmd.exe /c echo pwned > E:\...\file.txt成功写入 Windows 盘。interop 进程由 WSL 会话 init 在命名空间外拉起,bwrap 完全管不到 → 沙箱形同虚设。 - 封堵方式(已验证):专用发行版
/etc/wsl.conf设[interop] enabled=false+appendWindowsPath=false,重启发行版后cmd.exe: command not found,逃逸封死。 - 结论:沙箱发行版必须是专用、受控配置的,绝不能复用用户自己的 Ubuntu 等发行版。Claude Code 的 WSL 沙箱同样禁 interop,属行业共识做法。
网络环境适配(本机实测暴露)
- 宿主使用 localhost 代理时,WSL NAT 模式的 DNS 转发(10.255.255.254 → 宿主解析器)会失效(时好时坏);直连公共 DNS(223.5.5.5)正常 → 发行版需固化
generateResolvConf=false+ 公共 DNS。 - apk 官方源大包下载会卡死,换阿里云镜像后秒装 → 发行版应内置国内镜像(或做成可配置)。
- wsl.exe 每次启动都在 stderr 打 localhost 代理警告 → 执行器需过滤 stderr 该行(或在
.wslconfig全局设autoProxy=false,影响面大,不推荐默认做)。
工程细节
- Git Bash 调用 wsl.exe 会被 MSYS2 路径转换毁掉 Unix 参数(
/etc/...→E:/KimiData/...)→ 仅影响开发调试(设MSYS2_ARG_CONV_EXCL='*'),Python subprocess 不受影响。 bash需显式安装(Alpine 默认 busybox sh),且需ncurses-libs才能跑。
4. 与 macOS 沙箱的能力对照(最终形态)
| 能力 | macOS(现状) | Windows WSL2+bwrap(PoC 验证后) |
|---|---|---|
| 只读模式 | sandbox-exec deny-write | bwrap 全 ro-bind ✅ |
| 工作区可写 | profile writable paths | ro-bind / + bind ws ✅ |
| 敏感路径禁读 | deny subpath / regex | tmpfs/dev-null 掩蔽(无 regex,需逐个路径)⚠️ |
| 网络三档 | profile 网络规则 | share-net / unshare-net(restricted≈仅回环)✅ |
| 审批链语境 | Unix 报错关键词 | 完全一致(Read-only file system / Permission denied)✅ |
| 只读命令白名单 | Unix 命令集 | 完全一致(同为 Linux bash)✅ |
| 交互终端 | sandbox-exec bash -i | bwrap bash -i ✅ |
遗留限制(二期):单次调用 ~950ms 开销需长驻 WSL 进程复用;禁读不支持 regex(可改为启动前枚举 .env 类文件逐个掩蔽);seccomp 过滤器暂未接入(bwrap 本身已满足 FS/网络隔离需求)。
5. 原始实验日志
完整 24 项原始输出见 .wsl-poc/results.md(含 E5.2 逐文件 Read-only 报错洪流,可佐证 9P 挂载保护的真实性)。实验脚本:.wsl-poc/run_experiments.sh。