犀牛鸟 2026 研究笔记 estelledc.github.io

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 的安全架构想象成一座高安全实验室:

类比边界:实验室是人主动遵守规则,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:

网络策略(三种模式)

模式 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 模式拦截的系统调用:

[源码] 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 核对)


局限性


证据等级汇总

结论 来源
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