gw-07 — Analysis
The design review for the mTLS gateway you build in steps/.
Required behaviors
- Mutual authentication. The gateway requires and verifies a client certificate chain to a trusted CA; an untrusted or expired client cert is rejected at the handshake.
- Identity extraction. The workload identity (SPIFFE ID from the SAN, or CN) is extracted and made available to authz as the authenticated principal.
- Per-route authorization. Given the identity, a policy decides allow/deny per route + method; denials are 403, not 500.
- Hot cert rotation. New certs are picked up for new handshakes via atomic swap, with zero dropped connections and no event-loop block.
- Explicit failure policy. The behavior when the authz dependency is unavailable is configured per route (fail-open vs fail-closed), not accidental.
Design decisions
-
tls.Config.GetCertificatefor hot rotation. The server cert is read from anatomic.Pointer[tls.Certificate]; a rotation goroutine swaps it. New handshakes get the new cert; in-flight connections are untouched. Same pattern as gw-03 route reload and gw-04 membership. -
VerifyPeerCertificatefor identity + policy. Beyond chain validation, a custom verifier extracts the SPIFFE ID from the SAN and rejects identities outside the trust domain early (before any request work). -
Authz as an interface with a local fast path. End-user tokens are validated locally against cached JWKS (no per-request network call); service identities come from the cert. Only coarse, cacheable policy decisions may call an external service, always with a timeout and a per-route fail policy.
-
Trust boundary made explicit. The gateway only trusts client identity from a verified cert (or a token it validated itself) — never a plaintext header like
X-Userfrom the client, and never a PROXY header (gw-01) orX-Forwarded-Forfrom an untrusted peer.
Tradeoffs worth flagging
-
mTLS handshake cost vs reuse. Doubling the cert work per handshake makes connection reuse (gw-04) a security-cost optimization. Quantify: at N new connections/sec, mTLS handshakes are a measurable CPU line item; pooling + resumption + h2 multiplexing are the levers.
-
Short-lived certs vs rotation reliability. Hourly certs make revocation a non-issue but make the rotation pipeline a Tier-0 dependency — if rotation breaks, everything expires together. Monitor cert age and alert well before expiry; have a break-glass longer- lived cert.
-
Fail-open vs fail-closed. A single global default is wrong; it must be per-route by sensitivity. The cost of getting it wrong is either an outage (fail-closed on a flaky authz dep) or a breach (fail-open on sensitive data). Document the decision per route.
-
Termination vs passthrough. Terminating TLS enables L7 policy but forfeits
spliceand means the gateway holds private keys (a juicy target). SNI passthrough keeps keys at the origin and stays L4 but gives up L7 inspection. Choose per traffic class.
What production adds beyond this lab
- A real issuer (SPIRE/Metatron/cert-manager) with workload attestation and the Workload API / SDS push to the data plane (gw-08).
- Certificate transparency / inventory and expiry alerting across the fleet.
- mTLS to the origin as well as from the client (end-to-end identity), and identity propagation in request context for downstream authz.
- A policy engine (OPA/Rego or an internal equivalent) with versioned, reviewed, hot-reloadable policy — and audit logging of authz decisions (gw-11).
- DDoS / WAF / bot-management stages and TLS fingerprinting at the edge.