
Introduction
Istio uses mutual TLS (mTLS) to ensure all service-to-service communication within the mesh is encrypted.
This short article covers setting up a sample environment, checking the current mTLS settings, and exploring how to enforce mTLS for both inbound and outbound traffic.
Prerequisites
- You have a local Kubernetes development cluster
- You have
kubectl
installed and working - You have
istioctl
installed and working - You have istio configured
Note: This example code is intended for lightweight tutorial purposes only and is not suitable for production use.
Setup
Before we get started, let’s create a dummy environment containing:
- 2x
Deployment
with Nginx with Istio sidecars - 2x
ConfigMap
containing Nginx config - 2x
Service
This setup allows us to test traffic between two Nginx sidecars.
Create istio-mtls.yaml
:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-1
spec:
type: NodePort
ports:
- name: http-web
port: 80
targetPort: 80
nodePort: 30003
selector:
app: nginx-1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-1
data:
nginx.conf: '
events {
}
http {
server {
listen 80;
location / {
return 200 "Hello world - nginx-svc-1!";
}
}
}
'
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-1
spec:
selector:
matchLabels:
app: nginx-1
strategy:
type: Recreate
template:
metadata:
labels:
app: nginx-1
spec:
containers:
- image: nginx:latest
name: nginx-1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/
volumes:
- name: config-vol
configMap:
name: nginx-config-1
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-2
spec:
type: NodePort
ports:
- name: http-web
port: 80
targetPort: 80
nodePort: 30002
selector:
app: nginx-2
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-2
data:
nginx.conf: '
events {
}
http {
server {
listen 80;
location / {
return 200 "Hello world - nginx-svc-2!";
}
}
}
'
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-2
spec:
selector:
matchLabels:
app: nginx-2
strategy:
type: Recreate
template:
metadata:
labels:
app: nginx-2
spec:
containers:
- image: nginx:latest
name: nginx-2
ports:
- containerPort: 80
name: web
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/
volumes:
- name: config-vol
configMap:
name: nginx-config-2
items:
- key: nginx.conf
path: nginx.conf
Create the namespace, enable Istio sidecar injection, and apply istio-mtls.yaml
:
kubectl create namespace mtls-test
kubectl label namespace mtls-test istio-injection=enabled --overwrite
kubectl apply -f istio-mtls.yaml -n mtls-test
Confirm setup
istioctl analyze -n mtls-test
kubectl get deployments -n mtls-test
kubectl get pods -n mtls-test
Check current mTLS settings
1) Check PeerAuthentication
Istio uses PeerAuthentication to configure mTLS settings for inbound traffic:
kubectl get peerauthentication -A
Look for the following mtls.mode
settings:
STRICT
- mTLS is enforcedPERMISSIVE
- mTLS is optionalDISABLE
- mTLS is off
If you see this result then it has not been enforced:
$ kubectl get peerauthentication -A
No resources found
2) Check DestinationRule
Istio uses DestinationRule to configure mTLS settings for outbound traffic:
kubectl get destinationrule -A
Look for rules with trafficPolicy.tls.mode:
ISTIO_MUTUAL
- mTLS is enabledDISABLE
- mTLS is disabled
3) Describe Pod using istioctl
Now to check which of these rules actually apply to a pod we can use istioctl
.
Get pod name:
kubectl get pod -n mtls-test
Use istioctl
, replacing <pod-name>
and <namespace>
with your own values:
istioctl x describe pod <pod-name>.<namespace>
# e.g.: istioctl x describe pod nginx-1-6dfdc84d8c-8hw66.mtls-test
Example output:
Pod: nginx-1-6dfdc84d8c-8hw66.mtls-test
Pod Revision: default
Pod Ports: 80 (nginx-1), 15090 (istio-proxy)
--------------------
Service: nginx-svc-1.mtls-test
Port: http-web 80/HTTP targets pod port 80
--------------------
Effective PeerAuthentication:
Workload mTLS mode: PERMISSIVE
We can see that this pod is still using PERMISSIVE
mode, which means mTLS is optional.
4) Check global settings
Finally, you can check if global mTLS is enabled on the cluster by running:
kubectl get configmap istio -n istio-system -o yaml | grep -i mtls
If enabled, you should see something similar to:
enableAutoMtls: true
Enforce Inbound mTLS for namespace
We can now enforce mTLS for our mtls-test
namespace by deploying this peer-authentication.yaml
:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: istio-mtls-test
namespace: mtls-test
spec:
mtls:
mode: STRICT
Then apply:
kubectl apply -f peer-authentication.yaml -n mtls-test
If you set PeerAuthentication
with mtls.mode: STRICT, the server side (the pod receiving traffic) requires mTLS.
Now we can confirm by running the commands from earlier. Remember to replace <pod-name>.<namespace>
with your own values:
istioctl x describe pod <pod-name>.<namespace>
Expected output:
Pod: nginx-1-6dfdc84d8c-8hw66.mtls-test
Pod Revision: default
Pod Ports: 80 (nginx-1), 15090 (istio-proxy)
--------------------
Service: nginx-svc-1.mtls-test
Port: http-web 80/HTTP targets pod port 80
--------------------
Effective PeerAuthentication:
Workload mTLS mode: STRICT
Applied PeerAuthentication:
istio-mtls-test.mtls-test
You can see that this PeerAuthentication
is now set to STRICT
and enforced for inbound traffic to this pod.
Confirm traffic still works between containers:
kubectl exec -it nginx-1-6dfdc84d8c-8hw66 -n mtls-test -c nginx-1 -- curl http://nginx-svc-2.mtls-test.svc.cluster.local
Enforce Outbound mTLS for namespace
We can now outbound mTLS for our mtls-test
namespace by deploying this destination-rule.yaml
:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: istio-mtls-test
namespace: mtls-test
spec:
host: "*.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
Then apply:
kubectl apply -f destination-rule.yaml -n mtls-test
The DestinationRule tells the Envoy proxy to send outbound traffic using mTLS (using tls.mode: ISTIO_MUTUAL
).
If no DestinationRule is present or it doesn’t specify mTLS, the client may send plaintext traffic.
Now we can confirm by running the commands from earlier. Remember to replace <pod-name>.<namespace>
with your own values:
istioctl x describe pod <pod-name>.<namespace>
Expected output:
Pod: nginx-1-6dfdc84d8c-8hw66.mtls-test
Pod Revision: default
Pod Ports: 80 (nginx-1), 15090 (istio-proxy)
--------------------
Service: nginx-svc-1.mtls-test
Port: http-web 80/HTTP targets pod port 80
DestinationRule: istio-mtls-test.mtls-test for "*.svc.cluster.local"
Traffic Policy TLS Mode: ISTIO_MUTUAL
--------------------
Effective PeerAuthentication:
Workload mTLS mode: STRICT
Applied PeerAuthentication:
istio-mtls-test.mtls-test
You can see that the DestinationRule
is now applied to outbound traffic from this pod to other services within the local mesh ("*.svc.cluster.local"
)
Again, confirm traffic still works between containers:
kubectl exec -it nginx-1-6dfdc84d8c-jt64r -n mtls-test -c nginx-1 -- curl http://nginx-svc-2.mtls-test.svc.cluster.local
Cleanup
Now to delete all our test resources:
kubectl delete -f istio-mtls.yaml -n mtls-test
kubectl delete -f peer-authentication.yaml -n mtls-test
kubectl delete -f destination-rule.yaml -n mtls-test
kubectl delete namespace mtls-test
Summary
Hopefully this gives you a good starting point for enforcing and debugging mTLS in Istio.
You should now be able to:
- Check current mTLS settings
- Enforce mTLS for Inbound traffic
- Enforce mTLS for Outbound traffic