Istio: mTLS and RBAC

Continuation of Kubernetes Istio, this time we’ll focus on Security.

Table of Contents

Security

mTLS (Mutual TLS)

This will ensure that service-to-service traffic is encrypted and authenticated.

Istio allows you to configure three main modes per namespace, workload, or globally:

Mode Behavior When to Use
STRICT Only allows mTLS-encrypted traffic. Plain HTTP connections are rejected. Best for production when you want full security between services.
PERMISSIVE Accepts both mTLS-encrypted and plain HTTP traffic. Useful during gradual migration to mTLS. Old workloads can still communicate without encryption.
DISABLE Does not use mTLS at all. For testing, legacy workloads, or non-critical traffic.

You might get confused with the HTTPS traffic, it just mean that all traffic are converted by istio envoy to mTLS. So as long as the request is going through first the sidecar then it is valid.

Let’s demonstrate that here. Deploy curl pod in universe namespace and curl backend-v2 service.

1kubectl create ns universe
2kubectl run curl-test -n universe -it --rm --image=curlimages/curl -- sh
3
4curl http://backend-v2.demo.svc.cluster.local:3000/api/version
5{"version":"2.0.0"}

Now create PeerAuthentication manifest for mtls.

mtls.yaml

1apiVersion: security.istio.io/v1
2kind: PeerAuthentication
3metadata:
4  name: demo-app-mtls
5spec:
6  mtls:
7    mode: STRICT
1kubectl create -f security/mtls.yaml -n demo

Verify mtls mode using istioctl, test on one of the running pod.

 1istioctl x describe pod backend-v1-8456d4864-h4fnm -n demo
 2Pod: backend-v1-8456d4864-h4fnm
 3   Pod Revision: default
 4   Pod Ports: 3000 (backend)
 5--------------------
 6Service: backend
 7   Port: http 3000/HTTP targets pod port 3000
 8--------------------
 9Service: backend-v1
10   Port: http 3000/HTTP targets pod port 3000
11--------------------
12Effective PeerAuthentication:
13   Workload mTLS mode: STRICT
14Applied PeerAuthentication:
15   demo-app-mtls.demo

Using the same curl pod, the request will fail.

1kubectl run curl-test -n universe -it --rm --image=curlimages/curl -- sh
2
3curl http://backend-v2.demo.svc.cluster.local:3000/api/version
4curl: (56) Recv failure: Connection reset by peer

Next test is injecting sidecar to the namespace.

1kubectl label namespace universe istio-injection=enabled

Deploy curl-test.

1kubectl run curl-test -n universe -it --rm --image=curlimages/curl -- sh

Verify, a istio sidecar pod is injected.

1kubectl get pods -n universe
2NAME        READY   STATUS    RESTARTS   AGE
3curl-test   2/2     Running   0          10s

Now access backend-v2 service with curl with a sidecar.

1curl http://backend-v2.demo.svc.cluster.local:3000/api/version
2{"version":"2.0.0"}

Curl can access backend-v2 service because request or traffic go through first the istio sidecar.

AuthorizationPolicy (RBAC)

To make traffic more secure we can add AutorozationPolicy, this will limit even namespaces with istio sidecar injected.

Take note, this is dependent on service account and matchLabels. Review the content of /kube manifest.

1kubectl get sa -n demo
2NAME                     AGE
3backend-sa               7h39m
4default                  13h
5demo-app-gateway-istio   62m
6frontend-sa              7h39m
7monitor-sa               7h39m
8redis-sa                 7h39m

For this example let’s limit access to backend, only monitor can only access backend

First lets create read access only.

rbac-read-monitor-to-backend.yaml

 1apiVersion: security.istio.io/v1
 2kind: AuthorizationPolicy
 3metadata:
 4  name: read-monitor-to-backend 
 5spec:
 6  selector:
 7    matchLabels:
 8      app: backend
 9  action: ALLOW
10  rules:
11  - from:
12    - source:
13        principals: 
14        - "cluster.local/ns/demo/sa/monitor-sa"
15        - "cluster.local/ns/demo/sa/demo-app-gateway-istio"
16    to:
17    - operation:
18        methods: ["GET", "POST"]
19        paths:
20        - "/api/login"
21        - "/api/version"
22        - "/api/users"
23        - "/api/users/*" # delete user
1kubectl create -f security/mtls/rbac/rbac-read-monitor-to-backend.yaml -n demo

Verify on http://192.168.254.221/app.

To make frontend and monitor have read/write access to backend add this new rule.

rbac-readwrite-monitor-to-backend.yaml

 1---
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: read-write-monitor-to-backend 
 6spec:
 7  selector:
 8    matchLabels:
 9      app: backend
10  action: ALLOW
11  rules:
12  - from:
13    - source:
14        principals: 
15        - "cluster.local/ns/demo/sa/monitor-sa"
16        - "cluster.local/ns/demo/sa/demo-app-gateway-istio"
17    to:
18    - operation:
19        methods: ["GET", "POST", "DELETE", "PUT"]
20        paths:
21        - "/api/login"
22        - "/api/version"
23        - "/api/users"
24        - "/api/users/*" # delete user
1kubectl create -f security/mtls/rbac/rbac-readwrite-monitor-to-backend.yaml -n demo

rbac-read-frontend-to-backend.yaml

 1---
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: read-write-frontend-to-backend 
 6spec:
 7  selector:
 8    matchLabels:
 9      app: backend
10  action: ALLOW
11  rules:
12  - from:
13    - source:
14        principals: 
15        - "cluster.local/ns/demo/sa/frontend-sa"
16        - "cluster.local/ns/demo/sa/demo-app-gateway-istio"
17    to:
18    - operation:
19        methods: ["GET", "POST", "DELETE", "PUT", "OPTIONS"]
20        paths:
21        - "/api/register"
22        - "/api/login"
23        - "/api/profile"
24        - "/api/version"
25        - "/api/users"
1kubectl create -f security/mtls/rbac/rbac-readwrite-frontend-to-backend.yaml -n demo