Open Interpreter(Codex)网络代理深入解析:codex-network-proxy 的本地网络策略强制执行机制
本篇指南以 codex-rs/network-proxy/README.md 为核心,系统讲解 Codex 本地网络策略代理 codex-network-proxy 的完整用法与实现原理:从 config.toml 中 permissions profile 下的网络配置、域名 allow/deny 列表规则、full/limited 双模式,到 HTTPS MITM 钩子、SOCKS5 隧道、x-proxy-error 阻断诊断、库嵌入 API 与 OTEL 审计事件。读完本文,你可以完整配置并运行该代理,并结合源码定位每一次网络策略决策的判定路径。
1. codex-network-proxy 是什么
codex-network-proxy 是 Codex 的本地网络策略强制执行(policy enforcement)代理。根据 README 的定义,它同时运行两个监听器:
- HTTP 代理:默认
127.0.0.1:3128; - SOCKS5 代理:默认
127.0.0.1:8081,默认启用(enable_socks5 = true)。
它执行两类核心策略:allow/deny 域名策略,以及一个面向只读网络访问的 "limited"(受限)模式。代理既可以作为独立进程运行(cargo run -p codex-network-proxy),也可以作为库被嵌入托管的 Codex 运行时中,其策略决策会以结构化 OTEL 事件对外发出。
从源码模块划分看(lib.rs),该 crate 的职责被清晰地拆分为:http_proxy(HTTP/CONNECT 处理)、socks5(SOCKS5 协议)、policy/network_policy(策略引擎与策略钩子)、mitm/mitm_hook(HTTPS 解密与请求改写)、certs/native_certs(MITM CA 管理)、upstream(上游代理转发)、state/runtime(运行状态与审计)等,Windows 平台另有 windows_proxy_ingress 与 windows_tcp_attribution 两个条件编译模块,用于代理入口流量的归因。
2. 配置:permissions profile 下的网络设置
codex-network-proxy 读取的是 Codex 合并后的 config.toml(经由 codex-core 的配置加载流程),网络设置位于所选 permissions profile 之下。以下是 README 给出的完整配置示例,保留了全部关键注释:
default_permissions = "workspace"
[permissions.workspace.network]
enabled = true
proxy_url = "http://127.0.0.1:3128"
# SOCKS5 监听器(默认启用)。
enable_socks5 = true
socks_url = "http://127.0.0.1:8081"
enable_socks5_udp = true
# 当 `enabled` 为 false 时,代理空转(no-op)且不绑定任何监听器。
# 为 true 时,对上游请求尊重 HTTP(S)_PROXY/ALL_PROXY(仅限 HTTP(S) 代理),
# full 模式下也包括 CONNECT 隧道。
allow_upstream_proxy = true
# 默认情况下,非回环绑定会被钳制(clamp)到回环地址以保证安全。
# 若希望把这些监听器暴露到 localhost 之外,必须显式选择加入。
dangerously_allow_non_loopback_proxy = false
mode = "full" # 未设置时的默认值;只读模式请用 "limited"
# 当 `mode = "limited"` 或配置了 MITM hooks 时,HTTPS MITM 会自动启用。
# CA 私钥始终保留在代理内存中。MITM 激活时,被派生的命令会收到指向
# $CODEX_HOME/proxy/ 下不可变公钥文件的 CA bundle 环境变量,
# 使常见 HTTPS 客户端信任该受管 CA。
# 若为 false,本地/私有网络访问将被拒绝;必须显式 allowlist
# 本地 IP 字面量(或 `localhost`)才允许访问它们。
# 解析到本地/私有 IP 的主机名即使被 allowlist 也会继续被拦截。
# 对回环始终绕过代理的客户端(如 Go 的 `net/http`),
# 在禁用本地绑定时仍会被操作系统沙箱拦截。
allow_local_binding = false
# 危险(仅限 macOS):绕过 unix socket allowlist,
# 允许 `x-unix-socket` 中的任意绝对 socket 路径。
dangerously_allow_all_unix_sockets = false
# 主机必须匹配 allowlist(除非被 deny)。
# 使用精确主机或作用域通配符,如 `*.openai.com` 或 `**.openai.com`。
# 全局 `*` 通配符会被拒绝。
# 如果没有任何域名条目标记为 `allow`,代理会一直拦截请求,直到配置了 allowlist。
[permissions.workspace.network.domains]
"*.openai.com" = "allow"
"localhost" = "allow"
"127.0.0.1" = "allow"
"::1" = "allow"
"evil.example" = "deny"
# MITM hooks 在 CONNECT 被终止(解密)之后匹配 HTTPS 请求。
[permissions.workspace.network.mitm.hooks.github_write]
host = "api.github.com"
methods = ["POST", "PUT"]
path_prefixes = ["/repos/openai/"]
action = ["strip_auth"]
# 具名 action 可跨 hook 共享,并可被更高优先级的配置层覆盖。
[permissions.workspace.network.mitm.actions.strip_auth]
strip_request_headers = ["authorization"]
# 仅限 macOS:当请求包含 `x-unix-socket: /path` 时,允许代理到该 unix socket。
[permissions.workspace.network.unix_sockets]
"/tmp/example.sock" = "allow"
2.1 配置字段与默认值(源码级核对)
上述 TOML 字段最终反序列化为 config.rs 中的 NetworkProxyConfig 结构体。对照源码可以确认各字段及其默认行为:
| 字段 | 默认值 | 说明 |
|---|---|---|
enabled |
false |
为 false 时代理空转,不绑定任何监听器(运行时强制) |
proxy_url |
http://127.0.0.1:3128 |
HTTP 代理监听地址(见 default_proxy_url) |
enable_socks5 / socks_url |
— / http://127.0.0.1:8081 |
SOCKS5 监听开关与地址 |
enable_socks5_udp |
false |
是否允许 SOCKS5 UDP 关联(limited 模式下仍会被拦截) |
allow_upstream_proxy |
false |
是否尊重客户端环境中的 HTTP(S)_PROXY/ALL_PROXY 作为上游 |
dangerously_allow_non_loopback_proxy |
false |
允许监听器绑定非回环地址 |
dangerously_allow_all_unix_sockets |
false |
绕过 unix socket allowlist(macOS-only) |
mode |
full |
full 或 limited,见下文 NetworkMode |
domains / unix_sockets |
None |
域名与 unix socket 策略表 |
allow_local_binding |
false |
是否允许本地/私有网络访问 |
mitm / credential_broker / mitm_hooks |
false / false / 空 |
MITM、凭据代理与请求改写钩子 |
需要说明的是,README 示例中的 dangerously_allow_plaintext_credential_injection、credential_broker 等字段同样存在于该结构体中,但未在 README 示例里展开。
3. 域名策略:allowlist-first、deny-wins 与通配符语义
3.1 权限优先级
域名条目只有 allow / deny 两种值(另有内部哨兵值 None)。config.rs 中 NetworkDomainPermission 枚举的注释明确写道:
Variant order encodes effective precedence for duplicate patterns:
None < Allow < Deny,so deny wins over allow when entries conflict.
即当同一个 pattern 在多个配置层重复出现时,deny 永远压过 allow——这与 README「Deny wins」的安全说明一致。
3.2 通配符的匹配语义
policy.rs 中对支持的模式有权威注释:
| 模式 | 语义 |
|---|---|
example.com |
精确匹配该主机 |
*.example.com |
仅匹配子域(不匹配 apex 本身) |
**.example.com |
匹配 apex 及其所有子域 |
* |
匹配一切主机,仅在 allowlist 编译时显式启用才接受;denylist 中直接报错拒绝 |
实现上(compile_globset_with_policy),denylist 编译遇到全局通配符会直接 bail! 报错:unsupported global wildcard domain pattern "*"; use exact hosts or scoped wildcards like *.example.com or **.example.com。匹配则通过 globset 完成:*.domain 被展开为 ?*.{domain},**.domain 展开为 apex 与 ?*.{domain} 两个 glob(见 expand_domain_pattern),并统一做大小写归一化与尾部点剥离。
3.3 请求主机的归一化
策略匹配前,所有请求主机会先经过 normalize_host 归一化:剥离 IPv6 方括号、剥离单一 :port、转小写、去掉 FQDN 尾部点,并将可解析的 IP 字面量(含 %25/% 编码的 zone 分隔符)规范化为 IP 文本形式。这保证了策略表里写 127.0.0.1 与请求携带 127.0.0.1:8080 时能正确对齐。
4. full 与 limited 两种模式
NetworkMode 定义于 config.rs,其文档注释与 README 的说明完全对应:
limited(只读):HTTP 仅允许GET/HEAD/OPTIONS(由allows_method实现,见 config.rs);HTTPSCONNECT被拦截,除非启用 MITM,因为代理必须终止隧道才能对内层请求执行方法策略;SOCKS5 UDP与非 HTTPS 的SOCKS5 TCP(即非 443 端口目标)在 limited 模式下一律被拦截。full:允许所有 HTTP 方法,HTTPS CONNECT 直接透传隧道;配置 MITM hooks 本身不会使 full 模式进入 MITM。
单元测试 policy.rs 明确验证了这套行为:Limited.allows_method("POST") 与 CONNECT 返回 false,而 GET/HEAD/OPTIONS 返回 true。
两个值得注意的细节:
- WebSocket:
wss://客户端通常经由 HTTPSCONNECT隧道传输,其 CONNECT 目标仍会走同一套 host allowlist/denylist 检查(README 明确说明); - 方法拦截的日志位置:HTTP 直接请求的拦截日志见 http_proxy.rs,MITM 解密后的内层请求拦截日志见 mitm.rs,两者都打印
allowed_methods=GET, HEAD, OPTIONS,可用于诊断是哪一层拦截了请求。
5. 本地/私有网络保护与监听器安全
5.1 私有 IP 判定
当 allow_local_binding = false 时,代理会拦截回环及常见私有/link-local 网段。policy.rs 的 is_non_public_ipv4 / is_non_public_ipv6 给出了完整的拦截范围:
- IPv4:loopback、private、link-local、unspecified、multicast、broadcast,外加标准库未覆盖的 CGNAT(
100.64.0.0/10)、TEST-NET-1/2/3、RFC 6890 保留段等; - IPv6:loopback、
fc00::/7ULA、fe80::/10link-local、unspecified、multicast(注释说明其意图是 SSRF 防御)。
README 还强调两条边界:必须显式 allowlist 本地 IP 字面量或 localhost 才能放行本地访问;解析到本地/私有 IP 的主机名即使命中 allowlist 也继续被拦截(基于尽力而为的 DNS 查询)。对于 Go net/http 这类对回环流量直接绕过代理的客户端,README 指出此时要靠操作系统沙箱兜底。
5.2 非回环绑定的钳制
clamp_non_loopback 实现了 README 所说的「非回环绑定默认被钳制到 loopback」:若请求的绑定地址非回环且未设置 dangerously_allow_non_loopback_proxy,日志会告警并把绑定地址改写为 127.0.0.1:{port};若显式开启,则会打出 DANGEROUS: ... listening on non-loopback address 警告日志。此外,当 unix socket 代理启用时,所有代理监听器会被强制收拢到回环,避免代理沦为「远程到本地守护进程」的桥梁。
6. HTTPS MITM 与请求改写钩子
6.1 MITM 的自动启用与 CA 管理
按 README,当 mode = "limited" 或配置了 MITM hooks 时,HTTPS MITM 自动启用。CA 私钥始终保留在代理内存中;MITM 激活时,被派生的命令会收到指向 $CODEX_HOME/proxy/ 下不可变公钥文件的 CA bundle 环境变量,使常见的 HTTPS 客户端信任受管 CA。CA 相关实现位于 certs.rs 与 native_certs.rs,公开常量 CUSTOM_CA_ENV_KEYS(lib.rs)供嵌入方使用。
6.2 MITM hooks 的结构
hooks 在 CONNECT 终止后对解密出的 HTTPS 请求做匹配与改写。配置结构定义于 mitm_hook.rs:
- 匹配条件(
MitmHookMatchConfig,TOML 中字段名为match):methods(HTTP 方法列表)、path_prefixes(路径前缀)、query(查询参数约束)、headers(请求头约束)、body(JSON 体匹配); - 动作(
MitmHookActionsConfig):strip_request_headers(剥离指定请求头,如 README 示例中的authorization)与inject_request_headers(注入请求头,支持secret_env_var/secret_file两种机密来源,并可加prefix,见 InjectedHeaderConfig)。
README 示例展示的 action = ["strip_auth"] 是「具名 action」形式:具名 action 可跨 hook 共享,并允许被更高优先级的配置层覆盖,这是多配置层合并语义的一部分。
7. 运行代理并接入客户端
按 README 的 Quickstart:
1) 配置:写入上文第 2 节的 config.toml(由 codex-core 加载合并)。
2) 启动:
cargo run -p codex-network-proxy --
3) 指向代理——HTTP(S) 流量:
export HTTP_PROXY="http://127.0.0.1:3128"
export HTTPS_PROXY="http://127.0.0.1:3128"
export WS_PROXY="http://127.0.0.1:3128"
export WSS_PROXY="http://127.0.0.1:3128"
SOCKS5 流量(enable_socks5 = true 时):
export ALL_PROXY="socks5h://127.0.0.1:8081"
一个值得深入的事实:嵌入托管运行时后,Codex 不只是设置标准的 HTTP_PROXY,proxy.rs 中的 PROXY_URL_ENV_KEYS 还覆盖了一大组常见工具链的代理变量,包括 YARN_HTTP(S)_PROXY、NPM_CONFIG_HTTP(S)_PROXY、BUNDLE_HTTP(S)_PROXY(RubyGems)、DOCKER_HTTP(S)_PROXY 以及对应的 NO_PROXY 变体,保证包管理器与容器工具也走受管通道。而代理自身的上游转发(allow_upstream_proxy = true 时)则在 upstream.rs 中读取标准的 HTTP_PROXY/HTTPS_PROXY 环境变量,仅支持 HTTP(S) 上游代理。
8. 被拦截时如何诊断:x-proxy-error
请求被拦截时,代理返回 403 并附带 x-proxy-error 响应头。README 列出了四类取值;对照源码 responses.rs 的 blocked_header_value 映射,实际上游内部原因(定义于 reasons.rs)到头部值的完整映射为:
x-proxy-error 头部值 |
触发条件 |
|---|---|
blocked-by-allowlist |
主机未命中 allowlist(含本地地址未显式放行,not_allowed / not_allowed_local) |
blocked-by-denylist |
主机命中 deny 条目 |
blocked-by-method-policy |
limited 模式下方法不在 GET/HEAD/OPTIONS 白名单内 |
blocked-by-mitm-hook |
请求命中 MITM hook 的 deny 动作 |
blocked-by-mitm-required |
limited 模式下 HTTPS CONNECT/443 SOCKS5 目标需要 MITM 而未启用 |
blocked-by-policy |
其他策略来源的兜底原因 |
其中后两项是源码中已实现、但 README 的清单未枚举的头部值——排查「limited 模式下 HTTPS 全被拒」类问题时,blocked-by-mitm-required 是关键线索:它意味着你需要启用 MITM 而不是调整 allowlist。相关断言在测试 http_proxy.rs 与 mitm_tests.rs 中均有覆盖。
9. 库嵌入 API 与策略钩子(exec-policy 映射)
codex-network-proxy 可以薄 API 形式嵌入为库(README 的示例代码):
use codex_network_proxy::{NetworkProxy, NetworkDecision, NetworkPolicyRequest};
let proxy = NetworkProxy::builder()
.http_addr("127.0.0.1:8080".parse()?)
.policy_decider(|request: NetworkPolicyRequest| async move {
// 示例:当 exec 策略已批准某命令前缀时自动放行。
if let Some(command) = request.command.as_deref() {
if command.starts_with("curl ") {
return NetworkDecision::Allow;
}
}
NetworkDecision::Deny {
reason: "policy_denied".to_string(),
}
})
.build()
.await?;
let handle = proxy.run().await?;
handle.shutdown().await?;
策略钩子(NetworkPolicyDecider,类型见 network_policy.rs 的 NetworkPolicyRequest)能收到 command 与 exec_policy_hint 字段(由宿主应用提供),使核心可以把 exec 审批映射到网络访问:例如用户已为某个会话批准 curl *,decider 就可以自动放行源自该命令的网络请求。
其能力边界(README 的 Important 提示,并由源码印证):
- 显式 deny 规则仍然获胜。decider 只能把基线策略的
not_allowed(allowlist 未命中)翻转为允许,不能推翻denied或not_allowed_local; NetworkDecision(network_policy.rs)除了Allow与Deny { reason, source, decision }外,还支持NetworkDecision::ask(...)构造的 ask 语义(NetworkPolicyDecision::Ask),用于请求交互确认;source字段(NetworkDecisionSource)标注决策来自哪一层:baseline_policy、mode_guard、proxy_state、decider,与 OTEL 事件中的network.policy.source一一对应;- 当 unix socket 代理启用时,代理绑定覆盖值仍会被钳制到回环,防止把代理变成远程到本地守护进程的桥。
10. OTEL 审计事件(嵌入/托管场景)
当 codex-network-proxy 嵌入托管 Codex 运行时,策略决策会发出结构化的 OTEL 兼容事件,target = codex_otel.network_proxy(常量定义见 network_policy.rs)。
事件名:codex.network_proxy.policy_decision,对每个策略决策(domain 与 non_domain)各发一次:
network.policy.scope = "domain":主机策略评估(evaluate_host_policy);network.policy.scope = "non_domain":模式守卫/代理状态检查,包括 unix-socket 守卫路径与 unix-socket 放行决策。
常用字段:
event.name、event.timestamp(RFC3339 UTC,毫秒精度);- 可选元数据:
conversation.id、app.version、user.account_id; - 策略/网络:
network.policy.scope、network.policy.decision(allow/deny/ask)、network.policy.source(baseline_policy/mode_guard/proxy_state/decider)、network.policy.reason、network.transport.protocol、server.address、server.port、http.request.method(缺省为"none")、client.address(缺省为"unknown")、network.policy.override(仅当 decider 的 allow 覆盖了基线not_allowed时为true)。
unix socket 被拦截路径的审计使用哨兵端点值:server.address = "unix-socket"、server.port = 0。README 特别强调:审计事件刻意不记录完整的 URL/path/query 数据,这是隐私上的设计选择。network_policy.rs 的测试断言了这些字段(如 target、http.request.method)的正确性。
11. 平台差异与安全边界
平台说明(README Platform notes):
- unix socket 代理(
x-unix-socket头)是 macOS-only;其他平台直接拒绝 unix socket 请求; - HTTPS 隧道基于 rustls(Rama 的
rama-tls-rustls)实现,目的是规避混合 TLS 依赖图中 BoringSSL/OpenSSL 的符号冲突。
已实现的安全防护(README Security notes 与源码相互印证):
- allowlist-first:
domains中没有任何allow条目时,代理持续拦截,直到配置了 allowlist; - deny-wins:
deny条目永远压过 allowlist; - 本地/私有网络保护:如上第 5 节所述,含 DNS 解析后的尽力拦截;
- limited 模式强制:方法白名单 + CONNECT/443-SOCKS5 需要 MITM;
- 监听器安全默认:非回环绑定被钳制,除非显式
dangerously_allow_non_loopback_proxy;unix socket 代理启用时监听器强制回环; dangerously_allow_all_unix_sockets = true完全绕过 unix socket allowlist(仍为 macOS-only 且仅限绝对路径),应仅在强受控环境使用;enabled运行时强制:false 时代理空转、不绑定监听器。
明确的局限性:DNS rebinding 很难在不把解析出的 IP 一直钉到传输层的情况下完全防住。如果威胁模型包含敌对 DNS,README 建议在更下层同时实施网络出口管控(如防火墙/VPC/企业代理策略)。
小结
codex-network-proxy 是 Codex 沙箱体系中的网络出口闸门:以 allowlist-first + deny-wins 的域名策略为基线,用 full/limited 双模式约束方法与协议,用 MITM 与 hooks 在解密后的流量上执行方法策略与请求改写,用回环钳制、私有 IP 拦截和 unix socket allowlist 封住本地攻击面,并全程以 OTEL 事件保留可审计的决策轨迹。配置入口是 permissions profile 下的 [permissions.<profile>.network] 段,排查入口是 403 + x-proxy-error 响应头,嵌入方则可以通过 NetworkProxyBuilder 与策略 decider 把自己的 exec 审批体系接入网络放行。关键实现集中在 codex-rs/network-proxy/src/:策略匹配在 policy.rs,模式与配置解析在 config.rs,钩子在 mitm_hook.rs,诊断响应在 responses.rs,审计事件在 network_policy.rs,可作为延伸阅读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00