Istio mTLS with SPIRE - How It Works
Overview
When using Istio with SPIRE, applications communicate using plain HTTP, but the Istio sidecars automatically upgrade connections to mTLS using SPIRE-issued certificates. This provides transparent security without requiring application code changes.
Communication Flow
sequenceDiagram
participant Curl as curl container
(Plain HTTP) participant CurlProxy as curl's istio-proxy
SPIFFE: spiffe://foo.com/ns/default/sa/curl participant HttpbinProxy as httpbin's istio-proxy
SPIFFE: spiffe://foo.com/ns/default/sa/httpbin participant Httpbin as httpbin container
(Plain HTTP) Curl->>CurlProxy: 1. HTTP Request
http://httpbin:8000/headers CurlProxy->>HttpbinProxy: 2. mTLS Handshake
(mutual authentication) CurlProxy->>HttpbinProxy: 3. Encrypted mTLS Connection
(SPIRE certificates) HttpbinProxy->>Httpbin: 4. HTTP Request
(decrypted, localhost) Httpbin->>HttpbinProxy: HTTP Response HttpbinProxy->>CurlProxy: 5. Encrypted Response
(adds X-Forwarded-Client-Cert) CurlProxy->>Curl: HTTP Response
(decrypted)
(Plain HTTP) participant CurlProxy as curl's istio-proxy
SPIFFE: spiffe://foo.com/ns/default/sa/curl participant HttpbinProxy as httpbin's istio-proxy
SPIFFE: spiffe://foo.com/ns/default/sa/httpbin participant Httpbin as httpbin container
(Plain HTTP) Curl->>CurlProxy: 1. HTTP Request
http://httpbin:8000/headers CurlProxy->>HttpbinProxy: 2. mTLS Handshake
(mutual authentication) CurlProxy->>HttpbinProxy: 3. Encrypted mTLS Connection
(SPIRE certificates) HttpbinProxy->>Httpbin: 4. HTTP Request
(decrypted, localhost) Httpbin->>HttpbinProxy: HTTP Response HttpbinProxy->>CurlProxy: 5. Encrypted Response
(adds X-Forwarded-Client-Cert) CurlProxy->>Curl: HTTP Response
(decrypted)
Step-by-Step Process
1. Application Makes HTTP Request
| |
- The curl container sends a plain HTTP request
- No TLS, no certificates, no encryption at application level
2. Sidecar Intercepts Request
- curl’s istio-proxy sidecar intercepts the outbound HTTP request
- Determines the destination is httpbin service
3. mTLS Handshake
- curl’s sidecar initiates mTLS connection to httpbin’s sidecar
- Both sidecars present their SPIRE-issued certificates:
- curl sidecar:
spiffe://foo.com/ns/default/sa/curl - httpbin sidecar:
spiffe://foo.com/ns/default/sa/httpbin
- curl sidecar:
- Mutual authentication succeeds using SPIRE trust domain
4. Encrypted Communication
- HTTP request is encrypted and sent over mTLS connection
- Only the sidecars handle encryption/decryption
- Application containers remain unaware of TLS
5. Sidecar Forwards to Application
- httpbin’s sidecar decrypts the request
- Forwards plain HTTP to httpbin container on localhost
- Adds
X-Forwarded-Client-Certheader with client identity
6. Response Returns
- httpbin container sends HTTP response
- httpbin’s sidecar encrypts it with mTLS
- curl’s sidecar decrypts and forwards to curl container
Evidence of mTLS
X-Forwarded-Client-Cert Header
| |
This header proves:
- By: Server identity (httpbin’s sidecar)
- URI: Client identity (curl’s sidecar)
- Subject: Certificate issued by SPIRE
- Hash: Certificate fingerprint
Certificate Verification
| |
Why HTTP Instead of HTTPS?
Benefits of Transparent mTLS
Zero Application Changes
- No TLS libraries needed in application code
- No certificate management in applications
- Developers write simple HTTP code
Centralized Security
- Security policy managed by platform team
- Consistent mTLS across all services
- Certificate rotation handled automatically
Simplified Development
- Local development uses plain HTTP
- Production gets automatic mTLS
- No environment-specific code
Performance
- Sidecars handle TLS termination
- Applications focus on business logic
- Optimized TLS implementation in Envoy
Key Components
SPIRE
- Issues X.509 certificates (SVIDs) to workloads
- Provides SPIFFE IDs based on Kubernetes identity
- Manages certificate lifecycle and rotation
SPIFFE CSI Driver
- Mounts SPIRE socket into pods
- Path:
/run/secrets/workload-spiffe-uds - Enables Envoy to fetch certificates from SPIRE
Istio Sidecar (Envoy)
- Intercepts all inbound/outbound traffic
- Performs mTLS handshake with peer sidecars
- Fetches certificates from SPIRE via CSI socket
- Adds identity headers for authorization
Application Container
- Sends/receives plain HTTP
- Unaware of mTLS or certificates
- Focuses on business logic only
Configuration Requirements
1. SPIRE Installation
| |
2. Istio Configuration
| |
3. Workload Registration
| |
4. Pod Deployment
| |
Verification Commands
Test mTLS Communication
| |
Check Certificate
| |
Verify SPIRE Entries
| |
Common Misconceptions
❌ “Applications must use HTTPS”
Reality: Applications use HTTP. Sidecars handle mTLS automatically.
❌ “Need to manage certificates in application”
Reality: SPIRE and Istio manage all certificates. Applications are unaware.
❌ “mTLS requires code changes”
Reality: Zero code changes. Just add labels and annotations to pods.
❌ “HTTP is insecure in service mesh”
Reality: HTTP between container and sidecar is on localhost. mTLS protects network traffic.
Security Guarantees
- Mutual Authentication: Both client and server verify each other’s identity
- Encryption: All network traffic encrypted with TLS 1.3
- Identity-based Authorization: Policies based on SPIFFE IDs
- Automatic Rotation: Certificates rotated without application restart
- Zero Trust: Every connection authenticated, even within cluster
Troubleshooting
Pod Stuck at 1/2 Ready
- Check if pod has label:
spiffe.io/spire-managed-identity: "true" - Verify ClusterSPIFFEID matches the pod
- Check istio-proxy logs:
kubectl logs pod-name -c istio-proxy
“workload is not authorized” Error
- ClusterSPIFFEID selectors don’t match the pod
- SPIRE entry not created for the workload
- Check:
kubectl get clusterspiffeid -o yaml
No X-Forwarded-Client-Cert Header
- mTLS not enabled or working
- Check both pods have sidecars injected
- Verify both pods have SPIRE certificates
