mTLS: Mutual TLS for Service-to-Service Authentication
Learn how mutual TLS secures communication between microservices, how to implement it, and how service meshes simplify mTLS management.
mTLS authenticates service peers with certificates, while authorization policies determine which identities may access each service. This guide covers CA hierarchies, certificate rotation, Istio and Linkerd, SPIFFE/SPIRE workload identity, and the performance costs of TLS handshakes and proxies. It also shows how to enforce policies, investigate failures, and monitor certificate and proxy health.
mTLS: Mutual TLS for Service-to-Service Authentication
Introduction
Mutual TLS (mTLS) authenticates both ends of a connection: the client proves its identity to the server, and the client verifies the server’s certificate. In service-to-service systems, workload identity and certificate policy help limit impersonation and protect traffic between services.
This guide explains the certificate authority hierarchy and lifecycle, then compares service mesh mTLS with SPIFFE/SPIRE workload identity. It also covers implementation, testing, observability, and failure cases such as expired certificates or mismatched enforcement modes.
How mTLS Differs from Regular TLS
Regular TLS uses a one-way authentication model. The client verifies the server’s certificate. At the transport layer, the server does not request a client certificate by default. The application can still require a separate credential, such as a session token or API key.
graph LR
Client -->|1. ClientHello| Server
Server -->|2. Certificate| Client
Client -->|3. Verify server| Client
Server -->|4. Encrypted channel established| Client
In a service-to-service system, server-only TLS does not give a server a cryptographic identity for its caller. Applications can still authenticate callers separately, but mTLS lets the server verify a client certificate during the connection handshake.
mTLS adds client-certificate authentication to the TLS handshake. In a full TLS 1.3 handshake, the server requests and presents its certificate in the server flight; the client then sends its certificate and proof of possession. Here is the simplified message flow:
sequenceDiagram
participant Client
participant Server
Client->>Server: ClientHello with key share
Server-->>Client: ServerHello with key share
Server-->>Client: EncryptedExtensions, CertificateRequest, Certificate, CertificateVerify, Finished
Client->>Server: Certificate, CertificateVerify, Finished
Note over Client,Server: Both sides validate identity and derive traffic keys
Certificate Authority Hierarchy
mTLS relies on a chain of trust. Understanding how this hierarchy works helps when debugging certificate issues and designing your PKI (Public Key Infrastructure).
Root CA
At the top sits the Root Certificate Authority. Root CAs are long-lived, often for decades and stored securely, usually offline. You do not use the Root CA directly to sign workload certificates. Instead, you create intermediate CAs.
The Root CA is your ultimate trust anchor. Every certificate in your mTLS hierarchy traces back to it. If the Root CA private key is compromised, every chain that depends on it is at risk. Recovery requires distributing a replacement trust anchor, removing trust in the compromised one, and issuing new intermediates and leaf certificates. This is why Root CAs are kept offline, often in hardware security modules (HSMs) or air-gapped systems, and are restricted from signing workload certificates directly.
In production, the Root CA rarely touches a network. You generate intermediate CA certificates with the Root CA, then use those intermediates for everything operational. The Root CA is used for initial setup, intermediate renewal, or planned trust-anchor rotation. Separate roots for production and staging can keep a staging compromise from affecting production.
Intermediate CA
Intermediate Certificate Authorities sit between the Root CA and leaf certificates. They are signed by the Root CA and can sign other certificates. Intermediates limit exposure: if one is compromised, replace it and reissue dependent leaf certificates without replacing the Root CA; client enforcement of revocation varies.
Production mTLS setups usually have one Root CA and multiple intermediates per environment or per team.
The intermediate layer is where most operational work happens. Teams may use separate intermediates per environment to reduce blast radius. If an intermediate is compromised, operators can replace it and reissue dependent leaf certificates without replacing the Root CA. Whether leaf revocation is enforced depends on how clients check revocation status.
Intermediates also enable automation. Your certificate issuance pipeline signs leaf certificates using the intermediate’s private key. If that key leaks, replace the intermediate, reissue dependent leaf certificates, and update trust or revocation data as supported by clients. Painful, but contained. If you had signed everything directly from the Root CA, a compromise would require rebuilding the entire PKI from scratch.
Leaf Certificates
Leaf certificates (also called workload certificates) are what services actually use. Each service instance gets its own leaf certificate containing its service identity: service name, namespace, service account, and similar metadata.
Leaf certificates are short-lived. Hours or days, not years. This limits damage if a certificate is stolen. The CA issues new certificates automatically through rotation.
graph TD
RootCA[Root CA] -->|signs| IntermediateCA[Intermediate CA]
IntermediateCA -->|signs| ServiceA[Service A Certificate]
IntermediateCA -->|signs| ServiceB[Service B Certificate]
IntermediateCA -->|signs| ServiceC[Service C Certificate]
Certificate Lifecycle
Certificates are not set-and-forget. They need issuance, distribution, rotation, and revocation. Get any of these wrong and services stop communicating.
Issuance
When a new service pod starts, it needs a certificate. The pod requests a certificate from the CA through an API. The CA verifies the request, signs the certificate, and returns it.
Istio uses SDS (Secret Discovery Service) for this. The control plane issues certificates, and Envoy fetches them via SDS without restarts. Linkerd uses its identity control-plane component to issue workload certificates; a separate admission webhook injects the proxy into selected pods.
Rotation
Certificates expire. Leaf certificates often have short TTLs in service meshes; the configured lifetime varies by mesh and deployment. The CA issues new certificates before the old ones expire. Services pick up the new certificates automatically.
If rotation breaks, services lose communication when certificates expire. This is a common cause of production incidents. Monitor certificate expiration dates. Set alerts for certificates expiring within 7 days.
Revocation
Sometimes you need to invalidate a certificate before it expires. A service is compromised. A private key leaks. You need to stop trusting that certificate immediately.
CRLs (Certificate Revocation Lists) and OCSP (Online Certificate Status Protocol) are common revocation mechanisms in traditional PKI. Meshes often use short-lived workload certificates and control-plane updates instead of checking a CRL or OCSP responder on every connection. The exact revocation behavior depends on the mesh, proxy, and configuration; removing a workload from service does not by itself invalidate its certificate.
Istio can distribute updated trust and policy configuration through its control plane. The time to take effect depends on the configuration and proxy state, so validate the documented revocation path for your deployment.
How mTLS Works in Service Communication
When Service A calls Service B over mTLS, the handshake happens at the connection layer, transparently to your application code.
sequenceDiagram
participant A as Service A
participant PA as Proxy A
participant PB as Proxy B
participant B as Service B
A ->> PA: HTTP request to Service B
PA ->> PA: TLS handshake with PB
PA ->> PB: ClientCertificate, Finished
PB ->> PB: Verify PA certificate
PB ->> B: Forward request (plaintext)
B ->> PB: Response
PB ->> PA: TLS encrypted response
PA ->> A: HTTP response
Sidecar proxies terminate TLS. Service A makes a plaintext HTTP call to its local proxy. The proxy on Service A’s side establishes mTLS with the proxy on Service B’s side. Service B’s proxy forwards the plaintext request to Service B.
Your application code never sees certificates or TLS. It makes normal network calls. The mesh handles authentication and encryption.
Certificate Path Validation
When a proxy receives a certificate during the TLS handshake, it validates the entire chain:
- Check the certificate is not expired
- Check the signature against the Intermediate CA’s public key
- Check the Intermediate CA’s certificate against the Root CA’s public key
- Check the certificate is not revoked (if configured)
If any check fails, the connection gets rejected.
When to Use
Use mTLS in these scenarios:
- Service-to-service communication within a cluster: A service mesh can automate mTLS between meshed workloads; verify that all intended clients are included and that plaintext is rejected where required.
- Cross-cluster or multi-environment communication: mTLS with a configured SPIFFE federation can support services spanning multiple trust domains or environments.
- Services behind an API gateway: Implement mTLS between the gateway and backend services, even if the edge handles authentication differently.
- High-throughput, latency-critical internal paths: The security benefits justify the overhead. Connection pooling mitigates handshake latency for persistent workloads.
When NOT to Use
Do NOT use mTLS in these scenarios:
- Browser-based clients: mTLS requires client-side certificates, which browsers handle poorly. Use OIDC instead.
- External API calls (third-party services): Use TLS with server certificates only; client certificates require certificate distribution to external parties which is impractical.
- Mobile or desktop clients calling backend services: Use OAuth 2.0 / OIDC for user-facing flows instead of client certificates.
- Migration periods: Running PERMISSIVE mode long-term creates security gaps. Only use during controlled transitions.
- IoT devices with limited crypto capability: CPU overhead and certificate management may not be feasible for constrained devices.
- Stateless serverless functions: Cold start latency compounds with certificate provisioning. Evaluate whether connection-level authentication adds value for your invocation pattern.
Service Mesh Auto-mTLS
Setting up mTLS manually for every service is painful. You need to issue certificates, distribute them, handle rotation, and configure each service. Service meshes automate this.
Istio
Istio provides automatic mTLS through its control plane (istiod). Enable STRICT mode for a namespace and all communication requires mTLS.
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT
With STRICT mode, only connections with valid mTLS certificates are allowed. PERMISSIVE mode allows both mTLS and plain text, useful during migration.
Istio commonly issues short-lived workload certificates and rotates them automatically via SDS; the configured lifetime can vary. Envoy detects certificate changes and reloads TLS context without dropping active connections.
Linkerd
Linkerd uses a different approach. Each service pod gets a Linkerd proxy (written in Rust) that handles mTLS automatically.
Linkerd’s identity service issues short-lived workload certificates and rotates them automatically. You do not configure mTLS explicitly; it is on by default for all mesh traffic. There is no PeerAuthentication resource.
Certificate Management Tools
Outside of service meshes, you need tools to manage certificates. cert-manager and Vault are the most common choices.
cert-manager
cert-manager is a Kubernetes-native certificate controller. It manages certificates from various issuers (Let’s Encrypt, Vault, internal CA) and keeps them renewed.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: my-ca
spec:
ca:
secretName: ca-key-pair
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: service-a-cert
spec:
secretName: service-a-tls
issuerRef:
name: my-ca
commonName: service-a.default.svc
dnsNames:
- service-a.default.svc
duration: 24h
renewBefore: 4h
cert-manager handles rotation automatically. Before the certificate expires, cert-manager contacts the issuer, obtains a new certificate, and updates the Kubernetes secret.
HashiCorp Vault
Vault is a more complete secrets management solution. It can issue certificates dynamically, revoke them, and handle key rotation. Vault’s PKI secrets engine supports mTLS certificate issuance with short TTLs.
# Configure Vault PKI engine
vault secrets enable pki
# Set certificate TTL
vault secrets tune -max-lease-ttl=24h pki
# Create a role for service certificates
vault write pki/roles/service-mesh \
allowed_common_name="{{identity.entity.aliases.auth_jwt_aliased_entity_id}}.svc" \
allowed_uri_sans="spiffe://cluster/*" \
max_ttl=24h
Services authenticate to Vault using Kubernetes service accounts, request certificates, and receive short-lived credentials. Vault can also handle dynamic secret generation for other secrets beyond certificates.
Performance Implications
mTLS adds certificate messages and validation work to connection setup, along with encryption costs for application traffic. A full TLS 1.3 client-authenticated handshake does not add another round trip, but opening many short-lived connections can still raise latency and CPU use.
Handshake Latency
A full TLS 1.3 handshake takes about one round trip with or without client-certificate authentication. The client certificate and its proof are added to the handshake; validation adds CPU work, but does not require an extra round trip in the normal full handshake. A full TLS 1.2 handshake typically takes two round trips.
For short-lived connections, this matters more. If your services make many short-lived calls, use connection pooling to amortize handshake cost across many requests.
TLS 1.3 reduced the full handshake to about one round trip compared with two for a full TLS 1.2 handshake. Client authentication adds messages and certificate-validation work, but not an extra round trip in the normal full TLS 1.3 handshake. The key insight is that mTLS handshake latency is paid once per connection, not once per request. If your service makes 1000 requests over a persistent connection, the handshake cost is negligible. If your service makes 1000 requests using new connections each time, the cost multiplies.
Connection pooling is the standard mitigation. Rather than opening a new connection for each request, services maintain a pool of persistent connections to each upstream service. The first request pays the handshake cost; subsequent requests reuse the connection. Most HTTP clients and service mesh proxies handle this automatically.
CPU Overhead
TLS encryption and decryption consume CPU. AES-NI hardware acceleration helps significantly. Modern CPUs handle TLS overhead well for most workloads.
Under heavy load with many concurrent connections, CPU may become a bottleneck. Profile your services with mTLS enabled.
The CPU cost comes from symmetric encryption and the asymmetric operations during the handshake, such as certificate-signature verification and ephemeral key agreement. The symmetric encryption is fast — modern CPUs have AES-NI instructions that handle it in hardware. The asymmetric operations are slower, but they only happen during handshake, not per-request.
Under heavy load with many concurrent connections doing handshakes, the asymmetric cost dominates. Profiling shows this as high CPU in the TLS library during connection establishment. If your services do many new connections per second (not reusing pooled connections), watch for this. The fix is usually connection pooling, not more CPU.
Memory Overhead
Each TLS connection consumes memory for buffers and session state. Sidecar proxies add memory consumption per service instance.
Envoy’s memory usage scales with connection count. At high connection counts, tune buffer sizes and connection limits.
mTLS vs SPIFFE/SPIRE for Workload Identity
SPIFFE (Secure Production Identity Framework for Everyone) and its implementation SPIRE provide a standardized approach to workload identity that goes beyond certificates alone.
SPIFFE
SPIFFE defines a URI scheme for workload identity: spiffe://trust-domain/path. These identities are embedded in X.509 certificates (SVIDs - SPIFFE Verifiable Identity Documents) or JWTs.
SPIFFE focuses on the identity layer. It answers: how do I know which workload is making this request?
SPIRE
SPIRE is the implementation. It runs as an agent on each node and a server that manages registration and policy. The agent attests the workload’s environment (Kubernetes, AWS, etc.) and obtains SVIDs from the server.
graph TD
SPIREServer[SPIRE Server] -->|issues SVID| Agent[SPIRE Agent]
Agent -->|attests| Workload[Workload]
Workload -->|uses SVID| Service[Service]
Comparison
mTLS provides encryption and authentication. SPIFFE/SPIRE provides the identity layer that mTLS relies on. They work together.
Istio supports SPIFFE-based identity natively. Linkerd has its own identity system that is SPIFFE-compatible. Some service meshes use SPIFFE-compatible workload identities, but compatibility and federation behavior depend on the mesh configuration.
Use SPIRE directly when you need workload identity outside a service mesh or across multiple platforms. SPIRE can provision mTLS certificates for any workload, not just Kubernetes.
Trade-Off Table
The decision to adopt mTLS involves balancing security benefits against operational complexity. Here is a structured comparison:
Security vs Complexity
| Factor | Regular TLS | mTLS |
|---|---|---|
| Authentication scope | Server only | Mutual (both sides) |
| Setup complexity | Lower | Higher |
| Certificate management | Basic | Requires CA hierarchy, rotation automation |
| Operational overhead | Low | High (rotation, revocation, chain validation) |
| Security posture | Server verified | Both endpoints cryptographically verified |
Performance vs Security
| Factor | Impact | Mitigation |
|---|---|---|
| Handshake latency | Client authentication adds messages within the TLS 1.3 handshake | Connection pooling, TLS session resumption |
| CPU overhead | Encryption + client cert validation | AES-NI acceleration, hardware offload |
| Memory usage | Sidecar proxy memory per connection | Tune buffer sizes, connection limits |
Operational Maturity
| Factor | Self-managed mTLS | Service Mesh mTLS |
|---|---|---|
| Certificate issuance | Manual or custom tooling | Automatic via control plane |
| Rotation | Manual intervention required | Automatic with short TTLs |
| Policy enforcement | Per-service configuration | Namespace-wide defaults |
| Observability | Limited built-in metrics | Rich metrics, tracing, logging hooks |
Production Runbook
Failure Scenarios and Mitigations
Scenario: Certificate Expiration Outage
Symptoms: Services stop communicating. Logs show “certificate verify failed” or “handshake failure” errors. Intermittent 503s between specific service pairs.
Diagnosis:
# For cert-manager-managed certificates, inspect readiness and expiry information
kubectl get certificates -A
# For Istio, inspect secrets loaded by a proxy, including certificate validity
istioctl proxy-config secret <pod-name> -n <namespace>
# For Linkerd, verify whether service edges are secured with mTLS
linkerd viz edges deployment/<name> -n <namespace>
Mitigation:
- Identify which certificates are expired vs approaching expiry
- If rotation failed for specific workloads, inspect control-plane and proxy status first; roll only affected pods if the documented refresh path does not recover them
- If the CA itself has issues, avoid weakening authentication as a blanket workaround; use a documented recovery plan and scope any temporary exception
- After recovery, verify the peer-authentication policy and confirm the affected connections are secured;
istioctl x authz checkinspects authorization policy, not certificate expiry
Prevention:
- Alert at 7 days, 3 days, and 24 hours before expiration
- Test certificate rotation in staging every sprint
- Have backup CA certificates available for emergency key rotation
Scenario: Mixed mTLS Mode Security Gap
Symptoms: Security audit finds services accepting plain text connections. PERMISSIVE mode still configured in production namespaces.
Diagnosis:
# Check Istio PeerAuthentication policies
kubectl get peerauthentication -A -o yaml
# Find namespaces with PERMISSIVE mode
kubectl get peerauthentication -A | grep -v STRICT
# Verify with Envoy access logs
# Look for non-mTLS connections in logs
Mitigation:
- Identify all PERMISSIVE configurations
- Audit which services legitimately need PERMISSIVE (typically only during migration)
- Plan migration to STRICT for each service pair
- Apply STRICT mode incrementally, monitoring for breakage
Prevention:
- Policy-as-code to detect PERMISSIVE in production
- Automated security scans on namespace configurations
- Require PR approval for any PERMISSIVE mode change
Scenario: Intermediate CA Certificate Chain Break
Symptoms: “certificate verify failed” errors without clear indication of which certificate in the chain is problematic. Some services work, others do not.
Diagnosis:
# Check certificate chain in a pod
kubectl exec -it <pod> -c istio-proxy -- openssl s_client -connect <service>:443 -showcerts
# Verify chain against known good Root CA
openssl verify -CAfile /etc/certs/root-cert.pem /etc/certs/cert-chain.pem
# Check Istio control plane certificate
kubectl get secret istio-ca-secret -n istio-system -o yaml
Mitigation:
- Identify which intermediate CA signed the problematic certificates
- Distribute the missing intermediate CA certificate to affected services
- In Istio, restart the control plane to propagate updated certificates
- Verify the full chain is present in Envoy configuration
Prevention:
- Test certificate chain validation in CI/CD pipeline
- Monitor for “unable to get local issuer certificate” errors
- Store intermediate CA certificates in a configmap that updates automatically
Scenario: Sidecar Proxy Memory Pressure
Symptoms: OOM kills on sidecar proxies. Services experiencing latency spikes. Envoy memory usage growing unbounded.
Diagnosis:
# Check Envoy memory usage
kubectl top pods -n <namespace> -l app=<service>
# Check Envoy stats
curl -s http://<pod>:15000/stats | grep "memory"
# Check connection counts
istioctl proxy-config stats <pod> | grep "cluster.grpc.*connections"
Mitigation:
- Reduce connection limits in Envoy configuration
- Tune buffer sizes for your workload
- If due to connection buildup, check for failed health checks causing connection accumulation
- Scale horizontally if individual services have too many connections
Prevention:
- Set resource limits on sidecar proxies
- Monitor Envoy memory trends
- Configure circuit breakers to prevent cascading connection buildup
Observability Hooks
Metrics to Capture
Metric names and thresholds below are illustrative; map them to counters exported by your mesh and proxy.
| Metric | What It Tells You | Alert Threshold |
|---|---|---|
mtls_handshake_success_total |
Example mTLS handshake metric | Set against the service baseline |
mtls_handshake_duration_seconds |
Example handshake latency series | Alert on sustained deviation from baseline |
certificate_expiration_seconds |
Time until certificate expires | <7 days warning, <1 day critical |
envoy_worker_threads_busy_percent |
Example proxy saturation signal | Set against limits and baseline |
envoy_memory_heap_size_bytes |
Example proxy memory signal | Alert on sustained growth or resource pressure |
Logs to Collect
From Envoy sidecar (structured logging):
{
"event": "mtls_handshake",
"connection_id": "abc123",
"source_workload": "payment-service",
"destination_workload": "invoice-service",
"result": "success|failure",
"failure_reason": "certificate_expired|chain_validation_failed|revoked",
"tls_version": "1.3",
"duration_ms": 12
}
Key log fields: source identity, destination identity, handshake result, failure reason, duration, TLS version.
Traces to Capture
Enable tracing on Envoy with Jaeger or Zipkin. Key span attributes:
mTLS.peer.certificate.valid: booleanmTLS.peer.certificate.expiry.unix_timestamp: certificate expirationmTLS.peer.identity: SPIFFE ID of the remote peer
Dashboards to Build
- mTLS Health Overview: Handshake success rate, failure breakdown by reason, certificate expiration countdown
- Sidecar Resource Utilization: Memory, CPU per service, connection counts
- Certificate Lifecycle: Issuance rate, rotation success rate, upcoming expirations
- Security Posture: PERMISSIVE mode violations, unauthorized connection attempts
Alerting Rules
# Certificate expiring
- alert: CertificateExpiringSoon
expr: certificate_expiration_seconds < 86400 * 7
labels:
severity: warning
annotations:
summary: "Certificate expiring in {{ $value }}"
- alert: CertificateExpiringCritical
expr: certificate_expiration_seconds < 86400
labels:
severity: critical
# mTLS handshake failures
- alert: MTLSHandshakeFailures
# Example threshold only; use a metric your proxy exports and tune from baseline
expr: rate(mtls_handshake_failure_total[5m]) > 0.001
labels:
severity: warning
annotations:
summary: "mTLS handshake failures above the configured service baseline"
Common Pitfalls / Anti-Patterns
mTLS adds complexity. Several failure modes cause production incidents.
Certificate Expiration
Certificate expiration is the top reason mTLS breaks in production. Certificates expire, rotation fails, and services stop talking to each other. This happens when rotation logic has bugs, when network partitions block certificate fetch, or when someone misconfigured the TTLs.
The failure mode is predictable. When a certificate expires, the TLS handshake fails immediately. Services that were communicating fine start reporting “certificate verify failed” or “handshake failure” errors. Failures begin when clients encounter an expired certificate they still need to validate.
Root causes fall into three buckets. Rotation logic bugs: the CA or control plane does not issue a new certificate before the old one expires. Network partition during rotation: the pod requests a new certificate but the request never reaches the CA, or the response never comes back. Misconfigured renewal timing: the issuer starts renewal too close to expiry, leaving too little time to recover from failed issuance.
Monitor expiration actively. Set alerts at 7 days, 3 days, and 1 day before expiry. Test rotation in staging regularly. Watch for partial rotation failures where some services in your fleet successfully rotate while others fail silently—that pattern usually points to a specific pod or namespace with a network or configuration problem.
Partial rotation failures can affect only some pods when issuance, control-plane delivery, or proxy updates fail. Compare certificate validity and proxy configuration across healthy and unhealthy workloads; follow the mesh-specific refresh procedure before restarting pods.
Revocation Checking Failures
If you rely on CRL or OCSP for revocation, failures can cause connection timeouts. Clients wait for revocation checks to timeout before failing. A timeout can delay or affect validation depending on the client’s fail-open/fail-closed policy, cache state, and responder configuration.
CRL-based revocation has a structural problem: the client must download the entire revocation list to check a single certificate. Clients may cache CRLs, so they do not necessarily download and parse the list on every connection; large lists can still increase distribution and processing costs. OCSP is more efficient—the client sends a single certificate serial number and gets back a status—but it introduces a new network dependency. If the OCSP responder is unreachable, clients may fail closed or continue without a confirmed revocation status, depending on implementation and configuration. Whether a client fails open or closed depends on its TLS stack and configuration; document and test that behavior for your clients.
One reason service meshes often avoid per-connection revocation checks is latency. If a client checks OCSP synchronously, the lookup can add DNS, TCP, and HTTP work to connection setup. At scale, this multiplies. Instead, service meshes rely on short certificate lifetimes—typically 24 hours. Removing a compromised service from network access or updating its authorization policy can cut off communication; short certificate lifetimes also limit how long a certificate remains valid. These controls have separate effects and delays.
If you use CRLs, make them accessible and set cache behavior appropriate for your clients. For faster containment, use a documented control-plane policy or identity revocation mechanism and verify how quickly it reaches proxies.
Mixed mTLS Modes
During migration, you may run PERMISSIVE mode in some namespaces and STRICT in others. Forgetting to switch back to STRICT leaves security gaps. The danger is subtle: a namespace in PERMISSIVE mode accepts plain text connections, which means a misconfiguration or a mistake in network policy can allow unauthenticated traffic to reach a service that should be protected.
The problem with PERMISSIVE mode is that it hides the true security state of your system. When all services are in STRICT mode, any plain text connection attempt fails visibly. When some namespaces are PERMISSIVE, developers and operators stop noticing those failures—they become background noise that gets ignored. Then a security audit finds that a production namespace has been in PERMISSIVE mode for six months, and nobody caught it because the failures were never visible.
PERMISSIVE mode exists for one reason: controlled migration from plain text to mTLS. The migration path typically looks like this. Start with PERMISSIVE so all services can communicate regardless of their mTLS configuration. Gradually move each service pair to STRICT mode as you verify their certificates are working. Once all service pairs in a namespace are on STRICT, lock the namespace. Then move to the next namespace.
The risk is leaving PERMISSIVE mode in place after migration is complete. Some teams use PERMISSIVE mode during debugging and forget to remove it. Others use it for “temporary” exceptions that become permanent. Audit PERMISSIVE configurations regularly. Use STRICT by default and only use PERMISSIVE temporarily during controlled migrations. Set a calendar reminder to review any PERMISSIVE configuration older than 30 days—if it is still in PERMISSIVE mode after 30 days, it is probably not a temporary migration setting.
Certificate Chain Issues
If intermediate CA certificates are not distributed correctly, verification fails with cryptic errors. Applications see “certificate verify failed” without clear indication of the missing intermediate. The error message gives you no clue which certificate in the chain is missing or why the verification failed. This makes debugging frustrating, especially because the fix is usually simple once you know what is missing.
The chain of trust in mTLS flows from the leaf certificate up through any intermediate CAs to the Root CA. The client must trust the Root CA certificate to verify the peer’s certificate. But in many setups, the server also needs to present the full chain—including the intermediate CA certificates—so the client can verify the chain without needing every intermediate pre-installed. If the server only sends its leaf certificate and not the intermediates, the client tries to build the chain using the intermediates it has locally. If the client does not have the right intermediate, verification fails.
This is a common deployment problem when you first set up your internal CA or when you rotate intermediate CA certificates. The CA administrator issues a new intermediate, signs leaf certificates with it, but forgets to distribute the new intermediate to the servers or clients that need to verify those leaf certificates. The server may have the full chain on disk while its TLS configuration serves only the leaf certificate. The client receives only the leaf and cannot build the chain.
Ensure your server configuration includes the full certificate chain. Most TLS libraries accept a chain file containing the leaf certificate followed by the intermediate certificates; clients should already have the trusted root CA in their trust store, so servers usually omit that root from the sent chain. Verify the chain using openssl commands before deploying: check that the server’s certificate file contains the full chain, and check that the client’s trust store contains the root CA and any intermediate CAs in the chain. Test in staging before production deployment.
Namespace Isolation Gaps
mTLS policies sometimes have gaps between namespaces. A service in Namespace A may accept connections from Namespace B even if you intended isolation.
Define authorization policies explicitly. Assume default-deny. Explicitly allow only the service pairs that must communicate.
Security and Compliance Notes
mTLS handles authentication, but you still need authorization and key protection to round out your security posture.
Certificate Private Key Protection
Leaf certificates hold private keys that need protection. Use short-lived certificates (24 hours or less), store CA keys in hardware security modules (Cloud KMS or AWS KMS), and disable private key export. cert-manager can generate private keys in-cluster and store them in Kubernetes Secrets; restrict access to those Secrets.
Network Segmentation
mTLS verifies identity, but it does not control what verified identities can do. Layer Kubernetes NetworkPolicy or Istio AuthorizationPolicy on top to specify which service pairs are allowed to communicate. Think of mTLS as proving who you are, and authorization policies as deciding what you can access.
Compliance Considerations
For regulated environments, you will want:
- Certificate monitoring: Monitor public CT logs for publicly trusted certificates and track private-CA issuance through internal audit logs
- Audit Trail: Capture source and destination workload identities in logs for all mTLS connections
- Rotation Testing: Document and regularly test certificate rotation procedures for compliance audits
Observability Checklist
Monitor these key areas to detect mTLS issues before they cause production outages.
Key Metrics to Capture
Metric names and thresholds below are illustrative. Use series exported by your mesh and proxy, then set alert thresholds against a measured baseline and service objectives.
| Metric | What It Tells You | Alert Threshold |
|---|---|---|
mtls_handshake_success_total |
Example mTLS handshake metric | Set against the service baseline |
mtls_handshake_duration_seconds |
Example handshake latency series | Alert on sustained deviation from baseline |
certificate_expiration_seconds |
Time until certificate expires | <7 days warning, <1 day critical |
envoy_worker_threads_busy_percent |
Example proxy saturation signal | Set against limits and baseline |
envoy_memory_heap_size_bytes |
Example proxy memory signal | Alert on sustained growth or resource pressure |
Critical Alerts to Configure
- Certificate expiring within 7 days: Warning alert to replace certificates before they cause outages
- Certificate expiring within 24 hours: Critical alert for immediate action
- Sustained mTLS handshake failure increase: Investigate certificate, policy, and configuration changes
- Sidecar memory pressure: Alert before sustained growth reaches the pod limit
What to Monitor
- mTLS Health Overview: Handshake success rate, failure breakdown by reason, certificate expiration countdown
- Sidecar Resource Utilization: Memory, CPU per service, connection counts
- Certificate Lifecycle: Issuance rate, rotation success rate, upcoming expirations
- Security Posture: PERMISSIVE mode violations, unauthorized connection attempts
- Envoy session statistics: Session timeout settings and memory growth trends for long-running services
Structured Logs to Collect
Structure logs with these fields for mTLS handshake events:
source_workload: Identity of the calling servicedestination_workload: Identity of the receiving serviceresult: success or failurefailure_reason: certificate_expired, chain_validation_failed, revoked, or othertls_version: TLS 1.3 or TLS 1.2duration_ms: Handshake duration in milliseconds
Production Checklist
Before going to production with mTLS:
- mTLS set to STRICT mode (not PERMISSIVE) in all namespaces
- Authorization policies define which service pairs can communicate
- Certificate rotation tested and monitored
- Alerts configured for certificate expiration (7 days, 3 days, 1 day)
- Certificate chain validated in staging
- Monitoring for mTLS handshake failures
- Resource limits set for sidecar proxies
- Performance profiled under load with mTLS enabled
- Backup CA certificates stored securely
- Rotation procedures documented and tested
Implementation Walkthrough
Setting up mTLS involves configuring the CA, issuing certificates, and configuring services to use them. Here is a practical sequence for getting mTLS working in a Kubernetes environment.
Prerequisites
Before starting, you need a working Kubernetes cluster, kubectl access, and a service mesh installed (Istio or Linkerd). For this walkthrough, we use Istio with SDS-based certificate provisioning.
Step 1: Install Istio
Install Istio using the profile appropriate for your cluster. Automatic mTLS upgrades traffic between meshed workloads, but the default server policy can still accept plaintext. Apply a STRICT PeerAuthentication policy in the next step when the workload is ready to reject non-mTLS connections:
# Install the default profile; this does not set STRICT peer authentication
istioctl install --set profile=default
Step 2: Configure Automatic Certificate Rotation
Istio rotates certificates automatically via SDS. Verify rotation is working:
# Check current certificate expiration
istioctl proxy-config secret <pod-name> -n <namespace>
# Monitor certificate rotation events
kubectl logs -n istio-system -l app=istiod | grep certificate
Step 3: Configure per-Namespace STRICT Mode
Apply STRICT mode to specific namespaces while leaving others in PERMISSIVE for migration:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: production-strict
namespace: production
spec:
mtls:
mode: STRICT
---
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: staging-permissive
namespace: staging
spec:
mtls:
mode: PERMISSIVE
Step 4: Verify mTLS is Working
Test that mTLS connections are enforced:
# Inspect the peer-authentication policy and workload status
kubectl get peerauthentication -n <namespace>
istioctl x describe pod <pod-name> -n <namespace>
# Inspect authorization policy separately
istioctl x authz check <pod-name> -n <namespace>
Step 5: Monitor Certificate Health
Set up monitoring for certificate expiration and handshake success. Metric names shown here are examples; use counters and gauges provided by your mesh or proxy:
# Export Envoy stats
kubectl port-forward <pod> 15000
# Inspect TLS-related Envoy counters; names vary by Envoy version and listener
curl -s http://localhost:15000/stats | grep -E "ssl.handshake|downstream_cx_ssl"
# Check certificate expiration
curl -s http://localhost:15000/certs | jq '.[] | {sha: .sha, expire: .expiration}'
mTLS Testing Strategies
Testing mTLS requires validating both the happy path and failure conditions. Here are strategies for comprehensive testing.
Certificate Chain Validation Testing
Verify the full certificate chain validates correctly:
# Test full chain validation
kubectl exec -it <pod> -c istio-proxy -- \
openssl s_client -connect <service>:443 -CAfile /etc/certs/root-cert.pem -showcerts
# Verify intermediate CA is included
openssl s_client -connect <service>:443 -showcerts 2>&1 | grep "Certificate chain"
Certificate Expiration Testing
Test renewal in a staging mesh or with a test issuer configured for a short certificate lifetime. Avoid deleting mesh-managed secrets in a live namespace. Confirm that the renewed certificate reaches the proxy and that new connections succeed before the old certificate expires:
# Check cert-manager-managed certificate readiness
kubectl get certificates -n <namespace>
# Inspect the certificate currently loaded by an Istio proxy
istioctl proxy-config secret <pod-name> -n <namespace>
Mutual Authentication Failure Testing
Verify unauthorized connections are rejected:
# From a test pod without a mesh proxy, confirm plaintext is rejected
kubectl run curl-test --namespace <namespace> --image=curlimages/curl --rm -it -- \
curl -v http://<service>.<namespace>.svc.cluster.local
# From a meshed application pod, confirm the service request succeeds
kubectl exec <source-pod> -n <namespace> -c <app-container> -- \
curl -v http://<service>.<namespace>.svc.cluster.local
Performance Testing
Measure mTLS overhead under load:
# Using hey or wrk to generate load
hey -n 10000 -c 100 -m POST \
-H "Content-Type: application/json" \
-d '{"test":"data"}' \
http://<service>:80/api
# Monitor Envoy worker threads
curl -s http://<pod>:15000/stats | grep "worker_threads_busy"
Security Policy Testing
Test that authorization policies work with mTLS:
# Deny all by default
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: production
spec: {}
---
# Allow specific service pairs
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-payment-to-invoice
namespace: production
spec:
selector:
matchLabels:
app: invoice-service
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/payment-service"]
Test that only allowed pairs communicate:
# Verify allowed connection works
kubectl exec payment-pod -n production -c <app-container> -- \
curl -v http://invoice-service.production.svc.cluster.local:80
# Verify disallowed connection fails
kubectl exec other-pod -n production -c <app-container> -- \
curl -v http://invoice-service.production.svc.cluster.local:80
Security Considerations
Beyond the trade-offs discussed earlier, here are security-hardening measures for mTLS deployments.
Certificate Private Key Protection Measures
Leaf certificates contain private keys that must be protected:
| Measure | Implementation |
|---|---|
| Use short-lived certificates | Set TTL to 24 hours or less |
| Hardware security modules | Use Cloud KMS or AWS KMS for CA keys |
| Disable private key export | cert-manager can generate private keys in-cluster and store them in Kubernetes Secrets; restrict access to those Secrets |
| Rotate frequently | Automated rotation reduces exposure window |
Network Segmentation with mTLS
mTLS provides authentication but not authorization. Combine it with network policies:
# Kubernetes NetworkPolicy (requires CNI plugin)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-payment-to-invoice
spec:
podSelector:
matchLabels:
app: invoice-service
ingress:
- from:
- podSelector:
matchLabels:
app: payment-service
ports:
- protocol: TCP
port: 8080
---
# Istio AuthorizationPolicy (enforces after mTLS)
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payment-to-invoice-authz
spec:
selector:
matchLabels:
app: invoice-service
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/payment-service"]
- source:
namespaces: ["production"]
Interview Questions
Expected answer points:
- Regular TLS only proves server identity to clients (one-way authentication); mTLS proves both sides identity (mutual authentication)
- Without peer authentication, a reachable workload may attempt connections without proving its service identity; mTLS authenticates the presented identity but does not stop misuse of a compromised workload or key
- Server-only TLS does not give the receiving service a client-certificate identity; the application may still authenticate the caller with another mechanism
- mTLS ensures both the client and server present and verify certificates before establishing a connection
Expected answer points:
- Root CA sits at the top of the hierarchy, is long-lived, often for decades, stored securely offline, and signs Intermediate CAs
- Intermediate CAs sit between the Root and leaf certificates and can be replaced without replacing the Root; enforcement of revocation depends on client configuration
- Leaf certificates (workload certificates) are what services actually use, contain service identity (name, namespace, service account), are short-lived (hours to days), and support automatic rotation
Expected answer points:
- mTLS with a full TLS 1.3 handshake takes about 1 RTT, while a full TLS 1.2 handshake typically takes 2 RTTs
- In TLS 1.3, the server sends its certificate and requests a client certificate in its encrypted handshake flight; the client then sends its certificate and proof of possession
- A full TLS 1.3 handshake takes about 1 RTT with or without client-certificate authentication; eligible resumptions can send 0-RTT early data with replay considerations
- mTLS adds client certificate verification overhead on both sides, plus CPU cost for validating the full certificate chain
Expected answer points:
- SPIFFE (Secure Production Identity Framework for Everyone) defines a URI scheme for workload identity: spiffe://trust-domain/path
- SPIFFE identities are embedded in X.509 certificates as SVIDs (SPIFFE Verifiable Identity Documents)
- SPIFFE answers the question: how do I know which workload is making this request?
- mTLS provides encryption and authentication; SPIFFE provides the standardized identity layer that mTLS relies on
- Istio supports SPIFFE-based identity natively; Linkerd has its own SPIFFE-compatible system
Expected answer points:
- Leaf certificates have short TTLs (typically 24 hours in production)
- Services automatically fetch new certificates before expiry through the control plane (Istio uses SDS, Linkerd uses its own CA)
- If rotation fails: services lose communication when certificates expire, causing production incidents
- Common rotation failure causes: bugs in rotation logic, network issues preventing certificate fetch, misconfigured TTLs
- Prevention: Monitor certificate expiration dates, set alerts at multiple thresholds (7 days, 3 days, 1 day), test rotation in staging
Expected answer points:
- STRICT mode: only mTLS connections accepted, plain text connections rejected
- PERMISSIVE mode: both mTLS and plain text connections accepted
- PERMISSIVE is useful only during controlled migrations when transitioning services to mTLS
- Running PERMISSIVE long-term creates security gaps and should be avoided
- Audit PERMISSIVE configurations regularly; use STRICT by default
Expected answer points:
- Sidecar proxies (Envoy in Istio, Linkerd proxy in Rust) terminate TLS on behalf of services
- Service A makes plaintext HTTP call to its local proxy; proxy establishes mTLS with Service B's proxy
- Service B's proxy forwards plaintext request to Service B after verification
- Application code makes normal network calls without any certificate management
- This transparently handles authentication and encryption without application changes
Expected answer points:
- Online CRL or OCSP checks can add a network dependency and latency when clients perform them during connection setup; caching and client policy change the behavior
- Many service-mesh configurations rely on short-lived identities and control-plane updates rather than an online revocation check for every connection
- Short certificate lifetimes limit how long a stolen certificate remains valid, but removing network access or changing authorization policy may be needed for faster containment
- Trust and policy updates can be distributed through the control plane; verify propagation and timing for the deployed mesh
- If CRL is used, ensure CRLs are small and accessible to avoid timeout delays
Expected answer points:
- A full TLS 1.3 handshake remains about 1 RTT with client authentication; certificate messages and validation add work, while connection reuse amortizes setup
- CPU overhead: TLS encryption/decryption plus certificate chain validation on both sides; AES-NI hardware acceleration helps significantly
- Memory overhead: sidecar proxies consume memory per service instance, connection buffers scale with connection count
- For short-lived connections: use connection pooling to amortize handshake cost across many requests
- Profile services under load with mTLS enabled to identify actual bottlenecks
Expected answer points:
- Browser-based clients: browsers handle client certificates poorly; use OIDC/OAuth 2.0 instead
- Third-party integrations: distributing internal CA certificates to external parties is impractical; use API keys or OAuth
- Mobile/desktop clients calling backends: use OAuth 2.0/OIDC for user-facing flows
- IoT devices with limited crypto: CPU overhead and certificate management may not be feasible; evaluate alternatives
- Services behind API gateway handling auth: mTLS between gateway and backends (not at edge)
- Stateless serverless: cold start latency compounds with certificate provisioning; evaluate connection-level auth value
Expected answer points:
- cert-manager is a Kubernetes-native certificate controller that automates certificate lifecycle management
- Key objects: Issuer (or ClusterIssuer) defines the CA, Certificate defines the desired certificate
- The Issuer references a Kubernetes Secret containing the CA certificate and private key; restrict access to that Secret
- Certificate resources specify commonName, dnsNames, duration (e.g., 24h), and renewBefore (e.g., 4h)
- cert-manager handles rotation automatically by monitoring expiration and re-issuing before expiry
- For mTLS, each peer must trust the CA chain used to verify the other peer; the two certificates do not have to be issued by the same CA
Expected answer points:
- SPIRE runs as an agent on each node and a server that manages registration and policy
- The agent attests the workload's environment (Kubernetes, AWS, GCP, etc.) and obtains SVIDs from the server
- SPIRE can run on any platform: Kubernetes, VMs, bare metal, cloud providers
- Choose SPIRE when you need workload identity outside a service mesh or across multiple platforms simultaneously
- Service mesh identity is easier to set up but tied to that mesh; SPIRE is more portable
- SPIRE supports agents on Kubernetes, VMs, and bare metal, making multi-platform identity consistent
Expected answer points:
- Check PeerAuthentication policy: ensure namespace is in STRICT mode, not PERMISSIVE
- Inspect loaded proxy certificates with: istioctl proxy-config secret <pod-name> -n <namespace>
- Check certificate expiration: expired certificates cause immediate handshake failure
- Verify certificate chain: openssl s_client -connect <service>:443 -showcerts to see full chain
- Check Envoy logs: kubectl logs <pod> -c istio-proxy for handshake errors
- Use istioctl x authz check to inspect authorization policy; inspect PeerAuthentication and proxy state separately for mTLS
- Check control plane certificates: kubectl get secret istio-ca-secret -n istio-system
Expected answer points:
- PERMISSIVE mode accepts both mTLS and plain text connections, leaving a security gap
- A compromised service can connect to protected services without presenting a certificate
- Plain text traffic can be intercepted, enabling eavesdropping and man-in-the-middle attacks
- Detection: inspect declared mesh-, namespace-, and workload-level PeerAuthentication policies, then verify effective policy for selected workloads
- Envoy access logs can reveal which connections are plain text vs mTLS
- Prevention: policy-as-code that flags PERMISSIVE in production namespaces, PR approval required for changes
- Audit any PERMISSIVE configuration older than 30 days as likely permanent misconfiguration
Expected answer points:
- mTLS and NetworkPolicy serve different purposes and complement each other
- mTLS authenticates: proves which workload is making the request (identity verification)
- NetworkPolicy filters network traffic based on pod selectors, ports, and namespaces
- mTLS authenticates a presented peer identity but does not decide what that identity may access; authorization policies enforce that decision
- NetworkPolicy can block traffic at the network layer before mTLS negotiation occurs
- Together they provide defense in depth: NetworkPolicy restricts which pods can talk, mTLS verifies the certificate identity
- Best practice: mTLS handles identity, NetworkPolicy handles network-level access control
Expected answer points:
- Emergency Root CA rotation is high-risk: all certificates trace back to the Root CA
- Step 1: Generate new Root CA key pair, store the new certificate
- Step 2: Create new Intermediate CA signed by new Root CA
- Step 3: Issue new leaf certificates signed by new Intermediate CA
- Step 4: Distribute new Root CA certificate to all trust stores before deploying new certificates
- Step 5: Verify that proxies receive the new trust bundle and leaf certificates; restart only workloads that do not refresh through the supported rotation path
- Risk: if old Root CA is still trusted and old certificates still valid, security gap exists
- Prevention: have backup CA certificates stored securely, test rotation in staging regularly
Expected answer points:
- Without a service mesh, you need to manage certificates manually or with cert-manager
- Application code must configure TLS with client certificates using libraries like Go's crypto/tls, OpenSSL
- Each service needs the CA certificate to verify peers, and its own key/certificate for authentication
- Trade-offs: more development work, certificate distribution complexity, rotation management burden
- Benefits: no sidecar overhead, simpler architecture for small deployments
- cert-manager handles issuance and rotation, but application still needs to read certificates from secrets
- Service mesh is generally preferred for more than a few services due to operational complexity
Expected answer points:
- Linkerd uses its own identity system built on top of Kubernetes service accounts
- Linkerd identity is SPIFFE-compatible but uses a different trust anchor structure
- Linkerd does not use PeerAuthentication; mTLS is automatic between meshed pods, while plaintext from non-meshed clients may still be accepted by default
- Istio supports SPIFFE-based identity natively with its own trust domain structure
- Istio allows custom trust domains and integrates with external CAs more flexibly
- Linkerd's identity is simpler to configure; Istio's is more flexible but complex
- Both issue workload certificates automatically with short TTLs
Expected answer points:
- Inspect effective mesh-, namespace-, and workload-level PeerAuthentication policy, including inherited defaults
- Verify no PERMISSIVE mode exists in production namespaces
- Check certificate expiration dates across all services: no certificates expiring within 7 days
- Verify AuthorizationPolicy default-deny is in place for sensitive namespaces
- Check certificate chain validation is enforced in all TLS configurations
- Review Envoy logs for handshake failures and plain text connections
- Verify monitoring and alerting for mTLS handshake success rate and certificate expiration
- Check that intermediate CA certificates are distributed to all namespaces
- Document findings and create remediation tickets for any gaps found
Expected answer points:
- Each tenant should have its own trust domain or CA hierarchy to prevent cross-tenant identity
- Namespace isolation: separate intermediates per tenant namespace
- SPIFFE trust domain per tenant: spiffe://tenant-a/domain vs spiffe://tenant-b/domain
- Authorization policies must explicitly deny cross-tenant communication
- Service mesh control plane must support multi-tenant isolation (Istio Ambient, Linkerd)
- Certificate requests should be scoped to prevent one tenant from obtaining certificates for another
- Consider whether tenants need to communicate across trust boundaries; if so, use SPIFFE federation
- Resource quotas on certificate issuance prevent denial of service against the CA
Further Reading
Service meshes handle mTLS automatically. For more on how meshes work, see Service Mesh and Istio and Envoy.
Resilience patterns like circuit breakers protect against cascading failures. See Circuit Breaker Pattern and Resilience Patterns.
For authentication beyond service identity, see API Contracts for how services establish contracts.
Official documentation
- Istio authentication policy — configure mTLS modes and verify workload behavior.
- Linkerd automatic mTLS — identity issuance, trust anchors, and plaintext caveats.
- SPIFFE overview — workload identities and SVIDs.
Conclusion
mTLS secures service-to-service communication in microservices architectures. Both client and server authenticate each other, but mTLS does not replace authorization or protect a compromised service from misuse of its own identity. Certificates issued by a trusted CA establish peer identity. Short lifetimes can reduce how long a stolen certificate remains valid, while authorization policies limit what an authenticated workload may do.
Service meshes like Istio and Linkerd handle mTLS automatically. They issue certificates, rotate them, and enforce policies without application code changes. This makes mTLS practical even at scale.
The operational complexity is real. Certificate expiration causes outages. Mixed modes leave security gaps. But with proper monitoring, automated rotation, and explicit authorization policies, mTLS provides strong security for service communication.
Plaintext transport leaves traffic exposed to interception on the network path and provides no TLS workload identity to the receiving service. Use mTLS when workload identity and encrypted service-to-service transport are requirements; pair it with authorization and key-protection controls.
Category
Related Posts
OAuth 2.0 and OIDC for Microservices
Learn how OAuth 2.0 and OpenID Connect enable delegated authorization and federated identity in microservices architectures.
Secrets Management: Vault, Kubernetes Secrets, and Env Vars
Learn how to securely manage secrets, API keys, and credentials across microservices using HashiCorp Vault, Kubernetes Secrets, and best practices.
Service Identity: SPIFFE and Workload Identity in Microservices
Understand how SPIFFE provides cryptographic identity for microservices workloads and how to implement workload identity at scale.