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
| Symptom | Cause | Fix |
|---|---|---|
| Connection refused between services | Strict mTLS without DestinationRule | Create DestinationRule with ISTIO_MUTUAL mode |
| 403 Forbidden errors | Authorization policy denying access | Check AuthorizationPolicy rules and service account principals |
| TLS handshake failures | Certificate rotation or CA issues | Restart workloads: kubectl rollout restart deployment/app -n namespace |
| JWT validation fails | Invalid JWKS URI or issuer | Verify RequestAuthentication configuration and token format |
| Policy not taking effect | Label selector mismatch | Check pod labels match policy selectors with kubectl describe |
| Istio proxy not injected | Namespace not labeled for injection | kubectl label namespace production istio-injection=enabled |
Next steps
- Set up Istio multi-cluster service mesh with cross-cluster communication
- Configure Istio traffic management with virtual services and destination rules
- Monitor Istio service mesh with Prometheus and Grafana dashboards
- Implement Jaeger security with TLS encryption and authentication for distributed tracing
- Configure Istio Gateway with cert-manager for automated certificate management
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Istio Security Policies Configuration Script
# Configures mutual TLS and authorization policies for Kubernetes service mesh
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
NAMESPACE="${1:-production}"
TOTAL_STEPS=8
# Usage message
usage() {
echo "Usage: $0 [namespace]"
echo " namespace: Target namespace for Istio security policies (default: production)"
exit 1
}
# Cleanup on error
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
kubectl delete namespace "${NAMESPACE}" 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -eq 0 ]]; then
echo -e "${YELLOW}[WARNING] Running as root. Consider using a regular user with kubectl access.${NC}"
fi
}
# Auto-detect distro and package manager
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian) PKG_MGR="apt"; PKG_INSTALL="apt install -y" ;;
almalinux|rocky|centos|rhel|ol|fedora) PKG_MGR="dnf"; PKG_INSTALL="dnf install -y" ;;
amzn) PKG_MGR="yum"; PKG_INSTALL="yum install -y" ;;
*) echo -e "${RED}Unsupported distro: $ID${NC}"; exit 1 ;;
esac
else
echo -e "${RED}/etc/os-release not found. Cannot detect distribution.${NC}"
exit 1
fi
}
# Check prerequisites
check_prerequisites() {
echo -e "${GREEN}[1/${TOTAL_STEPS}] Checking prerequisites...${NC}"
# Check kubectl
if ! command -v kubectl &> /dev/null; then
echo -e "${YELLOW}kubectl not found. Installing...${NC}"
if [ "$PKG_MGR" = "apt" ]; then
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list
apt update && $PKG_INSTALL kubectl
else
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
$PKG_INSTALL kubectl
fi
fi
# Check cluster connectivity
if ! kubectl cluster-info &> /dev/null; then
echo -e "${RED}Cannot connect to Kubernetes cluster. Check your kubeconfig.${NC}"
exit 1
fi
}
# Verify Istio installation
verify_istio() {
echo -e "${GREEN}[2/${TOTAL_STEPS}] Verifying Istio installation...${NC}"
if ! kubectl get namespace istio-system &> /dev/null; then
echo -e "${RED}Istio system namespace not found. Please install Istio first.${NC}"
exit 1
fi
if ! kubectl get pods -n istio-system | grep -q "Running"; then
echo -e "${RED}Istio pods are not running properly.${NC}"
exit 1
fi
echo -e "${GREEN}Istio installation verified successfully.${NC}"
}
# Create test namespace and applications
create_test_apps() {
echo -e "${GREEN}[3/${TOTAL_STEPS}] Creating test namespace and applications...${NC}"
kubectl create namespace "${NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f -
kubectl label namespace "${NAMESPACE}" istio-injection=enabled --overwrite
# Create test applications manifest
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: ${NAMESPACE}
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: ${NAMESPACE}
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: ${NAMESPACE}
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: ${NAMESPACE}
spec:
selector:
app: backend
ports:
- port: 80
targetPort: 80
EOF
echo -e "${GREEN}Test applications created successfully.${NC}"
}
# Configure namespace-wide mTLS
configure_namespace_mtls() {
echo -e "${GREEN}[4/${TOTAL_STEPS}] Configuring namespace-wide strict mTLS...${NC}"
cat <<EOF | kubectl apply -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-strict
namespace: ${NAMESPACE}
spec:
mtls:
mode: STRICT
EOF
echo -e "${GREEN}Namespace mTLS policy applied successfully.${NC}"
}
# Configure service-specific mTLS
configure_service_mtls() {
echo -e "${GREEN}[5/${TOTAL_STEPS}] Configuring service-specific mTLS policies...${NC}"
cat <<EOF | kubectl apply -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: backend-strict
namespace: ${NAMESPACE}
spec:
selector:
matchLabels:
app: backend
mtls:
mode: STRICT
portLevelMtls:
80:
mode: STRICT
EOF
echo -e "${GREEN}Service-specific mTLS policy applied successfully.${NC}"
}
# Configure DestinationRules for mTLS
configure_destination_rules() {
echo -e "${GREEN}[6/${TOTAL_STEPS}] Configuring DestinationRules for mTLS...${NC}"
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: default-mtls
namespace: ${NAMESPACE}
spec:
host: "*.${NAMESPACE}.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: backend-mtls
namespace: ${NAMESPACE}
spec:
host: backend.${NAMESPACE}.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
connectionPool:
tcp:
maxConnections: 100
EOF
echo -e "${GREEN}DestinationRules configured successfully.${NC}"
}
# Configure authorization policies
configure_authorization() {
echo -e "${GREEN}[7/${TOTAL_STEPS}] Configuring authorization policies...${NC}"
cat <<EOF | kubectl apply -f -
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: frontend-policy
namespace: ${NAMESPACE}
spec:
selector:
matchLabels:
app: frontend
rules:
- from:
- source:
principals: ["cluster.local/ns/${NAMESPACE}/sa/default"]
- to:
- operation:
methods: ["GET", "POST"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: backend-policy
namespace: ${NAMESPACE}
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals: ["cluster.local/ns/${NAMESPACE}/sa/default"]
to:
- operation:
methods: ["GET"]
paths: ["/health", "/api/*"]
EOF
echo -e "${GREEN}Authorization policies configured successfully.${NC}"
}
# Verify configuration
verify_configuration() {
echo -e "${GREEN}[8/${TOTAL_STEPS}] Verifying configuration...${NC}"
# Wait for pods to be ready
kubectl wait --for=condition=ready pod -l app=frontend -n "${NAMESPACE}" --timeout=300s
kubectl wait --for=condition=ready pod -l app=backend -n "${NAMESPACE}" --timeout=300s
# Check PeerAuthentication policies
if kubectl get peerauthentication -n "${NAMESPACE}" | grep -q "default-strict"; then
echo -e "${GREEN}✓ PeerAuthentication policies are active${NC}"
else
echo -e "${RED}✗ PeerAuthentication policies not found${NC}"
fi
# Check DestinationRules
if kubectl get destinationrule -n "${NAMESPACE}" | grep -q "default-mtls"; then
echo -e "${GREEN}✓ DestinationRules are configured${NC}"
else
echo -e "${RED}✗ DestinationRules not found${NC}"
fi
# Check AuthorizationPolicies
if kubectl get authorizationpolicy -n "${NAMESPACE}" | grep -q "frontend-policy"; then
echo -e "${GREEN}✓ Authorization policies are active${NC}"
else
echo -e "${RED}✗ Authorization policies not found${NC}"
fi
# Verify Istio proxy injection
PROXY_COUNT=$(kubectl get pods -n "${NAMESPACE}" -o jsonpath='{.items[*].spec.containers[*].name}' | tr ' ' '\n' | grep -c istio-proxy || true)
if [ "$PROXY_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ Istio proxies are injected (${PROXY_COUNT} containers)${NC}"
else
echo -e "${YELLOW}⚠ No Istio proxies detected${NC}"
fi
echo -e "${GREEN}Configuration verification completed!${NC}"
}
# Main execution
main() {
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
fi
echo -e "${GREEN}Starting Istio Security Policies Configuration...${NC}"
check_privileges
detect_distro
check_prerequisites
verify_istio
create_test_apps
configure_namespace_mtls
configure_service_mtls
configure_destination_rules
configure_authorization
verify_configuration
echo -e "${GREEN}Istio security policies configuration completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Test service communication: kubectl exec -n ${NAMESPACE} deployment/frontend -- curl backend"
echo "2. Monitor with: kubectl get peerauthentication,authorizationpolicy,destinationrule -n ${NAMESPACE}"
echo "3. View logs: kubectl logs -n ${NAMESPACE} -l app=frontend -c istio-proxy"
}
main "$@"
Review the script before running. Execute with: bash install.sh