6.6 KiB
codex-network-proxy
codex-network-proxy is Codex's local network policy enforcement proxy. It runs:
- an HTTP proxy (default
127.0.0.1:3128) - a SOCKS5 proxy (default
127.0.0.1:8081) - an admin HTTP API (default
127.0.0.1:8080)
It enforces an allow/deny policy and a "limited" mode intended for read-only network access.
Quickstart
1) Configure
codex-network-proxy reads from Codex's merged config.toml (via codex-core config loading).
Example config:
[network_proxy]
enabled = true
proxy_url = "http://127.0.0.1:3128"
admin_url = "http://127.0.0.1:8080"
# By default, non-loopback binds are clamped to loopback for safety.
# If you want to expose these listeners beyond localhost, you must opt in explicitly.
dangerously_allow_non_loopback_proxy = false
dangerously_allow_non_loopback_admin = false
mode = "limited" # or "full"
[network_proxy.policy]
# Hosts must match the allowlist (unless denied).
# If `allowed_domains` is empty, the proxy blocks requests until an allowlist is configured.
allowed_domains = ["*.openai.com"]
denied_domains = ["evil.example"]
# If false, local/private networking is rejected unless the host is explicitly allowlisted.
# This includes `localhost`, loopback, and common private ranges (RFC1918, IPv6 ULA, link-local).
allow_local_binding = false
# macOS-only: allows proxying to a unix socket when request includes `x-unix-socket: /path`.
allow_unix_sockets = ["/tmp/example.sock"]
[network_proxy.mitm]
# Enables CONNECT MITM for limited-mode HTTPS. If disabled, CONNECT is blocked in limited mode.
enabled = true
# When true, logs request/response body sizes (up to max_body_bytes).
inspect = false
max_body_bytes = 4096
# These are relative to the directory containing config.toml when relative.
ca_cert_path = "network_proxy/mitm/ca.pem"
ca_key_path = "network_proxy/mitm/ca.key"
2) Initialize MITM directories (optional)
This ensures the MITM directory exists (and is a good smoke test that the binary runs):
cargo run -p codex-network-proxy -- init
3) Run the proxy
cargo run -p codex-network-proxy --
Optional flags:
# Enable SOCKS5 UDP associate support (off by default).
cargo run -p codex-network-proxy -- --enable-socks5-udp
4) Point a client at it
For HTTP(S) traffic:
export HTTP_PROXY="http://127.0.0.1:3128"
export HTTPS_PROXY="http://127.0.0.1:3128"
For SOCKS5 traffic:
export ALL_PROXY="socks5://127.0.0.1:8081"
5) Understand blocks / debugging
When a request is blocked, the proxy responds with 403 and includes:
x-proxy-error: one of:blocked-by-allowlistblocked-by-denylistblocked-by-method-policyblocked-by-mitm-requiredblocked-by-policy
In "limited" mode, only GET, HEAD, and OPTIONS are allowed. In addition, HTTPS CONNECT
requires MITM to be enabled to allow read-only HTTPS; otherwise the proxy blocks CONNECT with
reason mitm_required.
Library API
codex-network-proxy can be embedded as a library with a thin API:
use codex_network_proxy::{NetworkProxy, NetworkDecision, NetworkPolicyRequest};
let proxy = NetworkProxy::builder()
.http_addr("127.0.0.1:8080".parse()?)
.socks_addr("127.0.0.1:1080".parse()?)
.admin_addr("127.0.0.1:9000".parse()?)
.policy_decider(|request: NetworkPolicyRequest| async move {
// Example: auto-allow when exec policy already approved a command prefix.
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?;
Policy hook (exec-policy mapping)
The proxy exposes a policy hook (NetworkPolicyDecider) that can override allowlist-only blocks.
It receives command and exec_policy_hint fields when supplied by the embedding app. This lets
core map exec approvals to network access, e.g. if a user already approved curl * for a session,
the decider can auto-allow network requests originating from that command.
Important: Explicit deny rules still win. The decider only gets a chance to override
not_allowed (allowlist misses), not denied or not_allowed_local.
Admin API
The admin API is a small HTTP server intended for debugging and runtime adjustments.
Endpoints:
curl -sS http://127.0.0.1:8080/health
curl -sS http://127.0.0.1:8080/config
curl -sS http://127.0.0.1:8080/patterns
curl -sS http://127.0.0.1:8080/blocked
# Switch modes without restarting:
curl -sS -X POST http://127.0.0.1:8080/mode -d '{"mode":"full"}'
# Force a config reload:
curl -sS -X POST http://127.0.0.1:8080/reload
Platform notes
- Unix socket proxying via the
x-unix-socketheader is macOS-only; other platforms will reject unix socket requests.
Security notes (important)
This section documents the protections implemented by codex-network-proxy, and the boundaries of
what it can reasonably guarantee.
- Allowlist-first policy: if
allowed_domainsis empty, requests are blocked until an allowlist is configured. - Deny wins: entries in
denied_domainsalways override the allowlist. - Local/private network protection: when
allow_local_binding = false, the proxy blocks loopback and common private/link-local ranges (and does a best-effort DNS lookup to catch hostnames that resolve to those ranges). - Limited mode enforcement:
- only
GET,HEAD, andOPTIONSare allowed - HTTPS
CONNECTrequires MITM to be enabled, otherwise CONNECT is blocked (to avoid “tunnel hides method” bypass).
- only
- Listener safety defaults:
- the admin API is unauthenticated; non-loopback binds are clamped unless explicitly enabled via
dangerously_allow_non_loopback_admin - the HTTP proxy listener similarly clamps non-loopback binds unless explicitly enabled via
dangerously_allow_non_loopback_proxy - when unix socket proxying is enabled, both listeners are forced to loopback to avoid turning the proxy into a remote bridge into local daemons.
- the admin API is unauthenticated; non-loopback binds are clamped unless explicitly enabled via
- MITM CA key handling:
- the CA key file is created with restrictive permissions (
0600) and written atomically using create-new + fsync + rename, to avoid partial writes or transiently-permissive modes.
- the CA key file is created with restrictive permissions (
Limitations:
- DNS rebinding is hard to fully prevent without pinning the resolved IP(s) all the way down to the transport layer. If your threat model includes hostile DNS, enforce network egress at a lower layer too (e.g., firewall / VPC / corporate proxy policies).