教程 / Codex 与 AI 编程

Codex 的沙箱、权限与审批怎么配才安全

让 Agent 干活,本质上是把一部分 shell 权限交出去。Codex 给了三套彼此独立的控制:沙箱管「能碰什么」,审批管「动手前问不问」,各功能自己的开关管「除了命令之外还能做什么」。搞混这三层,配出来的东西一定不是你以为的那样。

更新于 2026-10-24由 GPTHubs 团队 整理约 11 分钟读完Codex 与 AI 编程

先分清三层控制,别指望一个配置解决所有问题

层次管什么在哪配
沙箱 / 权限配置本地命令能读哪些路径、能写哪些路径、能不能联网、能连哪些域名default_permissions + [permissions.<name>],或旧的 sandbox_mode
审批策略某条命令执行前是否要你点确认approval_policy、CLI 的 --ask-for-approval,以及 auto-review 机制
能力开关网页搜索、连接器、MCP、内置浏览器、Computer Use、云端环境各自的网络与权限各自的功能配置(沙箱白名单不会自动作用到这些面上)

新旧两套配置互斥,先看你用的是哪套

Codex 正在把权限体系从 sandbox_mode / [sandbox_workspace_write] 迁移到 permission profiles(目前标着 Beta,可能变)。两套不能混用,判断规则很直接:

  • 只要任何一个加载的配置文件里出现 sandbox_mode,或者你传了 --sandbox,或者选中的 profile 里设了 sandbox_mode,Codex 就走旧沙箱设置,:permissions 那套被忽略。
  • 唯一例外:企业托管配置里出现 allowed_permission_profiles,会强制按 permission profiles 运行。
  • 权限配置和旧的 sandbox_mode 不能组合,一份配置里只能选一种写法。
  • 企业混版本部署时,可以暂时保留托管的 allowed_sandbox_modes 作为兼容约束,等客户端都升到 0.138.0 以上再删。

所以排查「我配了却不生效」时,第一步永远是:搜一遍所有配置文件里有没有残留的 sandbox_mode。两套命名其实是一一对应的:旧写法 read-only 对应 :read-only,workspace-write 对应 :workspace,danger-full-access 对应 :danger-full-access。看到别处文档写旧名,按这张表换过来就行。

三个内置档位,够用就别自己写

内置 profile允许什么适合
:read-only本地命令只能读,不能写读代码、做审计、让 Agent 只出方案不动手
:workspace可写工作区根与系统临时目录;.git、.agents、.codex、.aws 这些路径保持只读日常开发,绝大多数人用这个就对了
:danger-full-access移除本地沙箱限制只在明确需要全权限时用,且要清楚这意味着什么

:workspace 那个附带保护值得单独说一句:它把工作区里的版本库与凭证目录钉成只读,能挡掉相当一部分「Agent 顺手改了 .git/config 或读走 .aws 凭证」的事故。自己从零写 profile 反而容易丢掉这层。

自定义 profile 的骨架:用 extends 继承内置基线,只覆盖差异
default_permissions = "project-edit"

[features]
network_proxy = true

[permissions.project-edit]
description = "工程编辑:可写工作区,可访问 OpenAI API。"
extends = ":workspace"

[permissions.project-edit.filesystem]
":minimal" = "read"

[permissions.project-edit.filesystem.":workspace_roots"]
"." = "write"
".devcontainer" = "read"
"**/*.env" = "deny"

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

[permissions.project-edit.network.domains]
"api.openai.com" = "allow"
"*.github.com" = "allow"
"tracking.example.com" = "deny"

extends 只能指向 :read-only、:workspace 或另一个命名 profile,不能继承 :danger-full-access,也不会继承父级描述。未知父级和继承环会被直接拒绝。

文件规则:read / write / deny 与优先级

值含义
read可读可列目录,不能创建、修改、重命名、删除
write可读可改,操作系统允许时也能创建与删除
deny读写都拒绝,用来从更大的授权里挖掉一块

优先级是「越具体越优先」;同一条路径冲突时 deny 赢过 write,write 赢过 read。这条规则让「先给大范围,再挖洞」成为标准写法:

工作区可写,但 .env 与生成目录永远读不到
[permissions.project-edit.filesystem.":workspace_roots"]
"." = "write"
"docs" = "read"
"generated" = "deny"
"**/*.env" = "deny"

支持的路径形式有 :root、:minimal(常用工具需要的运行时路径)、:workspace_roots、:tmpdir、:slash_tmp、绝对路径和 ~/path。子路径用 . 表示基准路径本身,不允许 .. 往上跳。

反过来说,deny 也能被更具体的路径重新打开:先 ~/Documents = deny,再 ~/Documents/codex = write,结果是只有 codex 那个子目录可写。做「只开放一个项目」的场景时很好用。

网络:开代理和开网络是两件事

这是 Codex 权限体系里最容易配错的地方:permissions.<name>.network.enabled = true 只是允许命令联网,它不会启动代理。不启用代理时,域名规则完全不生效,命令直连。

网络代理实际结果
关—命令完全不能联网,代理设置无所谓
开关直连,无任何域名过滤(规则被忽略)
开开走代理,按 domains 允许/拦截;没有 allow 条目就全拦

所以要让域名策略真正起作用,除了 network.enabled,还得在 config.toml 里打开 features.network_proxy = true,或者由管理员在 requirements.toml 里用 [experimental_network] 启动代理。

  • example.com 匹配精确主机;*.example.com 只匹配子域;**.example.com 匹配主域和子域;* 是仅用于 allow 的全局通配。
  • deny 覆盖 allow,主机名会被规范化(去空格、转小写、去尾点、去端口)。
  • 代理默认还会挡本地与私有网段,防 DNS rebinding。要访问本机服务得显式 "localhost" = "allow" 或 "127.0.0.1" = "allow"。
  • 默认监听 http://127.0.0.1:3128(HTTP)与 http://127.0.0.1:8081(SOCKS5)。
  • dangerously_* 系列(非回环代理、全部 Unix socket 放行、allow_local_binding)是逃生口,普通开发不要开。
  • Docker 之类的本地集成需要 Unix socket 白名单,按需逐条加,别整体放行。

审批策略:沙箱之外的第二道闸

沙箱决定「即使跑起来也伤不到哪去」,审批决定「要不要先问你一声」。两者要匹配着设:沙箱越宽,审批就越该保留人工确认;沙箱越窄,无人工审批的自动化才越安全。

  • CLI 上可以用 --ask-for-approval 控制审批强度,脚本化时常用 codex --ask-for-approval never "..." 这种完全无人值守的形式——用它之前先确认当前沙箱足够窄。
  • auto-review 机制可以在请求审批时先自动评审一遍,适合「大体能自动跑、但想留一层兜底」的场景。
  • 企业侧用 requirements.toml 托管配置,可以把可选 profile 锁死:一旦写了 allowed_permission_profiles,没列出来的 profile 全部被拒,包括未来的新内置项。
  • 托管配置的优先级高于用户配置,用户不能通过本地改写放宽组织策略。

把审批理解成「人还在环里」的标志:任何 never 的自动化,都应该建立在一个自己验证过的窄沙箱之上,而不是建立在「它应该不会乱跑」的假设上。

按场景给四套配置

场景沙箱网络审批
只读审计 / 让 Agent 先出方案:read-only(或 extends 后只给 read)按需 allowlist 单个 API 域名保留人工确认
日常开发:workspace + 额外 deny .env 与凭证目录关(默认)保留人工确认,命令被拦时再放宽
需要联网的开发(装依赖、拉包):workspace + deny 敏感文件开 + features.network_proxy = true + 域名白名单保留人工确认,重点看网络请求
CI / 无人值守脚本:workspace 且限定到仓库路径,禁网关,或只放行构建必需域名never,但必须配合窄沙箱与独立容器
  1. 01

    先选最窄的档位跑一周

    从 :read-only 或 :workspace 起步,记录哪些操作被拦。被拦是信息,不是障碍——它告诉你真正需要放宽什么。

  2. 02

    只放宽被证明需要的部分

    每次只加一条文件或域名规则,并且加注释。避免一次性抄一份「全开」模板,那样等于没配。

  3. 03

    做写入授权前想一遍后续执行

    脚本、构建步骤、包管理器钩子、shell 启动文件、共享目录里的写入,都可能在你离开沙箱后被执行。这类路径的写权限要单独考虑。

  4. 04

    在平台层面确认隔离能落地

    macOS 用 Seatbelt;策略落不下去时 Codex 会拒绝执行而不是无沙箱运行。Linux / WSL 用 bubblewrap + seccomp(旧的 Landlock 不能隔离 app-server socket,升级时要关掉 features.use_legacy_landlock)。Windows 用 MXC,elevated / unelevated 是旧回退,unelevated 的网络隔离更弱。

最后一句实话:权限配置的目标不是「配到最严」,而是「配到刚好够用、且你知道边界在哪」。一份自己看不懂的配置,比默认配置更危险。

常见问题

我配了 permission profile,但看起来没生效?

先查有没有残留的 sandbox_mode。只要任何一个加载的配置文件里出现它、或者你传了 --sandbox、或者当前 profile 里设了 sandbox_mode,Codex 就会走旧沙箱设置并忽略 permission profiles。托管配置里的 allowed_permission_profiles 是唯一会强制切回 profiles 的例外。

开了 network.enabled,为什么域名白名单不拦请求?

因为 network.enabled 只放开网络,不启动代理。没有代理时命令直连,域名规则不生效。要真正过滤,还得打开 features.network_proxy = true,或让管理员在 requirements.toml 里启用代理。

:workspace 和 :danger-full-access 差别有多大?

很大。:workspace 只允许写工作区根与临时目录,并且把工作区里的 .git、.agents、.codex、.aws 保持只读;:danger-full-access 移除本地沙箱限制,等于把整台机器交出去。后者只应在明确需要时使用。

把 .env deny 掉就安全了吗?

不完全是。在 Linux、WSL 和原生 Windows 上,deny 的 glob 是在沙箱进程启动时枚举的,进程运行期间新建的匹配文件不会被拦。要更严格,用固定路径规则、限制深度,或者把凭证放到工作区之外再加 deny。

官方文档与参考

本文依据上述官方文档整理;价格、额度与功能会随官方更新变化,第三方中转站的行为以其自身说明为准。