Codex 沙箱与安全精读
调研时间:2026-06-22 源码 commit:98845e4 本地 CLI:codex-cli 0.125.0
一句话定位
Codex 的安全模型核心洞察:安全在 OS 层,不在 prompt 层。三个平台各用内核级机制(macOS Seatbelt、Linux bubblewrap+seccomp+Landlock、Windows Restricted Token)做物理隔离。即使模型被越狱生成恶意命令,sandbox 仍在内核层拦截非法操作。
日常类比
把 Codex 的安全架构想象成一座高安全实验室:
- exec policy(门禁系统):刷卡认证,白名单人员直接进,陌生人叫保安
- Approval(保安审批):确认来访目的后放行
- Sandbox(实验室隔离间):即使人进来了,也只能在指定区域操作;桌上的危化品锁在柜子里(protected paths),不能带出实验室(no network)
- Escalation(紧急开锁):隔离间限制了正常实验?向实验室主任申请临时开权限
类比边界:实验室是人主动遵守规则,sandbox 是内核强制执行——被 sandbox 拒绝的操作在用户态根本无法完成。
三平台沙箱对比
flowchart LR
SM[SandboxManager] -->|macOS| SB[Seatbelt]
SM -->|Linux| LX[Bwrap + seccomp + Landlock]
SM -->|Windows| WT[Restricted Token]
| 维度 | macOS Seatbelt | Linux Bwrap+seccomp+Landlock | Windows |
|---|---|---|---|
| 内核特性 | App Sandbox / sandbox-exec | user/PID/net namespace + seccomp-BPF + Landlock LSM | Restricted Token |
| 文件系统 | SBPL deny default + 白名单 subpath | ro-bind 全盘只读 + bind 可写根 | Token 降权 + filesystem overrides |
| 网络 | SBPL 规则 deny/allow network | unshare-net namespace隔离 + seccomp 拦截 socket | 待确认 |
| 受保护路径 | excluded_subpaths 在可写根内保持只读 | ro-bind 覆盖 bind + Landlock 规则 | filesystem overrides |
| 进程隔离 | sandbox-exec 子进程继承 | PID namespace unshare-pid | Token-level 进程隔离 |
macOS:Seatbelt 详解
工作原理
macOS 使用 /usr/bin/sandbox-exec 启动受限进程,传入 SBPL(Sandbox Profile Language)策略文件。
默认策略
基础策略以 (deny default) 开始,默认拒绝所有系统调用,然后逐条白名单放行:
;; seatbelt_base_policy.sbpl(简化)
(version 1)
(deny default)
(allow process-exec)
(allow process-fork)
(allow file-write* (literal "/dev/null"))
(allow file-read* (literal "/dev/null"))
(allow sysctl-read)
动态策略生成
Codex 在运行时根据 FileSystemSandboxPolicy 动态生成额外规则。对于可写根 /Users/jason/project 和受保护子路径 .git:
- (allow file-read* (subpath …)) 开放读
- (allow file-write* (subpath …)) 开放写
- (deny file-write* (subpath …/project/.git)) 受保护子路径恢复只读
网络策略(三种模式)
| 模式 | SBPL 输出 | 效果 |
|---|---|---|
| disabled | 无 allow network 规则 | 所有网络被 deny default 拒绝 |
| enabled | allow network-outbound + inbound | 全放行 |
| proxy-only | 仅 allow localhost:port + DNS 53 + AF_UNIX | 只能通过代理出站 |
[源码] Seatbelt 的优势:策略在进程启动时加载,运行中无法被进程自身解除。
Linux:三层组合防御
Linux 沙箱是 Codex 最复杂的实现——三种内核机制叠加使用。
第一层:Bubblewrap(文件系统隔离)
bubblewrap 利用 Linux user namespace 构建隔离的文件系统视图:
bwrap \
--unshare-user \
--unshare-pid \
--unshare-net \
--ro-bind / / \
--bind /project /project \
--ro-bind /project/.git /project/.git \
--dev /dev \
-- /bin/bash -c "..."
[源码] 挂载顺序关键:先全盘只读 - 再开可写根 - 最后恢复受保护子路径只读。
第二层:seccomp-BPF(系统调用过滤)
在进程启动后,通过 BPF 过滤器直接在内核层拦截危险系统调用。
Restricted 模式拦截的系统调用:
- 网络:connect, accept, bind, listen, sendto, sendmmsg, recvmmsg
- socket:只允许 AF_UNIX(检查第一个参数)
- 提权:ptrace, process_vm_readv, process_vm_writev
- 绕过:io_uring_enter, io_uring_setup, io_uring_register
[源码] seccomp 是最后一道防线——即使 namespace 或文件系统挂载被绕过,系统调用仍被拒绝。
第三层:Landlock LSM(备用文件系统控制)
Landlock 使用 Linux Security Module 框架,ABI V5:
// install_filesystem_landlock_rules(简化)
let abi = ABI::V5;
let ruleset = Ruleset::default()
.set_handled_access(AccessFs::from_read(abi) | AccessFs::from_write(abi));
ruleset.add_rule(PathBeneath::new("/", AccessFs::from_read(abi)))?;
ruleset.add_rule(PathBeneath::new("/dev/null", AccessFs::from_all(abi)))?;
for root in writable_roots {
ruleset.add_rule(PathBeneath::new(root, AccessFs::from_all(abi)))?;
}
ruleset.restrict_self()?;
[源码] Landlock 是遗留/备用方案;主路径使用 bwrap。两者可同时生效(层叠)。
三层如何协同
进程启动
|-- Bubblewrap: 创建隔离的文件系统视图 + network namespace
|-- 进程进入新 namespace 后:
| |-- PR_SET_NO_NEW_PRIVS (阻止提权)
| |-- seccomp: 安装 BPF 过滤器拦截系统调用
| |-- Landlock: 安装 LSM 规则(可选,层叠)
|-- 进程执行命令
所有文件/网络/系统调用操作同时受三层约束
权限模型
PermissionProfile
每个工具执行请求携带一个 PermissionProfile:
struct PermissionProfile {
file_system: FileSystemSandboxPolicy,
network: NetworkSandboxPolicy,
}
struct WritableRoot {
root: AbsolutePathBuf,
read_only_subpaths: Vec<String>, // 如 [".git"]
protected_metadata_names: Vec<String>, // 如 [".git", ".agents", ".codex"]
}
SandboxManager 选择逻辑
fn select_initial(preference: SandboxablePreference, ...) -> SandboxType {
match preference {
Forbid => SandboxType::None,
Require => get_platform_sandbox(...).unwrap(),
Auto => if should_sandbox(...) {
get_platform_sandbox(...).unwrap_or(None)
} else { SandboxType::None }
}
}
[源码] should_sandbox 返回 true 的条件:有非空的 file system policy 或 network 被限制。
三道防线协同
安全决策是三道防线叠加,不是择一:
命令到达
[第一道] exec policy -> Forbidden? 直接拒绝
-> Skip { bypass_sandbox: true }? 跳过 sandbox
-> NeedsApproval? 走 approval
[第二道] Approval -> Denied? 不执行 / Approved? 继续
[第三道] Sandbox -> 根据 PermissionProfile 选择沙箱类型
-> 在 OS 级隔离中执行命令
-> Denied? 走 escalation
特殊情况:exec policy 返回 Skip { bypass_sandbox: true } 时,命令完全跳过 sandbox——这只在规则文件显式 Allow 且所有命令段都匹配时才发生。
与开源框架的对比
| 维度 | Codex | LangGraph | CrewAI | tRPC-Agent |
|---|---|---|---|---|
| 隔离层级 | OS 内核 namespace/seccomp/Seatbelt | 无(应用层) | 无 | 服务边界 |
| 文件系统控制 | 白名单可写根 + 受保护子路径 | 无内置机制 | 无 | 无 |
| 网络控制 | namespace + seccomp + proxy 模式 | 无内置机制 | 无 | 服务网格 |
| 进程隔离 | PID namespace + user namespace | 无 | 无 | 容器化可选 |
| 多平台 | macOS + Linux + Windows | N/A | N/A | 依赖部署环境 |
Codex 是目前开源 Agent 框架中唯一在 OS 内核层面做安全隔离的。
与 Claude Code 的差异(待 Agent A 核对)
- Codex sandbox 是 OS 内核级强制(sandbox-exec / bwrap / seccomp),进程在用户态无法绕过;Claude Code 的隔离推测主要在应用层
- Codex 有三种网络模式(全开/全关/proxy-only),代理模式允许受控出站;Claude Code 的网络控制粒度未知
- Codex 受保护路径(.git, .agents, .codex)在 sandbox 层面强制只读;Claude Code 可能通过 permission classifier 在应用层实现
- Codex sandbox escalation 允许 runtime 从有沙箱降级到无沙箱(需二次审批);Claude Code 是否有类似动态降级机制未知
- Codex 三平台实现各自独立;Claude Code 作为 TS 实现,可能无法直接调用 OS 沙箱机制
局限性
- macOS Seatbelt:Apple 已弃用 sandbox-exec 用户态 API(内核能力仍在),未来版本可能需迁移 [源码]
- Linux 依赖链:bubblewrap 需要 user namespace 支持(某些容器环境禁用),Landlock 需 5.13+ 内核 [源码]
- Windows 覆盖最弱:Restricted Token 不提供 namespace 隔离,需额外启用 Windows Sandbox 特性 [源码]
- 性能开销:每次 tool call 创建新 namespace/进程,高频短命令场景有启动开销 [待验证]
- Proxy 模式复杂性:proxy-only 网络需正确配置 MITM CA 信任链,否则 HTTPS 失败 [源码]
- 不可读 glob 模式依赖文件系统扫描,深层目录有性能问题(有 glob_scan_max_depth 限制)[源码]
证据等级汇总
| 结论 | 来源 |
|---|---|
| macOS 使用 sandbox-exec + SBPL 策略 | [源码] sandboxing/src/seatbelt.rs |
| Linux 三层:bwrap + seccomp + Landlock | [源码] linux-sandbox/ + sandboxing/ |
| seccomp 拦截 connect/accept/bind/listen/io_uring | [源码] linux-sandbox |
| SandboxManager 按平台自动选择 | [源码] sandboxing/src/manager.rs |
| PermissionProfile 定义文件系统+网络权限 | [源码] codex_protocol |
| 受保护路径在 sandbox 层面强制只读 | [源码] seatbelt.rs + bwrap |
| PR_SET_NO_NEW_PRIVS 阻止提权 | [源码] linux-sandbox |
| Windows 使用 Restricted Token | [源码] SandboxType enum |