mTLS authenticates both parties in a TLS connection. In common certificate modes, the client verifies the server, and the server also requests and verifies the client certificate along with proof of private key possession. It associates the connection with a verified principal but does not automatically determine which tenant the principal can read or which API it can call.
Consider an order service calling a payment service: mTLS can confirm that the caller is a trusted order workload; whether it is allowed to execute a charge or refund is still determined by the payment service's authorization policies. A test service from the same CA should not thereby gain production payment permissions.
Common Question
Core Distinction
Does requesting a client certificate mean a certificate is mandatory?
The request mechanism and the policy rejecting anonymity are separate.
Must I set up my own CA?
You can use controlled managed issuance or pre-registered trust methods.
Does passing certificate verification mean access is granted?
Identity mapping and specific permission checks are still required.
Does enabling mTLS on the gateway propagate to the backend?
It depends on the actual TLS termination point.
Is the SPIFFE ID written in the CN?
X.509-SVID uses the URI SAN.
Does rotating certificates immediately replace all connection identities?
Existing connections and session resumption have independent states.
Handshake: Certificates, Signatures, and Key Confirmation Complement Each Other
In the full certificate authentication handshake of TLS 1.3, the server sends CertificateRequest, and the client sends Certificate, CertificateVerify, and Finished if it has a suitable certificate. The certificate expresses the identity recognized by the issuer, CertificateVerify binds the proof of private key possession to this handshake, and Finished confirms the handshake keys and history.
When the client cannot find a suitable certificate, it returns an empty certificate_list and does not send CertificateVerify; the server can continue anonymously based on policy or abort with certificate_required. Gateways requiring mTLS must ensure that anonymous paths are rejected; it is not enough to just check if CertificateRequest was sent.
certificate_authorities is a hint for certificate selection; omitting it does not mean trusting any CA. When this extension appears in TLS 1.3, the list itself has a non-empty length requirement; the absence of the extension is not the same as encoding an empty list. signature_algorithms, oid_filters, etc., also have their own definitions; there is no universal process for "filling in the SPIFFE OID in certificate_extensions". RFC 9846: Client Authentication
TLS 1.3 also supports post-handshake authentication after negotiation; its implementation and upper-layer protocol support need to be confirmed, and one cannot assume that temporary authentication can be added when accessing a specific HTTP path. In practice, it should first be clarified whether the gateway requires client identity for the entire connection, and then design routing and authorization.
How to Obtain an Authorizable Identity After Verification
Chain verification is responsible for confirming conditions such as issuer relationships, time, and usage; identity mapping is responsible for obtaining the principal according to agreed-upon fields. Server identities commonly use DNS SAN, while clients may use URI SAN, specially registered certificate mappings, or other application conventions. Do not arbitrarily take the first CN string as a system-wide unique identity.
Take spiffe://prod.example/ns/orders/sa/api as an example; it can be used as an explicitly allowed principal, and then it is decided whether it can execute charge. The same path appearing under spiffe://staging.example/... is a different identity; containing orders in the string or having prod.example as a prefix is not enough.
Authentication and authorization failures should be recorded separately: unknown issuer, inappropriate certificate usage, and unmappable identity belong to different problems; an authenticated principal lacking refund permissions is an authorization rejection. This allows explaining "TLS success but HTTP 403", rather than uniformly re-signing certificates.
The input to the following function must be generated by a real verification layer; it does not read client-reported HTTP headers, nor does it implement X.509 or full SPIFFE parsing. It only demonstrates how authentication results participate in subsequent policies.
fromdataclassesimportdataclass@dataclass(frozen=True)classPeer:authenticated:boolidentity:str|NoneRULES={'spiffe://prod.example/ns/orders/sa/api':{'charge'},'spiffe://prod.example/ns/finance/sa/worker':{'refund'},}defallow(peer,action,tenant,permitted_tenants):return(peer.authenticatedandpeer.identityinRULESandactioninRULES[peer.identity]andtenantinpermitted_tenants.get(peer.identity,set()))orders='spiffe://prod.example/ns/orders/sa/api'tenants={orders:{'tenant-a'}}assertallow(Peer(True,orders),'charge','tenant-a',tenants)assertnotallow(Peer(True,orders),'refund','tenant-a',tenants)assertnotallow(Peer(True,orders),'charge','tenant-b',tenants)assertnotallow(Peer(False,orders),'charge','tenant-a',tenants)assertnotallow(Peer(True,orders.replace('prod.','staging.')),'charge','tenant-a',tenants)assertnotallow(Peer(True,orders+'/extra'),'charge','tenant-a',tenants)assertnotallow(Peer(True,None),'charge','tenant-a',tenants)print('Principal, action, tenant, and unauthenticated rejection branches passed')
Workload identities and user identities should also be separated. When the order service initiates operations on behalf of a user, mTLS proves the order service; the user, tenant, and delegation scope require reliable request context or tokens, and it must be checked whether the service has the right to perform the action on behalf of that user.
Where Does Trust Come From: Private CAs Are Common, But Not the Only Way
Building or hosting a private CA facilitates control over service identity naming, issuance conditions, and trust scope. The client authentication trust store should limit usage and cannot directly accept any valid client certificate under the entire public browser root library for convenience.
mTLS does not require the organization to personally operate an offline root CA. For example, OAuth's mTLS client authentication defines both PKI methods and self_signed_tls_client_auth based on registration materials. The latter trusts pre-bound client public key materials, not "accepting any self-signed certificate seen". RFC 8705: Client Authentication
The issuance system also requires initial identity basis. A new instance cannot simply submit "I am payments" to obtain payment identity; it needs platform evidence, workload registration, and policies to establish authorization relationships. Certificate issuance automation solves credential delivery but does not solve the initial trusted identity problem out of thin air.
SPIFFE: Identity Specification, Credentials, and Delivery Interfaces
Name
Responsibility
SPIFFE ID
Identifies workloads under the trust domain
X.509-SVID
Expresses verifiable SPIFFE identity using X.509
JWT-SVID
Expresses another SVID using JWT, not equivalent to mTLS certificate
Trust bundle
Public trust material for verifying credentials of the corresponding domain
Workload API
Delivers credentials, keys, and trust updates to identified workloads
SPIRE
One implementation of SPIFFE, responsible for attestation, registration, and credential management
X.509-SVID must have exactly one URI SAN to carry the SPIFFE ID; it can have other types of SANs, but cannot fall back to CN, nor can it randomly select one among multiple URIs. Leaf certificates are also subject to CA flags, KU, and identity path constraints; verifying SPIFFE identity requires using the trust material of the corresponding trust domain. X.509-SVID Specification
SVID is not "the certificate content contains the private key": the certificate is a public object, and the Workload API can deliver the corresponding private key as another material. Short-term rotation is part of the overall design, but "default one hour" cannot be written as a permanent unified SPIFFE regulation. Multiple instances can also share service-level identities, which does not require them to share the same private key. SPIFFE Concepts
Proxy Boundaries: Certificate Identities Do Not Automatically Cross TLS Termination
If the gateway terminates A's mTLS and then establishes a second mTLS to B, the TLS peer directly authenticated by B is the gateway G. If B needs the identity of the original caller A, a trusted delivery convention from the gateway to the backend should be defined, and the backend should be limited to accepting this convention only from qualified gateways.
Take X-Forwarded-Client-Cert as an example; it is a configurable proxy header: the ingress must clean up or overwrite externally forged values, then generate content based on the verified connection; the backend needs to verify the source, delivery rules, and required fields. Putting a PEM or Subject text into a header does not allow A to participate in the CertificateVerify of the second TLS. Envoy: XFCC
If TLS passthrough is used, the backend completes the original handshake directly, but the gateway usually cannot simultaneously read the encrypted HTTP path for regular layer 7 routing. Choosing between termination and passthrough depends on which layer needs to see the identity and business information.
Does the Backend Authenticate the Original Caller or the Gateway?
In termination mode, even if the original principal is trustworthy, B's direct TLS peer is still the gateway. If any delivery condition is broken, the orders can no longer be treated as a trusted original principal. In passthrough mode, A and B handshake directly; changing the action to refund will still result in an authorization rejection.
Preparing the visual
Does the Backend Authenticate the Original Caller or the Gateway?
Two mTLS connections establish two connection identities. Trusted proxy delivery can carry the original principal, but it does not cause the original caller to participate in the latter half of the handshake; authorization also requires matching principal and action.
{"id":"mtls-proxy-identity","title":"Does the Backend Authenticate the Original Caller or the Gateway?","summary":"Two mTLS connections establish two connection identities. Trusted proxy delivery can carry the original principal, but it does not cause the original caller to participate in the latter half of the handshake; authorization also requires matching principal and action.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"mode\"><button data-mode=\"terminate\" data-i18n=\"terminate\"></button><button data-mode=\"pass\" data-i18n=\"passthrough\"></button></div><label class=\"check\"><input type=\"checkbox\" id=\"clean\" checked><span data-i18n=\"clean\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"trusted\" checked><span data-i18n=\"trusted\"></span></label><label class=\"form-field\"><span data-i18n=\"action\"></span><select id=\"action\"><option value=\"charge\">charge</option><option value=\"refund\">refund</option></select></label><svg id=\"path\" class=\"diagram\" viewBox=\"0 0 360 180\" role=\"img\" data-i18n-label=\"heading\"></svg><div class=\"metrics\"><div><span data-i18n=\"peer\"></span><strong id=\"peer\"></strong></div><div><span data-i18n=\"principal\"></span><strong id=\"principal\"></strong></div><div><span data-i18n=\"allow\"></span><strong id=\"allow\"></strong></div></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n .metrics strong{font-size:18px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}let mode='terminate';function draw(){const pass=mode==='pass',ok=pass||($('#clean').checked&&$('#trusted').checked);graph('path',[{name:'A',x:45,y:55,sub:'orders'},{name:'G',x:180,y:130,sub:'gateway'},{name:'B',x:315,y:55,sub:'payments'}],pass?[[0,2,true]]:[[0,1,true],[1,2,true]]);set('peer',pass?'orders':'gateway');set('principal',ok?'orders':'∅');set('allow',ok&&$('#action').value==='charge'?'ALLOW':'DENY');$('#principal').dataset.valid=String(ok);pressed(mode);}document.querySelectorAll('input,select').forEach(e=>e.onchange=draw);document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});draw();","audio":false,"strings":{"action":"Request Action","allow":"Business Authorization","clean":"Ingress Overwrites External Identity Headers","heading":"Does the Backend Authenticate the Original Caller or the Gateway?","intro":"Switch TLS termination methods and identity delivery conditions to observe B's direct TLS identity and trustworthy business principal.","mode":"Connection Mode","passthrough":"TLS Passthrough","peer":"B's Direct TLS Peer","principal":"Trustworthy Original Principal","reset":"Reset","scope":"A=orders, G=gateway, B=payments, certificate checks all passed. Only orders are allowed to execute charge. Header cleanup and gateway source checks are represented by switches, not implementing HTTP proxy or real certificate verification.","scopeLabel":"Model Scope and Conventions","terminate":"Gateway Terminates TLS","trusted":"Backend Verifies Gateway Source and Delivery Convention"}}
Service Mesh: Separate Encryption Coverage from Permission Policies
The Mesh can have proxies handle certificate delivery and mutual authentication, but the actual coverage depends on whether traffic enters the data plane, whether workloads are managed, and port policies. Sidecar is just one deployment method; not all meshes can be drawn as having a sidecar in every Pod.
For example, in Istio, PeerAuthentication controls the receiving side's mTLS requirements, and AuthorizationPolicy controls access authorization. PERMISSIVE can be compatible with plaintext and mTLS; one cannot infer that all ingress points have rejected plaintext just by "enabling automatic mTLS"; ambient and other modes also have their own capability constraints. Istio Security Concepts, PeerAuthentication
Encryption between proxies does not mean that applications between local proxies necessarily use TLS. Evaluation should draw real termination points, process isolation, and bypassable ingress points, rather than just judging end-to-end identity boundaries based on a green lock displayed on the console.
Rotation: Old Certificates, Old Connections, and Resumption Tickets Are Three States
Assume the leaf certificate in the teaching deployment is valid for two hours, rotating every hour. A new certificate issued at 10:00 being replaced at 11:00 does not mean that a long connection established at 10:30 immediately re-authenticates. Certificate expiration usually does not automatically trigger chain verification on every application write; whether to limit connection lifetime, actively disconnect, or re-authorize requires explicit policies.
Root or intermediate CA rotation can use overlapping trust: first distribute a bundle containing old and new anchors, confirm that the receiver has loaded it, then switch to the new issuance chain, and finally remove the old trust after the old credentials expire and other related states are handled. Switching only the issuer first will cause clients that have not yet updated to immediately lose interoperability.
Session resumption may inherit the previous authentication context without resending certificates. When disabling an identity, in addition to new handshake verification, existing connections, resumption tickets, and authorization caches must be considered; short validity periods only shorten part of the exposure window. One cannot treat 0-RTT as an ordinary request that has completed this client certificate authentication just because the system uses mTLS.
How to Collaborate with API Keys and Access Tokens
Mechanism
Common Proof Object
Parts That Should Be Designed Separately
mTLS
TLS peer holds private key bound to identity
Termination point, trust store, connection and credential lifecycle
API Key
Requester holds an application credential
Transport protection, permissions, revocation, and leak handling
Bearer access token
Requester holds an issued access token
issuer/audience/expiration/permissions and replay boundaries
Certificate-bound access token
Token is associated with the current client certificate
Resource server must check the binding relationship
OAuth certificate-bound tokens associate the certificate digest with the token; the resource server checks whether the current mTLS certificate matches the binding, reducing availability after the token is leaked alone. It is an example of collaboration between transport identity and request authorization, and cannot replace verification with "using JWT means no need to look at mTLS". RFC 8705: Binding Access Tokens
There is no fixed overhead ranking detached from connection reuse, signature algorithms, token verification caches, and request scale. API Keys cannot be labeled as inherently incompatible with zero trust either: the key is to clarify identity, least privilege, and continuous enforcement policies.