Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

mTLS pod-to-pod : chiffrer le trafic entre services

18 min de lecture

Par défaut, le trafic entre Pods Kubernetes circule en clair. N’importe quel Pod dans le cluster peut intercepter les communications d’un autre Pod — il suffit d’un accès réseau. Le mTLS (mutual TLS) résout ce problème en chiffrant automatiquement tout le trafic inter-services et en vérifiant l’identité de chaque extrémité. Un service mesh comme Istio gère cette couche de sécurité de façon transparente : pas de modification du code applicatif, rotation automatique des certificats, et contrôle d’accès basé sur l’identité du service. Ce guide vous montre comment activer et vérifier le mTLS dans un cluster Kubernetes, avec les ressources PeerAuthentication et AuthorizationPolicy attendues à l’examen CKS.

  • Pourquoi le trafic en clair entre Pods est un risque de sécurité
  • Comment le mTLS fonctionne (identité SPIFFE, certificats X.509, handshake)
  • Configurer Istio pour imposer le mTLS en mode STRICT
  • Vérifier que le chiffrement est actif entre deux services
  • Contrôler les accès avec AuthorizationPolicy et les principals mTLS
  • Choisir entre les approches sidecar et ambient (ztunnel)
  • Cluster Kubernetes avec accès administrateur
  • Familiarité avec les Pods et les Network Policies
  • Compréhension des concepts de service mesh (data plane, control plane)
  • istioctl installé (ligne de commande Istio)

Dans un cluster Kubernetes, le réseau est plat par défaut : chaque Pod peut communiquer avec tous les autres Pods sans restriction. Ce modèle simplifie le déploiement, mais crée un risque majeur : si un attaquant compromet un seul Pod, il peut intercepter (sniffing), modifier (man-in-the-middle) ou usurper le trafic des autres services.

Analogie : un réseau Kubernetes sans mTLS, c’est comme un open space où tout le monde parle à voix haute. N’importe qui peut écouter les conversations des autres. Le mTLS transforme chaque échange en conversation privée où les deux interlocuteurs vérifient leur identité avant de parler, et où tout le contenu est chiffré.

Les Network Policies résolvent une partie du problème en filtrant le trafic au niveau L3/L4 (IPs et ports), mais elles ne font ni chiffrement ni authentification. Le tableau ci-dessous clarifie la complémentarité :

MécanismeCoucheChiffrementAuthentificationCe qu’il contrôle
NetworkPolicyL3/L4NonNonQuels Pods peuvent se parler (IP/port)
mTLSL4/L7Oui (TLS 1.2+)Oui (certificats X.509)Qui parle à qui (identité cryptographique)

En combinant les deux, vous obtenez un modèle défense en profondeur : les NetworkPolicies limitent les flux autorisés, et le mTLS garantit que chaque flux est chiffré et que les deux extrémités sont bien qui elles prétendent être.

Le TLS classique (celui de HTTPS) est unilatéral : seul le client vérifie l’identité du serveur. Le mutual TLS (mTLS) ajoute une étape : le serveur vérifie aussi l’identité du client. Les deux parties présentent un certificat X.509.

  1. Le client initie la connexion TLS : il envoie un “ClientHello” avec les suites de chiffrement qu’il supporte.

  2. Le serveur présente son certificat : le client vérifie que le certificat est signé par une autorité de confiance (CA) et que l’identité correspond au service attendu.

  3. Le serveur demande le certificat du client : c’est l’étape supplémentaire par rapport au TLS classique. Le client présente son propre certificat.

  4. Le serveur vérifie le certificat du client : il confirme que le client est bien un service autorisé, signé par la même CA.

  5. La session chiffrée est établie : les deux parties échangent une clé de session symétrique. Tout le trafic suivant est chiffré et authentifié.

Dans un cluster Kubernetes, l’identité de chaque service est encodée dans le certificat sous la forme d’un SPIFFE ID (Secure Production Identity Framework For Everyone). Le format standard est :

spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>

Par exemple, un service utilisant le ServiceAccount api-server dans le namespace production aura l’identité :

spiffe://cluster.local/ns/production/sa/api-server

Cette identité basée sur le ServiceAccount Kubernetes est au cœur du modèle de sécurité mTLS. Elle permet de définir des politiques d’accès granulaires : “seul le service frontend dans le namespace production peut appeler le service api-server”.

Sans service mesh, vous devriez gérer manuellement les certificats pour chaque service : génération, distribution, rotation, révocation. Avec un service mesh comme Istio, tout est automatisé :

ÉtapeCe qui se passe
GénérationLe proxy (Envoy ou ztunnel) génère une clé privée et envoie une CSR (Certificate Signing Request) au control plane
SignatureLe control plane (istiod) signe la CSR après avoir vérifié l’identité du Pod via le ServiceAccount
DistributionLe certificat signé est distribué au proxy via l’API SDS (Secret Discovery Service)
RotationLes certificats sont renouvelés automatiquement avant expiration (durée par défaut : 24h)

Le code applicatif ne voit rien de tout cela : le proxy gère le mTLS de façon transparente.

Istio est le service mesh le plus répandu et le plus fréquemment référencé dans le contexte CKS. Il propose deux modèles de déploiement : le mode sidecar classique et le mode ambient (sans sidecar). Dans les deux cas, le mTLS se configure avec les mêmes ressources Kubernetes.

Pour un cluster de lab, l’installation minimale avec le profil demo :

Fenêtre de terminal
# Télécharger istioctl
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.29.1 sh -
cd istio-1.29.1
export PATH=$PWD/bin:$PATH
# Installer avec le profil demo (inclut ingress + egress gateways)
istioctl install --set profile=demo -y

Vérification :

Fenêtre de terminal
kubectl get pods -n istio-system

Résultat attendu — istiod et les gateways doivent être Running :

NAME READY STATUS RESTARTS AGE
istio-egressgateway-6bbb4c99b5-69jw8 1/1 Running 0 80s
istio-ingressgateway-8574598487-pjhrq 1/1 Running 0 79s
istiod-857888d8dd-6zt2t 1/1 Running 0 92s

En mode sidecar, Istio injecte un proxy Envoy dans chaque Pod. Pour activer l’injection automatique sur un namespace :

Fenêtre de terminal
kubectl label namespace default istio-injection=enabled

Tout Pod créé dans ce namespace contiendra désormais deux conteneurs : votre application et le proxy Envoy.

La ressource PeerAuthentication contrôle le mode mTLS pour un namespace ou un workload spécifique. C’est la ressource clé pour la CKS.

Trois modes sont disponibles :

ModeComportementUsage
PERMISSIVEAccepte le trafic mTLS et en clairMigration progressive (défaut Istio)
STRICTAccepte uniquement le trafic mTLSProduction sécurisée
DISABLEDésactive le mTLSDéconseillé sauf cas très spécifique

Pour imposer le chiffrement sur tous les namespaces du cluster :

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system # namespace racine = s'applique à tout le mesh
spec:
mtls:
mode: STRICT
Fenêtre de terminal
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
EOF

Pour appliquer le mode STRICT uniquement sur le namespace production :

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT

Pour cibler un workload précis avec un selector :

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: api-strict
namespace: production
spec:
selector:
matchLabels:
app: api-server
mtls:
mode: STRICT

AuthorizationPolicy : contrôle d’accès basé sur l’identité

Section intitulée « AuthorizationPolicy : contrôle d’accès basé sur l’identité »

Le mTLS en mode STRICT garantit que tout le trafic est chiffré et authentifié. Mais il ne contrôle pas qui peut appeler quoi. C’est le rôle de l’AuthorizationPolicy.

Grâce au mTLS, Istio connaît l’identité SPIFFE de chaque requête. Vous pouvez exploiter cette information pour définir des règles d’accès granulaires.

L’exemple suivant autorise uniquement le service frontend (via son ServiceAccount) à accéder au service api-server :

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: api-server-access
namespace: production
spec:
selector:
matchLabels:
app: api-server
action: ALLOW
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/frontend"

Le champ principals utilise le SPIFFE ID du service source. Sans mTLS en mode STRICT, ce champ ne fonctionne pas (Istio ne peut pas extraire l’identité d’un flux en clair).

Pour un namespace en mode “zero trust”, commencez par refuser tout le trafic, puis ouvrez explicitement les flux autorisés :

# Étape 1 : refuser tout par défaut
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: production
spec:
action: ALLOW
# Pas de rules → ne match jamais → tout est refusé

Un service qui tente d’appeler api-server reçoit désormais une erreur HTTP 403 :

RBAC: access denied
# Étape 2 : autoriser explicitement le flux frontend → api-server
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
selector:
matchLabels:
app: api-server
action: ALLOW
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/frontend"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]

Après avoir configuré le mTLS, vous devez pouvoir vérifier qu’il fonctionne réellement. Istio fournit plusieurs méthodes de vérification.

Fenêtre de terminal
# Voir la politique mTLS effective pour un workload
istioctl x describe pod <pod-name> -n <namespace>

Exemple de sortie :

Pod: api-server-5b7c5fd757-4jmtj
Pod Revision: default
Pod Ports: 80 (nginx)
--------------------
Service: api-server
Port: 8080/auto-detect targets pod port 80
--------------------
Effective PeerAuthentication:
Workload mTLS mode: STRICT
Applied PeerAuthentication:
default.production

La sortie indique les politiques PeerAuthentication appliquées et le mode mTLS effectif sur le workload.

Fenêtre de terminal
# Inspecter le certificat utilisé par le proxy Envoy d'un Pod
istioctl proxy-config secret <pod-name> -n <namespace>

La sortie affiche les certificats actifs, leur validité et leur numéro de série :

RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE
default Cert Chain ACTIVE true a082e25c3b111ace6e53f6b9891ab30d 2026-03-28T09:14:48Z 2026-03-27T09:12:48Z
ROOTCA CA ACTIVE true c253fd7d7a186f43c1e200c5c8513835 2036-03-24T09:12:25Z 2026-03-27T09:12:25Z

La ligne default (Cert Chain) est le certificat mTLS du workload avec le SPIFFE ID intégré. La ligne ROOTCA est l’autorité de certification racine d’Istio.

Pour confirmer que le mode STRICT fonctionne, tentez d’envoyer du trafic en clair depuis un Pod sans proxy vers un service protégé :

Fenêtre de terminal
# Depuis un namespace sans injection sidecar
kubectl create namespace no-mesh
kubectl run test-clear --image=curlimages/curl:8.12.1 --rm -it --restart=Never -n no-mesh \
-- curl -s -o /dev/null -w "HTTP %{http_code}\n" --max-time 5 http://api-server.production.svc.cluster.local:8080

Résultat attendu avec mTLS STRICT :

HTTP 000
pod "test-clear" deleted
pod no-mesh/test-clear terminated (Error)

Le code HTTP 000 et l’exit code curl 56 (connection reset by peer) confirment que le proxy côté serveur rejette tout trafic non-mTLS. La connexion TCP s’établit mais le handshake TLS échoue.

Fenêtre de terminal
# Les métriques Istio indiquent le type de connexion
istioctl dashboard prometheus

La métrique istio_tcp_connections_opened_total avec le label connection_security_policy="mutual_tls" confirme que les connexions sont chiffrées.

Activer le mTLS STRICT d’un coup sur un cluster existant risque de casser les services qui n’ont pas encore de proxy. La migration recommandée se fait en trois étapes :

  1. Vérifier l’injection des proxies : assurez-vous que tous les namespaces applicatifs ont l’injection sidecar activée et que tous les Pods ont redémarré avec leur proxy.

    Fenêtre de terminal
    # Vérifier que tous les Pods ont 2/2 conteneurs (app + proxy)
    kubectl get pods -n production -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.containerStatuses[*].name}{"\n"}{end}'
  2. Activer le mode PERMISSIVE (défaut) : ce mode accepte le trafic mTLS et en clair. Les services avec proxy communiquent déjà en mTLS entre eux, et les services sans proxy continuent de fonctionner.

    Fenêtre de terminal
    istioctl x describe pod <pod-name> -n production

    Vérifiez que les connexions entre Pods avec proxy utilisent bien mTLS.

  3. Passer en mode STRICT : une fois que tous les Pods ont un proxy, basculez en STRICT. Surveillez les erreurs de connexion dans les logs.

    Fenêtre de terminal
    kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: PeerAuthentication
    metadata:
    name: default
    namespace: production
    spec:
    mtls:
    mode: STRICT
    EOF
SymptômeCause probableSolution
connection reset by peer depuis un Pod sans proxyLe service cible est en mTLS STRICT et rejette le trafic en clairInjecter le sidecar Istio dans le Pod source, ou passer temporairement en PERMISSIVE
upstream connect error or disconnectLe proxy n’a pas encore reçu son certificatVérifier les logs d’istiod : kubectl logs -n istio-system deploy/istiod | grep -i error
AuthorizationPolicy refuse des requêtes légitimesLe principal SPIFFE dans la règle ne correspond pas au ServiceAccount réelVérifier l’identité : istioctl proxy-config secret <pod> et comparer avec le champ principals
Les métriques ne montrent pas mutual_tlsL’injection sidecar n’est pas active sur le namespaceVérifier le label : kubectl get ns <ns> --show-labels | grep istio
Latence élevée après activation du mTLSLe handshake TLS ajoute de la latence sur les premières requêtesNormal pour les connexions courtes. Les connexions persistantes (keep-alive) amortissent le coût. Envisager le mode ambient (ztunnel) pour une empreinte plus légère
  • Le trafic entre Pods est en clair par défaut dans Kubernetes — le mTLS est indispensable pour le chiffrer et authentifier.
  • Le mTLS vérifie l’identité des deux extrémités via des certificats X.509, contrairement au TLS classique qui ne vérifie que le serveur.
  • L’identité est basée sur le SPIFFE ID (spiffe://cluster.local/ns/<namespace>/sa/<service-account>), liée au ServiceAccount Kubernetes.
  • PeerAuthentication contrôle le mode mTLS (PERMISSIVE, STRICT, DISABLE). Le mode STRICT est requis en production.
  • AuthorizationPolicy avec des principals SPIFFE permet un contrôle d’accès granulaire basé sur l’identité cryptographique des services.
  • Le mTLS et les NetworkPolicies sont complémentaires : l’un chiffre et authentifie, l’autre filtre les flux réseau.
  • La rotation des certificats est automatique avec un service mesh — aucune intervention manuelle nécessaire.
  • Pour la CKS, maîtrisez la chaîne : injection proxy → PeerAuthentication STRICT → AuthorizationPolicy → principals SPIFFE.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn