第 09 章

沙箱与权限

给 Codex 戴上安全帽 —— Config Profiles 让不同场景拥有不同边界

3 种沙箱模式 · 3 种审批策略 · 多 Profile 管理

总是在事后才发现?

那些"早知道就该拦住它"的瞬间

它删了我不想删的文件

Codex 执行 git clean -fd 时你心头一紧 —— 那些未跟踪的配置文件就这样消失了。没有任何确认环节。

它在后台悄悄联网

Codex 调用了外部 API,把项目数据发了出去。你甚至不知道它做了网络请求。没有网络访问限制。

每次弹确认太烦了

你在做本地实验,Codex 每次写文件都要你点"允许"。第 20 次的时候你开始无脑回车,安全机制形同虚设。

沙箱与权限系统就是为这个设计的:用配置文件定义边界 —— 哪些文件可写、哪些命令要确认、网络是否放行。规则一旦写好,每次会话自动生效。你不需要"记得问" —— 系统永远不会忘。

开始之前

适合谁读

  • 已经日常使用 Codex CLI,想加固安全边界的团队
  • 需要给不同项目配置不同权限的安全工程师
  • 希望统一团队 Codex 行为规范的 Tech Lead

读完你应该已经能

  • 理解 3 种沙箱模式的差异与适用场景
  • 配置 approval_policy 控制命令审批流程
  • 使用 Config Profile 管理多套权限配置
  • 排查常见沙箱相关错误与权限问题

暂时不碰

  • 沙箱底层的 Linux namespace 实现细节
  • subagent 内部沙箱继承规则
  • 企业级 Codex 部署的组策略管理
前置知识:假设你已经读过第 01–07 章,熟悉 Codex CLI 的基本使用和 config.toml 配置文件结构。

沙箱是什么?

给 Codex 戴上"安全手套"

沙箱(Sandbox)是 Codex 运行命令时的隔离环境。它决定了 Codex 能读哪些文件、能写哪些文件、能否访问网络。

一句话理解

沙箱 = 一个"虚拟的工作空间"。Codex 内部的操作(读/写/删/网络)都被限制在这个盒子里。盒子之外的资源,Codex 碰不到。

为什么需要它?

  • 防止误操作:删库跑路的命令被拦截
  • 防止数据泄露:敏感文件不可被读取
  • 防止外部通信:不期望的 API 调用被阻止
# config.toml —— sandbox_mode 是顶层键
sandbox_mode = "workspace-write"

模式一:read-only

只读沙箱 —— 最安全的观察者

read-only 模式下,Codex 不能修改任何文件。它只能读文件、运行不会产生副作用的命令。

谁能做什么?

  • ✅ 读取任意文件
  • ✅ 运行只读命令(ls、cat、git status)
  • ❌ 写入任何文件
  • ❌ 执行写操作(rm、git push)

典型场景

  • 代码审查 / review
  • 只读对话(问 Codex 问题)
  • 安全审计(Codex 分析你的代码但不碰它)
# 只读模式配置
sandbox_mode = "read-only"
注意:read-only 模式下命令不能写文件。某些依赖写入临时目录的工具链可能受限。

模式二:workspace-write

工作区写入 —— 日常开发首选

workspace-write 是推荐默认模式。对受版本控制的目录,Codex 默认使用此模式。它可以在你的项目目录内自由读写文件,但目录外的文件仍然只读,网络默认关闭。

谁能做什么?

  • ✅ 读写项目目录内文件
  • ✅ 网络可配置开启(需显式设置)
  • ✅ 创建、删除项目内文件
  • ❌ 修改系统目录(/etc、/usr)
  • ❌ 写入其他项目目录

典型场景

  • 日常编码任务
  • 运行测试和构建
  • 安装依赖包(需开启网络)
# workspace-write 配置
sandbox_mode = "workspace-write"

# workspace-write 专属子配置
[sandbox_workspace_write]
network_access = true                          # 布尔值,不加引号
writable_roots = ["/tmp", "/home/user/data"]   # 额外可写目录

模式三:danger-full-access

完全访问 —— 自由但危险

danger-full-access 模式下沙箱几乎不存在。Codex 以你的用户权限执行任何命令,没有文件系统隔离。

谁能做什么?

  • ✅ 读写任意文件
  • ✅ 执行任意命令
  • ✅ 访问网络
  • ✅ 安装系统级包

典型场景

  • 本地封闭开发环境
  • 容器 / VM 内的开发
  • 系统级配置任务
# 完全访问(慎用)
sandbox_mode = "danger-full-access"
慎用:danger-full-access 下 Codex rm -rf / 也能执行。只在完全信任的环境里启用。

三种模式对比

一眼看清差异

维度read-onlyworkspace-writedanger-full-access
项目文件读取✅✅✅
项目文件写入❌✅✅
系统文件读取✅✅✅
系统文件写入❌❌✅
网络访问默认关闭可配置开启✅
风险等级🟢 低🟡 中🔴 高
推荐场景审查/问答日常编码隔离环境
经验法则:不确定就选 workspace-write。只在明确需要系统级操作时才用 danger-full-access。

审批策略:never

全自动 —— 零打断

approval_policy = "never" 意味着 Codex 不弹任何审批提示。但注意:沙箱限制仍然生效——越界操作会直接失败而非弹窗。

# 全自动模式(approval_policy 是顶层键)
approval_policy = "never"

适用场景

  • 已经开启 read-only 沙箱
  • CI/CD 非交互自动化流程
  • 沙箱已定义清晰边界的场景

⚠️ 风险

  • 搭配 danger-full-access 极其危险
  • 错误命令没有人工审核环节

审批策略:on-request

按需确认 —— 平衡安全与效率

approval_policy = "on-request" 是官方推荐配置。沙箱内的常规操作自动执行,仅当操作需要越出沙箱边界(如写沙箱外文件、访问网络)时才弹出确认。

# 按需审批(受版本控制目录的默认推荐值)
approval_policy = "on-request"

确认时你会看到什么

  • 即将执行的命令内容
  • 影响范围(文件路径、网络 URL)
  • 选项:允许本次 / 拒绝

与沙箱的协作

沙箱定义技术边界(能写哪里),审批策略定义何时问你。on-request 下沙箱内操作零打断,越界才问——兼顾效率与安全。

审批策略:untrusted

每次确认 —— 最高安全门槛

approval_policy = "untrusted" 下每条命令都需要手动确认。

# 每次命令都要确认
approval_policy = "untrusted"

适用场景

  • 处理生产环境配置
  • 第一次运行不熟悉的仓库
  • 安全审计期间的额外谨慎

缺点

  • 每一步都打断,效率较低
  • 习惯后容易"机械回车"
  • 降低 Codex 的自动化价值
注意:never 与 untrusted 是两个极端。大部分团队最常用 workspace-write + on-request 的组合(官方 "Auto" 预设)。

已弃用的 on-failure

曾经的"失败后确认"模式

approval_policy = "on-failure" 在早期版本中存在,命令在沙箱内失败后才请求确认。但官方文档已将其标记为弃用(deprecated)。

官方建议

  • 交互式使用 → 改用 on-request
  • 非交互自动化 → 改用 never
  • codex exec --full-auto 同样已弃用

为什么不推荐

  • 语义不直观,容易误解
  • 权限升级请求行为不符合预期
  • 新版本可能随时移除
如果你在旧教程里看到 on-failure,请替换为 on-request。

危险命令拦截

0.144.5:更强的危险命令检测,拒绝原因更清晰

覆盖更多 rm 形式

之前有些"强制删除"的变体(带各种 flag 组合的 rm)能绕过检测。0.144.5 补全了更多强制 rm 形式,减少漏网。

拒绝原因更清楚

被拦截时不再只是干巴巴的"拒绝",而是给出更清晰的原因——告诉你为什么这条命令危险,方便判断是真威胁还是误判。

和审批策略的关系:危险命令检测是底层硬拦截,独立于 approval_policy。即便设了 never,这些命令也会被拦——这是 Codex 给你的最后一道安全网。

代理配置统一

0.146:HTTP 代理被各类请求严格遵循

配了代理却仍有请求"漏网"直连,是企业内网最头疼的问题。0.146 起,以下流量全部遵循已配置的代理:

  • 认证请求
  • Plugin 下载
  • MCP 授权握手
  • LM Studio 连接
排错价值:在内网受限环境里,如果某个 MCP server 或 plugin 死活连不上,现在可以确认它走的也是你配的代理——排除了"绕过代理直连失败"这种隐蔽原因。

config.toml 配置

用文件定义安全边界

# $HOME/.codex/config.toml —— 全局配置
# sandbox_mode 和 approval_policy 都是顶层键

sandbox_mode = "workspace-write"

# workspace-write 专属子配置段
[sandbox_workspace_write]
network_access = true                            # 布尔值,不加引号
writable_roots = ["/tmp", "/var/log/myapp"]      # 额外可写目录

# 审批策略(顶层键,不是 [approvals] 段)
approval_policy = "on-request"

# 审核者(顶层键)
approvals_reviewer = "user"
配置文件位置:全局配置在 $CODEX_HOME/config.toml(CODEX_HOME 默认 ~/.codex),项目级配置放在项目的 .codex/config.toml。项目级配置优先(且需项目受信任)。

CLI 运行时覆盖

命令行参数优先级最高

# 覆盖沙箱模式(-s 是 --sandbox 简写)
codex -s read-only

# 覆盖审批策略(-a 是 --ask-for-approval 简写)
codex -a untrusted

# 同时覆盖模型 + 沙箱
codex -m gpt-5.6 -s read-only

# 叠加配置文件 Profile
codex -p oss
参数作用示例
-s / --sandbox覆盖沙箱模式-s danger-full-access
-a / --ask-for-approval覆盖审批策略-a never
-m / --model选择模型-m gpt-5.6
-p / --profile加载额外配置 Profile-p work
优先级:CLI 参数 > 项目 config.toml > 全局 config.toml > 默认值。

Permission Profiles(Beta)

用命名 Profile 定义细粒度文件/网络规则

Codex 支持实验性的 Permission Profiles 系统,在 [permissions] 下定义命名 Profile,用文件系统规则 + 网络域名规则实现最小权限。

重要:Permission Profiles 与旧的 sandbox_mode/sandbox_workspace_write 设置不能混用。配了 default_permissions 就不要再用 sandbox_mode。
# 三种内置 Profile
# :read-only          — 只读
# :workspace          — 工作区可写
# :danger-full-access — 无限制

# 自定义 Profile 示例
default_permissions = "project-edit"

[permissions.project-edit.workspace_roots]
"~/code/app" = true
"~/code/shared-lib" = true

[permissions.project-edit.filesystem.":workspace_roots"]
"." = "write"
"**/*.env" = "deny"          # deny 规则始终优先生效

[permissions.project-edit.network]
enabled = true

[permissions.project-edit.network.domains]
"api.openai.com" = "allow"
"*.github.com" = "allow"
关键特性:窄路径的 deny 规则即使被更宽的 read/write 覆盖时也始终生效。例如工作区可写但 .env 文件始终被拒绝。

approvals_reviewer

谁来"看门"?

approvals_reviewer 决定谁负责审批 Codex 的操作请求。它控制的是 on-request 策略下触发的审批提示由谁来审。

两种模式

# 由人类审批(默认,顶层键)
approvals_reviewer = "user"

# 由 AI 自动审批(审核子代理)
approvals_reviewer = "auto_review"

auto_review 的风险

  • AI 自行判断命令是否安全
  • 可能存在"AI 误判"
  • 建议只在 read-only 沙箱下用

[apps.<name>] 覆盖

为特定工具定制规则

为不同的 App / MCP 连接器指定不同的工具审批和启用规则。apps 段控制的是连接器工具调用,不是 shell 命令。

# 默认所有 App 的行为
[apps._default]
enabled = true                  # 默认启用所有 App 工具
destructive_enabled = false     # 带破坏性提示的工具默认需审批
open_world_enabled = false      # 带开放世界提示的工具默认需审批

# 特定 App 覆盖(如 GitHub 连接器)
[apps.github]
default_tools_enabled = true
default_tools_approval_mode = "auto"   # 自动放行该 App 的工具调用

# 危险 App:始终需要审批
[apps.docker]
destructive_enabled = false
default_tools_approval_mode = "prompt" # 每次都弹确认
注意:apps 段控制的是 MCP / 连接器工具调用的审批行为,不是 shell 命令。Shell 命令的审批由 approval_policy + sandbox_mode 控制。

Config Profiles 机制

一套配置,多种场景

Profile 是 TOML 的"叠加层"。通过 -p <name> 加载 $CODEX_HOME/<name>.config.toml。

# 文件结构
$CODEX_HOME/
├── config.toml          # 基础配置
├── work.config.toml     
├── personal.config.toml 
└── oss.config.toml      

codex -p work   # 加载 config.toml + work.config.toml
codex -p oss    # 加载 config.toml + oss.config.toml
核心理念:基础配置放全局,差异配置放 Profile。-p 叠加时,Profile 覆盖同名字段。

Profile:work

工作项目 —— 适度防御

# $CODEX_HOME/work.config.toml
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = false          # 断网

approval_policy = "on-request"
approvals_reviewer = "user"     # 人工审批
特征:内网开发、无外网、越界操作需要人确认。适合对代码泄露敏感的企业环境。

Profile:personal

个人项目 —— 舒适高效

# $CODEX_HOME/personal.config.toml
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true
writable_roots = ["/tmp", "/home/user/downloads"]

approval_policy = "on-request"  # 沙箱内零打断
特征:联网自由,可写 /tmp。沙箱内操作自动放行,越界才确认。适合本地个人开发。

Profile:oss

开源 repo —— 只读审查

# $CODEX_HOME/oss.config.toml
sandbox_mode = "read-only"

approval_policy = "never"
为什么 read-only + never?纯审查场景,Codex 只读文件输出意见,不需要交互确认,零打断。

实战:workspace-write + 网络

npm install 全流程

场景

让 Codex 初始化一个新的 Express 项目,需要 npm init、npm install。

# config.toml
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true            # npm install 需要网络

approval_policy = "on-request"   # 沙箱内零打断,越界才确认
$ codex "帮我初始化一个 Express 项目"
> ✅ npm init -y          (沙箱内 — 自动执行)
> ✅ npm install express  (沙箱内 + 网络 — 自动执行)
> ✅ npm i -D typescript  (沙箱内 + 网络 — 自动执行)
> ✅ npx tsc --init       (沙箱内 — 自动执行)
关键:所有操作都在沙箱内,on-request 下零打断。只有越出沙箱边界(如写项目外文件)才会弹出确认。

实战:只读 + /review

绝对安全的审查

codex -s read-only
> 请 review src/auth.ts 的安全性

Codex 能做什么

  • ✅ 读取 src/auth.ts 及相关文件
  • ✅ 分析安全漏洞并输出报告
  • ❌ 修改任何文件(沙箱禁止)
  • ❌ 上网搜索(read-only 默认断网)

为什么最安全

  • Prompt 里写"修复它"也无效
  • Prompt injection 无法破墙
  • 审计日志清晰:只有读取

实战:多 Profile 切换

用 alias 管理场景

# ~/.zshrc or ~/.bashrc
alias codex-work='codex -p work'
alias codex-dev='codex -p personal'
alias codex-review='codex -s read-only -a never'

$ codex-work "写一个支付模块"
$ codex-dev "做个 side project 首页"
$ codex-review "review 这个 PR"
进阶:在项目 .codex/config.toml 中直接写差异配置,进入该项目目录时自动叠加生效(无需 -p 指定)。

Profile 叠加规则

字段如何合并?

规则

  • 标量:Profile 覆盖基础
  • 数组:Profile 覆盖(不追加!)
  • 表段:合并后覆盖同名字段
# config.toml (基础)
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = false
writable_roots = ["/tmp"]

# personal.config.toml (Profile)
[sandbox_workspace_write]
network_access = true
writable_roots = ["/tmp", "/home/user/downloads"]

# 最终生效:
# network_access = true        ← Profile 覆盖
# writable_roots = ["/tmp", "/home/user/downloads"]  ← 整个数组被替换
数组不是追加!Profile 的 writable_roots 会替换整个基础数组,而不是在后面追加。

实战:Profile 切换演示

同一任务,不同行为

Profilesandboxapproval运行 "git push"
workworkspace-writeon-request + 人工弹出确认 ✅
personalworkspace-writeon-request沙箱内自动 ✅
ossread-onlynever沙箱拒绝 ❌
同一个 git push,三种命运。这就是 Profile 管理的价值所在。

常见错误 1:沙箱审批冲突

on-request + danger-full-access

# 危险组合
sandbox_mode = "danger-full-access"
approval_policy = "on-request"
问题:danger-full-access 下没有沙箱边界,on-request 的"越界才问"失去意义。既不安全也不高效。
# 修复:用 workspace-write 保持边界
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true

approval_policy = "on-request"

常见错误 2:忘开网络

npm install 突然失败

sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = false  # ← 问题所在
$ codex "安装依赖"
> Error: ECONNREFUSED registry.npmjs.org
原因:workspace-write 下 network_access 默认为 false(网络默认关闭)。需要显式设为 true。
# 修复
[sandbox_workspace_write]
network_access = true

常见错误 3:数组覆盖陷阱

Profile 丢了基础可写目录

# config.toml(基础)
[sandbox_workspace_write]
writable_roots = ["/tmp/data", "/cache"]

# work.config.toml(Profile)
[sandbox_workspace_write]
writable_roots = ["/tmp/work"]  # 替换整个数组!
结果:/tmp/data 和 /cache 不再可写。Profile 的数组替换而非追加基础数组。
# 修复:完整列出所有需要的目录
# work.config.toml
[sandbox_workspace_write]
writable_roots = ["/tmp/data", "/cache", "/tmp/work"]

常见错误 4:reviewer 误用

AI 自己审自己

# 危险
sandbox_mode = "danger-full-access"
approval_policy = "on-request"
approvals_reviewer = "auto_review"
问题:沙箱完全打开 + AI 自行判断安全性 = 无安全边界。Codex 可能为了完成任务跳过安全检查。
# 修复:高风险必须人工 + 保留沙箱
sandbox_mode = "workspace-write"
approval_policy = "on-request"
approvals_reviewer = "user"

常见错误 5:-p 顺序

多 Profile 层层叠加

# 可以接多个 profile
codex -p work -p personal  # work 先加载,personal 后加载(后者优先)

加载顺序

  1. 加载全局 config.toml
  2. 按顺序加载每个 -p Profile
  3. 后面的 Profile 覆盖前面的同名字段
建议:不要叠加两个以上 Profile,否则覆盖关系难以追踪。

沙箱速查表

快速参考

你想…CLI 参数config.toml
只读审查-s read-onlysandbox_mode = "read-only"
日常写代码默认推荐sandbox_mode = "workspace-write"
写系统文件-s danger-full-accesssandbox_mode = "danger-full-access"
开启联网—[sandbox_workspace_write]
network_access = true
启动不加确认-a neverapproval_policy = "never"

审批策略速查

三种有效策略

策略行为最配沙箱推荐度
never不弹确认read-only⭐⭐⭐ 审查/CI
on-request越界才确认workspace-write⭐⭐⭐⭐⭐ 日常
untrusted每条确认danger-full-access⭐⭐ 高安全
已弃用:on-failure 不再推荐。交互式用 on-request,非交互用 never。

总结

给 Codex 一个边界

今天学到了什么

  • 3 种沙箱模式:read-only / workspace-write / danger-full-access
  • 3 种审批策略:never / on-request / untrusted(+ 弃用的 on-failure)
  • CLI 运行时覆盖:-s / -a / -m / -p
  • Config Profile 管理多场景
  • approvals_reviewer + [apps] 细粒度控制

核心原则

  • 沙箱越宽,审批越严
  • Profile 用差异
  • 数组覆盖需谨慎
  • 高风险人工审
下一步:第 10 章 Thread Management —— 多会话管理、上下文传递、对话分支。