
Cloud Service Mesh on GKE: mTLS, Traffic Splitting & Authorization Policies | 2026
Cloud Service Mesh (CSM) — Google's managed Istio control plane for GKE — automatically encrypts all pod-to-pod communication with mutual TLS (mTLS) without touching application code. On top of encryption, it provides a rich traffic management layer: canary deployments via VirtualService weight splitting, circuit breakers via DestinationRule, and fine-grained authorization with AuthorizationPolicy. This guide walks through enabling CSM on GKE, configuring STRICT mTLS PeerAuthentication, deploying a canary traffic split, and locking down service-to-service access with authorization policies.
By Mateusz Chmielewski · Aug 29, 2026 · 16 min read
What is Cloud Service Mesh?
Cloud Service Mesh (CSM) is Google's managed service mesh based on Istio. It injects Envoy sidecar proxy containers into every pod in enabled namespaces. These proxies handle all inbound and outbound traffic, enforcing mTLS encryption, traffic routing policies, retry logic, and observability — entirely transparently to your application code.
Think of a microservice without a service mesh like a conversation in an open-plan office — anyone nearby can hear your messages, and there's no record of who said what. Cloud Service Mesh is like giving every employee a secure encrypted walkie-talkie with an ID badge. All communications are encrypted, logged, and only authorized employees can contact specific departments — your application code just 'talks', the walkie-talkie handles everything else.
| Concept | Explanation | When to use |
|---|---|---|
| Envoy Sidecar Proxy | A high-performance L7 proxy container automatically injected alongside every application container in mesh-enabled namespaces. | Automatically managed by CSM — no explicit configuration needed. |
| PeerAuthentication | Istio custom resource that configures mTLS mode (PERMISSIVE or STRICT) for traffic between pods within a namespace. | Set to STRICT in production to enforce encrypted pod-to-pod communication. |
| VirtualService | Defines traffic routing rules — weight splitting between service versions, retries, timeouts, and header-based routing. | Deploy for canary releases, A/B testing, or blue-green rollouts. |
| DestinationRule | Configures traffic policies for a target service — load balancing algorithm, connection pool limits, circuit breaker thresholds. | Pair with VirtualService for complete traffic management. |
| AuthorizationPolicy | Istio RBAC policy that permits or denies requests between services based on source service account, namespace, or request attributes. | Restrict which services can call each other — implements zero-trust service-to-service access control. |
mTLS Service Mesh vs Plain Kubernetes Networking
Default Kubernetes pod networking transmits all inter-service HTTP traffic in plaintext. Any compromised pod or rogue container in the cluster can sniff or spoof traffic between microservices. Standard Kubernetes NetworkPolicies operate at L3/L4 and cannot authenticate service identity.
Cloud Service Mesh enforces mutual TLS authentication and encryption at L7 between every pair of communicating pods. SPIFFE/SPIRE identity certificates are automatically rotated every 24 hours. AuthorizationPolicies add fine-grained service identity-based access control beyond what NetworkPolicy provides. Pair with [GKE Gateway API](/tutorial/gke-gateway-api-replace-ingress-multi-service-routing) for ingress routing and enforce image provenance via [Binary Authorization](/tutorial/binary-authorization-artifact-registry-signed-images-gke).
| Feature | thisService | altA | altB |
|---|---|---|---|
| Encryption | CSM: mTLS L7 (pod-to-pod, automatic) | Kubernetes NetworkPolicy: None (L3/L4 only) | Manual TLS: App-level code required |
| Service Identity | SPIFFE X.509 SVIDs (automatically rotated) | IP-based (spoofable) | Manual certificate management |
| Traffic Splitting | VirtualService weights (0-100%) | Deployment replica ratio only | Load balancer rules (manual) |
| Observability | Automatic L7 metrics, traces, and topology | L4 flow logs only | Application-instrumented only |
| Access Control | AuthorizationPolicy (service account identity) | NetworkPolicy (IP CIDR based) | Application-level API keys |
Prerequisites
- GKE cluster (Standard or Autopilot) running Kubernetes v1.28+
- Cloud Service Mesh feature enabled on the GKE fleet
- kubectl and istioctl CLIs configured
- gcloud CLI v480.0+ authenticated
Step-by-Step Guide
Step 1: Enable Cloud Service Mesh on GKE Fleet
Register your GKE cluster to a GCP Fleet and enable the Cloud Service Mesh managed feature. Fleet registration allows Google's managed CSM control plane to inject Envoy sidecars and synchronize mesh configuration without self-managing an Istiod control plane.
# Step 1: Register GKE cluster to Fleet
gcloud container fleet memberships register prod-gke-cluster \
--gke-cluster=europe-west1/prod-gke-cluster \
--enable-workload-identity \
--project=PROJECT_ID
# Step 2: Enable Cloud Service Mesh feature on Fleet
gcloud container fleet mesh enable --project=PROJECT_ID
# Step 3: Enable CSM on the specific cluster membership
gcloud container fleet mesh update \
--management=automatic \
--memberships=prod-gke-cluster \
--project=PROJECT_ID \
--location=global
# Step 4: Enable sidecar injection on target namespace
kubectl label namespace production istio-injection=enabled
Step 2: Configure STRICT mTLS with PeerAuthentication
Apply a PeerAuthentication resource to enforce STRICT mTLS across the production namespace. PERMISSIVE mode accepts both plaintext and mTLS traffic during migration. STRICT mode rejects all non-mTLS connections, ensuring zero unencrypted traffic within the mesh.
# peer_authentication.yaml — Enforce STRICT mTLS in production namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-strict-mtls
namespace: production
spec:
mtls:
mode: STRICT
Step 3: Deploy VirtualService and DestinationRule for Canary Traffic Splitting
Route 95% of traffic to the stable service version and 5% to the canary version using VirtualService weight configuration. VirtualService traffic splitting enables progressive delivery — gradually shifting traffic to a new version while limiting blast radius if bugs are detected.
# destination_rule.yaml — Define stable and canary subsets
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: api-service-destinations
namespace: production
spec:
host: api-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: UPGRADE
subsets:
- name: stable
labels:
version: v1
- name: canary
labels:
version: v2
---
# virtual_service.yaml — 95/5 traffic split
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-service-split
namespace: production
spec:
hosts:
- api-service
http:
- route:
- destination:
host: api-service
subset: stable
weight: 95
- destination:
host: api-service
subset: canary
weight: 5
timeout: 10s
retries:
attempts: 3
perTryTimeout: 3s
Step 4: Create AuthorizationPolicy for Zero-Trust Service Access
Restrict which services can call the payments API using AuthorizationPolicy bound to Kubernetes service account identities. Without AuthorizationPolicy, any compromised pod in the cluster can send requests to sensitive services like payment processors. SPIFFE identity-based access control prevents lateral movement.
# authorization_policy.yaml — Allow only checkout-service to call payments-api
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payments-api-access-policy
namespace: production
spec:
selector:
matchLabels:
app: payments-api
action: ALLOW
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/checkout-service-account"
to:
- operation:
methods: ["POST"]
paths: ["/v1/payments/*"]
---
# Deny-all fallback policy (deny everything not explicitly allowed)
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payments-api-deny-all
namespace: production
spec:
selector:
matchLabels:
app: payments-api
action: DENY
rules:
- from:
- source:
notPrincipals:
- "cluster.local/ns/production/sa/checkout-service-account"
Step 5: Observe Mesh Traffic with Cloud Trace and Monitoring
Verify mesh telemetry using Cloud Monitoring service mesh dashboards and Cloud Trace distributed tracing. CSM automatically exports L7 RED metrics (Request rate, Error rate, Duration) to Cloud Monitoring without any application instrumentation.
# Step 1: Check mesh proxy injection status
kubectl get pods -n production -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].name}{"\n"}{end}'
# Expected output includes 'istio-proxy' alongside each app container:
# api-deployment-abc123 api-server istio-proxy
# payments-api-xyz456 payments-api istio-proxy
# Step 2: Verify mTLS connection between services
istioctl authn tls-check -n production checkout-pod payments-api.production.svc.cluster.local
# Step 3: View service mesh topology in Cloud Console
# Navigate: GKE -> Service Mesh -> Topology -> production namespace
Verification & Health Check
Best Practices
- Start with PERMISSIVE mTLS Before Enforcing STRICT
- Scope AuthorizationPolicy with Explicit Deny-All Fallback
Common Mistakes
- {"errorCode":"SIDECAR_NOT_INJECTED","symptoms":"Pods start successfully but traffic is not encrypted. `kubectl describe pod` shows only one container per pod.","rootCause":"The namespace label `istio-injection=enabled` was not present before pod creation, or the pod has annotation `sidecar.istio.io/inject: 'false'`.","fixCommand":"kubectl label namespace production istio-injection=enabled --overwrite && kubectl rollout restart deployment -n production","code":"kubectl label namespace production istio-injection=enabled --overwrite\nkubectl rollout restart deployment --namespace=production\n","language":"bash","filename":"fix_injection.sh","prevention":"Automate namespace labeling in Terraform using `kubernetes_namespace` resource with the label."}
Cost Analysis
| Feature | metric | cost1k | cost10k | cost100k | cost1m |
|---|---|---|---|---|---|
| CSM Managed Control Plane | $0.00 | $0.00 | $0.00 | $0.00 / month | |
| Envoy Sidecar Memory Overhead (~50MB per pod) | ~$8.00/mo | ~$8.00/mo | ~$8.00/mo | ~$96.00/yr | |
| Cloud Trace (mesh distributed tracing) | $0.20/M spans | $0.20/M spans | $0.20/M spans | ~$24.00/yr |