istio/istio
mTLS and identity
Active contributors: hzxuzhonghu, costinm, ramaraochavali, keithmattix
What users do
- Encrypt all traffic between mesh workloads with mTLS, automatically.
- Verify the identity of the calling workload via SPIFFE.
- Authenticate end users with JWT (
RequestAuthentication). - Authorize calls based on identity, JWT claims, methods, and paths (
AuthorizationPolicy).
CRDs
| CRD | Purpose | Schema |
|---|---|---|
PeerAuthentication |
Govern peer (workload-to-workload) mTLS mode | security/v1beta1/peer_authentication.proto |
RequestAuthentication |
JWT/OIDC validation for incoming requests | security/v1beta1/request_authentication.proto |
AuthorizationPolicy |
Allow/deny rules at L4/L7 | security/v1beta1/authorization_policy.proto |
How identity is established
sequenceDiagram
participant Pod as App pod (with sidecar)
participant Agent as pilot-agent
participant Istiod
participant CA as IstioCA
Pod->>Pod: SA token projected to /var/run/secrets/tokens/istio-token
Agent->>Agent: read SA token
Agent->>Istiod: Authenticated CSR over mTLS<br/>(JWT bearer)
Istiod->>CA: Sign with SPIFFE URI
CA-->>Istiod: Signed cert chain
Istiod-->>Agent: Cert + chain via SDS
Note over Agent,Pod: Agent serves cert to Envoy via UDS<br/>Envoy uses cert in TLS handshakeThe SPIFFE URI for a workload is spiffe://<trust-domain>/ns/<namespace>/sa/<serviceaccount>. The trust domain defaults to cluster.local and is configurable via MeshConfig.trustDomain.
The agent caches certs in memory and rotates ahead of expiry (default cert TTL: 24h, rotation at 50% of lifetime).
How mTLS happens on the wire
In sidecar mode:
- Outbound: Envoy intercepts the connection, opens an mTLS connection to the destination's Envoy, and tunnels the original payload.
- Inbound: Envoy on port 15006 terminates the mTLS, validates the peer cert, and forwards plaintext to the app.
- Mode (
STRICT,PERMISSIVE,DISABLE) is per-port and resolved by merging mesh-default + namespace + workload-levelPeerAuthentication. Resolution lives inpilot/pkg/security/authn/and is consumed by the listener compiler innetworking/core/.
In Ambient mode:
- Ztunnel terminates and originates HBONE (HTTP/2 CONNECT over mTLS). It uses the same SPIFFE identities and the same CA.
- Mode is L4 only at this layer; L7 mTLS / per-method authn happens at the waypoint Envoy.
AuthorizationPolicy compilation
AuthorizationPolicy is compiled into Envoy RBAC filter config. Compilers:
- Sidecar / waypoint Envoy:
pilot/pkg/security/authz/builder/. Producesenvoy.filters.http.rbacandenvoy.filters.network.rbacconfig. - Ztunnel (L4 only):
pilot/pkg/serviceregistry/kube/controller/ambient/authorization.go. Produces theAuthorizationproto inpkg/workloadapi/security/.
Selector resolution (which policies apply to a given proxy) is in pilot/pkg/model/authorization.go. The selector matches workload labels; targetRef (Gateway-API style) takes precedence over selector.
Order of operations on a request:
- Layered-deny — DENY rules at workload, namespace, and root namespace levels (
AuthorizationPolicywithaction: DENY). - Allow — at least one matching ALLOW rule must permit.
- Audit — separate ALLOW-with-AUDIT mode emits logs without affecting the decision.
Custom action types (CUSTOM) integrate ext-authz services.
RequestAuthentication
RequestAuthentication configures JWT validation per workload. The compiler builds Envoy envoy.filters.http.jwt_authn config. Behavior:
- Tokens are validated against the configured
issuer+jwks(orjwksUri). - Validated claims become available to
AuthorizationPolicyviarequest.auth.*. - A request without a valid token still enters the proxy chain — it is up to
AuthorizationPolicyto deny it.
The compiler is in pilot/pkg/security/authn/. JWT keys are cached in istiod and refreshed periodically.
Trust bundle and cross-trust-domain
pilot/pkg/trustbundle/ aggregates roots from:
- The local CA.
MeshConfig.caCertificates.- Cert-manager
ClusterTrustBundle(when enabled). - Federated trust domains (multi-cluster mesh federation).
The aggregated bundle is shipped to proxies via SDS as ROOTCA. Cross-trust-domain auth means a workload from cluster1.local can present its cert to a workload that trusts only cluster2.local's root, provided both roots are in the bundle. The SPIFFE URI's trust-domain field stays as-issued.
Trade-offs
- STRICT vs PERMISSIVE — strict mTLS rejects any plaintext peer, including kubelet probes. The probe rewriter in injection (
pkg/kube/inject/app_probe.go) avoids this for k8s probes, but other plaintext paths (port-forwarded debugging, third-party operators) require namespace-levelPeerAuthenticationoverrides. - mTLS cost — with sidecars, the encryption/decryption happens in Envoy. With Ambient, ztunnel does it. Either way, expect ~10-30% latency overhead and CPU cost vs cleartext.
- Cert rotation visibility — when a CA rolls, every workload must re-sign within
MAX_WORKLOAD_CERT_TTL. Slow rollouts can leave a long tail of invalid certs. Thecitadel_server_cert_chain_expiry_timestampmetric is the canary.
Key source files
| File | Purpose |
|---|---|
pilot/pkg/security/authn/policy_applier.go |
mTLS + JWT compilation (sidecar/waypoint) |
pilot/pkg/security/authz/builder/builder.go |
RBAC compilation (sidecar/waypoint) |
pilot/pkg/serviceregistry/kube/controller/ambient/authorization.go |
RBAC compilation (ztunnel L4) |
pilot/pkg/model/authorization.go |
Policy selector resolution |
pilot/pkg/trustbundle/trustbundle.go |
Aggregated root certs |
security/pkg/pki/ca/ca.go |
CSR signer |
security/pkg/server/ca/server.go |
CSR gRPC server |
pilot/pkg/keycertbundle/watcher.go |
Live root reload |
See also
- systems/security-ca — the CA implementation.
- pilot-agent — workload-side identity client.
- features/ambient-mode — how ambient handles identity differently.
- Istio docs: Security — user-facing reference.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.