Demo tutorial showing how to configure Istio mTLS with PeerAuthentication, illustrating global, namespace, and workload policy precedence and permissive migration using a helloworld app
1 — Confirm sidecar injection on the default namespace
Verify Istio sidecar injection label on default:
kubectl get ns --show-labels
Expected (example) output:
NAME STATUS AGE LABELSdefault Active 3m4s istio-injection=enabled,kubernetes.io/metadata.name=defaultistio-system Active 91s kubernetes.io/metadata.name=istio-systemkube-node-lease Active 3m4s kubernetes.io/metadata.name=kube-node-leasekube-public Active 3m4s kubernetes.io/metadata.name=kube-publickube-system Active 3m4s kubernetes.io/metadata.name=kube-system
3 — Create a separate namespace and run a test pod
Create a new namespace test and run a pod inside it. Important: include -n test when creating the pod.
kubectl create ns test# Wrong (runs in default):# Correct: run the pod in the test namespacekubectl run test --image=nginx -n testkubectl get pods -n test
Example pod output:
NAME READY STATUS RESTARTS AGEtest 1/1 Running 0 4s
Remember: enabling or disabling sidecar injection is a namespace-level label. Pods must be (re)created after changing the label to pick up injection behavior. If you label a namespace for injection, delete and recreate pods to get an Envoy sidecar.
Create a new non-injected namespace app and run a pod there to demonstrate reachability:
kubectl create ns appkubectl get ns --show-labelskubectl run test --image=nginx -n appkubectl get pods -n app
Example get ns output:
NAME STATUS AGE LABELSapp Active 10s kubernetes.io/metadata.name=appdefault Active 9m49s istio-injection=enabled,kubernetes.io/metadata.name=defaultistio-system Active 8m16s kubernetes.io/metadata.name=istio-systemtest Active 6m22s istio-injection=enabled,kubernetes.io/metadata.name=test
From the app pod (non-injected), curl helloworld in default:
kubectl exec -ti -n app test -- curl --head helloworld.default.svc:5000/hello
Expected success (PERMISSIVE accepts plaintext and mTLS):
9 — Limit permissive mode to only the helloworld workload
Instead of allowing all workloads in default to accept plaintext, add a selector to the namespace PeerAuthentication so only workloads with app: helloworld are PERMISSIVE.Update peer_auth_default.yaml:
Quick reference — PeerAuthentication modes and precedence
Term / Resource
Purpose
GLOBAL (namespace: istio-system)
Applies to entire mesh unless overridden. Use for cluster-wide defaults.
NAMESPACE (namespace-level PeerAuthentication)
Overrides global settings for all workloads in the namespace.
WORKLOAD (PeerAuthentication with selector)
Overrides namespace/global for matching workloads only. Highest precedence.
PeerAuthentication modes:
Mode
Effect
STRICT
Enforces mTLS — only TLS-authenticated connections allowed.
PERMISSIVE
Accepts both mTLS and plaintext connections. Useful during migration.
DISABLE or unset
mTLS is not used for the scope (see Istio docs).
Summary bullets
PeerAuthentication resources apply at different scopes: global (istio-system) → namespace → workload (via selector).
Namespace and workload PeerAuthentication override the global policy for their scope.
Use PERMISSIVE to support both plaintext and mTLS while migrating to full mTLS.
Use istioctl analyze to detect injection and configuration issues.
Exam tip: Be able to read the PeerAuthentication API (modes: STRICT, PERMISSIVE, DISABLE/UNSET) and explain scope precedence (global → namespace → workload selector). This concept is commonly tested.