> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Authentication

> Demo tutorial showing how to configure Istio mTLS with PeerAuthentication, illustrating global, namespace, and workload policy precedence and permissive migration using a helloworld app

This guide shows how to configure authentication in Istio using the sample `helloworld` app. You'll:

* Deploy the sample app with Istio sidecar injection.
* Enforce cluster-wide mTLS with a global PeerAuthentication.
* Demonstrate how namespace- and workload-level PeerAuthentication override the global policy.
* Show how to limit permissive mode to specific workloads.

Prerequisites and links

* Istio installed and running.
* kubectl configured for your cluster.
* istioctl available for analysis: `istioctl analyze`
* Reference: [PeerAuthentication API](https://istio.io/latest/docs/reference/config/security/peer_authentication/) and [Istio mTLS task guide](https://istio.io/latest/docs/tasks/security/authentication/peer_auth/).

## 1 — Confirm sidecar injection on the default namespace

Verify Istio sidecar injection label on `default`:

```bash theme={null}
kubectl get ns --show-labels
```

Expected (example) output:

```text theme={null}
NAME                 STATUS   AGE     LABELS
default              Active   3m4s    istio-injection=enabled,kubernetes.io/metadata.name=default
istio-system         Active   91s     kubernetes.io/metadata.name=istio-system
kube-node-lease      Active   3m4s    kubernetes.io/metadata.name=kube-node-lease
kube-public          Active   3m4s    kubernetes.io/metadata.name=kube-public
kube-system          Active   3m4s    kubernetes.io/metadata.name=kube-system
```

## 2 — Deploy the helloworld sample

Apply the helloworld sample in the `default` namespace:

```bash theme={null}
kubectl apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/helloworld/helloworld.yaml
```

Expected creation output:

```text theme={null}
service/helloworld created
deployment.apps/helloworld-v1 created
deployment.apps/helloworld-v2 created
```

Verify pods:

```bash theme={null}
kubectl get pods
```

Example output:

```text theme={null}
NAME                             READY   STATUS    RESTARTS   AGE
helloworld-v1-7459d7b54b-f7cxb   2/2     Running   0          24s
helloworld-v2-654d97458-r84kp    2/2     Running   0          24s
```

## 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.

```bash theme={null}
kubectl create ns test
# Wrong (runs in default):
# Correct: run the pod in the test namespace
kubectl run test --image=nginx -n test
kubectl get pods -n test
```

Example pod output:

```text theme={null}
NAME   READY   STATUS    RESTARTS   AGE
test   1/1     Running   0          4s
```

<Callout icon="warning" color="#FF6B6B">
  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.
</Callout>

## 4 — Verify connectivity from the non-injected pod

Check the `helloworld` service and curl it from the `test` pod:

```bash theme={null}
kubectl get svc
```

Example service:

```text theme={null}
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
helloworld   ClusterIP   10.108.92.244   <none>        5000/TCP    62s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP     4m15s
```

From the `test` pod (non-injected), call helloworld:

```bash theme={null}
kubectl exec -ti -n test test -- curl helloworld.default.svc:5000/hello
```

Expected response (plaintext allowed by default):

```text theme={null}
Hello version: v2, instance: helloworld-v2-654d97458-r84kp
```

Explanation: By default Istio does not enforce mTLS, so plaintext traffic from a non-injected pod works.

## 5 — Enforce cluster-wide mTLS with a global PeerAuthentication

Create `peer_auth_global.yaml`:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT
```

Apply it and verify:

```bash theme={null}
kubectl apply -f peer_auth_global.yaml
kubectl get peerauthentications.security.istio.io -A
```

Expected:

```text theme={null}
peerauthentication.security.istio.io/default created

NAMESPACE      NAME      MODE    AGE
istio-system   default   STRICT  10s
```

## 6 — Observe failure from non-injected pods after STRICT mTLS

With the global STRICT policy in place, a non-injected pod (plaintext) cannot talk to services that require mTLS:

```bash theme={null}
kubectl exec -ti -n test test -- curl --head helloworld.default.svc:5000/hello
```

Example failure:

```text theme={null}
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56
```

Reason: The destination expects mTLS; the source pod lacks Envoy and sends plaintext.

## 7 — Enable injection and re-create the test pod to succeed with mTLS

Enable automatic sidecar injection for the `test` namespace and re-create the pod:

```bash theme={null}
istioctl analyze -n test

kubectl label namespace test istio-injection=enabled
kubectl delete pod test -n test
kubectl run test --image=nginx -n test
```

Test again:

```bash theme={null}
kubectl exec -ti -n test test -- curl --head helloworld.default.svc:5000/hello
```

Expected success headers:

```text theme={null}
HTTP/1.1 200 OK
server: envoy
date: Tue, 15 Apr 2025 18:12:06 GMT
content-type: text/html; charset=utf-8
content-length: 60
x-envoy-upstream-service-time: 122
```

Now traffic is routed through Envoy sidecars and mTLS succeeds.

## 8 — Override global STRICT with a namespace-level PERMISSIVE policy

Create `peer_auth_default.yaml` to set PERMISSIVE for the `default` namespace:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: default
spec:
  mtls:
    mode: PERMISSIVE
```

Apply and confirm:

```bash theme={null}
kubectl apply -f peer_auth_default.yaml
kubectl get peerauthentications.security.istio.io -A
```

Example output:

```text theme={null}
peerauthentication.security.istio.io/default created

NAMESPACE       NAME      MODE        AGE
default         default   PERMISSIVE  6s
istio-system    default   STRICT      3m7s
```

Create a new non-injected namespace `app` and run a pod there to demonstrate reachability:

```bash theme={null}
kubectl create ns app
kubectl get ns --show-labels
kubectl run test --image=nginx -n app
kubectl get pods -n app
```

Example `get ns` output:

```text theme={null}
NAME             STATUS   AGE     LABELS
app              Active   10s     kubernetes.io/metadata.name=app
default          Active   9m49s   istio-injection=enabled,kubernetes.io/metadata.name=default
istio-system     Active   8m16s   kubernetes.io/metadata.name=istio-system
test             Active   6m22s   istio-injection=enabled,kubernetes.io/metadata.name=test
```

From the `app` pod (non-injected), curl helloworld in `default`:

```bash theme={null}
kubectl exec -ti -n app test -- curl --head helloworld.default.svc:5000/hello
```

Expected success (PERMISSIVE accepts plaintext and mTLS):

```text theme={null}
HTTP/1.1 200 OK
server: istio-envoy
date: Tue, 15 Apr 2025 18:14:44 GMT
content-type: text/html; charset=utf-8
content-length: 60
x-envoy-upstream-service-time: 136
x-envoy-decorator-operation: helloworld.default.svc.cluster.local:5000/*
```

Explanation: A namespace-level PeerAuthentication overrides the global STRICT policy for that namespace, allowing plaintext traffic when `PERMISSIVE`.

## 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`:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: default
spec:
  selector:
    matchLabels:
      app: helloworld
  mtls:
    mode: PERMISSIVE
```

Apply it:

```bash theme={null}
kubectl apply -f peer_auth_default.yaml
```

Example:

```text theme={null}
peerauthentication.security.istio.io/default configured
```

Behavior now:

* Global `STRICT` still applies cluster-wide except where namespace/workload-specific PeerAuthentication overrides it.
* Only workloads in `default` labeled `app=helloworld` will accept plaintext (PERMISSIVE).
* Other workloads in `default` (e.g., Bookinfo `productpage`) still require mTLS from non-injected clients.

Demonstration:

From `test` (injected) to `productpage.default.svc:9080`:

```bash theme={null}
kubectl exec -ti -n test test -- curl --head productpage.default.svc:9080
```

Expected success since `test` is injected:

```text theme={null}
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 1683
server: envoy
date: Tue, 15 Apr 2025 18:18:37 GMT
x-envoy-upstream-service-time: 23
```

From `app` (not injected) to `productpage.default.svc:9080`:

```bash theme={null}
kubectl exec -ti -n app test -- curl --head productpage.default.svc:9080
```

Expected failure:

```text theme={null}
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56
```

But `app` can still reach `helloworld` because the selector matches that workload:

```bash theme={null}
kubectl exec -ti -n app test -- curl --head helloworld.default.svc:5000/hello
```

Expected success:

```text theme={null}
HTTP/1.1 200 OK
server: istio-envoy
date: Tue, 15 Apr 2025 18:20:09 GMT
content-type: text/html; charset=utf-8
content-length: 60
x-envoy-upstream-service-time: 103
x-envoy-decorator-operation: helloworld.default.svc.cluster.local:5000/*
```

## 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.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/zDpSzOByf0QVxNkX/images/Prep-Course-Istio-Certified-Associate-ICA-Certification/Securing-Workloads/Demo-Authentication/istio-peer-authentication-mtls-table.jpg?fit=max&auto=format&n=zDpSzOByf0QVxNkX&q=85&s=00ed05c2e7905afff8af5b566798f6ae" alt="A screenshot of the Istio documentation webpage showing the PeerAuthentication / MutualTLS section, including a table describing fields like &#x22;mtls&#x22; and &#x22;portLevelMtls.&#x22; The page layout shows a sidebar, header navigation, and a &#x22;MutualTLS&#x22; heading with a field/description table." width="1920" height="1080" data-path="images/Prep-Course-Istio-Certified-Associate-ICA-Certification/Securing-Workloads/Demo-Authentication/istio-peer-authentication-mtls-table.jpg" />
</Frame>

<Callout icon="lightbulb" color="#1CB2FE">
  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.
</Callout>

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/17ba1cac-61f4-48b6-b354-c2c735f5791d/lesson/7afc3a1c-dc16-4d2f-9c30-2964d7839bfe" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/17ba1cac-61f4-48b6-b354-c2c735f5791d/lesson/074126a9-c3c1-4922-a4d1-6c40c70e4cd8" />
</CardGroup>
