Skip to content

Validate on-demand TLS against active tunnels#14

Merged
mechelon merged 1 commit into
mainfrom
feature/local-ondemand-tls-ask
Jun 24, 2026
Merged

Validate on-demand TLS against active tunnels#14
mechelon merged 1 commit into
mainfrom
feature/local-ondemand-tls-ask

Conversation

@mechelon

@mechelon mechelon commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Why

Caddy's on-demand TLS ask currently points at the platform (https://expose.dev/api/tunnel), which approves any subdomain of a registered custom domain. On eu-1 that has produced 259 registered domains → ~16,954 stored certs (~65× fan-out) — bot/crawler/www-variant subdomains each mint a permanent cert. That huge pool is what Caddy loads + parses (crypto/x509.parseCertificate + encoding/pem.Decode dominate the heap) under handshake floods, spiking memory ~1 GB and pinning CPU — which on smaller boxes (us-1: 1 vCPU / 1 GB) trips the health-check timeout/restart loop.

What

A local endpoint GET /expose/can-issue-certificate?domain=<host> on the expose server that Caddy can ask before issuing:

  • 200 if a live tunnel exists for the host — using findControlConnectionForSubdomainAndServerHost, the same lookup request routing uses.
  • 404 otherwise.

Gated to the loopback interface (Host127.0.0.1[:port]) so it can only be reached by the local Caddy process — it can't shadow a tunnelled app's path or leak which tunnels are live to external callers. Registered before the catch-all tunnel route.

Tests

  • it_refuses_certificate_issuance_for_a_host_without_an_active_tunnel → 404
  • it_allows_certificate_issuance_for_a_host_with_an_active_tunnel → 200

Full tests/Feature/Server suite passes (48 tests).

Add a local /expose/can-issue-certificate endpoint that Caddy's on-demand TLS asks before issuing a certificate. It returns 200 only when a live tunnel exists for the requested host (using the same connection lookup as request routing), 404 otherwise. Gated to the loopback interface so it can't shadow tunnelled paths or leak active-tunnel info externally.

Stops the on-demand cert pool from growing for hosts nobody is tunnelling: the current platform ask approves any subdomain of a registered domain, causing large cert fan-out (259 registered domains -> ~17k stored certs on eu-1) that Caddy then loads/parses under handshake floods, spiking memory and CPU.
@mechelon mechelon force-pushed the feature/local-ondemand-tls-ask branch from 77d2e5a to 1b15ba3 Compare June 24, 2026 14:48
@mechelon mechelon merged commit e3166f7 into main Jun 24, 2026
12 checks passed
@mechelon mechelon deleted the feature/local-ondemand-tls-ask branch June 24, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant