Windows:受限令牌、防火墙过滤器与两个专用系统用户
没有 seatbelt,也没有 bubblewrap。Windows 上的沙箱是三道关卡叠起来的:令牌管写,专用账户管你是谁,WFP 按这个身份滤网。AppContainer 对不上这份权限模型,整套还默认关着。
- Windows 开关关着,平台沙箱返回空,后面不会包包装器manager.rs L62
- 需要沙箱时,用自己的 exe 加隐藏参数当包装器wrapper.rs L20
- 三个标志裁出受限令牌:削特权、LUA、写必须过交叉检查token.rs L480
- RestrictedToken 这一档,读检查不看 capability SIDwindows.rs L109
- Elevated 先用专用账户登录,再从那个令牌往下裁runner_client.rs L348
- 两个账户名写死:Offline 与 Onlinesetup.rs L50
- 网络策略未开或走代理时,选 Offline 身份setup.rs L706
- 只给离线账户装持久 WFP 过滤器wfp.rs L75
- 用户配置根默认挖空 .ssh 等目录setup.rs L56
- 没有后端时,只读档案加 Never,未匹配命令必须 Forbiddenexec_policy_windows_tests.rs L113
同事在 Mac 上跑 Codex,agent 去碰用户目录里的 SSH 私钥路径,立刻被 Seatbelt 拦住,错误写着 Operation not permitted。同一条读取放到 Windows 上,报错变成 Access is denied。再换一台没开 Windows 沙箱的机器,这条命令可能根本走不到 ACL,execpolicy 先把它判成 Forbidden 或 Prompt。
三台机器、同一份意图、三种失败。模型看到的下一句话不一样,下一步也会跟着变。如果 Windows 上假装有和 Unix 一样的默认沙箱,模型会按自己被关着去规划,实际命令裸跑。
macOS 限制这个进程能对哪些路径做哪些操作。Linux 限制这个进程能看见什么、能调哪些系统调用。Windows 没有 namespace,也没有 seccomp。Codex 换成另一套问题:这个身份能碰哪些资源。
令牌决定你是谁。RestrictedToken 档从当前用户令牌裁,Elevated 档先用专用账户登录,再从那个令牌裁。两边最后都进同一组标志:DISABLE_MAX_PRIVILEGE、LUA_TOKEN、WRITE_RESTRICTED。特权被削薄,写必须同时通过普通 ACL 和 restricting SID 的交叉检查。
读在 RestrictedToken 这一档仍然跟当前用户走。源码自己写了:WRITE_RESTRICTED 令牌上的 capability SID deny-read ACE 不参与读检查,所以读限制必须走 Elevated,裸跑会被直接拒绝。出处:codex-rs/windows-sandbox-rs/src/token.rs 第 480 行;codex-rs/sandboxing/src/windows.rs 第 109 至 117 行
主体能做什么,和客体能被谁碰,是两套隔离原语。换语言重写,Windows 上仍然没有 bwrap 可以调用。该问的还是:你有没有一份身份,这份身份被允许碰什么。
包装方式也是同一类自我调用。Windows 用自己的 exe 加上隐藏参数 --run-as-windows-sandbox,跟 Linux 改 arg0 是同一思路。出处:codex-rs/windows-sandbox-rs/src/wrapper.rs 第 20 行
WFP 是 Windows Filtering Platform,一套内核里的包过滤框架。它和防火墙规则都按 SID 匹配。一个 SID 只能对应一套网络策略。同一身份没法同时表示完全没网,也没法同时表示可以走代理或出网。
所以用两个本地用户当两个网络身份:CodexSandboxOffline 和 CodexSandboxOnline。from_permissions 在强制走代理,或网络策略未启用时,选 Offline。离线身份装出站阻断、代理端口白名单,再加 12 条 WFP,覆盖 ICMP、DNS 53、DNS-over-TLS 853 和 SMB。在线身份不装这套阻断。
Unix 的网络隔离是给进程换一张网卡视图。WFP 是进程还在主机网络栈上,只是这个 SID 发出的特定流量被挡住。进程仍然能看见网卡、解析失败、连别的端口。
过滤器用稳定 GUID 标识,带着持久标志,重启后还在。卸载和崩溃都不会自动拆掉它们。仓库里检索不到删除用户或拆除 WFP 的函数。WFP 失败被写成非致命,防火墙失败会让 setup 整段失败。出处:codex-rs/windows-sandbox-rs/src/setup.rs 第 50 至 51 行、第 706 至 714 行;codex-rs/windows-sandbox-rs/src/wfp.rs 第 69 至 95 行
按身份做访问控制,身份数量必须覆盖策略组合数。网络策略有两档,就需要两个身份。这和用两张门禁卡进两栋楼是同一件事,跟 WFP 这个具体 API 无关。
有人会问为什么不上 AppContainer。它的可读范围默认很窄,任意路径读取要改宿主 DACL。Codex 需要 workspace 可读、平台根可读,再按 deny 列表挖空。受限令牌加 ACL 更贴这份权限模型。在 codex-rs/ 里检索 AppContainer,业务代码零命中。对照仓库 DeepSeek Harness 把同一判断写成了明文:AppContainer 做不了任意路径读取。
Elevated 要把读和网络也管住,就要建账户、弹 UAC、改防火墙和 WFP。企业策略可能禁止本地建用户。把默认设成 Disabled,等于把这道产品摩擦留给第一次启用。
枚举自己把默认值写成关。没写 windows.sandbox、两个 legacy flag 也没有时,就落回 Disabled。legacy flag 已经被标成 Removed。
pub enum WindowsSandboxLevel {
#[default]
Disabled,
RestrictedToken,
Elevated,
}
openai/codex,核对文件 codex-rs/protocol/src/config_types.rs,commit 4f39251a01,核对日期 2026-08-22。代码块保留源码原文,#[default] 钉在 Disabled 上。关掉之后,平台沙箱确实没了。get_platform_sandbox(false) 在 Windows 上返回空,select_initial 把它变成 SandboxType::None。命令仍可能被策略层拦住。Windows 测试锁住了这条:没有后端时,只读档案加 Never 审批,未匹配的 cmd.exe /c dir 必须是 Decision::Forbidden。出处:codex-rs/sandboxing/src/manager.rs 第 62 至 72 行、第 301 行;codex-rs/core/src/exec_policy_windows_tests.rs 第 113 至 130 行
隔离深度跟安装预算挂钩。只防模型误改仓库,当前用户受限令牌就够。要防读出密钥再从这台机器往外传,必须再做读限制和按身份的网络过滤。承诺了做不到的墙,模型和用户都会按那道墙做决策。
跨平台能力不对等时,对外说法必须按实际启用的那一层来写。macOS 默认就有 Seatbelt,Windows 默认可能是 Disabled。用户听到有沙箱,会以为三台机器同一道墙。
DeepSeek Harness:同一组标志,停在写限制
DSH 的 @deepseek-ai/dsh-sandbox-windows-acl 用同一组 CreateRestrictedToken 标志:DISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED。README 写明读、网络、进程可见性不受限,enforcement 标成 partial。它不建专用账户,不装 WFP。
设计笔记把 AppContainer 否掉的原因写死了:AppContainer 令牌没有环境读权限,任意路径读取必须预先授权,对不上 harness 的读模型。mxc 被否是因为系统版本底线太新,任意路径读取还是要改宿主 DACL。Codex Elevated 用两个账户加 WFP 把读和网络身份也做进去,代价是安装摩擦和卸载残留。DSH 换到的是装得上、跑得动,读侧和网络侧留给别的机制。
已核对 DSH token.ts / win32-abi.ts / README / 设计笔记 · 2026-08-22 · DSH · 沙箱:从 seatbelt 到执行世界Claude 拒绝执行,Grok 把平台标成 unknown
Claude Code 在原生 Windows 上写明没有沙箱。企业策略要求沙箱且禁止无沙箱命令时,PowerShell 直接拒绝执行,文案是 sandboxing is not available on native Windows。它不假装有一层隔离。Grok Build 的沙箱 crate 把平台写成 linux/landlock 和 macos/seatbelt,其余标 unknown。在 xai-grok-sandbox 里检索 AppContainer、RestrictedToken、CreateRestrictedToken,没有对应实现。
答案分成三档。Claude 和 Grok 在 Windows 上不做文件系统沙箱。DSH 做了写限制,装得上,读和网络留给别的机制。Codex 把身份、ACL、WFP、专用账户全做进去,摩擦更大,所以默认关着。
已核对 Claude PowerShellTool.tsx、Grok types.rs · 2026-08-22 · Grok · 五种沙箱 Profile同一条读取,换档之后卡在哪
把演示里的动作留在读工作区外,档位从 Elevated 降到 RestrictedToken。先写下你的预测:哪一关会放行,哪一关会消失,最后是 Access is denied 还是穿过。再点播放核对。
进阶一问:把动作换成发网络。为什么 RestrictedToken 档第三关是跳过,Elevated 档才会亮起 WFP。