Mutual TLS (mTLS) is the foundation of zero-trust service-to-service auth. Both client and server present X.509 certs; both verify each other. Done by hand it's painful; via a service mesh (Istio, Linkerd) it's automatic. SPIFFE is the open standard that ties identity to certs.

Advertisement

Why mTLS over bearer tokens

Bearer tokens (API keys, JWTs) leak. A compromised token gives the attacker full access until the token expires (often days). mTLS ties identity to a short-lived cert (1hr typical), bound to a private key that never leaves the workload. Compromise is bounded.

SPIFFE/SPIRE

SPIFFE: standard for service identity (spiffe://trust-domain/workload-id). SPIRE: reference implementation issuing X.509 certs to workloads based on attestation (Kubernetes pod selectors, AWS IAM, hostnames). Used by Istio, Tetrate, Cloudflare, large banks.

Advertisement

Mesh integration

Istio Citadel auto-issues certs, rotates hourly, injects into every Envoy sidecar. Application code stays HTTP — sidecar wraps it in mTLS. Same for Linkerd. Per-service cost: <5ms latency, 5-10% CPU overhead. Worth it.

Without a mesh

If no mesh, libraries do the same job. Go: crypto/tls with ClientAuth: RequireAndVerifyClientCert. Python: requests with cert=(cert,key) + verify=ca. Rotate certs via cron + cert-manager. More work; same outcome.

Policy: who can call whom

Identity gets you mTLS. Authorization (which identity can call which endpoint) is a separate concern. Istio AuthorizationPolicy, OPA/Gatekeeper, Cedar — declarative policy engines that check identity against resource. Always layer authz on top of authn.

mTLS via mesh = automatic identity. SPIFFE for standards. Always layer authorization policy on top.