Mutual TLS

Mutual TLS (mTLS, also called two-way TLS or client-certificate authentication) is a variant of TLS in which both the client and the server present X.509 certificates and authenticate each other cryptographically. This contrasts with the default mode of TLS deployed on the public Web, in which the client authenticates the server but the server treats the client as anonymous (or pushes that responsibility to an application-layer mechanism such as cookies, OAuth 2.0 bearer tokens, or JSON Web Tokens).

mTLS is not new — the TLS handshake has supported the optional CertificateRequest message since the original SSL 3.0 draft in 1996 and every TLS version since (RFC 2246 §7.4.4 for TLS 1.0; RFC 8446 §4.3.2 for TLS 1.3). What is new is its central role in zero-trust microservices networking: starting around 2017, the rise of Service Mesh System Design (Istio, Linkerd, Consul Connect, AWS App Mesh) and the SPIFFE / SPIRE identity standard turned mTLS from a niche enterprise-VPN technology into the default cross-service authentication mechanism in modern cloud-native architectures.

This note covers the protocol mechanics (how the handshake actually changes), the operational realities (why short-lived rotating certs are mandatory at scale and what makes that hard), the SPIFFE identity model (the URI-in-SAN convention that decoupled cert content from public DNS), and the pitfalls (CRL/OCSP staleness, debugging-tool blast radius, CA federation across clusters).

1. Definition and Scope

A standard “one-way” TLS handshake authenticates only the server. The client opens a TCP connection to https://example.com:443, the server presents its X.509 certificate, the client validates that certificate against a trusted Certificate Authority (CA) chain, and an encrypted channel is established. The server has no cryptographic knowledge of who the client is — application-layer auth (a session cookie, a JWT, a username/password) handles that.

In mutual TLS, the server, during the handshake, sends a CertificateRequest message asking the client to also present a certificate. The client responds with its own X.509 certificate, accompanied by a signature over the handshake transcript using the client’s private key (the CertificateVerify message). The server validates the client’s cert against its trust store and verifies the signature, proving the client holds the matching private key. After the handshake, both ends know who the other is, cryptographically. No application-layer auth is required for identity establishment; identity is a property of the TLS connection itself.

The X.509 certificate format (RFC 5280) carries:

  • A Subject (a distinguished name, typically a service identity).
  • A Subject Alternative Name (SAN) — DNS names, IP addresses, or URIs the cert is valid for. In SPIFFE-style mTLS, the SAN is a spiffe:// URI rather than a DNS name (§4).
  • A validity period (notBefore, notAfter).
  • A public key.
  • A signature from a CA whose public key the verifier trusts.

The verifier walks the chain from the leaf cert up to a trust anchor (a self-signed root CA in its trust store), checking each link’s signature, validity, and revocation status.

1.1 The X.509 Certificate, in Practice

A leaf certificate viewed via openssl x509 -in cert.pem -text -noout typically displays:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4f:a8:d3:1a:...
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN = istiod.example.com, O = MeshCA
        Validity
            Not Before: Nov  7 10:00:00 2026 GMT
            Not After : Nov  7 11:00:00 2026 GMT       <-- 1-hour SVID
        Subject:                                         <-- empty for SPIFFE
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
            ECDSA Public-Key: (256 bit)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                URI:spiffe://prod.example.com/ns/payments/sa/checkout-service
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
    Signature Algorithm: ecdsa-with-SHA256
        30:46:02:21:00:c3:...                            <-- CA's signature

Several fields warrant explanation. Validity brackets the cert’s lifetime — verifiers reject certs outside this window (with optional clock-skew tolerance). Subject is empty in the SPIFFE convention because the workload identity is moved into the SAN URI; in older deployments, Subject would carry a Distinguished Name like CN=svc.foo.example.com. SAN URI is the SPIFFE workload identity, the field policy systems read for authorization decisions. Key Usage and Extended Key Usage specify what the cert may be used for; TLS Web Server Authentication permits the cert as a server cert, TLS Web Client Authentication permits it as a client cert. mTLS certs typically have both — the same cert is used regardless of direction. Basic Constraints CA:FALSE marks this as a leaf cert; CA certs would have CA:TRUE plus a pathLenConstraint. Signature Algorithm at the bottom names the algorithm by which the issuing CA signed the certificate body — this is what verifiers re-compute against the CA’s public key.

1.2 Trust Anchors and Chain Validation

Verifying a cert in mTLS means walking from the leaf cert up to a trust anchor — a root CA whose public key the verifier holds in its trust store. The verifier checks each link’s signature, validity, and revocation status (§7), and confirms the chain ends at a trusted root. A typical chain has three links: leaf cert → intermediate CA → root CA. Intermediate CAs exist so that the root’s private key can be kept offline (in an HSM, often air-gapped) while a less-protected intermediate signs day-to-day issuances. If an intermediate is compromised, only that intermediate is revoked, not the root.

The trust store is the security boundary of the entire system. A workload that trusts an attacker-controlled CA accepts attacker-issued certs as valid identities. Trust-store distribution must be itself trustworthy — typically baked into the container image at build time, validated by image signing (Sigstore, Notary), and refreshed only via signed updates.

2. The mTLS Handshake — What Changes vs One-Way TLS

The TLS 1.3 handshake (RFC 8446 §2) in one-way mode is:

Client                                           Server
------                                           ------
ClientHello                       --------->
                                                 ServerHello
                                                 EncryptedExtensions
                                                 Certificate
                                                 CertificateVerify
                                                 Finished
                                  <---------
Finished                          --------->
[Application Data]                <-------->     [Application Data]

In mutual mode, the server’s flight includes a CertificateRequest message after EncryptedExtensions, and the client’s reply flight includes its own Certificate and CertificateVerify messages before Finished:

Client                                           Server
------                                           ------
ClientHello                       --------->
                                                 ServerHello
                                                 EncryptedExtensions
                                                 CertificateRequest        <-- new
                                                 Certificate
                                                 CertificateVerify
                                                 Finished
                                  <---------
Certificate                                                                <-- new
CertificateVerify                                                          <-- new
Finished                          --------->
[Application Data]                <-------->     [Application Data]

The CertificateRequest message can include a certificate_authorities extension listing acceptable CAs (so the client knows which of its possibly-many client certs to send) and a signature_algorithms extension constraining the algorithms the server will accept. In TLS 1.3 the entire handshake from EncryptedExtensions onward is encrypted under handshake keys, so the certificates themselves (which can leak service identity) are not visible to a passive eavesdropper — a privacy improvement over TLS 1.2 where certs were sent in the clear.

The CertificateVerify message on each side proves possession of the private key. It is a signature over a hash of the handshake transcript so far. Because the transcript depends on the random ClientHello.random and ServerHello.random values, the signature cannot be replayed against a future handshake — it binds the private-key holder to this specific session.

3. Why mTLS Now: Zero-Trust Microservices

Through the 2000s and 2010s, the dominant model for service-to-service auth inside a data center was network-layer trust: services lived in a VPN or VPC, traffic was assumed authentic by virtue of being on the internal network, and explicit auth on internal RPC was rare. The model failed in three ways as architectures scaled:

  1. Lateral movement after perimeter breach. A single compromised pod inside the cluster could call any other service unauthenticated. The 2017 Equifax breach, the 2014 Target breach, and many others demonstrated that “I’m on the internal network” is no security claim worth making.
  2. Multi-cluster, multi-cloud, hybrid topologies. When workloads span AWS + GCP + on-prem, there is no single “internal network” to trust. The boundary becomes the workload itself, not the network.
  3. The microservices explosion. A single user request might fan out across 50+ services. Authenticating the user once at the edge and trusting all 50 calls to be authentic is the same lateral-movement problem at scale.

The response was the zero-trust model, codified in NIST SP 800-207 (2020): “never trust, always verify” — every connection, including service-to-service inside a cluster, must be authenticated and authorized on its own, regardless of network position. mTLS is the canonical mechanism for the authentication half of this: every service has a cryptographic identity (its certificate), and every connection cryptographically verifies the identities of both endpoints.

The economics shifted around 2017 with the rise of Service Mesh System Design: a service mesh (Istio, Linkerd) injects sidecar proxies (Envoy or Linkerd2-proxy) into every pod, terminating mTLS transparently to the application. The application code remains unchanged — it speaks plaintext HTTP/gRPC to its sidecar, which encrypts and authenticates with the destination’s sidecar. The mesh control plane handles certificate issuance, distribution, and rotation. This eliminated the historic operational pain of mTLS (managing certs by hand) and enabled mTLS-by-default rollouts at organizations like Google (BeyondCorp, BeyondProd), Lyft (Envoy’s origin), and Netflix.

4. SPIFFE and SPIRE — Identity at Scale

A central design question in mTLS-for-microservices is what does the certificate identify? DNS-based identities (web-frontend.prod.example.com) work poorly when services run in Kubernetes pods with ephemeral IPs and no stable DNS, when the same logical service runs in many regions with different DNS, and when one pod hosts multiple logical workloads.

SPIFFE — Secure Production Identity Framework For Everyone — is a CNCF standard that solves this with a workload-identity URI in the cert’s SAN extension. A SPIFFE ID looks like:

spiffe://prod.example.com/ns/payments/sa/checkout-service

The format is spiffe://<trust_domain>/<workload_path>. The trust domain (prod.example.com) names the issuing authority; the path identifies the workload (here, the checkout-service ServiceAccount in the payments Kubernetes namespace). SPIFFE spec defines the URI scheme; the cert profile is SVID (SPIFFE Verifiable Identity Document), encoded either as X.509 or as JWT.

SPIRE is the reference implementation: a SPIRE Server runs in each trust domain as the workload-identity CA, and SPIRE Agents run on every node, issuing short-lived SVIDs to local workloads after attesting their identity (via Kubernetes ServiceAccount, AWS instance metadata, or other attestation plugins). Typical SVID lifetime is 1 hour, with auto-rotation 30 minutes before expiry.

Why such short lifetimes? Three reasons:

  1. Compromised cert blast radius is bounded. A leaked SVID is useless within an hour.
  2. Revocation becomes an SLO problem, not a protocol problem. Instead of CRL/OCSP (§7), revocation is achieved by not reissuing — the cert expires.
  3. Operational maturity is forced. With 1-hour certs, manual rotation is impossible. Automation is the only option, which means the rotation pipeline is well-tested and dependable.

The SAN-as-URI pattern is significant because it decouples cert identity from DNS. A workload in prod.example.com/ns/payments/sa/checkout-service can be reached at any number of network addresses; the identity is the SPIFFE ID, not the IP. Authorization policies are written against the SPIFFE ID (“only checkout-service may call payment-gateway”), enabling identity-driven policy that survives network reconfiguration.

5. mTLS in the Major Service Meshes

Each of the major meshes implements mTLS slightly differently, but all converge on the same model: sidecar-terminated, automatic, short-lived.

Istio (docs). The control plane component istiod acts as the CA, issuing SPIFFE-format certs to sidecars on bootstrap and rotating every ~24 hours by default (configurable down to ~1 hour). PeerAuthentication policies enforce mTLS modes (STRICT, PERMISSIVE, DISABLE), and AuthorizationPolicy rules grant access based on the verified SPIFFE ID.

Linkerd (docs). mTLS is on by default for all meshed-to-meshed traffic, with no opt-in needed. The Linkerd identity controller acts as a CA issuing 24-hour certs; on-disk private keys are short-lived (1 hour) and rotated automatically. Linkerd uses a stripped-down, audited Rust proxy (linkerd2-proxy) that is itself smaller attack surface than Envoy.

Consul Connect (docs). Built on Consul’s existing service catalog. Connect issues SPIFFE-format certs and provides intentions (allow/deny rules between SPIFFE IDs). Common in HashiCorp shops and bare-metal/non-Kubernetes deployments.

AWS App Mesh (docs). Integrates with AWS Private CA for cert issuance; client cert is required end-to-end. Less automated rotation than Istio/Linkerd; certs typically live longer (days to weeks).

The common shape: every connection cryptographically verifies both endpoints, the application is oblivious, and operators write policy against high-level identity (spiffe://prod/ns/payments/sa/checkout) rather than IPs or DNS names.

6. mTLS Compared to Other Authentication Mechanisms

vs API Keys

An API key is a long-lived shared secret (sk_live_abc123...), typically baked into config or environment variables, presented as an HTTP header. The failure modes are well-known: leaked into git history, leaked into logs, leaked into client-side bundles, never rotated because rotation breaks every consumer simultaneously.

mTLS addresses each:

  • The cert’s private key never leaves the holder (in cryptographic protocol terms — operationally, a private key in a file on disk is still a leak target, but mature mTLS deployments use HSMs or kernel TPM for the key, or accept short lifetimes that bound the leak window).
  • Rotation is automatable because the protocol does not require both sides to coordinate at rotation time — each side rotates independently against the CA.
  • Revocation is bounded by short lifetime.
  • Identity is cryptographic, not a string-comparable secret, so passive eavesdropping cannot harvest credentials.

vs JWT Bearer Tokens

JSON Web Tokens are bearer tokens at the application layer: whoever holds the JWT can use it. mTLS is at the transport layer: identity is bound to the TLS session, and the private key cannot be exfiltrated by inspecting traffic.

In practice both are used together in service meshes. The pattern is:

  • mTLS authenticates the calling service (the service identity at the transport layer).
  • A JWT propagates the user identity through the call chain (the human end-user the original request was on behalf of).

The frontend service receives a user JWT from the login flow. It calls the orders service over mTLS; the call carries the JWT in a header. The orders service verifies (a) the caller is frontend-service via mTLS, and (b) the user is alice via JWT. Both are independent verifications.

vs IP Allowlisting

IP allowlisting is the historical “internal network” model. It is brittle (IPs change, network reconfigurations break auth, NAT confounds it), is not crypto (a forged source IP is undetectable on lossy NAT’d paths), and provides no per-call identity (just per-source-address). mTLS replaces it everywhere except for legacy systems.

7. Operational Realities

Certificate Lifecycle Is the Hard Problem

mTLS protocol itself is straightforward. Cert lifecycle management at scale is what kills naive deployments. Concerns:

  • Issuance. Every new pod / instance needs a cert before it can serve traffic. Bootstrapping requires identity attestation (proving the workload is what it claims) before cert issuance — Kubernetes ServiceAccount tokens, AWS instance identity documents, GCP metadata, or hardware attestation.
  • Distribution. Certs and keys must reach the workload. Service meshes inject them via sidecar; standalone services use a daemon (SPIRE Agent) that exposes them via Unix socket; some systems mount them as Kubernetes Secrets (a worse pattern because Secrets are long-lived).
  • Rotation. Certs near expiry must be re-issued and the running process must adopt the new cert without downtime. Most TLS libraries (OpenSSL, Go’s crypto/tls, Rustls) support hot-reload of certs.
  • Revocation. When a cert must be invalidated before expiry (compromised host, decommissioned service), traditional X.509 has CRL (Certificate Revocation List) and OCSP (Online Certificate Status Protocol) — both well-known to be operationally fragile (stale CRLs, OCSP availability problems, soft-fail by default). The pragmatic answer is short lifetimes (1 hour SVIDs) so revocation reduces to “stop reissuing”.

CA Federation Across Clusters

A single SPIRE deployment per trust domain works inside one cluster, but cross-cluster or multi-region calls require trust-domain federation: trust domain A trusts trust domain B’s CA, so A’s services accept SVIDs signed by B’s CA for specified workload paths. SPIFFE Federation (spec) defines the bundle exchange protocol — each domain publishes a JWKS-like bundle of trusted CA public keys, federated peers fetch and trust them.

This is non-trivial: a misconfigured federation grants cross-cluster impersonation. Best practice is to scope federation tightly (specific workload paths only), to monitor unusual cross-domain auth events, and to keep the bundle exchange itself authenticated.

Debugging and Tooling

mTLS-everywhere breaks the most common debugging shortcut: curl http://internal-service/. A developer cannot just ssh into a host and exercise an internal API — without a client cert, the call is rejected at TLS handshake. Mitigations:

  • Mesh-aware tooling: istioctl proxy-config, linkerd viz, and per-mesh dashboards expose what’s happening inside the mesh without bypassing mTLS.
  • Debug certs issued to bastion hosts, scoped narrowly and audit-logged.
  • Request-scoped tracing (OpenTelemetry, Zipkin) so the engineer can follow a request through the mesh without intercepting it.

The cultural lesson: turning on mTLS everywhere is a tooling-investment decision, not just a security decision. Organizations underestimate the debugging cost.

Common Failure Modes in Cert Distribution

A subtle but recurring class of incidents: the cert-distribution pipeline silently fails for one workload while continuing to succeed for others. The workload runs with an expired cert; downstream callers fail TLS handshake; the workload appears “down” even though its application code is healthy. Debugging this without specifically thinking about the cert lifecycle is hours of confusion.

Defenses include exporting cert-expiry metrics from each sidecar (Prometheus’s envoy_server_seconds_until_first_cert_expiring is the canonical Istio metric), alerting on expires_in < 30m, and continuously testing the rotation pipeline with synthetic workloads that intentionally consume short certs.

A second failure mode: clock skew on a workload’s host. A cert issued at T with notBefore = T will be rejected by a peer whose clock is 30 seconds behind T. NTP discipline is operationally mandatory wherever mTLS runs — but cloud VMs that come up before NTP synchronizes, container images with stale clocks, and embedded devices without NTP are all real-world sources of incidents.

The Bootstrapping Problem (Identity Attestation)

Before a workload can receive its first cert, the CA must verify the workload is what it claims. This is the bootstrap attestation problem. Without it, an attacker who can run an arbitrary process on the cluster can request a cert for any identity (“I am the payment-service” — the CA must verify no, you are not).

The standard mechanisms:

  • Kubernetes ServiceAccount tokens: SPIRE Agent presents the workload’s mounted ServiceAccount token to the SPIRE Server, which validates it against the Kubernetes API. The Pod’s namespace/ServiceAccount uniquely identifies the workload.
  • AWS instance-identity documents: signed by AWS, presented as proof “this process is running on this EC2 instance”.
  • Hardware attestation (TPM): the workload’s host TPM signs a quote proving the host’s identity and software state. Strongest but most operationally complex.

If attestation is weak, the entire identity story is weak — a compromised attestor lets an attacker claim any identity. SPIFFE’s threat model assumes the attestor is trusted; the security of the deployment reduces to the security of attestation.

8. Pitfalls

Long-Lived Certs Without Revocation Plan

A common anti-pattern: deploy mTLS with year-long client certs because rotation tooling is hard. Then a host is compromised — no automated way to revoke the cert, CRL is not consumed by clients, OCSP is soft-fail. The cert remains valid for months. Mitigation: invest in short-lived certs and automated rotation from day one.

Assuming the SAN Is Trustworthy Without Path-to-Trust-Anchor Validation

Some libraries return the cert’s SAN to the application layer without ensuring the cert chained to a trusted root. Defense: always validate cert.Verify(roots, intermediates) before reading any field; never trust SAN from an unverified cert.

Wildcard Trust Domains

Configuring policy as “trust anything spiffe://prod.example.com/*” defeats the purpose. Always scope by workload path, not by trust domain alone.

CRL or OCSP as the Only Revocation

CRLs are typically refreshed every 24 hours (so up to 24 hours of stale-validity). OCSP requires a live network call to the CA — frequently soft-fail (proceed if OCSP is unreachable), defeating the security goal. The robust answer is short lifetimes, not strong revocation.

TLS Termination at a Load Balancer

If mTLS terminates at an external load balancer that then re-establishes a separate connection to the backend, the cert’s identity is not propagated to the backend. The backend must either be in the mTLS mesh itself (so the LB→backend hop is also mTLS) or the LB must propagate the verified identity in a header (e.g., AWS ALB’s X-Amzn-Mtls-Clientcert-Subject), which the backend trusts only if the LB itself is authenticated.

Confusing Authentication With Authorization

mTLS proves who the peer is. It does not, on its own, decide what they may do. Newcomers sometimes deploy mTLS, observe that connections succeed only between configured peers, and assume the security story is complete. It is not — the policy layer (Istio AuthorizationPolicy, NetworkPolicy, application-layer RBAC) must still encode “service A is allowed to call endpoint X on service B but not endpoint Y”.

The clean separation: mTLS = identity at transport layer; AuthorizationPolicy = ACL keyed by that identity. Without both, mTLS reduces to “encrypted channel between strangers”.

Performance Cost Is Not Zero

The mTLS handshake adds two extra messages (CertificateRequest, client Certificate+CertificateVerify) and one extra signature verification per connection. On a service handling many short-lived connections, this can be 5–20% extra CPU. Mitigations:

  • TLS session resumption (session_tickets in TLS 1.3, RFC 8446 §4.6.1): subsequent connections reuse a previous session’s keys without redoing the handshake. Drops handshake cost to ~1 RTT and skips the certificate exchanges entirely.
  • HTTP/2 connection reuse: a single mTLS-authenticated TCP connection multiplexes thousands of concurrent HTTP/2 streams. This is what makes mTLS practical in service meshes — the per-stream handshake cost is amortized over the lifetime of the long-lived connection.
  • Hardware acceleration: AES-NI, AVX-512 vectorized AES-GCM, and dedicated TLS offload cards make the bulk-encryption cost negligible.

In a production Istio deployment with HTTP/2 and session resumption, the measured overhead of mTLS is typically <2% CPU, which is small compared to the security guarantees gained.

9. Worked Example: Two Services in a Mesh

Service A (frontend) calls service B (orders), both in an Istio-meshed Kubernetes cluster, in the same trust domain cluster.local.

Setup:

  • A’s sidecar (Envoy) holds an SVID with SAN spiffe://cluster.local/ns/web/sa/frontend.
  • B’s sidecar holds an SVID with SAN spiffe://cluster.local/ns/orders/sa/orders.
  • Both certs are signed by istiod’s intermediate CA, which chains to a self-signed root CA whose public key is in every sidecar’s trust store.
  • An AuthorizationPolicy on B says: from: source.principals: ["spiffe://cluster.local/ns/web/sa/frontend"].

The call:

  1. The application in A’s pod sends plaintext POST http://orders:8080/order to its sidecar.
  2. A’s sidecar opens TCP to B’s sidecar’s mTLS port (:15443).
  3. TLS 1.3 handshake begins. A’s sidecar sends ClientHello. B’s sidecar responds with ServerHello, EncryptedExtensions, CertificateRequest (asking for client cert), Certificate (B’s cert), CertificateVerify (proving B’s private-key possession), Finished.
  4. A’s sidecar verifies B’s cert chains to the trusted root, extracts SAN spiffe://cluster.local/ns/orders/sa/orders, confirms it matches the expected destination identity (Istio enforces this).
  5. A’s sidecar sends its Certificate (A’s cert), CertificateVerify (proving A’s private-key possession), Finished.
  6. B’s sidecar verifies A’s cert chains, extracts SAN spiffe://cluster.local/ns/web/sa/frontend.
  7. Authentication is now complete on both ends. Each knows the other’s SPIFFE identity, cryptographically.
  8. Authorization happens next. B’s sidecar evaluates the AuthorizationPolicy: source principal spiffe://cluster.local/ns/web/sa/frontend matches; allow.
  9. The decrypted HTTP request is forwarded to B’s application container over plaintext localhost.
  10. B’s application processes the order and returns a response, encrypted on the way back.

The application code in A and B never touched a cert, never built a TLS context, never knew mTLS was happening. The mesh handled all of it. This is the operational property that finally made mTLS practical at scale.

9.1 What Is Logged

Operationally, every mTLS connection produces structured log lines on both sides. A typical Envoy access-log entry includes:

  • requested_server_name — the SNI sent by the client (which destination it asked for).
  • peer_certificate_subject — the verified identity of the calling peer.
  • peer_certificate_san_uri — the SPIFFE URI extracted from SAN.
  • tls_version, tls_cipher_suite.
  • bytes_sent, bytes_received, duration_ms.
  • response_code_details — Envoy’s micro-categorization of why the response was what it was.

These logs are themselves the audit trail for “who called whom” — they answer compliance questions about service-to-service access in a way pre-mTLS architectures could not. The cost is significant log volume; production deployments aggregate logs in real time and discard the bulk after a short retention, keeping only sampled traces for forensics.

10. mTLS at the Edge: Cloudflare, Public APIs

mTLS is also seeing adoption at the public Internet edge for high-assurance APIs:

  • Cloudflare API Shield lets API owners require client certs, enforced at the edge. Clients (typically partner integrations, mobile apps with embedded certs) cannot reach the origin without presenting a valid cert.
  • AWS API Gateway mTLS mode requires client certs, validated against an uploaded trust store.
  • Open Banking / PSD2 (the EU regulation governing bank API access) mandates mTLS plus signed JWTs for financial-institution-to-bank API calls.

These deployments confront the public-Web challenge: how does a partner get a cert? The answer is typically “out-of-band onboarding”: the partner submits a CSR (Certificate Signing Request), the API provider’s ops team signs it after vetting, and the cert is issued with a multi-year lifetime. This is where mTLS reverts to its older operational model — partners don’t have SPIRE agents, so cert rotation is human and slow.

10.1 The Onboarding Friction of Public-API mTLS

Public-API mTLS imposes a different operational shape than mesh-internal mTLS. The partner integrating with Cloudflare API Shield or AWS API Gateway mTLS must:

  1. Generate a key pair and a Certificate Signing Request (CSR).
  2. Submit the CSR through a manual onboarding process (often a portal upload, sometimes still e-mail).
  3. Wait for the API provider to vet the request (sometimes hours, sometimes weeks).
  4. Receive the issued cert.
  5. Install the cert + private key in their integration system.
  6. Repeat at each rotation, often manually.

This is much heavier than mesh-internal mTLS, but the volume is small (tens or hundreds of partners, not thousands of pods), so the manual step is tolerable. The security gain is significant — partner integrations are a frequent attack vector, and binding partner identity to a cert vetted at onboarding is much stronger than handing out long-lived API keys.

11. See Also