Skip to content

💡 Blind TLS pass-through for HTTP origins over Tunnel (browser-direct) #1654

@farwayer

Description

@farwayer

Describe the feature you'd like

Topology / motivation
We run a single cloudflared connector on a shared edge/bastion host, deliberately not on every origin:

  • no direct inbound path to the origins (they're only reachable from the connector over the internal network)
  • we don't want to deploy/maintain the cloudflared binary on every service host
  • a single connector is a centralized chokepoint for traffic monitoring and egress control -- connection/flow logs, destinations, traffic volumes, and tunnel/connector health observed in one place, instead of scattered across every origin host

The connector reaches internal services over HTTPS; those services carry their own self-signed / internally-issued certificates.

Problem
For a public hostname with service: https://internal-host, cloudflared acts as an L7 reverse proxy: it terminates the request, sees the full plaintext, then re-encrypts to the origin. So the connector host holds cleartext for all proxied traffic. If that shared host is compromised, passive capture and active MITM are trivial -- which is exactly the blast radius we were trying to avoid by not co-locating the connector with each origin.

We are fine with Cloudflare's edge terminating the public TLS (we want edge WAF/Access/cache). What we want is to stop extending that plaintext trust to the connector VM we operate.

service: tcp:// makes the connector blind, but the hostname is then no longer reachable directly from a browser (it needs cloudflared access/WARP on the client side), which breaks the "just open the URL" UX for a public hostname.

Desired outcome
A mode where:

  1. the public HTTPS hostname stays browser-accessible (TLS terminated at the CF edge, as today -- edge L7 features still apply)
  2. the edge->origin leg is end-to-end TLS that transits the connector as opaque ciphertext -- cloudflared forwards bytes to the configured internal destination without decrypting
  3. the origin presents a self-signed or Cloudflare-issued cert (a la Cloudflare Origin CA), and certificate validation for that leg happens at the CF edge (Full / Full-strict semantics), not at the connector

Net effect: a compromised connector host can no longer read or MITM application payloads (edge-side cert validation also prevents redirect-MITM), while origins stay non-internet-exposed and browser-direct access is preserved.

This stays compatible with the centralized-monitoring goal above: as a blind relay the connector still sees connection-level metadata (source flow, destination host:port, byte counts, timing) for flow logging and egress policy -- only the payload bytes become opaque, not the visibility into which flows go where.

One possible mechanism (just one option -- the right design is Cloudflare's to make):

  • a per-rule originRequest mode, e.g. tlsPassthrough: true, or a service: https+passthrough://internal-host:443 scheme -- the hostname->destination mapping from ingress is preserved
  • data plane: instead of the cloudflared-facing edge sending a decoded HTTP request over the tunnel, it originates the origin-side TLS itself (validating the origin cert via an uploaded CA / CF Origin CA) and relays that TLS stream over the tunnel as an opaque stream (reusing the existing TCP/stream path). cloudflared dials the rule's destination and pipes bytes -- no RoundTrip, no TLS termination at the connector
  • conceptually: "Full (Strict) SSL for Tunnel origins, with the connector as a transparent relay" -- bring the existing Origin CA / Full-strict trust model to a tunneled origin while taking the connector out of the payload trust boundary

Honest trade-offs / open questions:

  • This is not a connector-only change -- it needs Cloudflare Edge support. Today the connector receives structured HTTP over the tunnel; making it blind requires the edge to originate origin-side TLS and relay opaque bytes. A change in cloudflared alone is insufficient.
  • Connector-side L7 features are lost for such hostnames: per-path ingress routing within a hostname, HTTP-level Access middleware applied at the connector, header injection, connector request logs. Edge-side WAF/cache/Access/edge-logs are unaffected (the edge still terminates the public TLS). Per-hostname routing is fine (known at registration); per-path is not.
  • Trust boundary: origin-cert validation must happen at the edge for the guarantee to hold (the connector is blind and can't validate) -- operators configure the origin CA exactly as with classic Origin CA / Full-strict today.
  • Residual risk: this protects payload confidentiality/integrity against a compromised connector host and blocks redirect-MITM, but a compromised connector can still observe metadata (timing, byte counts, destination/SNI), cause DoS, or attempt credential/registration abuse. It reduces blast radius; it is not full isolation.

Describe alternatives you've considered

  • service: tcp:// + cloudflared access / WARP-to-Tunnel (private network routing): blind, end-to-end encrypted, supports self-signed origins -- but requires a client (WARP or cloudflared access) on the consumer side. Not browser-direct for a public hostname. Fails the "just open the URL, no client" requirement
  • Classic Full (Strict) + Cloudflare Origin CA: gives exactly the desired edge<->origin trust model, but requires the origin to be directly reachable from Cloudflare's edge -- which defeats the "no direct inbound to origin, connector-only ingress" requirement
  • Run cloudflared on each origin host: keeps plaintext local to the origin, but defeats the "one connector, don't deploy the binary everywhere" operational goal and enlarges the maintenance/attack surface
  • --no-tls-verify / caPool / originServerName: only affect the connector->origin leg; they don't change the fact that the connector still sees plaintext

Additional context
Related / adjacent issues:

Prior art inside Cloudflare: Spectrum already relays opaque TLS for L4 apps; this asks for an analogous edge-validated, connector-blind relay for HTTP apps served via Tunnel, while keeping edge-side L7 intact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: NormalMinor issue impacting one or more usersType: Feature RequestA big idea that would be split into smaller pieces

    Type

    No type
    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