Configure Istio security policies with mutual TLS and authorization for Kubernetes service mesh

Advanced 45 min Apr 11, 2026 272 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure mutual TLS authentication and authorization policies in Istio service mesh for secure microservices communication. This tutorial covers PeerAuthentication, AuthorizationPolicy, and RBAC configuration for production Kubernetes environments.

Prerequisites

  • Kubernetes cluster with Istio installed
  • kubectl configured
  • Basic understanding of Kubernetes and service mesh concepts
  • Root or sudo access

What this solves

Istio security policies enable zero-trust networking for microservices through mutual TLS (mTLS) authentication and fine-grained authorization controls. This tutorial configures production-grade security policies that encrypt service-to-service communication and enforce access control based on service identities, HTTP methods, and request paths.

Prerequisites and Istio installation verification

Verify Kubernetes cluster is running

Ensure your Kubernetes cluster is operational and kubectl is configured correctly.

kubectl cluster-info
kubectl get nodes

Check Istio installation

Verify Istio is installed and running in your cluster with the control plane components.

kubectl get namespace istio-system
kubectl get pods -n istio-system

Verify Istio proxy injection

Check that the istio-proxy sidecar containers are injected into your application pods.

kubectl get pods -o jsonpath='{.items[].spec.containers[].name}' | grep istio-proxy
kubectl describe pod  | grep istio-proxy

Create test namespace and applications

Set up a test environment with sample applications to demonstrate security policies.

kubectl create namespace production
kubectl label namespace production istio-injection=enabled
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: production
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
        version: v1
    spec:
      containers:
      - name: frontend
        image: nginx:1.25
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
  namespace: production
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: production
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
        version: v1
    spec:
      containers:
      - name: backend
        image: httpd:2.4
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: production
spec:
  selector:
    app: backend
  ports:
  - port: 80
    targetPort: 80
kubectl apply -f test-apps.yaml

Configure mutual TLS authentication policies

Enable strict mTLS for entire namespace

Configure PeerAuthentication to require mutual TLS for all services in the production namespace.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default-strict
  namespace: production
spec:
  mtls:
    mode: STRICT
kubectl apply -f peer-auth-strict.yaml

Configure service-specific mTLS policies

Create granular mTLS policies for specific services with custom requirements.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: backend-strict
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  mtls:
    mode: STRICT
  portLevelMtls:
    80:
      mode: STRICT
kubectl apply -f backend-peer-auth.yaml

Configure mesh-wide mTLS policy

Set a default mTLS policy for the entire service mesh in the istio-system namespace.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: mesh-strict
  namespace: istio-system
spec:
  mtls:
    mode: STRICT
kubectl apply -f mesh-peer-auth.yaml

Configure DestinationRule for mTLS

Create DestinationRule policies to enforce client-side mTLS configuration.

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: default-mtls
  namespace: production
spec:
  host: "*.production.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: backend-mtls
  namespace: production
spec:
  host: backend.production.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 10
kubectl apply -f destination-rule-mtls.yaml

Implement Istio authorization policies and RBAC

Create deny-all authorization policy

Start with a default deny policy and then explicitly allow required access patterns.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  {}
kubectl apply -f authz-deny-all.yaml

Configure service-to-service authorization

Allow specific services to communicate with each other based on service accounts and HTTP methods.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: frontend-to-backend
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/frontend"]
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/*", "/health"]
    when:
    - key: request.headers["x-api-version"]
      values: ["v1", "v2"]
kubectl apply -f authz-frontend-backend.yaml

Create service accounts for RBAC

Set up dedicated service accounts for each service to enable identity-based authorization.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: frontend
  namespace: production
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
  namespace: production
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: production
kubectl apply -f service-accounts.yaml

Update deployments with service accounts

Modify your application deployments to use the dedicated service accounts.

kubectl patch deployment frontend -n production -p '{"spec":{"template":{"spec":{"serviceAccountName":"frontend"}}}}'
kubectl patch deployment backend -n production -p '{"spec":{"template":{"spec":{"serviceAccountName":"backend"}}}}'

Configure role-based authorization policies

Create comprehensive authorization policies with role-based access control.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: backend-rbac
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/frontend"]
    to:
    - operation:
        methods: ["GET"]
        paths: ["/api/data"]
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/admin"]
    to:
    - operation:
        methods: ["GET", "POST", "PUT", "DELETE"]
        paths: ["/api/", "/admin/"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: frontend-external
  namespace: production
spec:
  selector:
    matchLabels:
      app: frontend
  rules:
  - from:
    - source:
        namespaces: ["istio-system"]
    to:
    - operation:
        methods: ["GET"]
        paths: ["/", "/static/*"]
  - when:
    - key: source.ip
      values: ["203.0.113.0/24", "198.51.100.0/24"]
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/*"]
kubectl apply -f authz-rbac.yaml

Configure JWT-based authorization

Implement JWT token validation for external API access with custom claims validation.

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-auth
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  jwtRules:
  - issuer: "https://auth.example.com"
    jwksUri: "https://auth.example.com/.well-known/jwks.json"
    audiences:
    - "api.example.com"
    outputPayloadToHeader: "x-jwt-payload"
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: jwt-authz
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  rules:
  - from:
    - source:
        requestPrincipals: ["https://auth.example.com/*"]
    when:
    - key: request.auth.claims["role"]
      values: ["admin", "user"]
    - key: request.auth.claims["sub"]
      notValues: ["blocked-user"]
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/secure/*"]
kubectl apply -f jwt-auth.yaml

Testing and monitoring security policies

Test mTLS connectivity

Verify that mutual TLS is working correctly between services using istioctl and test pods.

kubectl exec -n production deployment/frontend -c istio-proxy -- curl -v http://backend/
istioctl authn tls-check frontend-pod.production backend.production.svc.cluster.local

Test authorization policies

Create test scenarios to verify that authorization policies are enforcing access controls correctly.

kubectl run test-pod --rm -i --tty --image=curlimages/curl --namespace=production -- /bin/sh

Inside the test pod:

curl -v http://backend/api/data curl -v -X POST http://backend/api/data curl -v http://backend/admin/users

Monitor security with istioctl

Use istioctl commands to inspect and troubleshoot security policy configuration.

istioctl analyze -n production
istioctl proxy-config cluster frontend-pod -n production
istioctl proxy-config listener frontend-pod -n production --port 15006

Check security policy status

Verify that all security policies are applied correctly and view their current status.

kubectl get peerauthentication -n production
kubectl get authorizationpolicy -n production
kubectl get requestauthentication -n production
kubectl describe authorizationpolicy frontend-to-backend -n production

Monitor with access logs

Enable and analyze access logs to monitor security policy enforcement and troubleshoot issues.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: access-logs
spec:
  meshConfig:
    accessLogFile: /dev/stdout
    accessLogEncoding: JSON
    accessLogFormat: |
      {
        "timestamp": "%START_TIME%",
        "method": "%REQ(:METHOD)%",
        "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%",
        "response_code": "%RESPONSE_CODE%",
        "source_app": "%DOWNSTREAM_LOCAL_ADDRESS%",
        "destination_app": "%UPSTREAM_HOST%",
        "mtls": "%DOWNSTREAM_TLS_VERSION%",
        "principal": "%DOWNSTREAM_PEER_SUBJECT%"
      }
kubectl apply -f access-logs-config.yaml
kubectl logs -n production deployment/backend -c istio-proxy --tail=50

Verify your setup

kubectl get peerauthentication -A
kubectl get authorizationpolicy -A
kubectl get destinationrule -A
istioctl analyze -A
kubectl exec -n production deployment/frontend -c istio-proxy -- curl -v http://backend/
istioctl proxy-status

Common issues

SymptomCauseFix
Connection refused between servicesStrict mTLS without DestinationRuleCreate DestinationRule with ISTIO_MUTUAL mode
403 Forbidden errorsAuthorization policy denying accessCheck AuthorizationPolicy rules and service account principals
TLS handshake failuresCertificate rotation or CA issuesRestart workloads: kubectl rollout restart deployment/app -n namespace
JWT validation failsInvalid JWKS URI or issuerVerify RequestAuthentication configuration and token format
Policy not taking effectLabel selector mismatchCheck pod labels match policy selectors with kubectl describe
Istio proxy not injectedNamespace not labeled for injectionkubectl label namespace production istio-injection=enabled

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.