Skip to content

mTLS and Service Mesh Security

Why mTLS?

In a Kubernetes cluster, pod-to-pod communication is unencrypted by default. Any process on the cluster network can eavesdrop on traffic between services. Mutual TLS (mTLS) encrypts all service-to-service communication and provides mutual authentication -- both the client and server verify each other's identity.

CKS Exam Relevance

The CKS exam tests conceptual understanding of mTLS and service meshes. You should know:

  • What mTLS is and why it matters
  • How Istio implements mTLS
  • PeerAuthentication and AuthorizationPolicy resources
  • The difference between permissive and strict mTLS modes
  • Certificate rotation concepts

What Is mTLS?

Mutual TLS extends standard TLS by requiring both parties to authenticate with certificates, not just the server.

Standard TLS vs mTLS

AspectStandard TLSMutual TLS (mTLS)
Server authenticates to clientYesYes
Client authenticates to serverNo (usually)Yes
EncryptionYesYes
Identity verificationServer onlyBoth parties
Use caseWeb browsers to serversService-to-service

mTLS Handshake


Service Mesh Architecture

A service mesh implements mTLS transparently by injecting sidecar proxies alongside each application container. The application does not need to be modified.

How the Sidecar Proxy Works

  1. Outbound traffic from the app is intercepted by the sidecar proxy (via iptables rules)
  2. The proxy encrypts the traffic using mTLS with its certificate
  3. Inbound traffic arrives at the destination pod's sidecar proxy
  4. The proxy decrypts and verifies the client certificate
  5. Decrypted traffic is forwarded to the application container on localhost
  6. The application never sees TLS -- it communicates in plain HTTP on localhost

Istio Service Mesh Security

Installing Istio (Conceptual)

bash
# Download Istio
curl -L https://istio.io/downloadIstio | sh -

# Install with demo profile
istioctl install --set profile=demo

# Enable sidecar injection for a namespace
kubectl label namespace default istio-injection=enabled

# Verify
kubectl get pods -n istio-system

Automatic Sidecar Injection

When a namespace is labeled with istio-injection=enabled, Istio's mutating webhook automatically injects the Envoy sidecar proxy into every new pod:

bash
# Enable automatic injection
kubectl label namespace production istio-injection=enabled

# Verify injection
kubectl get pods -n production
# NAME                      READY   STATUS
# frontend-abc123           2/2     Running    # 2/2 means sidecar is present

PeerAuthentication

PeerAuthentication controls the mTLS mode for workloads in the mesh.

Modes

ModeBehavior
STRICTOnly mTLS traffic is accepted. Non-mTLS connections are rejected.
PERMISSIVEAccepts both mTLS and plain text traffic (default).
DISABLEmTLS is disabled entirely.
UNSETInherits from parent scope.

Mesh-Wide Strict mTLS

yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system          # Mesh-wide when in istio-system
spec:
  mtls:
    mode: STRICT                   # All services require mTLS

STRICT Mode

Enabling STRICT mTLS mesh-wide means any service without a sidecar proxy cannot communicate with mesh services. Ensure all workloads have sidecars before enabling STRICT mode.

Namespace-Level mTLS

yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production            # Only affects this namespace
spec:
  mtls:
    mode: STRICT

Workload-Specific mTLS

yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: frontend-mtls
  namespace: production
spec:
  selector:
    matchLabels:
      app: frontend                # Only this workload
  mtls:
    mode: STRICT

Port-Level mTLS

yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: db-mtls
  namespace: production
spec:
  selector:
    matchLabels:
      app: database
  mtls:
    mode: STRICT
  portLevelMtls:
    3306:
      mode: PERMISSIVE             # Allow non-mesh clients on MySQL port

AuthorizationPolicy

While PeerAuthentication controls whether mTLS is used, AuthorizationPolicy controls who can access what. It implements Layer 7 access control.

Deny All Traffic (Default Deny)

yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  {}                               # Empty spec = deny all

Allow Specific Traffic

yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  selector:
    matchLabels:
      app: api-server              # Target: api-server pods
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - "cluster.local/ns/production/sa/frontend"  # Source: frontend SA
    to:
    - operation:
        methods: ["GET", "POST"]   # Only GET and POST
        paths: ["/api/*"]          # Only /api/* paths

Deny Specific Traffic

yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-external
  namespace: production
spec:
  selector:
    matchLabels:
      app: internal-service
  action: DENY
  rules:
  - from:
    - source:
        notNamespaces: ["production"]  # Deny from other namespaces

Common Authorization Patterns

yaml
# Allow only same-namespace communication
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: same-namespace-only
  namespace: production
spec:
  action: ALLOW
  rules:
  - from:
    - source:
        namespaces: ["production"]
yaml
# Allow health checks from any source
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-health-checks
  namespace: production
spec:
  selector:
    matchLabels:
      app: api-server
  action: ALLOW
  rules:
  - to:
    - operation:
        methods: ["GET"]
        paths: ["/health", "/ready"]

Certificate Management

Istio Certificate Architecture

Istio's control plane component (formerly Citadel, now part of istiod) acts as the Certificate Authority:

Certificate Identity

Istio uses SPIFFE (Secure Production Identity Framework for Everyone) identities:

spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>

Example:

spiffe://cluster.local/ns/production/sa/frontend

This identity is embedded in the X.509 certificate's SAN (Subject Alternative Name) field.

Certificate Rotation

Istio automatically rotates workload certificates:

  • Default lifetime: 24 hours
  • Rotation: Automatic, no downtime
  • Grace period: Certificates are renewed before expiration
  • Root CA: Can be configured with custom CA certificates
yaml
# Custom certificate configuration in Istio
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    defaultConfig:
      # Certificate rotation settings
      proxyMetadata:
        SECRET_TTL: "12h"          # Certificate lifetime

Key Concept

Automatic certificate rotation is a major advantage of service meshes. Without a mesh, certificate management is manual and error-prone. With Istio, certificates are rotated automatically every 24 hours with zero downtime.


Zero Trust Networking

mTLS and service meshes enable Zero Trust networking principles:

PrincipleImplementation
Never trust, always verifymTLS authenticates every connection
Least privilege accessAuthorizationPolicy restricts access
Assume breachEncrypt all internal traffic
Verify explicitlyCertificate-based identity, not IP-based
Micro-segmentationPer-service access control policies

Before vs After Service Mesh

AspectWithout MeshWith Istio mTLS
Service identityIP-based (unreliable)Certificate-based (cryptographic)
EncryptionNone (or manual TLS)Automatic mTLS
Access controlNetworkPolicy (L3/L4)AuthorizationPolicy (L7)
ObservabilityLimitedFull traffic visibility
Certificate managementManualAutomatic rotation

Verifying mTLS

bash
# Check if mTLS is enabled between services
istioctl authn tls-check <pod-name> <service-name>

# Check PeerAuthentication policies
kubectl get peerauthentication --all-namespaces

# Check AuthorizationPolicy
kubectl get authorizationpolicy --all-namespaces

# Verify mTLS in proxy configuration
istioctl proxy-config listeners <pod-name> -o json

# Check if a specific connection uses mTLS
kubectl exec <pod-with-sidecar> -c istio-proxy -- \
  openssl s_client -connect <service>:80 -showcerts

# View Istio proxy logs for TLS errors
kubectl logs <pod-name> -c istio-proxy | grep -i tls

Service Mesh Security Summary for CKS

Exam Focus Areas

For the CKS exam, remember these key points:

  1. mTLS encrypts service-to-service communication and provides mutual authentication
  2. PeerAuthentication controls mTLS mode (STRICT, PERMISSIVE, DISABLE)
  3. AuthorizationPolicy controls who can access which services (Layer 7)
  4. STRICT mode rejects all non-mTLS connections
  5. PERMISSIVE mode accepts both mTLS and plain text (migration mode)
  6. SPIFFE identities are based on namespace and service account
  7. Certificate rotation is automatic in Istio (default: 24h)
  8. Service meshes implement zero trust networking

Quick Reference

bash
# PeerAuthentication - control mTLS mode
kubectl get peerauthentication -A
kubectl describe peerauthentication <name> -n <namespace>

# AuthorizationPolicy - control access
kubectl get authorizationpolicy -A
kubectl describe authorizationpolicy <name> -n <namespace>

# Istio injection
kubectl label namespace <ns> istio-injection=enabled
kubectl get namespace -L istio-injection

# Verify mTLS status
istioctl authn tls-check <pod>.<namespace> <service>.<namespace>.svc.cluster.local

# Check Istio proxy status
istioctl proxy-status

# Analyze Istio configuration
istioctl analyze -n <namespace>

Released under the MIT License.