Securing Kubernetes Microservices Using Istio and Strict mTLS
Modern microservices architectures offer incredible flexibility — but they also introduce a hidden problem:
security inside the cluster is often an afterthought.
When I started working with Kubernetes, everything worked smoothly… until I asked a simple question:
“How secure is communication between my services?”
The answer wasn’t reassuring.
The Problem: Insecure Internal Communication
In a default Kubernetes setup:
- Service-to-service traffic is not encrypted
- There is no strong identity verification
- Any compromised pod can:
- Intercept internal traffic
- Access other services
- Impersonate workloads
The Solution: Istio as a Security Layer
To solve this, I introduced Istio as a dedicated security layer.
Istio works by injecting sidecar proxies (Envoy) into every pod.
These proxies intercept all incoming and outgoing traffic, allowing Istio to:
- Control how services communicate
- Enforce security policies
- Provide deep visibility into traffic
Most importantly:
It enables secure communication without modifying application code
Understanding mTLS
Mutual TLS (mTLS) ensures that:
- Both client and server authenticate each other
- All traffic is encrypted
With Istio:
- Certificates are automatically issued and rotated
- Encryption is enforced transparently
- Developers don’t need to modify their applications
mTLS Modes in Istio
Istio supports three modes for mTLS:
1. PERMISSIVE
- Allows both encrypted and plain-text traffic
- Useful for migration
Not fully secure
2. STRICT (Used in This Project)
- Only encrypted (mTLS) traffic is allowed
- Plain-text traffic is rejected
This enforces:
- Full encryption
- Strong service authentication
- Zero Trust networking
3. DISABLE
- No encryption
- Not recommended for production
Why I Chose STRICT Mode
I enabled STRICT mTLS to enforce a Zero Trust architecture.
Get Ahmedatiia’s stories in your inbox
Join Medium for free to get updates from this writer.
This means:
- All communication between services is encrypted
- Each service has a verified identity
- Unauthorized traffic is automatically blocked
Real Impact
With STRICT mode enabled:
- Frontend → Backend communication is:
- Encrypted
- Authenticated
Any unknown service:
- Cannot connect
- Is rejected immediately
Ingress Gateway
The Istio Ingress Gateway acts as the entry point for external traffic.
It allows:
- Secure exposure of services
- TLS enforcement
- Advanced routing rules
Implementation Steps
Here’s how I implemented Istio in my environment:
First Kubernetes yaml files
# ingressclass-istio
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: istio
spec:
controller: istio.io/ingress-controller
---
# PeerAuthentication - Istio
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: strict-mtls
namespace: istio-system
spec:
mtls:
mode: STRICT
---
# Gateway - handles external traffic
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: main-gateway
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 8080
name: http
protocol: HTTP
hosts:
- "URL"
---
# VirtualService
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: Vertual-service-name
namespace: default
spec:
hosts:
- "URL"
gateways:
- main-gateway
http:
- route:
- destination:
host: svc-name
port:
number: service-portThen install Istio :
# 1. Install Istio with cert rotation settings
istioctl install --set profile=demo \
--set values.pilot.env.CITADEL_SELF_SIGNED_CA_CERT_TTL=8760h \
--set values.pilot.env.CITADEL_SELF_SIGNED_ROOT_CERT_CHECK_INTERVAL=1h \
--set values.pilot.env.CITADEL_SELF_SIGNED_ROOT_CERT_GRACE_PERIOD_PERCENTILE=20
kubectl delete secret istio-ca-secret -n istio-system
kubectl rollout restart deployment istiod -n istio-system
kubectl rollout status deployment istiod -n istio-system
# 2. Verify Istio control plane is running
kubectl get pods -n istio-system
# 3. Move ingressgateway off port 80/443 to avoid Traefik conflict
kubectl edit daemonsets.apps -n kube-system svclb-istio-ingressgateway-e5a375fc
# change 80 → 8080 and 443 → 8443
kubectl edit svc -n istio-system istio-ingressgateway
# change 80 → 8080 and 443 → 8443
# 4. Enable sidecar injection on your namespace
kubectl label namespace default istio-injection=enabled
# 5. Apply your app manifests (deployments, services, gateway, virtualservices, peerauth)
kubectl apply -f Kubernetes/
# 6. Restart pods so sidecars get injected
kubectl rollout restart deployment app
# 7. Verify pods show 2/2
kubectl get pods
# app-xxx 2/2 Running ✓
# 8. Verify all proxies are synced
istioctl proxy-status
# 9. Verify routes are on 8080
istioctl proxy-config routes \
$(kubectl get pod -n istio-system -l app=istio-ingressgateway \
-o jsonpath='{.items[0].metadata.name}') -n istio-system
# Should show http.8080 for backend.local and frontend.local
# 10. Test it works
curl -H "Host: URL" Server-IP:8080/url
# Expected: healthy msg
# 11. Install observability addons (Grafana, Kiali, Prometheus, Jaeger)
kubectl apply -f samples/addons/
# 12. Verify addons are running
kubectl get pods -n istio-systemGot questions? Drop them below — I’m all ears!
For more info
Here’s my GitHub repo: AhmedAtiia11
Here’s my Linkedin : Ahmed Atiia








