Skip to content

Chrome CDP unreachable from host even with --remote-debugging-address=0.0.0.0 + --remote-allow-origins=* #10

Description

@todie

Summary

Chrome 100+ refuses CDP HTTP requests where the SOURCE IP is not loopback, even when launched with `--remote-debugging-address=0.0.0.0` AND `--remote-allow-origins=*`. This breaks the obvious approach of "just publish 9222 to the host and curl it."

Repro:

```bash
reach create --name probe --extra-port 9222
docker exec -d -u sandbox -e DISPLAY=:99 probe google-chrome \
--no-sandbox --disable-dev-shm-usage \
--remote-debugging-address=0.0.0.0 \
--remote-debugging-port=9222 \
--remote-allow-origins=* \
--user-data-dir=/tmp/chrome-profile about:blank

From inside the container — works:

docker exec probe curl -s http://127.0.0.1:9222/json/version

{"Browser": "Chrome/146.0.7680.177", ...}

From the host — fails:

curl -sv http://localhost:9222/json/version

Connected to localhost (127.0.0.1) port 9222

> GET /json/version HTTP/1.1

> Host: localhost:9222

* Recv failure: Connection reset by peer

```

The TCP connection succeeds (the docker port mapping works) but Chrome closes the HTTP request mid-flight because the source IP is the docker bridge gateway (172.17.0.1 in my case), not 127.0.0.1.

Workaround in use

Run a localhost forwarder inside the container:

  1. Launch Chrome bound to loopback only: `--remote-debugging-address=127.0.0.1 --remote-debugging-port=9224`
  2. Run socat as a 0.0.0.0:9222 → 127.0.0.1:9224 forwarder so the docker-published port reaches Chrome through a loopback hop:
    ```bash
    docker exec -d socat TCP-LISTEN:9222,bind=0.0.0.0,reuseaddr,fork TCP:127.0.0.1:9224
    ```

This works. The host can now `curl http://localhost:9222/json/version\` and get a real CDP response. But it requires:

Suggested fixes

  1. Bake socat into the image (separate from net-tools, since this one is load-bearing for any CDP-from-host workflow).
  2. Add a reach create --cdp flag (or env var) that, when set, automatically launches Chrome with the loopback bind + spins up the socat forwarder + publishes 9222 to the host. The caller gets a working `http://localhost:9222\` for free.
  3. Document the loopback bridge pattern in `docs/cdp-access.md` so the next person doesn't burn an hour rediscovering it.

I'd push for (2). The reach pitch is "AI-drivable containerized desktop sandbox." A working CDP endpoint is the single most useful host-facing primitive after VNC.

Discovered while

Wiring todie/revenant's todie-daemon to drive Chrome inside reach via CDP, for posting Threads.com chains. Same workflow PR #4 was scoped for; the auth handoff piece works perfectly, the CDP access piece needed this dance.

The full working invocation is captured in todie/revenant start-daemon.sh for reference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstaleNo activity for 14+ days

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions