Skip to content

Every HTTPS call fails behind a TLS-inspecting proxy because the clients trust only bundled webpki roots #1107

Description

[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register.

Kind: bug. Source: new finding; register C75.

Problem (main @ b762f41)

The workspace builds reqwest with only the bundled Mozilla root set: Cargo.toml#L30.

reqwest = { version = "=0.12.28", features = ["rustls-tls", "json"], default-features = false }

In reqwest 0.12, rustls-tls means rustls-tls-webpki-roots. No production client builder adds a certificate or reads a CA setting:

grep -rn "add_root_certificate\|SSL_CERT" crates/*/src returns nothing.

The docs, however, send proxy users to the standard variables. CLI_CONTRACT.md#L1104 says "use the standard HTTP_PROXY/HTTPS_PROXY/NO_PROXY vars, which the HTTP client honors", and docs/configuration.md#L35-L37 says the same. A TLS-inspecting proxy (the usual corporate setup) re-signs traffic with a private CA that the user can't add, so every HTTPS call fails. Plain CONNECT proxies work.

Proof by execution. I used a debug build at b762f41, run twice with identical results, behind a TLS-inspecting HTTPS proxy whose CA is in SSL_CERT_FILE:

$ env -i PATH=$PATH HOME=$HOME HTTPS_PROXY=$HTTPS_PROXY SSL_CERT_FILE=<proxy CA bundle> SOCKET_NO_TELEMETRY=1 \
    socket-patch get 00000000-0000-4000-8000-000000000000 --json --proxy-url https://api.github.com
{ "status": "error",
  "error": "Network error: error sending request for url (https://api.github.com/patch/view/…): client error (Connect): invalid peer certificate: UnknownIssuer" }

$ env -i PATH=$PATH HTTPS_PROXY=$HTTPS_PROXY SSL_CERT_FILE=<proxy CA bundle> curl -sS -o /dev/null -w '%{http_code}' https://api.github.com/patch/view/x
403

curl completes the handshake through the same proxy and trusts the same bundle. socket-patch rejects the issuer. (--proxy-url points at an allowed host only so the request leaves the sandbox; the client and trust store are the ones every patch-API call uses.)

Symptoms

I found no open issue. Within one install, behavior is split:

  • scripts/install.sh downloads with curl (system trust), and the package-manager tools socket-patch spawns (npm, pip, go, mvn, …) use their own CA settings, so they all succeed;
  • the binary's own API, blob, vendoring-service, registry, telemetry and self-update calls all fail with UnknownIssuer.

Impact

Enterprise CI behind an inspecting proxy can't use scan, get, vendor, repair or --update at all, and nothing can be configured to fix it. The error names the certificate, not the proxy, so the documented HTTPS_PROXY advice reads as broken. The fix is small (one dependency feature plus one shared builder). It has one security-relevant default, below.

Proposed change

  • Build every reqwest client from one core constructor, for example utils::http::client_builder(), which sets the trust roots. Route the six builders above through it, and delete the per-site reqwest::Client::builder() calls (this overlaps Send telemetry through one Telemetry handle with a shared HTTP client instead of 19 track wrappers #770's shared telemetry client and Tracking: one retry primitive for the patch API client (JSON, vendor service, blob and diff) #676's retry primitive; take whichever lands first).
  • Trust roots = the bundled webpki roots ∪ the platform store. Use reqwest's rustls-tls-native-roots feature alongside rustls-tls-webpki-roots. rustls-native-certs honors SSL_CERT_FILE/SSL_CERT_DIR on Unix and reads the Windows and macOS system stores.
  • Optionally add an explicit extra-CA file (SOCKET_CA_FILE, or reuse NODE_EXTRA_CA_CERTS for the npm wrapper's users) appended with add_root_certificate. Leave it out if the platform store is enough.
  • Document the trust-store rule next to the HTTPS_PROXY line in CLI_CONTRACT.md and docs/configuration.md.

Adding the platform store widens trust from "Mozilla roots" to "Mozilla roots plus whatever the OS trusts", which is what curl, npm, pip and cargo already do. If maintainers want the Mozilla-only default kept, the alternative is the explicit SOCKET_CA_FILE knob alone. Either fixes the defect.

Size and scope

Acceptance criteria

  • Every production reqwest::Client is built through the one constructor. A text ratchet test fails on a new reqwest::Client::builder() outside it.
  • A unit test builds a client that trusts a test CA from SSL_CERT_FILE (or the explicit knob), and completes a handshake with a local TLS server whose certificate that CA signed. Without the CA, the handshake fails with UnknownIssuer.
  • The default build still trusts public endpoints with an empty SSL_CERT_FILE/system store (the webpki fallback).
  • CLI_CONTRACT.md and docs/configuration.md state how trust roots are chosen.
  • Existing api_client_errors_e2e, covgap_api_client and update tests stay green.

Dependencies


Backlog review — 2026-10-08

Priority: P3 → P2. Enterprise TLS-inspection environments cannot make any HTTPS request and have no supported CA configuration. This is a functional deployment blocker.

Activity

  1. added
    bugSomething isn't working
    arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)
    on Oct 8, 2026
  2. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p3: a cross-cutting HTTP client change, not tied to a p1/p2 ecosystem. It isn't a duplicate, and I found no open PR for it. As the issue says, it overlaps the client builders touched by #676, #770 and PR #1041. This also needs a maintainer decision on the trust default (bundled webpki plus the platform store, or Mozilla-only plus an explicit SOCKET_CA_FILE) before a fix is claimed.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions