OpenAI Codex · 代码模式 Windows:受限令牌、防火墙过滤器与两个专用系统用户 没有 seatbelt,也没有 bubblewrap。Windows 上的沙箱是三道关卡叠起来的:令牌管写,专用账户管你是谁,WFP 按这个身份滤网。AppContainer 对不上这份权限模型,整套还默认关着。 一句话速览 没有 seatbelt 和 bubblewrap 的平台怎么叠出沙箱,以及 AppContainer 为什么被否掉
课程目标 读完能说清三件事。Windows 上为什么只能按身份锁资源。受限令牌那三个标志为什么管得住写、管不住读和网络。为什么要两个专用系统用户,以及 AppContainer 为什么被放下。
先玩一遍 · 一个请求穿过三道关
换动作、换档位,看请求在哪一关停下
播放 单步 重置
动作
读工作区 读工作区外 发网络 改系统目录
档位
关 RestrictedToken Elevated
先看 Elevated 怎么把读挡在身份关。再降到 RestrictedToken,同一条读取会穿过去。
这次请求 读 C:\Users\you.ssh\id_rsa
当前身份 CodexSandboxOffline
关卡 1 · 受限令牌 待命 三个标志裁令牌。写必须过交叉检查,读在这一档仍跟当前用户走。
关卡 2 · 专用系统用户 待命 Elevated 才登录 CodexSandboxOffline 或 Online,按账户 ACL 挖空 .ssh 这类目录。
关卡 3 · 防火墙过滤器 待命 只给离线账户装持久 WFP。ICMP、DNS、SMB 被内核滤掉,普通出站给出站防火墙。
点播放,看这个请求依次走过三道关。
逻辑轨迹 · 动画每一步对应源码里的哪一段
Windows 开关关着,平台沙箱返回空,后面不会包包装器 manager.rs L62 需要沙箱时,用自己的 exe 加隐藏参数当包装器 wrapper.rs L20 三个标志裁出受限令牌:削特权、LUA、写必须过交叉检查 token.rs L480 RestrictedToken 这一档,读检查不看 capability SID windows.rs L109 Elevated 先用专用账户登录,再从那个令牌往下裁 runner_client.rs L348 两个账户名写死:Offline 与 Online setup.rs L50 网络策略未开或走代理时,选 Offline 身份 setup.rs L706 只给离线账户装持久 WFP 过滤器 wfp.rs L75 用户配置根默认挖空 .ssh 等目录 setup.rs L56 没有后端时,只读档案加 Never,未匹配命令必须 Forbidden exec_policy_windows_tests.rs L113
点播放,看这个请求依次走过三道关。
令牌档只锁写 RestrictedToken 从当前用户裁令牌。读工作区外的文件、往外发网络,这两条都会穿过。要拦它们,必须换 Elevated。 身份关才有挖空 .ssh 出现在用户配置排除列表里。只有登录了专用账户,这份 ACL 才会生效。 网络挂在 SID 上 WFP 只装给离线账户。令牌档没有这两个账户,第三关根本不存在。
教学示意:路径与账户名为课程化设定。逻辑轨迹右侧行号对应 openai/codex 仓库 commit 4f39251a01。
思路一 · 没有进程视图,就按身份锁资源
它解决什么问题 同事在 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 行
macOS 锁进程能做什么
SBPL 文本
file-read 规则拒绝 Operation not permitted
Linux 锁进程能看见什么
bubblewrap 视图
路径不在挂载里 No such file or directory
Windows 锁这个身份能碰什么
令牌加 ACL
身份没有权限 Access is denied
教学化结构图:同一条读取,三套隔离锁在不同层,模型看到的失败语义也不一样。
为什么长期成立 主体能做什么,和客体能被谁碰,是两套隔离原语。换语言重写,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 行
一个 SID,两套互相打架的网策
同一个账户
完全没网
可以走代理或出网 过滤器按 SID 匹配,装不上两套 两个账户,两套网策
CodexSandboxOffline
12 条 WFP 加出站阻断
Online 不装这套阻断
出网
教学化结构图:网络策略有两档,就需要两个身份。
为什么长期成立 按身份做访问控制,身份数量必须覆盖策略组合数。网络策略有两档,就需要两个身份。这和用两张门禁卡进两栋楼是同一件事,跟 WFP 这个具体 API 无关。
思路三 · AppContainer 对不上,Elevated 又太贵,所以默认关
它解决什么问题 有人会问为什么不上 AppContainer。它的可读范围默认很窄,任意路径读取要改宿主 DACL。Codex 需要 workspace 可读、平台根可读,再按 deny 列表挖空。受限令牌加 ACL 更贴这份权限模型。在 codex-rs/ 里检索 AppContainer ,业务代码零命中。对照仓库 DeepSeek Harness 把同一判断写成了明文:AppContainer 做不了任意路径读取。 Elevated 要把读和网络也管住,就要建账户、弹 UAC、改防火墙和 WFP。企业策略可能禁止本地建用户。把默认设成 Disabled ,等于把这道产品摩擦留给第一次启用。
思路是什么 枚举自己把默认值写成关。没写 windows.sandbox 、两个 legacy flag 也没有时,就落回 Disabled 。legacy flag 已经被标成 Removed。
codex-rs/protocol/src/config_types.rs 第 297 至 302 行
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 行
AppContainer 默认可读范围很窄 任意路径要改宿主 DACL 对不上这份权限模型
组合拳 令牌锁写 专用账户锁读 WFP 按 SID 滤网 要 UAC,要本地用户
默认 Disabled execpolicy 兜底
教学化结构图:用得上的那套太贵,默认先关,策略层兜住裸跑。
三道关卡,一道管写,一道管你是谁,一道管这个身份能出哪扇网门。
为什么长期成立 隔离深度跟安装预算挂钩。只防模型误改仓库,当前用户受限令牌就够。要防读出密钥再从这台机器往外传,必须再做读限制和按身份的网络过滤。承诺了做不到的墙,模型和用户都会按那道墙做决策。 跨平台能力不对等时,对外说法必须按实际启用的那一层来写。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
课堂练习
01
同一条读取,换档之后卡在哪 把演示里的动作留在读工作区外,档位从 Elevated 降到 RestrictedToken。先写下你的预测:哪一关会放行,哪一关会消失,最后是 Access is denied 还是穿过。再点播放核对。 进阶一问:把动作换成发网络。为什么 RestrictedToken 档第三关是跳过,Elevated 档才会亮起 WFP。
Takeaway: Windows 没有 seatbelt 和 bubblewrap,隔离只能按身份锁资源。RestrictedToken 那三个标志管写,不管读和网络。要拦密钥和出网,必须再上专用账户、ACL 和按 SID 装的 WFP。AppContainer 对不上这份可读集合,Elevated 又要 UAC,所以默认关着,execpolicy 在没有后端时改成 Forbidden 或 Prompt。