Files
codex/codex-rs/tcp-tunnel
Anton Panasenko 005f910210 Require OAuth CONNECT metadata for Portals TCP tunnels (#61428)
## What changed

Require `x-portals-auth-mode: oauth` in CONNECT metadata when the TCP tunnel listener connects to a known Portals gateway. Reject missing or non-OAuth metadata before reading credentials.

Remove local tunnel JWT type and version checks and treat tokens as opaque bearer credentials. Drop the unused `base64` dependency from `codex-tcp-tunnel`.

## Testing

Replace JWT validation tests with coverage that rejects missing OAuth metadata and accepts OAuth metadata with an opaque access token.

GitOrigin-RevId: 746816f0227280524c019dd90df203b182934751
2026-11-14 00:57:54 +00:00
..

HTTP/3 TCP tunnel

The ordinary Codex binary includes the hidden codex tcp-tunnel command. In listener mode, it forwards a loopback TCP listener to an explicitly selected host and port through a TLS-verified HTTP/3 CONNECT proxy. The proxy URL must be an exact HTTPS origin in the supplied policy file; the target is provided separately as host:port or [IPv6]:port.

The initial bearer must be provided as a single line on standard input (--auth-token-stdin). With --auth-token-updates-stdin, later lines replace the bearer for new connections, emit AUTH_UPDATED, and closure of the controlling pipe stops the tunnel. With --connect-headers-stdin, the first line is instead a JSON list of ["x-name","value"] pairs; token lines follow. Only non-forwarding x- extension headers are accepted. No header values or credentials belong in the command arguments.

The process emits LISTENING 127.0.0.1:<port> after connecting to the proxy. A proxy handshake does not authorize the target; each local connection makes its own authenticated CONNECT. The listener survives transport reconnects, but TCP streams are never replayed.

--diagnostics-json opts into newline-delimited JSON on stderr. Version 1 contains v, phase (startup, connect, transport, or control), code (failed, timeout, rejected, closed, draining, or invalid_input), and terminal. A rejected CONNECT may also include a numeric http_status (100–599, excluding 2xx). Records contain no credentials, addresses, headers, or raw error messages and are less than 256 bytes. Stdout readiness and credential acknowledgments are unchanged. A terminal record is flushed before an unsuccessful return; closing the controlling pipe normally emits no failure. The CLI may append a generic error line after the terminal record. Nonterminal events may concern one TCP stream or a reconnecting shared transport and do not identify the cause of a later child-process failure. Consumers must ignore records with unknown versions or fields and keep a generic fallback for helpers without this flag.

Version 1 terminal combinations are startup/{failed,timeout}, control/{failed,invalid_input}, and transport/failed. Nonterminal combinations are connect/{failed,rejected} and transport/{failed,timeout,closed,draining}. Other combinations are unknown records and must use the generic fallback.

OpenSSH ProxyCommand

With --environment-id ENV --port PORT, codex tcp-tunnel opens one HTTP/3 CONNECT and carries SSH bytes on stdin and stdout. The defaults are the public Portals gateway and port 22. You can also specify --proxy-url ORIGIN --target ENV:PORT. The gateway resolves the environment and port. A rejected CONNECT ends the command with its HTTP status on stderr.

This example uses a verified host key stored under ccarenv_example in known_hosts.

Host ccarenv_example
    User dev
    StrictHostKeyChecking yes
    ProxyCommand /opt/codex/bin/codex tcp-tunnel --environment-id ccarenv_example --port %p