Skip to content

rodney open fails with net::ERR_CONNECTION_RESET behind a transparent MITM egress proxy (Claude Code web sandbox) #53

Description

@ThomasSchoeller

Where it happens

Running rodney inside the Claude Code for web sandbox — an agent/CI environment whose
entire HTTPS egress is forced through a local, policy-enforcing MITM proxy exposed as
HTTPS_PROXY=http://127.0.0.1:<port>, with a custom CA bundle on disk and an SNI allow-list.

$ rodney start
$ rodney open https://example.com/
panic: navigation failed: net::ERR_CONNECTION_RESET

curl and openssl s_client reach the same URL through the very same proxy without any
problem, so the environment's egress itself is fine — the failure is specific to how Chrome
is launched.

What I think the root cause is

I dug into this with Chrome's --log-net-log and by comparing Chrome's ClientHello against
openssl's. My suspicion is a stack of four issues, all rooted in the sandbox's MITM egress
proxy:

  1. Chrome is never pointed at the proxy. detectProxy() only activates Chrome's proxy
    when the proxy URL carries credentials (user:pass). This sandbox's proxy is
    credential-less, so rodney launches Chrome with no --proxy-server; Chrome tries a
    direct connection, the sandbox firewall drops it, and navigation resets. (curl/openssl
    honor HTTPS_PROXY and therefore work.)
  2. The MITM CA isn't trusted. Hosts the proxy terminates present the sandbox's own CA,
    which Chrome's bundled root store doesn't know → ERR_CERT_AUTHORITY_INVALID
    (is_issued_by_known_root:false in the netlog).
  3. Secure DNS bypasses the proxy. Chrome's DNS-over-HTTPS tries public resolvers
    (e.g. 8.8.8.8:443) directly, around the proxy; in the locked-down sandbox those are
    dropped → ERR_NAME_NOT_RESOLVED.
  4. The proxy resets Chrome's TLS 1.3 ClientHello (main cause). Even after routing +
    cert-trust + DNS are handled, HTTPS still resets right after the CONNECT … 200 tunnel
    is established
    , inside the origin TLS handshake (SOCKET_READ_ERROR net_error=-101,
    ECONNRESET, immediately after the ClientHello). The proxy appears to peek at the tunneled
    ClientHello
    to enforce its SNI allow-list, and its parser chokes on Chrome's TLS 1.3
    ClientHello. Decoding the two hellos:
    • Chrome (~586 B): SNI, key_share, ALPN, GREASE, and crucially an
      Encrypted-ClientHello (ECH) GREASE extension (0xfe0d).
    • openssl (~308 B): plain — no ECH, no GREASE, no ALPN → passes cleanly.
      Notably, this Chromium build (1321438) keeps emitting the ECH GREASE extension even with
      --disable-features=EncryptedClientHello
      , so the offending extension can't be removed via
      that flag. For example.com the proxy doesn't even MITM (openssl gets the real Cloudflare
      origin cert), it just selectively resets Chrome's hello — which points at ClientHello
      inspection rather than TLS termination.

Workaround I'm using

Launching Chrome (via rodney) with, in effect:

  • route through the proxy (--proxy-server=$HTTPS_PROXY, loopback on a bypass list),
  • --ignore-certificate-errors (for the MITM CA),
  • disable DoH,
  • --ssl-version-max=tls1.2 — a TLS 1.2 ClientHello carries no ECH / no key_share, so the
    proxy's inspector lets it through.
    With that, rodney open https://example.comrodney title returns Example Domain,
    screenshots work, etc.
    This is a pragmatic workaround for middleboxes that choke on TLS 1.3 / ECH ClientHellos; a
    TLS-1.3-preserving fix would need the ECH GREASE extension gone (not possible via flag in this
    build) or the proxy's parser fixed — both outside rodney's scope.

Suggestion

Would it make sense for rodney to also route Chrome through a credential-less
HTTPS_PROXY (not just authenticated proxies), and optionally expose an escape hatch
(env var / flag) for these sandbox constraints — cert-error tolerance, disabling DoH, and
capping TLS at 1.2 on the proxy path? Happy to open a PR if that direction sounds reasonable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions