diff --git a/docker/plugin-setup/codex-auth-relay.js b/docker/plugin-setup/codex-auth-relay.js new file mode 100644 index 000000000..5d5e466a3 --- /dev/null +++ b/docker/plugin-setup/codex-auth-relay.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +/** + * Codex OAuth login relay for the hardened harness. + * + * `codex login` binds its callback server to 127.0.0.1:1455 inside the + * container. A Docker port publish forwards to the container interface, not + * loopback, so the host browser's redirect to http://localhost:1455 is + * refused. This bridges the published interface to loopback: + * + * host 127.0.0.1:1455 -> container 0.0.0.0:1456 (this relay) -> 127.0.0.1:1455 + * + * Run inside the networked session before `codex login`: + * node /ecc/docker/plugin-setup/codex-auth-relay.js & + * + * Env: ECC_RELAY_LISTEN (default 1456), ECC_RELAY_TARGET (default 1455). + */ + +'use strict'; + +const net = require('net'); + +const listenPort = Number(process.env.ECC_RELAY_LISTEN || 1456); +const targetPort = Number(process.env.ECC_RELAY_TARGET || 1455); + +net.createServer(client => { + const upstream = net.connect(targetPort, '127.0.0.1'); + client.pipe(upstream).pipe(client); + const drop = () => { + client.destroy(); + upstream.destroy(); + }; + client.on('error', drop); + upstream.on('error', drop); +}).listen(listenPort, '0.0.0.0', () => { + console.log(`codex-auth-relay: 0.0.0.0:${listenPort} -> 127.0.0.1:${targetPort}`); +}); diff --git a/docker/plugin-setup/compose.yaml b/docker/plugin-setup/compose.yaml index ef19064e5..15678eaa6 100644 --- a/docker/plugin-setup/compose.yaml +++ b/docker/plugin-setup/compose.yaml @@ -76,6 +76,9 @@ services: profiles: - networked network_mode: default + # Codex OAuth callback bridge: host browser -> codex-auth-relay.js -> loopback. + ports: + - "127.0.0.1:1455:1456" image: ecc-plugin-setup:debian real-cli-ubuntu: diff --git a/skills/docker-patterns/SKILL.md b/skills/docker-patterns/SKILL.md index e60c1d20f..53338725d 100644 --- a/skills/docker-patterns/SKILL.md +++ b/skills/docker-patterns/SKILL.md @@ -416,6 +416,27 @@ explicit Compose `--env NAME` flag, understand that the value is inspectable and can be exfiltrated for the container lifetime, and remove the exact named container immediately after. +### Authenticate Codex Inside the Networked Session + +`codex login` binds its OAuth callback server to `127.0.0.1:1455` inside the +container. A Docker port publish forwards to the container interface, not +loopback, so the host browser's redirect to `http://localhost:1455` is refused +without a bridge. The `real-cli-networked` service publishes host +`127.0.0.1:1455` to container port `1456`; bridge that port to loopback before +logging in: + +```bash +npm install --global @openai/codex # the harness image does not ship codex +node /ecc/docker/plugin-setup/codex-auth-relay.js & +codex login +``` + +Complete the sign-in in the host browser. Each attempt mints a fresh +`state`/`code`, so always follow the newest printed link. The token stays in +the disposable container's `~/.codex/auth.json`; removing the named container +revokes that copy. Only one container can hold host port 1455 at a time +because the OAuth redirect targets a fixed port. + Run the same focused suite natively on the host: ```bash