Safeguard
Kubernetes Security

Implementing mTLS in Kubernetes clusters: a hands-on guide

Kubernetes ships zero built-in encryption for pod-to-pod traffic — here's how cert-manager and service meshes fix that, and the five misconfigurations that quietly undo it.

Safeguard Research Team
Research
6 min read

Kubernetes has no built-in mechanism to encrypt traffic between pods — by default, a kubectl exec into any container on the cluster network can watch service-to-service requests go by in plaintext. Closing that gap requires either a service mesh sidecar (Istio, Linkerd) or manually wiring cert-manager into every workload's TLS config, and most teams get partway there and stop. cert-manager, the CNCF project originally built by Jetstack and donated to CNCF in 2020, now automates x509 certificate issuance and rotation through Issuer and ClusterIssuer custom resources, with its latest stable line, v1.20.0, released March 9, 2026, and support windows of roughly eight months per release. Istio, meanwhile, defaults new mesh installs to PERMISSIVE mode — a setting its own documentation says should be treated as a migration stepping stone, not a destination, because it silently accepts plaintext traffic alongside mTLS. This post walks through the actual mechanics of getting mTLS working across a cluster: what cert-manager automates, how Istio and Linkerd differ in their default posture, and the specific configuration mistakes — scoping errors, injection gaps, and permissive mode left on indefinitely — that leave clusters exposed even after someone believes mTLS is "done."

What does Kubernetes actually give you for free, and what do you have to build yourself?

Kubernetes gives you network-layer plumbing — Services, DNS, kube-proxy routing — but nothing that encrypts the bytes moving between pods. TLS termination at an Ingress controller protects traffic entering the cluster, but east-west traffic between internal services travels in the clear unless something adds encryption at the pod level. There are two paths: application-level TLS, where each service loads its own certificate and negotiates TLS itself, or a service mesh, where a sidecar proxy (Envoy for Istio, a Rust-based proxy for Linkerd) intercepts traffic transparently and handles the handshake without application code changes. cert-manager is the piece that makes either path viable at scale — without it, teams either skip certificate rotation entirely or build a bespoke cron job to reissue certs before expiry, which is exactly the kind of homegrown PKI tooling that tends to fail quietly.

How does cert-manager automate certificate issuance and rotation?

cert-manager watches Certificate custom resources and reconciles them against an Issuer or ClusterIssuer, requesting and renewing x509 certificates without a human running openssl by hand. It supports three main backends: ACME providers like Let's Encrypt for publicly trusted certs, HashiCorp Vault's PKI secrets engine for internal CAs, and a self-signed CA issuer for fully internal cluster traffic where no external trust chain is needed. When a Certificate resource is created, cert-manager requests a key pair, submits a CertificateRequest, and stores the signed certificate and key in a Kubernetes Secret that workloads mount as a volume — then it automatically renews before expiry based on the renewBefore field, so short-lived certificates (a security best practice, since a compromised 90-day cert is far less damaging than a 10-year one) don't require manual tracking. cert-manager follows a roughly four-month release cadence per its published release policy, which matters operationally: teams pinned to an out-of-support minor version stop receiving CVE patches for the controller itself.

Should you use a service mesh instead of wiring TLS into every app?

For clusters with more than a handful of services, a service mesh usually wins because it moves mTLS out of application code and into infrastructure that a platform team controls centrally. Istio injects an Envoy sidecar into each pod and manages certificate issuance and rotation through its own internal CA (or an external one via cert-manager integration), then enforces encryption policy through PeerAuthentication custom resources at the mesh, namespace, or workload level. Linkerd takes a more opinionated stance: meshed pods get mTLS by default, an opt-out design rather than Istio's opt-in-then-tighten model, which is one of the most frequently cited philosophical differences between the two projects. The tradeoff is operational weight — Istio's control plane and CRD surface are larger and more configurable, which is also why its default PERMISSIVE posture exists in the first place: it lets teams onboard workloads gradually without an instant network-wide outage the moment the mesh is installed.

Why is PERMISSIVE mode the single most common mTLS misconfiguration?

PERMISSIVE mode is dangerous not because it's insecure by design, but because teams install a mesh, watch dashboards go green, and never flip the switch to STRICT. In PERMISSIVE mode, a sidecar accepts both plaintext and mTLS connections simultaneously, which is genuinely useful during migration — a pod that hasn't received sidecar injection yet can still reach meshed services. But Istio's own documentation is explicit that this creates a policy-bypass risk: a request arriving as plaintext (say, from a pod outside the mesh, or a compromised node) gets forwarded by the sidecar with no verified source identity, meaning any AuthorizationPolicy that checks the source principal can end up evaluating against an empty or spoofable identity instead of rejecting the connection outright. Istio's guidance is to run PERMISSIVE only as an intermediate state, confirm sidecar injection is complete across every namespace, then move to STRICT — a step that in practice gets skipped because there's no error, alert, or dashboard signal telling a team the mesh is still accepting plaintext six months after go-live.

What other misconfigurations undermine mTLS even after STRICT is enabled?

Beyond PERMISSIVE mode, four patterns recur across cluster mTLS rollouts. First, incomplete sidecar injection: a namespace missing the injection label, or a pod with sidecar.istio.io/inject: "false" set for debugging and never reverted, creates an unmeshed gap where STRICT mode simply breaks connectivity rather than securing it, so teams sometimes revert to PERMISSIVE cluster-wide to unblock one service. Second, Issuer scope mistakes: a namespaced Issuer only signs Certificates in its own namespace, and teams that mean to use a cluster-wide CA but create an Issuer instead of a ClusterIssuer see renewal failures show up only when a second namespace's certs expire. Third, non-mesh applications that read a TLS certificate once at startup and cache it in memory won't pick up cert-manager's rotated cert without a reload mechanism (a sidecar-watcher, SIGHUP handler, or pod restart), leaving stale certs serving traffic past their intended lifetime. Fourth, and most overlooked: mTLS encrypts and authenticates transport, it does not authorize anything on its own — a cluster that ships STRICT mTLS everywhere but has no NetworkPolicy or AuthorizationPolicy resources still lets any two authenticated pods talk to each other freely.

How Safeguard fits in

Safeguard doesn't manage mTLS or mesh policy directly, but it touches the supply chain that makes a hardened setup possible: cert-manager ships as one of the hardened, signed Helm charts in Safeguard's Gold Registry, so teams pulling it as a dependency get a vetted, attested artifact rather than an unverified upstream chart. Separately, Safeguard's own asset-discovery agents send their telemetry back over mTLS, which is the same pattern this post describes — encrypting service-to-service traffic by default rather than layering it on after an incident. Beyond that, getting PeerAuthentication modes, Issuer scoping, and sidecar injection right across a cluster is still infrastructure work a platform team owns; treat this guide as the checklist for that work.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.