Un Secret Kubernetes natif n’est, par défaut, protégé par aucun chiffrement dans etcd : son contenu y est stocké tel quel après un simple encodage base64, trivialement réversible. EncryptionConfiguration corrige ce point précis, à une condition rarement anticipée : l’activation ne chiffre que ce qui s’écrit après elle, jamais ce qui existait déjà.
Le fichier qui change ce qu’etcd stocke réellement
EncryptionConfiguration est un fichier fourni au serveur API au démarrage, qui définit comment chiffrer certains types de ressources (les Secrets en priorité) avant leur écriture dans etcd. Une fois actif, chaque nouvelle écriture d’un Secret passe par ce chiffrement avant d’atteindre etcd, invisible côté kubectl puisque le déchiffrement se fait transparemment à la lecture.
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: ["secrets"]
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
L’ordre des providers décide du comportement, pas seulement du chiffrement
La liste de providers n’est pas une liste d’options équivalentes : le premier provider chiffre toute nouvelle écriture, mais les providers suivants restent actifs pour la lecture, permettant de déchiffrer des données écrites sous un ancien provider pendant une transition. Terminer la liste par identity: {} (aucun chiffrement) est une pratique courante en développement, jamais en production : ce provider accepterait silencieusement une écriture en clair si le premier provider chiffré échouait pour une raison quelconque.
# aesgcm chiffre toute nouvelle écriture ;
# aescbc reste disponible en lecture seule pendant la transition
providers:
- aesgcm:
keys:
- name: key2
secret: <base64-encoded-32-byte-key>
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
Le piège qui compte le plus : rien n’est rétroactif
Activer EncryptionConfiguration sur un cluster qui contient déjà des Secrets ne chiffre aucun d’entre eux automatiquement : seules les écritures postérieures à l’activation passent par le nouveau chiffrement. Un Secret créé avant l’activation et jamais modifié depuis reste stocké en clair dans etcd indéfiniment, sans aucun avertissement, aucun log, aucun signal que ce Secret précis n’a jamais été rechiffré.
# Force le rechiffrement de tous les Secrets existants
# sous le nouveau provider, la seule façon de les couvrir
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
Cette commande relit puis réécrit chaque Secret, déclenchant le chiffrement pour ceux qui ne l’avaient jamais reçu. Sans cette étape explicite, exécutée une fois après toute activation ou changement de provider, une partie du cluster reste protégée en apparence seulement.
La rotation de clé suit la même logique
Faire tourner une clé de chiffrement (remplacer key1 par une nouvelle clé) suit exactement le même principe : la nouvelle clé chiffre les écritures futures, mais les Secrets chiffrés sous l’ancienne clé restent lisibles (l’ancienne clé doit rester dans la configuration, en position secondaire) jusqu’à ce qu’un rechiffrement explicite les fasse basculer. Retirer une ancienne clé de la configuration avant d’avoir rechiffré tout ce qui en dépendait rend ces Secrets définitivement illisibles.
À retenir
EncryptionConfiguration chiffre les Secrets au repos dans etcd, mais uniquement pour les écritures postérieures à son activation : aucun Secret existant n’est rechiffré automatiquement, un piège qui laisse une partie du cluster protégée seulement en apparence si l’étape de rechiffrement explicite est oubliée. L’ordre des providers détermine le comportement de lecture pendant une transition, jamais à terminer par identity: {} en production. La rotation de clé suit la même règle : l’ancienne clé reste nécessaire jusqu’au rechiffrement complet, un des détails de sécurité qui comptent dès qu’une migration Kubernetes manipule des Secrets sensibles.