2654 字
约 8 分钟
1
Linux:先建视图,再上 seccomp,最后 exec
无标签

OpenAI Codex · 代码模式 Linux:先建视图,再上 seccomp,最后 exec 同一条读取,Mac 上回 Operation not permitted,Linux 上回 No such file or directory。差别在于 Linux 先换进程能看见的文件树,再收紧它能调用的系统接口。 一句话速览 同一条读取,Mac 上回 Operation not permitted,Linux 上回 No such file or directory。差别在于 Linux 先换进程能看见的文件树,再收紧它能调用的系统接口。

课程目标 读完能说清三件事。Linux 默认路径为什么分两拍:先用 bubblewrap 建文件系统视图,再上 seccomp。为什么要单独起一个 helper。为什么类型名还叫 LandlockCommand,默认却走 bubblewrap。

先玩一遍 · 一个请求穿过两层

换探针、拨开关,看请求在哪一层停下

播放 单步 重置

探针

读 /etc/shadow 读 workspace connect 外网 关掉 user namespace

开关

WSL1 legacy Landlock

先看默认路径上读 /etc/shadow 停在视图。再换成 connect,同一条路会走到过滤器。

这次请求 open /etc/shadow

当前路径 默认两阶段

第一层 · 预检 待命 transform 先问 helper 在不在、是不是 WSL1、能不能建 user namespace。进不去就在这里停。

第二层 · 视图 待命 bubblewrap 先换文件树。默认只读,可写根再叠上去。不在挂载里的路径,后面会报文件不存在。

第三层 · seccomp 待命 视图建好之后,才打开 no_new_privs、装网络过滤器。命中规则返回 EPERM。

点播放,看这个请求依次穿过两层。

逻辑轨迹 · 动画每一步对应源码里的哪一段

读入权限档案并做 WSL1 预检 manager.rs L413 拼出 helper argv,加上权限档案 JSON landlock.rs L23 外层 run_main 决定走 bwrap 还是捷径 linux_run_main.rs L152 拼内层命令,带上 --apply-seccomp-then-exec linux_run_main.rs L1520 bwrap 按顺序 ro-bind、bind、unshare bwrap.rs L306 exec bwrap 时插入 --as-pid-1 launcher.rs L39 内层 capget,确认能力已清零 linux_run_main.rs L216 打开 PR_SET_NO_NEW_PRIVS,然后装 seccomp landlock.rs L61 fork 用户命令,父进程 waitpid 收孤儿 linux_run_main.rs L243 execvp 接管进程镜像 linux_run_main.rs L1565

点播放,看这个请求依次穿过两层。

视图挡的是看见 路径不在挂载里,内核回 No such file or directory。这一层还没轮到系统调用表。 seccomp 挡的是调用 过滤器默认放行,命中 connect、bind、ptrace 这类规则才回 EPERM。已经看不见的文件,它不管。 进不去就明说 WSL1 或缺 user namespace 时,命令停在 transform。默认路径失败不会悄悄换成 Landlock。

教学示意:路径与探针为课程化设定。逻辑轨迹右侧行号对应 openai/codex 仓库 commit 4f39251a01。

思路一 · 先换它能看见的世界,再收紧调用

它解决什么问题 同事在 Mac 上跑 Codex,agent 去读 ~/.ssh/id_rsa ,Seatbelt 立刻回 Operation not permitted。同一份仓库放到 Linux 笔记本上,常见文案变成 No such file or directory。路径常常根本不在挂载里。 如果先打开 PR_SET_NO_NEW_PRIVS ,再去调系统自带的 bwrap,不少发行版上的 setuid 二进制会直接起不来。沙箱在装得最全的机器上反而失败。seccomp 是一套内核过滤器,用来规定进程还能调用哪些系统接口; no_new_privs 是它的前置条件,也会挡住 setuid 提权。

思路是什么 默认路径两次进入同一份 helper。外层只拼 bubblewrap,把文件系统换成默认只读,再叠可写根, .git 、 .agents 、 .codex 即使落在可写根里也保持只读。内层才打开 no_new_privs 、装网络 seccomp,然后 fork、把进程镜像让给用户命令。 函数头把顺序写成三步:

codex-rs/linux-sandbox/src/linux_run_main.rs 第 152 至 158 行

/// Entry point for the Linux sandbox helper.
///
/// The sequence is:
/// 1. When needed, wrap the command with bubblewrap to construct the
/// filesystem view.
/// 2. Apply in-process restrictions (no_new_privs + seccomp).
/// 3. `execvp` into the final command.

源码快照说明:依据本地仓库 openai/codex ,核对文件 codex-rs/linux-sandbox/src/linux_run_main.rs ,commit 4f39251a01 ,核对日期 2026-08-22。代码块保留源码原文,这三步就是默认路径的合同。 字段注释把原因写在旗标上:bubblewrap 可能依赖 setuid,必须先建好视图,再收紧。 出处: codex-rs/linux-sandbox/src/linux_run_main.rs 第 117 至 121 行; codex-rs/linux-sandbox/src/landlock.rs 第 57 至 65 行

外层 helper 拼 bubblewrap 旗标 --ro-bind 再 --bind 先换文件树 还不碰 no_new_privs

内层 helper capget 确认能力为零 no_new_privs 加 seccomp 再收紧系统调用 带 --apply-seccomp-then-exec

execvp 用户命令 接手进程

教学化结构图:同一份二进制进两次,中间隔着 bubblewrap 建好的世界。

为什么长期成立 能看见什么,和能调用哪些 syscall,是两道墙。先换世界,再收紧调用。换成别的语言重写,发行版上只要还有 setuid 包装器,这个顺序就还成立。

思路二 · 单独起 helper,留下一个收孤儿的人

它解决什么问题 bubblewrap 和 seccomp 必须发生在即将变成用户命令的那个进程里。主 CLI 自己 unshare 再 exec,失败会把整次会话带走,也没法在 PID namespace 里留下一个收孤儿的 1 号进程。超时和取消会留下睡眠进程。

思路是什么 Linux 看 argv[0] 的 basename。文件名已经是 codex-linux-sandbox 就保留,否则改成这个别名,把同一份二进制拐进 helper。外层 exec 的不是 ls ,是 helper 自己,带着 --apply-seccomp-then-exec 。launcher 还会插进 --as-pid-1 ,让 bubblewrap 自己当命名空间里的 1 号进程。 内层先 capget 。effective 或 permitted 任一非零,立刻 panic,命令不会跑。过了这道检查,才 fork。子进程 execvp ,父进程 waitpid(-1) 把子孙都收掉,退出码原样传出。 出处: codex-rs/linux-sandbox/src/linux_run_main.rs 第 216 至 221 行、第 242 至 243 行; codex-rs/linux-sandbox/src/launcher.rs 第 38 至 39 行

SandboxManager

外层 helper 拼内层 argv

bubblewrap --as-pid-1

内层 fork 出命令 子进程 execvp

父进程留下 waitpid 收孤儿 取消和超时靠还在的那一个 1 号进程

教学化时序图:进程让出去之后,收尸的人还在。

为什么长期成立 独立辅助进程加先包装、再收紧,是跨平台同一类自我调用。Windows 看隐藏参数,Linux 看 arg0。包装器失败时,固定退出码加固定 stderr 前缀更好自动归因。

思路三 · 默认不是 Landlock,失败也不回退

它解决什么问题 类型名还叫 LandlockCommand ,crate 目录也叫 linux-sandbox ,网上不少资料会写成 Codex 在 Linux 上用 Landlock。当前默认文件系统沙箱已经是 bubblewrap。Landlock 是显式打开的 legacy 回退,特性开关标成 Deprecated,默认关。legacy 吃不下受限读,也吃不下拆分出来的嵌套只读。 如果 bwrap 失败后静默换 Landlock,模型会按「我被关在视图里」规划,实际却还看得见主机文件树。下一步就会分叉。

思路是什么 外层写明 never falls back。bubblewrap 失败就失败。WSL1 在需要 bubblewrap 时, transform 返回 Wsl1UnsupportedForBubblewrap ,命令不会进 helper。全盘可写且不走代理时,根本不会进 bwrap,WSL1 可以过。user namespace 建不出来给启动警告,不会改走更弱的后端。 出处: codex-rs/linux-sandbox/src/linux_run_main.rs 第 290 至 294 行; codex-rs/sandboxing/src/manager.rs 第 413 至 420 行、第 696 至 710 行; codex-rs/features/src/lib.rs 第 1064 至 1069 行

本机发行版 有 user namespace 系统 bwrap 或打包二进制

默认两阶段

CI 容器 禁了 user namespace 不会静默改走 Landlock

启动警告,硬失败

WSL1 建不出 user namespace 需要 bwrap 的命令进不去

transform 阶段拒绝

教学化结构图:都叫 Linux 沙箱,三台机器启用的那一层并不一样。

看见和调用是两道墙。进不去就明说,不要合成一句已经沙箱了。

为什么长期成立 跨平台能力不对等时,最容易犯的错是用「我们有 Linux 沙箱」覆盖所有 Linux。本机有新 bwrap、CI 容器禁了 user namespace、同事还在 WSL1,三台机器不是同一道墙。产品文案和模型说明必须按实际启用的那一层来写。

横向对比 · 三拍拆开之后,各自漏什么

DeepSeek Harness:探得过就换档,合成一拍 DSH 的 Linux 链写死为 bwrap 再 landlock 。bwrap 档是视图加 exec,没有 seccomp。探不过才走 landlock-run :在自己身上装规则,再 exec 目标命令。文件系统限制和 exec 合在同一份 main 里。这条路径没有 setuid bwrap,所以不必把过滤器再拆到下一拍。 两边都走独立辅助进程。DSH 启动器失败是 125,还要同时看到 landlock-run: 致命行,更好自动归因。Codex helper 失败是 panic 或原样 wait status。两条链都探失败,DSH 拒绝裸跑。它可以换探测过的第二档,换完仍 fail closed。Codex 默认路径失败不回退 Landlock。 出处: packages/sandbox/sandbox-local/src/index.ts 第 159 至 166 行; native/landlock-run/packages/entry/src/main.c 第 254 至 261 行 已核对 DSH PLATFORM_CHAINS 与 landlock-run · 2026-08-22 · DSH · 沙箱

Grok:三拍打在 agent 上,失败可以回退 Grok 的顺序和 Codex 同一条工程理由:先让可能依赖 setuid 的 bwrap 跑完,再在已经进命名空间的进程里收紧。差别在主体。Grok 把三拍打在 agent 进程上,命令只继承结果。视图是补丁式的 --bind / / 再盖 deny 路径,主机树还在。子进程 spawn 时,若需要限网,才在 pre_exec 里装 seccomp。 bwrap exec 失败且不要求 read-deny 时,它警告一句,回退到 Landlock。内置 Profile 的 apply 失败记警告并继续, is_active() 才是真状态。Codex 外层写明 never falls back。 出处: crates/codegen/xai-grok-shell/src/config/mod.rs 第 1293 至 1320 行; crates/codegen/xai-grok-sandbox/src/lib.rs 第 181 至 192 行 已核对 Grok bwrap_reexec_for_profile 与 apply · 2026-08-22 · Grok · 五种沙箱 Profile

课堂练习

01

同一条读取,换开关之后卡在哪 把探针留在读 /etc/shadow ,先打开 WSL1。写下预测:停在哪一层,文案是什么。再关掉 WSL1,打开 legacy Landlock,看受限读会不会变成 UnsupportedOperation。 进阶一问:探针换成 connect 外网。为什么默认路径会穿过视图、停在 seccomp,文案从 No such file 变成 EPERM。

Takeaway: Linux 默认路径是三拍:先用 bubblewrap 换文件系统视图,再在已经进命名空间的 helper 里上 seccomp,最后才 exec。顺序反了,setuid 的系统 bwrap 会起不来。类型名还叫 Landlock,那是兼容,不是现行默认。进不去就在 spawn 之前拒绝,失败不换更弱的后端。

Linux:先建视图,再上 seccomp,最后 exec
http://www.clxhxhhr.top/posts/4058/
作者
clxstart
发布于
2026-09-25
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。