Exercise 4: Verify and Enforce mTLS
In this exercise, you will confirm that Istio is encrypting traffic between your meshed workloads with mutual TLS (mTLS), then move the namespace from the default permissive behaviour to strict mTLS so that only encrypted, identity-verified traffic is allowed.
When you added the istio-demo namespace to the mesh, every pod received an Envoy sidecar. By default the AKS Istio add-on runs mTLS in PERMISSIVE mode: sidecars will use mTLS when both ends are in the mesh, but will still accept plaintext. This makes onboarding easy, but it means plaintext traffic is not yet blocked.
Task 1: Confirm Traffic is Using mTLS
Make sure the application has received some traffic, then check the proxy configuration to confirm mTLS is in use between the gateway and the
webworkloads.The Istio sidecars expose their effective configuration on the local admin port. Confirm that the
webservice has sidecars and is therefore eligible for mTLS.kubectl get pods -n istio-demo ` -o "custom-columns=POD:.metadata.name,INIT-CONTAINERS:.spec.initContainers[*].name,CONTAINERS:.spec.containers[*].name"kubectl get pods -n istio-demo \ -o 'custom-columns=POD:.metadata.name,INIT-CONTAINERS:.spec.initContainers[*].name,CONTAINERS:.spec.containers[*].name'Each
webpod should lististio-proxyunder INIT-CONTAINERS (the AKS Istio add-on uses Kubernetes native sidecar containers, so the Envoy proxy is aninitContainerwithrestartPolicy: Alwaysrather than a regular sibling ofweb). The presence of the proxy on both the caller (the ingress gateway) and the callee (web) is what allows mTLS to be negotiated automatically.
Tip
You will also be able to see mTLS visually in the next exercise. In Kiali, enable the Security display badge on the graph and look for the padlock icon on the edges between workloads - that padlock means the connection is mTLS encrypted.
Task 2: Prove Permissive Mode Still Allows Plaintext
Send a plaintext request to the
webservice from a pod that is not in the mesh. Because the namespace is still in permissive mode, this request succeeds.kubectl run mtls-test --image=ghcr.io/microsoft/k8s-on-azure-workshop/curl:latest --restart=Never -i --rm ` -- curl -s -o /dev/null -w "%{http_code}`n" http://web.istio-demo.svc.cluster.localkubectl run mtls-test --image=ghcr.io/microsoft/k8s-on-azure-workshop/curl:latest --restart=Never -i --rm \ -- curl -s -o /dev/null -w "%{http_code}\n" http://web.istio-demo.svc.cluster.localThe
mtls-testpod runs in thedefaultnamespace, which is not part of the mesh, so it has no sidecar and sends plaintext. In permissive mode thewebsidecar accepts it and you get an HTTP200.
Task 3: Enforce Strict mTLS
Apply a
PeerAuthenticationresource to require mTLS for all workloads in theistio-demonamespace.@" apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: istio-demo spec: mtls: mode: STRICT "@ | kubectl apply -f -cat <<EOF | kubectl apply -f - apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: istio-demo spec: mtls: mode: STRICT EOFRepeat the plaintext request from the non-meshed pod. It should now fail, because the
websidecar rejects any connection that is not mTLS.kubectl run mtls-test --image=ghcr.io/microsoft/k8s-on-azure-workshop/curl:latest --restart=Never -i --rm ` -- curl -s -o /dev/null -w "%{http_code}`n" --max-time 5 http://web.istio-demo.svc.cluster.localkubectl run mtls-test --image=ghcr.io/microsoft/k8s-on-azure-workshop/curl:latest --restart=Never -i --rm \ -- curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 http://web.istio-demo.svc.cluster.localThe plaintext request now fails (the connection is reset rather than returning a
200). This confirms strict mTLS is being enforced.Confirm that legitimate traffic still works. Requests through the Istio ingress gateway continue to succeed, because the gateway is in the mesh and uses mTLS to reach
web.Invoke-WebRequest -UseBasicParsing "http://$INGRESS_IP" | Select-Object -ExpandProperty StatusCodecurl -s -o /dev/null -w "%{http_code}\n" "http://$INGRESS_IP"This should still return
200. Traffic from inside the mesh is encrypted and allowed; plaintext traffic from outside the mesh is blocked.
Info
STRICT mode rejects all non-mTLS traffic to the affected workloads. Only enable it once you are confident every legitimate caller is in the mesh - otherwise you can unintentionally block traffic from workloads that have not been onboarded yet. The PERMISSIVE default exists precisely to make that migration safe.