规范文件
Codex 沙箱、审批与命令网络:三个开关怎样组合核对
分清 sandbox_mode、approval_policy 与 network_proxy,并用离线脚本演算域名规则的匹配结果。
Codex 对命令的约束由两层组成:沙箱模式决定命令技术上能写哪里、能不能联网,审批策略决定什么时候必须先问你。想按域名放行网络,还要再打开 network_proxy。三者相互独立,最容易出的错是以为打开 network_proxy 就等于允许联网,或者以为写了域名规则就一定被执行。本文依据官方文档整理一张组合核对表和一个域名规则演算脚本,没有启动 Codex,没有放行或访问任何网络,演算结果不能代替真实产品的行为。
什么时候用
- 需要确认命令能否联网、联网时按哪些域名约束,或审批提示出现的原因。[1]
什么时候不用
- 需要约束网页搜索、MCP 连接或模型请求,这些不经过命令网络代理。[1]
先分清两层:沙箱与审批
- 沙箱模式(sandbox_mode)决定命令在技术上能做什么;审批策略(approval_policy)决定什么时候必须先问你。
- Codex CLI 与 IDE 扩展默认无网络,写权限限于当前工作区;工作区包含当前目录和 /tmp 之类的临时目录,可用 /status 查看。
- Auto 预设(workspace-write 加 on-request)下,工作区内的读写和命令自动进行;编辑工作区外的文件或运行需要联网的命令,会先请求批准。
- workspace-write 下,可写根目录里的 .git、.agents、.codex、.aws(为目录时)仍是只读,保护对其下所有内容递归生效。
- 官方已停用 approval_policy = "untrusted",留在用户或项目配置、profile、启动脚本或托管默认里,可能让客户端无法启动,需要删除并改用官方迁移段落给出的写法。
联网开关与 network_proxy 的组合
官方说明 network_proxy 只改变「已开启的网络访问如何被约束」,本身不授予网络访问;只写域名规则也不会启用代理。domains 未设置时采用 allowlist 行为,也就是没有任何外部目的地被允许,直到你写入 allow 规则。[1]
| sandbox_workspace_write.network_access | features.network_proxy.enabled | 官方说明的结果 |
|---|---|---|
| 关闭(默认) | 开启 | 网络仍关闭,代理功能不起作用 |
| 开启 | 关闭或未设置 | 网络开启,出站不受限,直接访问 |
| 开启 | 开启 | 网络开启,出站受你配置的网络策略约束 |
下面是一份原创配置样例:启用命令联网,再用代理把出站限制在 **.example.org 之内并单独拒绝 ads.example.org。我只用 Python 的 tomllib 解析确认语法有效,Codex 没有读取过它。[1]
approval_policy = "on-request"
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
network_access = true
[features.network_proxy]
enabled = true
domains = { "**.example.org" = "allow", "ads.example.org" = "deny" }
域名规则怎么匹配,以及演算脚本
- 精确主机名只匹配自身。
- *.example.com 匹配 api.example.com 这样的子域名,但不匹配 example.com 本身。
- **.example.com 同时匹配根域名和子域名。
- 单独的 * 是全局 allow,匹配任何未被拒绝的公共主机,官方建议按需使用范围更窄的规则,且 * 只允许出现在 allow 规则里。
- deny 永远优先于 allow。
下面的脚本只实现上面这些匹配规则和三种开关组合,并带有 8 条断言。它不判断「公共主机」,也不模拟 DNS 与私有地址检查,所以结果只适合在写规则前用来核对通配符的覆盖范围。[1]
import assert from "node:assert/strict";
const match = (rule, host) => {
if (rule === "*") return true; // docs: any public host; the public check is not simulated
if (rule.startsWith("**.")) return host === rule.slice(3) || host.endsWith(rule.slice(2));
if (rule.startsWith("*.")) return host.endsWith(rule.slice(1));
return host === rule;
};
export function decide({ networkAccess, proxy, domains = {} }, host) {
if (!networkAccess) return "blocked: command network is off";
if (!proxy) return "allowed: direct, domain rules not enforced";
const hit = want => Object.entries(domains).some(([rule, v]) => v === want && match(rule, host));
if (hit("deny")) return "blocked: deny rule";
if (hit("allow")) return "allowed: allow rule";
return "blocked: no allow rule";
}
const domains = { "api.openai.com": "allow", "*.example.com": "allow", "evil.example.com": "deny" };
const on = { networkAccess: true, proxy: true, domains };
assert.match(decide({ networkAccess: false, proxy: true, domains }, "api.openai.com"), /^blocked/);
assert.match(decide({ networkAccess: true, proxy: false, domains }, "anything.test"), /^allowed: direct/);
assert.match(decide(on, "api.openai.com"), /^allowed/);
assert.match(decide(on, "a.example.com"), /^allowed/);
assert.match(decide(on, "example.com"), /^blocked/); // *.example.com excludes the apex
assert.match(decide(on, "evil.example.com"), /^blocked: deny/); // deny wins
assert.match(decide({ ...on, domains: { "**.example.com": "allow" } }, "example.com"), /^allowed/);
assert.match(decide({ ...on, domains: {} }, "api.openai.com"), /^blocked: no allow/);
console.log("net policy model: all assertions passed");
| 主机 | 演算结果 | 原因 |
|---|---|---|
| example.org | 放行 | **.example.org 同时匹配根域名 |
| docs.example.org | 放行 | 子域名命中 allow 规则 |
| ads.example.org | 拒绝 | deny 优先于 allow |
| other.test | 拒绝 | 没有匹配的 allow 规则 |
代理管不到的地方
- 官方说明:命令网络代理过滤的是本地命令沙箱里的脚本、程序和子进程;它不过滤网页搜索、app 或 connector 工具调用、MCP 服务器连接、浏览器或 Computer Use 活动、云端任务,也不过滤客户端自己的模型与认证请求。
- 默认 allow_local_binding = false,会拦截环回、链路本地和私有网络目的地;解析到私有地址的主机名即使命中 allow 规则也保持被拦截。官方说明 DNS 与 IP 分类检查只能降低 DNS rebinding 风险,不能消除。
- 在受管环境里,管理员的网络要求单独配置;官方提醒要同时测试普通命令和被批准的沙箱升级,因为获批的完整沙箱升级可能绕过命令代理。
- 脚本不模拟以上任何一项;网页搜索另有 web_search 设置,默认读缓存,不属于命令网络。
配置优先级与本文范围
官方列出的取值顺序(从高到低)是:命令行标志与 -c 覆盖、受信项目的 .codex/config.toml、--profile 选中的 profile、用户 ~/.codex/config.toml、云托管默认、系统配置、内置默认,所以一次性的 -c 覆盖会盖住文件里的值;管理员的 requirements.toml 还可以禁止 approval_policy = "never" 之类的值。发现网络与预期不符时,先用 /status 与启动参数确认实际生效的值,再改规则。本文只核验官方说明,没有启动 Codex、没有放行任何域名、没有联网,也不认证任何套餐或额度。[1][2]
来源
以下均为官方页面,核验日期见各条;链接指向核验时的版本。