Skip to content

PATTERN Cited by 1 source

Isolated egress proxy for user URLs

Pattern

When a service must make HTTP requests to URLs supplied by users — webhook senders, URL preview fetchers, image proxies, URL shorteners, server-side PDF/HTML renderers — route every outbound request through a dedicated egress proxy (typically Envoy) that enforces at send time the same rules the application enforced at submission time:

  • Block connections to internal / private IPs.
  • Limit traffic to HTTPS ports.
  • (Extensible: allow-list specific ports, block specific IP ranges, require specific TLS versions, etc.)

The proxy tier runs in its own Kubernetes service / pod set, physically separated from the rest of the application's outbound traffic. User-URL-touching code paths have no direct network egress — their only path out is through the proxy.

Why — structure forcing DNS-rebinding defence

The load-bearing property is that the egress proxy re-resolves DNS for each outbound request and evaluates the rules against the actual IP it is about to connect to. This closes the DNS-rebinding check-to-send gap that URL validation alone leaves open: an attacker's authoritative DNS can flip the answer between submission-time check and send-time connect, but at the egress proxy's connect moment, the DNS answer is re-evaluated and a rebind to 10.0.0.1 is refused.

URL validation at submission is necessary (UX — tell the user immediately if their URL is invalid; first defence layer against naive misuse). But it is not sufficient for SSRF protection. The egress proxy is the real boundary — canonical verbatim framing from sources/2026-04-21-planetscale-webhook-security-a-hands-on-guide: "No matter how rigorous your URL validations are, you cannot fully trust any URL provided by a user. Because of this, it's critical to isolate and limit where the webhooks service can send HTTP requests."

Why — operational isolation composes with

blast-radius containment

Running the user-URL-touching service on isolated Kubernetes machines (the proxy itself + its user-URL worker fleet) means webhook abuse cannot impact other product surfaces. The service can be paused / rate- limited / killed / rolled back independently. See patterns/defense-in-depth-webhook-abuse-mitigation for the full abuse-mitigation stack this composes with.

Why Envoy

Natural fit because:

  • Cluster-based outbound. Envoy's cluster abstraction lets operators express "these destinations, those rules" without per-request custom code.
  • Health checks + circuit breakers. Protect against slow / unresponsive user receivers.
  • Metrics + tracing out of the box. Every outbound request observable without application-tier instrumentation.
  • xDS dynamic config. Blocklist / allow-list updates can be pushed without process restart.

Alternatives: Squid, HAProxy, NGINX, custom proxies. Envoy is the 2023+ default for greenfield teams already using Kubernetes.

Composition contract

  • Application-tier validates URL at submission (HTTPS, IP blocklist, domain blocklist, post-DNS re-check) — fast user feedback, first security layer.
  • Application-tier enqueues send job into an async queue (Sidekiq in PlanetScale's case).
  • Worker picks up job, makes HTTP request through Envoy — no direct egress.
  • Envoy re-resolves DNS, evaluates rules against actual connect IP, refuses if private / loopback / blocked port / non-HTTPS.
  • Worker receives connection-refused error; records as delivery failure.

Seen in

  • sources/2026-04-21-planetscale-webhook-security-a-hands-on-guideCanonical pattern disclosure (2023-11-21, Mike Coutermarsh). PlanetScale deploys webhooks as a dedicated Kubernetes service whose all HTTP egress flows through Envoy. Two enforcement rules at the proxy tier verbatim: "Block any connections to internal/private IPs. Limit traffic to HTTPS ports." The proxy's rules are near-identical to the URL-validation rules deliberately — "It has similar rules as the URL validations above, but are executed when the webhook is being sent." Two enforcement points, one policy. This disclosure also adds a fifth canonical role to Envoy on the wiki (alongside ingress gateway at Databricks, EDS-client-weight at Dropbox, sidecar at AWS App Mesh / ECS Service Connect, JWT-validator at Pinterest) — the outbound-egress-SSRF-guard role.
Last updated · 470 distilled / 1,213 read