Un Secret Kubernetes mal protégé suffit à exposer une base de données de production ou un jeton d’API en clair. Ce n’est pas une hypothèse: en 2026, la majorité des incidents liés aux conteneurs impliquent encore des identifiants stockés ou transmis sans chiffrement correct. Ce tutoriel vous montre comment créer, chiffrer, distribuer et faire tourner vos secrets Kubernetes selon des pratiques réellement utilisées en production, avec des exemples testables sur un cluster local.
Vous allez construire un projet complet: un cluster de test, un Secret chiffré au repos, une intégration avec un coffre-fort externe, une politique RBAC restrictive et un pipeline CI/CD qui ne stocke jamais de clé en clair. Comptez environ 90 minutes pour suivre l’ensemble des 12 étapes, prérequis compris.
Pourquoi la gestion des secrets Kubernetes est devenue critique en 2026
Kubernetes a normalisé le déploiement de milliers de microservices, et chacun d’eux a besoin de clés d’API, de mots de passe de base de données ou de certificats TLS pour fonctionner. Le problème, c’est que l’objet natif Secret ne chiffre rien par défaut: il encode simplement les valeurs en base64, un format lisible en une commande. Beaucoup d’équipes le découvrent trop tard, souvent après un audit de sécurité ou pire, après un incident.
La pression réglementaire n’aide pas à relâcher la vigilance. Le RGPD impose une protection “appropriée” des données à caractère personnel, et les autorités européennes considèrent qu’un secret en clair dans etcd ou dans un dépôt Git ne répond pas à ce critère. Ajoutez à cela la directive NIS2, qui étend les obligations de cybersécurité à des dizaines de milliers d’entités en France, et la gestion des secrets cesse d’être un détail d’ingénierie pour devenir un sujet de conformité.
Le scénario type ressemble à ceci: une équipe déploie rapidement un microservice avec un mot de passe de base de données codé en dur dans un manifeste, “juste pour tester”. Le manifeste finit versionné dans Git, le cluster grossit, d’autres équipes récupèrent le namespace en exemple pour leurs propres projets, et le mot de passe initial circule pendant des mois sans que personne ne le fasse tourner. Ce n’est ni de la négligence individuelle ni un manque de compétence technique: c’est l’absence d’un processus reproductible. C’est précisément ce que ce tutoriel corrige, étape par étape.
Ce guide part du principe que vous connaissez les bases de Kubernetes (pods, déploiements, namespaces) mais pas forcément les mécanismes de chiffrement et de rotation des secrets. Nous couvrons trois familles d’outils complémentaires: le chiffrement natif au repos, les opérateurs de synchronisation externe comme External Secrets Operator, et le chiffrement Git-friendly avec Sealed Secrets. Vous repartirez avec une configuration fonctionnelle, pas seulement une liste de recommandations théoriques.
Comprendre l’objet Secret Kubernetes : ce qu’il protège vraiment
Un objet Secret stocke des paires clé-valeur encodées en base64 dans etcd, la base de données du plan de contrôle. Contrairement à une idée reçue, base64 n’est pas un chiffrement: c’est un encodage réversible en une seule commande base64 -d. Toute personne ayant un accès en lecture à l’API Kubernetes ou à etcd peut lire un secret en clair, sauf si vous activez explicitement le chiffrement au repos.
Kubernetes distingue plusieurs types de secrets prédéfinis: Opaque pour des données génériques, kubernetes.io/dockerconfigjson pour les identifiants de registre de conteneurs, kubernetes.io/tls pour les paires certificat/clé privée, et kubernetes.io/basic-auth pour les identifiants HTTP. Chaque type impose une structure de champs différente, ce qui évite certaines erreurs de configuration mais ne remplace pas un vrai chiffrement.
Trois failles reviennent systématiquement dans les audits de clusters: l’absence de chiffrement au repos dans etcd, des permissions RBAC qui autorisent n’importe quel pod du namespace à lire n’importe quel secret, et des secrets copiés en clair dans les journaux applicatifs ou les captures d’écran de support technique. Les étapes suivantes traitent chacun de ces trois points, dans cet ordre de priorité.
Un détail technique mérite d’être signalé: depuis les versions récentes de Kubernetes, il est possible de marquer un Secret comme immutable: true. Une fois ce champ défini, toute tentative de modification du contenu échoue, ce qui force la création d’un nouvel objet plutôt qu’une mise à jour silencieuse. Ce comportement protège contre les modifications accidentelles et réduit la charge du plan de contrôle, puisque kubelet n’a plus besoin de surveiller les changements sur ce secret précis. C’est une bonne pratique pour les secrets qui changent rarement, comme les certificats racine, mais elle ne convient évidemment pas aux secrets destinés à tourner régulièrement.
Prérequis : outils, versions et accès nécessaires
Avant de commencer, installez et vérifiez les éléments suivants. Les commandes de vérification sont données pour chaque outil afin d’éviter les mauvaises surprises en cours de route.
- Un cluster Kubernetes en version 1.36.x (dernière branche stable, publiée le 22 juillet 2026) ou une version antérieure encore maintenue. Pour ce tutoriel, un cluster local via
kindouminikubesuffit amplement. - kubectl, dans une version compatible avec votre cluster (idéalement la dernière version stable disponible sur votre distribution).
- Helm, dernière version stable, pour installer External Secrets Operator et Sealed Secrets via des charts officiels.
- Docker ou un runtime de conteneurs compatible, nécessaire pour
kind. - Un accès administrateur ou
cluster-adminsur le cluster de test, pour créer des CRD et des ClusterRoleBinding. - kubeseal, le client CLI de Sealed Secrets, dernière version stable correspondant au contrôleur installé.
- Un compte HashiCorp Vault (auto-hébergé ou HCP Vault) si vous suivez la section sur l’intégration Vault, sinon cette étape reste facultative.
- Un dépôt Git pour versionner vos manifestes chiffrés (GitHub, GitLab ou Gitea, peu importe).
- 15 à 20 Go d’espace disque libre et 4 Go de RAM disponibles pour faire tourner le cluster local sans ralentissement.
Vérifiez rapidement votre environnement avec ces trois commandes avant de continuer :
kubectl version --client
helm version
kind version
Si l’une de ces commandes échoue, corrigez l’installation avant de poursuivre: les étapes suivantes supposent un environnement fonctionnel.
Étape 1 : préparer un cluster de test avec kind
Travailler sur un cluster de test isolé évite de casser un environnement de production pendant l’apprentissage. Créez un cluster nommé secrets-lab avec la configuration suivante, qui active un seul nœud de plan de contrôle et un nœud de travail :
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: secrets-lab
nodes:
- role: control-plane
- role: worker
kind create cluster --config kind-config.yaml
kubectl cluster-info --context kind-secrets-lab
kubectl create namespace demo-secrets
Le résultat attendu affiche l’URL du plan de contrôle et confirme que le namespace demo-secrets a été créé. Toutes les commandes des étapes suivantes s’exécutent dans ce namespace, sauf mention contraire.
Étape 2 : créer et monter votre premier Secret
Créez un Secret contenant des identifiants de base de données fictifs, directement depuis la ligne de commande plutôt que via un fichier YAML en clair versionné :
kubectl create secret generic db-credentials \
--namespace demo-secrets \
--from-literal=username=app_user \
--from-literal=password='Ch4ng3-M01-M4!nt3n4nt'
Montez ensuite ce Secret dans un pod, en privilégiant le montage en volume plutôt qu’en variable d’environnement. Les variables d’environnement apparaissent souvent dans les journaux de crash ou les outils d’introspection, alors qu’un volume monté en lecture seule limite l’exposition :
apiVersion: v1
kind: Pod
metadata:
name: demo-app
namespace: demo-secrets
spec:
containers:
- name: app
image: nginx:stable
volumeMounts:
- name: db-creds
mountPath: /etc/secrets/db
readOnly: true
volumes:
- name: db-creds
secret:
secretName: db-credentials
defaultMode: 0400
Le paramètre defaultMode: 0400 restreint la lecture du fichier monté au seul propriétaire du processus, ce qui évite qu’un autre processus dans le même conteneur y accède par erreur. Vérifiez le montage avec kubectl exec demo-app -n demo-secrets -- ls -l /etc/secrets/db.
Étape 3 : activer le chiffrement au repos dans etcd
Par défaut, les secrets sont stockés en clair (base64, pas chiffrés) dans etcd. La documentation officielle de Kubernetes sur le chiffrement des données décrit la procédure à suivre côté kube-apiserver. Créez un fichier de configuration de chiffrement sur chaque nœud du plan de contrôle :
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <CLE_BASE64_32_OCTETS>
- identity: {}
Générez une clé de 32 octets encodée en base64 avec head -c 32 /dev/urandom | base64, puis référencez ce fichier via l’option --encryption-provider-config du kube-apiserver. Sur un cluster managé (EKS, AKS, GKE), cette étape est généralement gérée par le fournisseur via une intégration KMS native: vérifiez la documentation de votre fournisseur plutôt que de tenter une configuration manuelle sur le plan de contrôle.
Une fois le chiffrement actif, les nouveaux secrets sont chiffrés automatiquement, mais les secrets existants ne le sont pas rétroactivement. Forcez leur réécriture avec la commande suivante :
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
Étape 4 : restreindre l’accès avec RBAC
Le chiffrement au repos ne sert à rien si n’importe quel compte de service du namespace peut lire les secrets via l’API. La documentation RBAC de Kubernetes recommande le principe du moindre privilège: un rôle ne doit accorder l’accès qu’aux secrets nommément listés, jamais à l’ensemble de la ressource.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: demo-secrets
name: db-credentials-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: db-credentials-reader-binding
namespace: demo-secrets
subjects:
- kind: ServiceAccount
name: demo-app-sa
namespace: demo-secrets
roleRef:
kind: Role
name: db-credentials-reader
apiGroup: rbac.authorization.k8s.io
Ce rôle autorise uniquement le compte de service demo-app-sa à lire le secret db-credentials, et rien d’autre. Testez la restriction avec kubectl auth can-i get secrets/other-secret --as=system:serviceaccount:demo-secrets:demo-app-sa -n demo-secrets, qui doit répondre no.
Étape 5 : synchroniser des secrets externes avec External Secrets Operator
Stocker des secrets directement dans Kubernetes pose un problème de source de vérité: si vous gérez déjà vos identifiants dans AWS Secrets Manager, Azure Key Vault ou GCP Secret Manager, dupliquer cette information dans etcd crée un risque de désynchronisation. External Secrets Operator (ESO) résout ce problème en synchronisant automatiquement les secrets depuis un fournisseur externe vers un objet Secret natif.
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets-system \
--create-namespace
Déclarez ensuite un SecretStore pointant vers votre fournisseur, puis un ExternalSecret qui référence la clé distante à synchroniser :
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: db-credentials-external
namespace: demo-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: prod/db/app_user
Le champ refreshInterval déclenche une resynchronisation automatique toutes les heures. Si le secret est modifié dans AWS Secrets Manager, le Secret Kubernetes correspondant se met à jour sans intervention manuelle ni redéploiement.
Étape 6 : intégrer HashiCorp Vault avec l’authentification Kubernetes
Pour les organisations qui centralisent déjà leur gestion des secrets dans HashiCorp Vault, l’authentification Kubernetes native évite de stocker un jeton Vault statique dans un pod. Le mécanisme repose sur le jeton du compte de service du pod, validé directement par Vault via l’API TokenReview de Kubernetes.
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_SERVICE_HOST:443"
vault write auth/kubernetes/role/demo-app \
bound_service_account_names=demo-app-sa \
bound_service_account_namespaces=demo-secrets \
policies=demo-app-read \
ttl=1h
Installez ensuite l’injecteur d’agent Vault via Helm, qui ajoute automatiquement un conteneur latéral à vos pods pour récupérer les secrets au démarrage :
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault \
--set "injector.enabled=true" \
--namespace vault-system --create-namespace
Ajoutez ensuite ces annotations à la spécification de votre pod pour activer l’injection automatique :
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "demo-app"
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/demo-app/db"
Le jeton Vault obtenu a une durée de vie limitée (ttl=1h dans l’exemple), ce qui réduit considérablement la fenêtre d’exploitation en cas de fuite comparé à un jeton statique valable indéfiniment.
Étape 7 : versionner vos secrets dans Git avec Sealed Secrets
Beaucoup d’équipes veulent versionner leurs manifestes Kubernetes dans Git, y compris les secrets, sans pour autant exposer les valeurs en clair dans l’historique du dépôt. Sealed Secrets, développé à l’origine par Bitnami, chiffre un Secret avec une clé publique asymétrique: seul le contrôleur installé dans le cluster cible peut le déchiffrer avec sa clé privée.
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets-controller sealed-secrets/sealed-secrets \
--namespace kube-system
kubectl create secret generic api-key \
--namespace demo-secrets \
--from-literal=key='sk_live_exemple' \
--dry-run=client -o yaml | \
kubeseal --controller-namespace kube-system \
--format yaml > sealed-api-key.yaml
Le fichier sealed-api-key.yaml peut être commité dans Git en toute sécurité: sans la clé privée du contrôleur, son contenu chiffré est inexploitable. Appliquez-le normalement avec kubectl apply -f sealed-api-key.yaml, et le contrôleur génère automatiquement le Secret déchiffré correspondant dans le cluster.
Intégrer les secrets scellés dans un flux GitOps avec ArgoCD
Sealed Secrets prend tout son sens combiné à un outil GitOps comme ArgoCD ou Flux, où Git devient la seule source de vérité pour l’état désiré du cluster. Dans ce modèle, un commit sur la branche principale déclenche automatiquement une synchronisation vers le cluster, secrets scellés compris, sans qu’un humain n’exécute kubectl apply manuellement.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-secrets-app
namespace: argocd
spec:
project: default
source:
repoURL: https://git.exemple.fr/plateforme/demo-secrets.git
targetRevision: main
path: manifests/demo-secrets
destination:
server: https://kubernetes.default.svc
namespace: demo-secrets
syncPolicy:
automated:
prune: true
selfHeal: true
Le paramètre selfHeal: true mérite une attention particulière avec des secrets: si quelqu’un modifie une valeur directement dans le cluster via kubectl edit secret, ArgoCD écrase ce changement au prochain cycle de synchronisation pour revenir à l’état défini dans Git. C’est un comportement voulu, mais qui surprend souvent les équipes qui découvrent GitOps: toute modification “à chaud” doit passer par un nouveau commit, jamais par une commande directe sur le cluster.
Pour les secrets synchronisés via External Secrets Operator plutôt que scellés manuellement, excluez la ressource ExternalSecret générée du contrôle strict de dérive d’ArgoCD, car sa valeur change en fonction du fournisseur externe et non d’un commit Git. La documentation d’ArgoCD propose un champ ignoreDifferences à cet effet, à appliquer sur le champ data du Secret cible.
Étape 8 : automatiser la rotation et l’expiration des secrets
Un secret qui ne tourne jamais est un secret dont la compromission passe inaperçue indéfiniment. Vault propose des secrets dynamiques pour les bases de données: au lieu d’un mot de passe statique, chaque application reçoit des identifiants générés à la demande, avec une durée de vie courte.
vault secrets enable database
vault write database/config/app-db \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db-host:5432/app" \
allowed_roles="app-role"
vault write database/roles/app-role \
db_name=app-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl="1h" \
max_ttl="24h"
Pour les secrets statiques que vous ne pouvez pas rendre dynamiques (clés d’API tierces, par exemple), planifiez une rotation régulière via un CronJob qui appelle l’API du fournisseur, génère une nouvelle valeur, puis met à jour le Secret Kubernetes correspondant. Documentez systématiquement la fréquence de rotation attendue pour chaque secret, faute de quoi elle finit toujours par être repoussée indéfiniment.
Étape 9 : auditer, journaliser et surveiller les accès
Activez l’audit logging de l’API Kubernetes pour tracer chaque lecture d’un objet Secret. Sans cette journalisation, il est impossible de déterminer a posteriori qui a consulté un secret compromis, ni depuis combien de temps l’accès était possible.
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
Référencez ce fichier via --audit-policy-file sur le kube-apiserver, puis redirigez les journaux vers votre pile SIEM (Wazuh, Elastic ou équivalent) pour déclencher des alertes sur les accès anormaux, par exemple une lecture massive de secrets en dehors des heures ouvrées ou depuis un compte de service inhabituel.
Étape 10 : intégrer la gestion des secrets dans le pipeline CI/CD
La majorité des fuites de secrets ne viennent pas du cluster lui-même mais du pipeline qui le déploie: un jeton stocké en variable d’environnement CI en clair, un fichier .env commité par erreur, ou un log de build qui affiche une commande complète. Utilisez le stockage de secrets natif de votre plateforme CI plutôt que des fichiers versionnés :
# .github/workflows/deploy.yml (extrait)
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Authentification Vault
uses: hashicorp/vault-action@v3
with:
url: ${{ secrets.VAULT_ADDR }}
method: jwt
role: github-actions-deploy
secrets: |
secret/data/demo-app/db password | DB_PASSWORD
- name: Déploiement
run: |
kubectl create secret generic db-credentials \
--from-literal=password="$DB_PASSWORD" \
--dry-run=client -o yaml | kubectl apply -f -
Ce modèle évite qu’un mot de passe transite par un fichier YAML en clair à un quelconque moment du pipeline: il est récupéré depuis Vault via authentification JWT à l’exécution, puis injecté directement dans la commande kubectl.
Étape 11 : valider et tester votre configuration complète
Avant de considérer votre configuration comme prête pour la production, exécutez cette checklist de validation :
# Vérifier que le chiffrement au repos est actif
kubectl get secrets --all-namespaces -o json | \
jq '.items[].metadata.annotations."encryption.k8s.io/status"' 2>/dev/null
# Vérifier qu'aucun compte de service n'a un accès trop large
kubectl auth can-i get secrets --all-namespaces \
--as=system:serviceaccount:demo-secrets:default
# Vérifier que la synchronisation ESO fonctionne
kubectl get externalsecret -n demo-secrets
# Vérifier que le contrôleur Sealed Secrets répond
kubectl get pods -n kube-system -l name=sealed-secrets-controller
Si chacune de ces commandes renvoie un résultat cohérent (chiffrement actif, accès refusé pour le compte par défaut, secret externe synchronisé, contrôleur en cours d’exécution), votre projet de gestion des secrets est fonctionnel de bout en bout.
Étape 12 : documenter et industrialiser la solution
Une configuration de sécurité qui n’est documentée nulle part se dégrade avec le temps, au fil des changements d’équipe. Consignez, dans un fichier SECRETS.md versionné avec vos manifestes, la liste des secrets utilisés, leur source de vérité (Vault, AWS Secrets Manager, Sealed Secrets), leur fréquence de rotation prévue et la procédure de révocation d’urgence.
Industrialisez ensuite la vérification via un outil de scan comme gitleaks ou trufflehog intégré au pipeline CI, qui bloque automatiquement tout commit contenant un motif ressemblant à une clé d’API ou un mot de passe en clair, avant même que le code n’atteigne la branche principale.
Projet complet : assembler tous les éléments
Une fois les 12 étapes suivies, votre dépôt Git devrait ressembler à l’arborescence ci-dessous. Elle sépare clairement ce qui peut être commité en clair (les manifestes de configuration) de ce qui doit toujours rester chiffré (les secrets scellés) ou externalisé (les références à Vault et au SecretStore) :
demo-secrets/
├── cluster/
│ └── kind-config.yaml
├── manifests/
│ ├── demo-secrets/
│ │ ├── namespace.yaml
│ │ ├── rbac-role.yaml
│ │ ├── rbac-rolebinding.yaml
│ │ ├── pod-demo-app.yaml
│ │ ├── sealed-api-key.yaml
│ │ └── external-secret-db.yaml
│ └── argocd/
│ └── application.yaml
├── vault/
│ ├── kubernetes-auth-role.hcl
│ └── database-role.hcl
├── ci/
│ └── .github/workflows/deploy.yml
├── audit/
│ └── audit-policy.yaml
└── SECRETS.md
Chaque dossier correspond à une des étapes de ce tutoriel: cluster/ pour la configuration kind de l’étape 1, manifests/demo-secrets/ pour les objets applicatifs des étapes 2 à 7, vault/ pour les définitions de rôles et de rotation dynamique de l’étape 8, ci/ pour le pipeline de l’étape 10, et audit/ pour la politique de journalisation de l’étape 9. Le fichier SECRETS.md à la racine centralise la documentation demandée à l’étape 12.
Pour déployer l’ensemble du projet depuis zéro sur un nouveau cluster, l’ordre d’application compte. Respectez cette séquence pour éviter les erreurs de dépendance manquante :
kind create cluster --config cluster/kind-config.yaml
kubectl apply -f manifests/demo-secrets/namespace.yaml
helm install sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system
helm install external-secrets external-secrets/external-secrets -n external-secrets-system --create-namespace
kubectl apply -f manifests/demo-secrets/rbac-role.yaml
kubectl apply -f manifests/demo-secrets/rbac-rolebinding.yaml
kubectl apply -f manifests/demo-secrets/sealed-api-key.yaml
kubectl apply -f manifests/demo-secrets/external-secret-db.yaml
kubectl apply -f manifests/demo-secrets/pod-demo-app.yaml
Le namespace et les contrôleurs (Sealed Secrets, External Secrets Operator) doivent exister avant que Kubernetes puisse résoudre les objets qui en dépendent. Une fois cette séquence appliquée avec succès, vous disposez d’un projet reproductible que n’importe quel membre de l’équipe peut recréer à l’identique sur un cluster vierge, sans dépendre de connaissances tacites ni d’étapes manuelles non documentées.
Ce que les auditeurs RGPD et NIS2 vérifient réellement
Si votre organisation est soumise à un audit de conformité, sachez que les contrôleurs ne se contentent presque jamais d’une réponse déclarative du type “nos secrets sont chiffrés”. Ils demandent généralement une preuve technique: l’extrait de configuration EncryptionConfiguration du plan de contrôle, la liste des rôles RBAC ayant accès à la ressource secrets, et un échantillon des journaux d’audit sur les trois à six derniers mois montrant qui a consulté quoi.
Un point sur lequel butent souvent les équipes: la traçabilité de la rotation. Un auditeur NIS2 peut demander la date de dernière rotation de chaque secret sensible et la justification de la fréquence choisie. Sans documentation centralisée comme le fichier SECRETS.md évoqué à l’étape 12, cette information n’existe généralement nulle part de façon exploitable, ce qui transforme une simple question d’audit en plusieurs jours de recherche manuelle dans l’historique Git et les journaux Vault.
La bonne nouvelle: la configuration mise en place dans ce tutoriel répond directement à ces exigences. Le chiffrement au repos (étape 3), le RBAC restrictif (étape 4), l’audit logging (étape 9) et la documentation de rotation (étape 12) constituent, ensemble, l’essentiel des preuves techniques demandées lors d’un contrôle. Conserver ces artefacts à jour en continu coûte beaucoup moins cher que de les reconstituer en urgence la veille d’un audit.
Tableau comparatif des solutions de gestion des secrets Kubernetes
| Solution | Stockage de la vérité | Rotation automatique | Complexité de mise en œuvre | Cas d’usage idéal |
|---|---|---|---|---|
| Secret natif + chiffrement etcd | etcd (chiffré) | Non | Faible | Petits clusters, budget limité |
| Sealed Secrets | Git (chiffré) + cluster | Non | Faible à moyenne | Équipes GitOps, secrets peu nombreux |
| External Secrets Operator | Fournisseur cloud externe | Oui (synchronisation planifiée) | Moyenne | Multi-cloud, secrets déjà centralisés |
| HashiCorp Vault | Vault (chiffré, dynamique) | Oui (secrets dynamiques) | Élevée | Production critique, conformité stricte |
| KMS natif (AWS/Azure/GCP) | Service cloud managé | Selon configuration | Moyenne | Équipes déjà mono-cloud |
Pièges courants à éviter
Ces erreurs reviennent le plus souvent dans les configurations que nous observons sur le terrain :
- Confondre base64 et chiffrement. Un Secret non chiffré au repos reste lisible par quiconque a accès à etcd, même sans droit RBAC sur l’API.
- Monter les secrets en variable d’environnement plutôt qu’en volume. Les variables d’environnement apparaissent dans
kubectl describe pod, dans les dumps de crash et dans certains outils d’observabilité. - Accorder un accès RBAC sur toute la ressource
secretsplutôt que sur desresourceNamesprécis, ce qui donne à un pod compromis l’accès à tous les secrets du namespace. - Committer un Secret non scellé dans Git “temporairement” en pensant le retirer plus tard. L’historique Git conserve la valeur même après suppression du fichier.
- Oublier de faire tourner la clé de chiffrement etcd elle-même. Une clé de chiffrement statique depuis des années perd une grande partie de son intérêt en cas de compromission du plan de contrôle.
- Ne pas limiter le TTL des jetons Vault ou des identifiants dynamiques, ce qui annule l’un des principaux avantages des secrets dynamiques.
- Négliger la sauvegarde de la clé privée Sealed Secrets. Sans elle, aucun secret scellé n’est récupérable en cas de perte du cluster.
Dépannage : problèmes fréquents et solutions
Voici les incidents les plus courants rencontrés lors de la mise en place de cette architecture, avec leur cause probable et la correction associée.
| Symptôme | Cause probable | Solution |
|---|---|---|
Le pod reste en CreateContainerConfigError | Le Secret référencé dans le volume n’existe pas dans le namespace | Vérifier avec kubectl get secret -n <namespace> et corriger le nom référencé |
kubeseal renvoie une erreur de certificat | Le contrôleur Sealed Secrets n’est pas joignable ou le namespace du contrôleur est mal spécifié | Vérifier --controller-namespace et l’état du pod contrôleur |
L’ExternalSecret reste en statut SecretSyncedError | Les identifiants du SecretStore n’ont pas les permissions IAM nécessaires | Vérifier la policy IAM associée au rôle utilisé par ESO |
| L’agent Vault ne s’injecte pas dans le pod | Les annotations sont mal placées ou le webhook mutant n’est pas actif | Vérifier kubectl get mutatingwebhookconfigurations et les annotations du pod |
| Le chiffrement etcd semble actif mais les anciens secrets restent en clair | La réécriture rétroactive n’a pas été effectuée après activation | Relancer kubectl get secrets --all-namespaces -o json | kubectl replace -f - |
| RBAC refuse l’accès même au bon compte de service | Le RoleBinding référence le mauvais namespace ou nom de compte | Comparer précisément les champs subjects avec le compte réel utilisé par le pod |
| La rotation Vault casse la connexion applicative | L’application ne relit pas la nouvelle valeur, elle garde l’ancienne en mémoire | Implémenter un rechargement à chaud ou redémarrer le pod après rotation |
| Les journaux d’audit ne contiennent aucun événement lié aux secrets | La politique d’audit ne cible pas la ressource secrets ou le flag --audit-policy-file est absent | Vérifier la configuration du kube-apiserver et la présence du fichier de politique |
Astuces avancées pour les clusters de production
Une fois les bases en place, plusieurs raffinements réduisent encore la surface d’exposition. Activez des NetworkPolicy qui limitent les connexions sortantes des pods manipulant des secrets sensibles, afin qu’un processus compromis ne puisse pas exfiltrer une valeur vers un serveur externe même s’il parvient à la lire.
Pour les environnements multi-tenants, isolez les secrets par namespace avec des ResourceQuota dédiées et évitez tout ClusterRole donnant un accès transversal aux secrets sur l’ensemble du cluster: même les comptes d’administration devraient passer par une élévation temporaire plutôt que par un accès permanent.
Enfin, envisagez la signature des images de conteneurs avec cosign pour garantir qu’un attaquant ne peut pas substituer une image compromise qui exfiltrerait vos secrets montés au démarrage. Combinée à une politique d’admission comme Kyverno ou Gatekeeper, cette vérification bloque le déploiement de toute image non signée avant même qu’elle n’accède aux volumes de secrets.
Pensez aussi au chiffrement en transit, souvent négligé une fois le chiffrement au repos en place. Un secret bien protégé dans etcd peut encore transiter en clair entre pods si aucune politique de maillage de service n’impose le mTLS. Des outils comme Istio ou Linkerd chiffrent automatiquement le trafic interne au cluster, ce qui ferme une fenêtre d’exposition que ni RBAC ni le chiffrement au repos ne couvrent: celle du réseau interne entre le moment où un pod récupère un secret et le moment où il l’utilise pour s’authentifier auprès d’un autre service.
Dernier point souvent oublié: testez régulièrement votre procédure de reprise après sinistre pour les secrets eux-mêmes. Si la clé privée de votre contrôleur Sealed Secrets ou le stockage backend de Vault disparaît sans sauvegarde, vous perdez l’accès à l’ensemble des secrets qui en dépendent, même si le reste du cluster redémarre normalement. Planifiez une sauvegarde chiffrée de ces clés maîtresses, stockée séparément du cluster lui-même, et testez sa restauration au moins une fois par trimestre.
Foire aux questions
Un Secret Kubernetes est-il chiffré par défaut ?
Non. Par défaut, un Secret est encodé en base64, ce qui est réversible en une commande. Le chiffrement au repos dans etcd doit être activé explicitement via une EncryptionConfiguration sur le plan de contrôle, comme détaillé à l’étape 3.
Faut-il choisir Vault ou External Secrets Operator ?
Les deux ne s’opposent pas forcément: ESO peut synchroniser des secrets depuis Vault vers Kubernetes. Si vous partez de zéro et gérez déjà des secrets dans un service cloud managé, ESO seul suffit souvent. Si vous avez besoin de secrets dynamiques à courte durée de vie, Vault apporte une couche supplémentaire que ESO seul ne fournit pas.
Sealed Secrets est-il suffisant pour la production ?
Il convient bien aux équipes GitOps avec un nombre limité de secrets, mais il ne propose pas de rotation automatique ni de secrets dynamiques. Pour des environnements réglementés ou à fort volume de secrets, associez-le à Vault ou à un KMS cloud plutôt que de vous en contenter seul.
Comment savoir si mes secrets existants sont déjà chiffrés au repos ?
Inspectez directement etcd (si vous y avez accès) ou vérifiez la configuration du kube-apiserver pour la présence du flag --encryption-provider-config. Sur un cluster managé, consultez la documentation spécifique du fournisseur, car le chiffrement peut être activé par défaut sans configuration visible côté utilisateur.
Quelle est la fréquence de rotation recommandée pour un secret statique ?
Il n’existe pas de règle universelle, mais une rotation trimestrielle constitue un point de départ raisonnable pour la plupart des identifiants applicatifs, avec une rotation immédiate obligatoire en cas de suspicion de compromission ou de départ d’un membre de l’équipe ayant eu accès à la valeur.
Les NetworkPolicy remplacent-elles le RBAC pour protéger les secrets ?
Non, elles sont complémentaires. RBAC contrôle qui peut lire un objet Secret via l’API Kubernetes, tandis que les NetworkPolicy contrôlent les flux réseau une fois qu’un pod a déjà accès à une valeur. Les deux couches doivent être configurées ensemble pour une protection cohérente.
Peut-on utiliser plusieurs solutions de gestion des secrets sur le même cluster ?
Oui, et c’est même courant: Sealed Secrets pour la configuration statique versionnée avec l’application, External Secrets Operator pour synchroniser des identifiants cloud, et Vault pour les secrets dynamiques à courte durée de vie sur les composants les plus sensibles.
Que faire immédiatement si un secret a fuité ?
Révoquez ou faites tourner la valeur exposée en priorité absolue, avant même d’analyser l’étendue de la fuite: chaque minute compte. Ensuite seulement, consultez les journaux d’audit pour déterminer quels comptes ont accédé au secret et depuis quand, afin d’évaluer si d’autres ressources ont pu être compromises via ce même accès.
Kind ou minikube, lequel choisir pour ce tutoriel ?
Les deux fonctionnent pour suivre ce guide. kind démarre généralement plus vite et convient bien à l’intégration continue, tandis que minikube propose davantage d’add-ons prêts à l’emploi (tableau de bord, ingress) utiles si vous voulez explorer au-delà des commandes présentées ici.
Vous disposez maintenant d’une architecture complète: chiffrement au repos, RBAC restrictif, synchronisation externe, versionnement Git sécurisé, rotation automatisée et audit des accès. La plupart des équipes n’implémentent qu’un ou deux de ces éléments à la fois; les combiner change réellement la donne en cas d’incident, puisqu’un attaquant doit alors franchir plusieurs couches indépendantes plutôt qu’une seule.
Related Coverage
- Durcir Kubernetes : 5 CVE Containerd en 12 Étapes [2026]
- DMA Cloud : l’UE Vise AWS et Azure d’Ici Novembre [2026]
- Cloudflare WAF vs AWS WAF vs ModSecurity : 0 $ vs 200 $/mois [2026]
- Keycloak dans Node.js : OAuth 2.0 et OpenID Connect en 12 Étapes [2026]
- Wazuh vs Splunk vs Elastic : SIEM Gratuit ou 1,5 M$/an ? [2026]
- Plus d’articles sur la cybersécurité




