mirror of
https://github.com/openai/codex.git
synced 2026-04-27 08:05:51 +00:00
chore: refactor network permissions to use explicit domain and unix socket rule maps (#15120)
## Summary This PR replaces the legacy network allow/deny list model with explicit rule maps for domains and unix sockets across managed requirements, permissions profiles, the network proxy config, and the app server protocol. Concretely, it: - introduces typed domain (`allow` / `deny`) and unix socket permission (`allow` / `none`) entries instead of separate `allowed_domains`, `denied_domains`, and `allow_unix_sockets` lists - updates config loading, managed requirements merging, and exec-policy overlays to read and upsert rule entries consistently - exposes the new shape through protocol/schema outputs, debug surfaces, and app-server config APIs - rejects the legacy list-based keys and updates docs/tests to reflect the new config format ## Why The previous representation split related network policy across multiple parallel lists, which made merging and overriding rules harder to reason about. Moving to explicit keyed permission maps gives us a single source of truth per host/socket entry, makes allow/deny precedence clearer, and gives protocol consumers access to the full rule state instead of derived projections only. ## Backward Compatibility ### Backward compatible - Managed requirements still accept the legacy `experimental_network.allowed_domains`, `experimental_network.denied_domains`, and `experimental_network.allow_unix_sockets` fields. They are normalized into the new canonical `domains` and `unix_sockets` maps internally. - App-server v2 still deserializes legacy `allowedDomains`, `deniedDomains`, and `allowUnixSockets` payloads, so older clients can continue reading managed network requirements. - App-server v2 responses still populate `allowedDomains`, `deniedDomains`, and `allowUnixSockets` as legacy compatibility views derived from the canonical maps. - `managed_allowed_domains_only` keeps the same behavior after normalization. Legacy managed allowlists still participate in the same enforcement path as canonical `domains` entries. ### Not backward compatible - Permissions profiles under `[permissions.<profile>.network]` no longer accept the legacy list-based keys. Those configs must use the canonical `[domains]` and `[unix_sockets]` tables instead of `allowed_domains`, `denied_domains`, or `allow_unix_sockets`. - Managed `experimental_network` config cannot mix canonical and legacy forms in the same block. For example, `domains` cannot be combined with `allowed_domains` or `denied_domains`, and `unix_sockets` cannot be combined with `allow_unix_sockets`. - The canonical format can express explicit `"none"` entries for unix sockets, but those entries do not round-trip through the legacy compatibility fields because the legacy fields only represent allow/deny lists. ## Testing `/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl https://www.example.com' ` gives 200 with config ``` [permissions.workspace.network.domains] "www.example.com" = "allow" ``` and fails when set to deny: `curl: (56) CONNECT tunnel failed, response 403`. Also tested backward compatibility path by verifying that adding the following to `/etc/codex/requirements.toml` works: ``` [experimental_network] allowed_domains = ["www.example.com"] ```
This commit is contained in:
@@ -37,24 +37,29 @@ mode = "full" # default when unset; use "limited" for read-only mode
|
||||
mitm = false
|
||||
# CA cert/key are managed internally under $CODEX_HOME/proxy/ (ca.pem + ca.key).
|
||||
|
||||
# Hosts must match the allowlist (unless denied).
|
||||
# Use exact hosts or scoped wildcards like `*.openai.com` or `**.openai.com`.
|
||||
# The global `*` wildcard is allowed in `allowed_domains` to delegate public-host filtering to
|
||||
# `denied_domains`.
|
||||
# If `allowed_domains` is empty, the proxy blocks requests until an allowlist is configured.
|
||||
allowed_domains = ["*.openai.com", "localhost", "127.0.0.1", "::1"]
|
||||
denied_domains = ["evil.example"]
|
||||
|
||||
# If false, local/private networking is rejected. Explicit allowlisting of local IP literals
|
||||
# (or `localhost`) is required to permit them.
|
||||
# Hostnames that resolve to local/private IPs are still blocked even if allowlisted.
|
||||
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"]
|
||||
# DANGEROUS (macOS-only): bypasses unix socket allowlisting and permits any
|
||||
# absolute socket path from `x-unix-socket`.
|
||||
dangerously_allow_all_unix_sockets = false
|
||||
|
||||
# Hosts must match the allowlist (unless denied).
|
||||
# Use exact hosts or scoped wildcards like `*.openai.com` or `**.openai.com`.
|
||||
# The global `*` wildcard is rejected.
|
||||
# If no domain entries are marked `allow`, the proxy blocks requests until an allowlist is configured.
|
||||
[permissions.workspace.network.domains]
|
||||
"*.openai.com" = "allow"
|
||||
"localhost" = "allow"
|
||||
"127.0.0.1" = "allow"
|
||||
"::1" = "allow"
|
||||
"evil.example" = "deny"
|
||||
|
||||
# macOS-only: allows proxying to a unix socket when request includes `x-unix-socket: /path`.
|
||||
[permissions.workspace.network.unix_sockets]
|
||||
"/tmp/example.sock" = "allow"
|
||||
```
|
||||
|
||||
### 2) Run the proxy
|
||||
@@ -124,7 +129,7 @@ let handle = proxy.run().await?;
|
||||
handle.shutdown().await?;
|
||||
```
|
||||
|
||||
When unix socket proxying is enabled (`allow_unix_sockets` or
|
||||
When unix socket proxying is enabled (`unix_sockets` or
|
||||
`dangerously_allow_all_unix_sockets`), proxy bind overrides are still clamped to loopback to
|
||||
avoid turning the proxy into a remote bridge to local daemons.
|
||||
|
||||
@@ -189,9 +194,9 @@ Audit events intentionally avoid logging full URL/path/query data.
|
||||
This section documents the protections implemented by `codex-network-proxy`, and the boundaries of
|
||||
what it can reasonably guarantee.
|
||||
|
||||
- Allowlist-first policy: if `allowed_domains` is empty, requests are blocked until an allowlist is configured.
|
||||
- Domain patterns: exact hosts plus scoped wildcards (`*.example.com`, `**.example.com`) are supported. A global `*` wildcard is allowed in `allowed_domains` to permit all public hosts by default, while `denied_domains` remains field-specific and still rejects global `*`.
|
||||
- Deny wins: entries in `denied_domains` always override the allowlist.
|
||||
- Allowlist-first policy: if `domains` has no `allow` entries, requests are blocked until an allowlist is configured.
|
||||
- Domain patterns: exact hosts are supported, `*.example.com` matches subdomains only, and `**.example.com` matches the apex plus subdomains; the global `*` wildcard is only accepted when explicitly enabled for allowlist compilation and is otherwise rejected.
|
||||
- Deny wins: `domains` entries marked `deny` always override the allowlist.
|
||||
- Local/private network protection: when `allow_local_binding = false`, the proxy blocks loopback
|
||||
and common private/link-local ranges. Explicit allowlisting of local IP literals (or `localhost`)
|
||||
is required to permit them; hostnames that resolve to local/private IPs are still blocked even if
|
||||
|
||||
Reference in New Issue
Block a user