9.3 Service Mesh, mTLS & Zero-Trust Service Identity
Key Takeaways
- A service mesh splits into a data plane of proxies that carry every request and a control plane that configures those proxies from the Kubernetes API.
- Envoy is the CNCF graduated proxy used as the data plane by Istio and several other meshes; Linkerd is a CNCF graduated mesh with its own lightweight Rust proxy.
- Automatic mutual TLS gives every workload a cryptographic identity and encrypts service-to-service traffic without changing application code.
- A mesh enforces layer 7 authorization such as allowing GET on one path while denying POST on another, which standard NetworkPolicy cannot express.
- Sidecar meshes add a proxy container to every Pod; ambient or node-level modes move that proxy out of the Pod to reduce per-Pod resource cost.
9.3 Service Mesh, mTLS & Zero-Trust Service Identity
Quick Answer: A service mesh is a dedicated infrastructure layer for service-to-service communication. Its data plane is a fleet of proxies — typically Envoy, a CNCF graduated project — that intercept every request in and out of a workload. Its control plane watches the Kubernetes API and programs those proxies. The result is automatic mutual TLS, L7 traffic control, and uniform telemetry, all without touching application code. "Service mesh" is named explicitly in the CNCF cloud native definition, so KCNA expects you to know what it is and what problem it solves.
1. The Problem a Mesh Solves
Once a monolith becomes forty microservices, every service needs the same non-business capabilities: retries with backoff, timeouts, circuit breaking, mTLS, request tracing, and per-route authorisation. Implementing those in each service means:
- writing them once per language (Go, Java, Python, Node),
- upgrading them in lockstep across forty repositories,
- and accepting that the behaviour will drift anyway.
A mesh moves that logic out of the application and into the platform. The application makes a plain HTTP call to payments; the proxy beside it handles TLS, retries, timeouts, load balancing, and telemetry.
2. Data Plane and Control Plane
┌─────────────────── CONTROL PLANE ───────────────────┐
│ watches the Kubernetes API (Services, Endpoints, │
│ routing CRDs, policies) and issues workload │
│ certificates; pushes config to every proxy │
└──────────────┬──────────────────────┬────────────────┘
│ config + certs │
┌─────────▼─────────┐ ┌─────────▼─────────┐
│ Pod: orders │ │ Pod: payments │
│ ┌──────┐ ┌──────┐ │ │ ┌──────┐ ┌──────┐ │
│ │ app │─│proxy │─┼──┼─│proxy │─│ app │ │
│ └──────┘ └──────┘ │ │ └──────┘ └──────┘ │
└───────────────────┘ └───────────────────┘
└────── mTLS on the wire ──────┘
| Plane | Job | Examples |
|---|---|---|
| Data plane | Carries every byte of application traffic; applies routing, retries, timeouts, mTLS, and emits metrics/traces | Envoy (CNCF graduated), linkerd2-proxy (Rust) |
| Control plane | Translates intent into proxy configuration; acts as a certificate authority | Istio istiod, Linkerd control plane, Consul, Kuma |
The application container is unaware any of it exists. Injection is done by a mutating admission webhook — the mechanism from section 5.3 — which appends the proxy container to the Pod spec at creation time.
3. Mutual TLS and Workload Identity
The headline security feature. In a plain cluster, Pod-to-Pod traffic is unencrypted and a receiving service has no cryptographic proof of who is calling — it sees only an IP address that may belong to a different workload a minute later.
A mesh gives every workload a short-lived X.509 certificate whose identity is derived from its ServiceAccount, and both proxies present certificates on every connection:
- Encryption in transit for all service-to-service traffic, automatically.
- Authentication of both ends — the caller is cryptographically identified, not merely IP-inferred.
- Automatic rotation, typically every 24 hours or less, with no application involvement.
- Identity-based authorization — policies reference
serviceAccount: orders, not a CIDR.
This is what "zero trust" means concretely: the network is assumed hostile, and every request is authenticated and authorised regardless of where it originated. SPIFFE/SPIRE is the CNCF project that standardises this workload-identity format, and meshes commonly emit SPIFFE-compatible identities.
Enforcement modes usually run PERMISSIVE first — accepting both plaintext and mTLS during migration — before switching to STRICT.
4. Layer 7 Traffic Control
Because the proxy parses HTTP and gRPC, it can act on request semantics that layer 3/4 controls cannot see:
| Capability | Example |
|---|---|
| Weighted traffic splitting | 95% to v1, 5% to v2 — canary independent of replica counts |
| Header/identity routing | Send requests with x-beta: true to v2 |
| Retries and timeouts | Retry idempotent calls twice with a 2-second per-attempt timeout |
| Circuit breaking / outlier detection | Eject an endpoint returning consecutive 5xx responses |
| Fault injection | Deliberately inject 500s or 3-second delays to test resilience |
| L7 authorization | Allow GET /api/orders from serviceAccount: web, deny POST /api/admin from everything |
That last row is the crisp distinction from section 7.3: NetworkPolicy operates at L3/L4 and can only allow or deny a port. Method- and path-level authorisation requires a mesh (or a CNI-specific L7 policy such as CiliumNetworkPolicy).
5. Observability for Free
Because every request crosses a proxy, the mesh emits golden signals — request rate, error rate, latency distribution, and saturation — for every service pair, without instrumenting anything. It also propagates trace headers automatically, so a distributed trace shows the full call graph as long as applications forward the incoming headers.
The honest caveat: the mesh can propagate context but cannot create spans inside your application. Meaningful traces still require application-level instrumentation, typically with OpenTelemetry.
6. Sidecar vs Ambient
| Model | How it works | Trade-off |
|---|---|---|
| Sidecar | One proxy container in every Pod | Strongest isolation and full L7 features per workload; costs CPU and memory per Pod and adds a container to every restart and upgrade |
| Ambient / node-level | A shared per-node proxy handles L4 and mTLS; an optional per-namespace L7 proxy is added only where needed | Much lower overhead and no Pod restarts to enrol; weaker per-Pod isolation |
| eBPF / sidecarless | Kernel-level dataplane (Cilium) performs L4 and identity in the kernel | Lowest overhead; L7 features vary by implementation |
Sidecar meshes also historically suffered a lifecycle problem — the proxy could start after the app or die before it finished draining. The restartable sidecar container from section 3.5 (an initContainers entry with restartPolicy: Always) is the Kubernetes-native fix.
7. The Main Implementations
| Project | CNCF status | Character |
|---|---|---|
| Istio | Graduated | The most feature-complete; Envoy data plane; supports sidecar and ambient modes |
| Linkerd | Graduated | Deliberately minimal; purpose-built Rust micro-proxy; prized for low overhead and simple operation |
| Cilium Service Mesh | Graduated (Cilium) | eBPF-based, sidecarless; merges CNI and mesh |
| Consul | HashiCorp | Multi-platform, extends beyond Kubernetes to VMs |
| Kuma | Sandbox | Envoy-based, multi-zone and multi-cluster focus |
Do you need one? A mesh is real operational weight: another control plane, another certificate authority, another upgrade cycle, and a new class of failure. For a handful of services, application libraries and NetworkPolicy are usually enough. The mesh earns its cost at scale, under a compliance requirement for encryption in transit, or when progressive delivery needs precise traffic control.
How does a service mesh normally add its proxy to an application Pod?
Which requirement can a service mesh satisfy that a standard Kubernetes NetworkPolicy cannot?
What does automatic mutual TLS in a service mesh provide beyond encrypting traffic?