首页
/ Open Interpreter(Codex)网络代理深入解析:codex-network-proxy 的本地网络策略强制执行机制

Open Interpreter(Codex)网络代理深入解析:codex-network-proxy 的本地网络策略强制执行机制

2026-09-06 13:01:53作者:钟日瑜

本篇指南以 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_ingresswindows_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 fulllimited,见下文 NetworkMode
domains / unix_sockets None 域名与 unix socket 策略表
allow_local_binding false 是否允许本地/私有网络访问
mitm / credential_broker / mitm_hooks false / false / 空 MITM、凭据代理与请求改写钩子

需要说明的是,README 示例中的 dangerously_allow_plaintext_credential_injectioncredential_broker 等字段同样存在于该结构体中,但未在 README 示例里展开。

3. 域名策略:allowlist-first、deny-wins 与通配符语义

3.1 权限优先级

域名条目只有 allow / deny 两种值(另有内部哨兵值 None)。config.rsNetworkDomainPermission 枚举的注释明确写道:

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);HTTPS CONNECT 被拦截,除非启用 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。

两个值得注意的细节:

  1. WebSocketwss:// 客户端通常经由 HTTPS CONNECT 隧道传输,其 CONNECT 目标仍会走同一套 host allowlist/denylist 检查(README 明确说明);
  2. 方法拦截的日志位置:HTTP 直接请求的拦截日志见 http_proxy.rs,MITM 解密后的内层请求拦截日志见 mitm.rs,两者都打印 allowed_methods=GET, HEAD, OPTIONS,可用于诊断是哪一层拦截了请求。

5. 本地/私有网络保护与监听器安全

5.1 私有 IP 判定

allow_local_binding = false 时,代理会拦截回环及常见私有/link-local 网段。policy.rsis_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::/7 ULA、fe80::/10 link-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.rsnative_certs.rs,公开常量 CUSTOM_CA_ENV_KEYSlib.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_PROXYproxy.rs 中的 PROXY_URL_ENV_KEYS 还覆盖了一大组常见工具链的代理变量,包括 YARN_HTTP(S)_PROXYNPM_CONFIG_HTTP(S)_PROXYBUNDLE_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.rsblocked_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.rsmitm_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.rsNetworkPolicyRequest)能收到 commandexec_policy_hint 字段(由宿主应用提供),使核心可以把 exec 审批映射到网络访问:例如用户已为某个会话批准 curl *,decider 就可以自动放行源自该命令的网络请求。

其能力边界(README 的 Important 提示,并由源码印证):

  • 显式 deny 规则仍然获胜。decider 只能把基线策略的 not_allowed(allowlist 未命中)翻转为允许,不能推翻 deniednot_allowed_local
  • NetworkDecisionnetwork_policy.rs)除了 AllowDeny { reason, source, decision } 外,还支持 NetworkDecision::ask(...) 构造的 ask 语义(NetworkPolicyDecision::Ask),用于请求交互确认;
  • source 字段(NetworkDecisionSource)标注决策来自哪一层:baseline_policymode_guardproxy_statedecider,与 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,对每个策略决策(domainnon_domain)各发一次:

  • network.policy.scope = "domain":主机策略评估(evaluate_host_policy);
  • network.policy.scope = "non_domain":模式守卫/代理状态检查,包括 unix-socket 守卫路径与 unix-socket 放行决策。

常用字段

  • event.nameevent.timestamp(RFC3339 UTC,毫秒精度);
  • 可选元数据:conversation.idapp.versionuser.account_id
  • 策略/网络:network.policy.scopenetwork.policy.decisionallow / deny / ask)、network.policy.sourcebaseline_policy / mode_guard / proxy_state / decider)、network.policy.reasonnetwork.transport.protocolserver.addressserver.porthttp.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 的测试断言了这些字段(如 targethttp.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 与源码相互印证):

  1. allowlist-firstdomains 中没有任何 allow 条目时,代理持续拦截,直到配置了 allowlist;
  2. deny-winsdeny 条目永远压过 allowlist;
  3. 本地/私有网络保护:如上第 5 节所述,含 DNS 解析后的尽力拦截;
  4. limited 模式强制:方法白名单 + CONNECT/443-SOCKS5 需要 MITM;
  5. 监听器安全默认:非回环绑定被钳制,除非显式 dangerously_allow_non_loopback_proxy;unix socket 代理启用时监听器强制回环;
  6. dangerously_allow_all_unix_sockets = true 完全绕过 unix socket allowlist(仍为 macOS-only 且仅限绝对路径),应仅在强受控环境使用;
  7. 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,可作为延伸阅读。

登录后查看全文
热门项目推荐
相关项目推荐