Kubernetes propose un objet natif Secret pour stocker les données sensibles.
Par défaut, cet objet est encodé en base64 (pas chiffré), stocké en clair
dans etcd, et ne fournit ni rotation ni audit sans configuration explicite.
Cela ne signifie pas que les Secrets natifs sont inutilisables. Avec les bonnes mesures (chiffrement etcd, RBAC strict, audit logs), ils deviennent acceptables pour certains cas. Pour des exigences plus fortes, External Secrets Operator et le Secrets Store CSI Driver apportent chacun des garanties supplémentaires — avec leurs propres compromis.
Ce guide couvre quatre dimensions : le stockage, la distribution, la consommation et la rotation des secrets.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Les limites des Kubernetes Secrets natifs — et comment les atténuer
- External Secrets Operator : synchronisation depuis un coffre-fort externe
- Secrets Store CSI Driver : montage direct depuis un provider externe
- Comment choisir selon le mode de consommation de votre application
- Les risques liés aux nœuds et pods privilégiés
Prérequis
Section intitulée « Prérequis »Les limites des Kubernetes Secrets natifs
Section intitulée « Les limites des Kubernetes Secrets natifs »Ce qu’est un Secret Kubernetes
Section intitulée « Ce qu’est un Secret Kubernetes »apiVersion: v1kind: Secretmetadata: name: db-credentialstype: Opaquedata: username: YWRtaW4= # "admin" en base64 password: cGFzc3dvcmQ= # "password" en base64Ce que c’est : un objet qui stocke des données sensibles, accessible par les pods via des volumes ou des variables d’environnement.
Ce que ce n’est pas : un coffre-fort sécurisé.
Limite 1 : base64 n’est pas du chiffrement
Section intitulée « Limite 1 : base64 n’est pas du chiffrement »# Le "secret" est trivial à décoderecho "cGFzc3dvcmQ=" | base64 -d# passwordTout utilisateur avec kubectl get secret peut lire le secret en clair.
Limite 2 : stockage en clair dans etcd (par défaut)
Section intitulée « Limite 2 : stockage en clair dans etcd (par défaut) »Par défaut, les secrets sont stockés en clair dans etcd (la base de données de Kubernetes). Un accès à etcd = accès à tous les secrets.
Le chiffrement at-rest peut être activé (voir la section suivante), mais il n’est pas actif par défaut.
Limite 3 : RBAC à configurer manuellement
Section intitulée « Limite 3 : RBAC à configurer manuellement »Kubernetes n’a pas de RBAC strict par défaut sur les secrets. Il faut créer des rôles explicites :
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: secret-readerrules: - apiGroups: [""] resources: ["secrets"] resourceNames: ["db-credentials"] # Secret spécifique verbs: ["get"]Limite 4 : pas de rotation métier native
Section intitulée « Limite 4 : pas de rotation métier native »Kubernetes ne fournit pas nativement de génération ni de rotation métier des credentials (contrairement à Vault ou un secret manager cloud). Il n’y a pas de mécanisme intégré d’expiration ou de versioning.
En revanche, quand un Secret monté en volume est mis à jour, Kubernetes
propage la nouvelle valeur dans le pod de façon “eventually consistent”.
Cette propagation ne fonctionne pas avec subPath.
Limite 5 : audit à activer explicitement
Section intitulée « Limite 5 : audit à activer explicitement »Kubernetes fournit un mécanisme d’audit via les audit logs, mais il n’est pas activé par défaut. Il faut le configurer explicitement pour tracer qui a lu quel secret et quand.
Sécuriser les Secrets natifs
Section intitulée « Sécuriser les Secrets natifs »Avec les mesures ci-dessous, les Secrets natifs deviennent acceptables pour des secrets à faible rotation dans un cluster bien géré.
Chiffrement at-rest dans etcd
Section intitulée « Chiffrement at-rest dans etcd »Activez le chiffrement des secrets dans etcd :
apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: <base64-encoded-32-byte-key> - identity: {} # Fallback pour les secrets non chiffrés# Configurer kube-apiserver--encryption-provider-config=/etc/kubernetes/encryption-config.yamlRBAC strict
Section intitulée « RBAC strict »Principe : aucun accès par défaut aux secrets.
# Rôle pour un namespace spécifiqueapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: app-secrets-reader namespace: productionrules: - apiGroups: [""] resources: ["secrets"] resourceNames: ["app-db-credentials", "app-api-key"] verbs: ["get"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: app-secrets-reader-binding namespace: productionsubjects: - kind: ServiceAccount name: my-app namespace: productionroleRef: kind: Role name: app-secrets-reader apiGroup: rbac.authorization.k8s.ioAudit logs
Section intitulée « Audit logs »Activez les audit logs pour tracer les accès aux secrets :
apiVersion: audit.k8s.io/v1kind: Policyrules: - level: Metadata resources: - group: "" resources: ["secrets"]Utiliser des volumes plutôt que des variables d’environnement
Section intitulée « Utiliser des volumes plutôt que des variables d’environnement »# ❌ Variables d'environnement : visibles dans /proc, logsenv: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password
# ✅ Volume : fichier avec permissions strictesvolumes: - name: db-creds secret: secretName: db-credentials defaultMode: 0400 # Lecture seule pour le ownerLes volumes permettent en plus la propagation automatique des mises à
jour du Secret (sauf avec subPath), ce qui n’est pas le cas des variables
d’environnement.
External Secrets Operator (ESO)
Section intitulée « External Secrets Operator (ESO) »ESO synchronise automatiquement les secrets depuis un coffre-fort
externe vers des Kubernetes Secrets. Il ne génère pas de secrets : il
récupère ce qui existe dans le provider externe et le copie dans un
objet Secret Kubernetes.
Installation
Section intitulée « Installation »helm repo add external-secrets https://charts.external-secrets.iohelm install external-secrets external-secrets/external-secrets \ -n external-secrets --create-namespaceConfiguration avec Vault
Section intitulée « Configuration avec Vault »# 1. SecretStore : connexion à VaultapiVersion: external-secrets.io/v1beta1kind: SecretStoremetadata: name: vault-backend namespace: productionspec: provider: vault: server: "https://vault.example.com" path: "secret" version: "v2" auth: kubernetes: mountPath: "kubernetes" role: "my-app" serviceAccountRef: name: my-app---# 2. ExternalSecret : quels secrets synchroniserapiVersion: external-secrets.io/v1beta1kind: ExternalSecretmetadata: name: db-credentials namespace: productionspec: refreshInterval: 1h # Synchronisation périodique secretStoreRef: name: vault-backend kind: SecretStore target: name: db-credentials # Nom du Secret K8s créé creationPolicy: Owner data: - secretKey: username remoteRef: key: secret/data/production/db property: username - secretKey: password remoteRef: key: secret/data/production/db property: passwordConfiguration avec AWS Secrets Manager
Section intitulée « Configuration avec AWS Secrets Manager »apiVersion: external-secrets.io/v1beta1kind: ClusterSecretStoremetadata: name: aws-secrets-managerspec: provider: aws: service: SecretsManager region: eu-west-1 auth: jwt: serviceAccountRef: name: external-secrets-sa namespace: external-secrets---apiVersion: external-secrets.io/v1beta1kind: ExternalSecretmetadata: name: db-credentials namespace: productionspec: refreshInterval: 1h secretStoreRef: name: aws-secrets-manager kind: ClusterSecretStore target: name: db-credentials dataFrom: - extract: key: production/db-credentialsAvantages d’ESO
Section intitulée « Avantages d’ESO »| Avantage | Description |
|---|---|
| Source unique de vérité | Le coffre-fort externe reste la référence |
| Synchronisation automatique | refreshInterval rafraîchit le Secret K8s périodiquement |
| Multi-providers | Vault, AWS, GCP, Azure, etc. |
| GitOps compatible | L’ExternalSecret est versionné dans Git, pas le secret lui-même |
Secrets Store CSI Driver
Section intitulée « Secrets Store CSI Driver »Le CSI Driver monte les secrets directement depuis le provider externe
dans le pod, sous forme de fichiers en tmpfs.
Le CSI Driver peut fonctionner sans créer de Secret Kubernetes (le
contenu reste uniquement dans le volume tmpfs du pod). Mais il peut aussi
synchroniser optionnellement un Secret Kubernetes — nécessaire
notamment si l’application consomme le secret via une variable
d’environnement.
Installation
Section intitulée « Installation »helm repo add secrets-store-csi-driver \ https://kubernetes-sigs.github.io/secrets-store-csi-driver/chartshelm install csi-secrets-store \ secrets-store-csi-driver/secrets-store-csi-driver \ -n kube-systemConfiguration avec Vault
Section intitulée « Configuration avec Vault »# 1. SecretProviderClass : définit la sourceapiVersion: secrets-store.csi.x-k8s.io/v1kind: SecretProviderClassmetadata: name: vault-db-creds namespace: productionspec: provider: vault parameters: vaultAddress: "https://vault.example.com" roleName: "my-app" objects: | - objectName: "db-username" secretPath: "secret/data/production/db" secretKey: "username" - objectName: "db-password" secretPath: "secret/data/production/db" secretKey: "password"---# 2. Pod : monte le volume CSIapiVersion: v1kind: Podmetadata: name: my-app namespace: productionspec: serviceAccountName: my-app containers: - name: app image: my-app:latest volumeMounts: - name: secrets mountPath: "/mnt/secrets" readOnly: true volumes: - name: secrets csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: vault-db-credsAvantages du CSI Driver
Section intitulée « Avantages du CSI Driver »| Avantage | Description |
|---|---|
| Pas de stockage etcd | Le secret n’est pas dans etcd (sauf sync optionnelle) |
| Montage tmpfs | Le contenu est en mémoire, pas sur disque |
| Éphémère | Le secret disparaît avec le pod |
Limites du CSI Driver
Section intitulée « Limites du CSI Driver »| Limite | Impact |
|---|---|
| Pas d’env vars directes | Nécessite la sync vers un Secret K8s pour les env vars |
| L’app doit recharger le fichier | Si le secret change, l’app doit relire le fichier |
| Pas de restart auto des pods | Le driver met à jour le mount, mais ne redémarre pas l’app |
| Complexité | Plus difficile à débugger |
| DaemonSet requis | Overhead sur chaque nœud |
Vault Agent Injector : une autre approche
Section intitulée « Vault Agent Injector : une autre approche »HashiCorp propose aussi le Vault Agent Injector, un mutation webhook qui injecte un sidecar dans les pods. Ce sidecar récupère les secrets depuis Vault et les écrit dans un volume partagé.
Cette approche est distincte d’ESO et du CSI Driver :
| Critère | ESO | CSI Driver | Vault Agent Injector |
|---|---|---|---|
| Crée un Secret K8s | Oui | Optionnel | Non |
| Mécanisme | Controller | DaemonSet + volume | Sidecar par pod |
| Dépendance | Multi-providers | Multi-providers | Vault uniquement |
| Auth | Configurable | Configurable | Auth Kubernetes Vault |
Le Vault Agent Injector est pertinent si vous utilisez déjà Vault et que
vous voulez une injection par pod sans passer par un objet Secret
Kubernetes. Pour un guide détaillé, consultez la documentation Vault.
Comment choisir selon la consommation applicative
Section intitulée « Comment choisir selon la consommation applicative »Le choix entre les approches ne dépend pas uniquement de la sécurité du stockage. Le critère le plus important en production est souvent : comment l’application consomme-t-elle le secret ?
| L’application… | Approche recommandée |
|---|---|
| Attend une variable d’environnement | ESO ou CSI avec sync vers Secret K8s |
| Sait lire un fichier et le recharger dynamiquement | CSI Driver (sans sync) |
| Est legacy et ne supporte aucun reload | Secret natif ou ESO (redéploiement au changement) |
| Est cloud-native et très sensible | CSI Driver ou Vault Agent Injector |
| A besoin de secrets dynamiques (credentials éphémères) | Vault (secrets engine) |
Comparaison des approches
Section intitulée « Comparaison des approches »| Critère | K8s Secrets natifs | External Secrets | CSI Driver |
|---|---|---|---|
| Complexité | Faible | Moyenne | Élevée |
| Sécurité | Acceptable (avec durcissement) | Haute (source externe) | Haute (pas de stockage etcd par défaut) |
| Rotation | Manuelle | Synchronisation automatique | Mise à jour du mount |
| GitOps | Secret en clair dans Git ❌ | ExternalSecret dans Git ✅ | SecretProviderClass dans Git ✅ |
| Secret dans etcd | Oui | Oui | Non (sauf sync optionnelle) |
| Variables d’env | Oui | Oui | Via sync vers Secret K8s |
| Reload applicatif | Volume : oui (sauf subPath) | Dépend du montage | Oui (si l’app relit le fichier) |
Tableau de décision
Section intitulée « Tableau de décision »| Situation | Recommandation |
|---|---|
| Démarrage rapide | Secrets natifs + chiffrement etcd + RBAC + audit |
| Production avec source externe | ESO avec refreshInterval |
| Réduire l’exposition etcd | CSI Driver sans sync vers Secret K8s |
| Multi-cluster | ESO avec ClusterSecretStore |
| Secrets dynamiques | Vault (secrets engine) + ESO ou CSI |
| GitOps avec secrets versionnés | Sealed Secrets ou SOPS |
| App legacy (env var, pas de reload) | ESO (redéploiement au changement) |
Migration progressive
Section intitulée « Migration progressive »-
Inventorier l’existant
Avant toute migration, identifiez :
- Quels workloads consomment quels secrets
- Quelles applications supportent le reload de fichier
- Quels secrets doivent rester en variable d’environnement
- Quels accès cloud ou Vault sont nécessaires
-
Sécuriser les Secrets natifs
Chiffrement etcd + RBAC strict + audit logs. C’est le socle minimum, même si vous prévoyez de migrer vers ESO ou CSI.
-
Déployer ESO
Commencez par synchroniser quelques secrets non critiques et vérifiez que le
refreshIntervalfonctionne correctement. -
Migrer progressivement
Remplacez les Secrets natifs par des
ExternalSecrets. Validez le comportement applicatif à chaque étape (l’app reçoit-elle bien le secret ? le recharge-t-elle ?). -
Supprimer les Secrets manuels
Une fois la migration validée, supprimez les définitions manuelles.
-
Évaluer CSI Driver
Pour les workloads les plus sensibles dont l’application sait relire un fichier, envisagez le CSI Driver.
À retenir
Section intitulée « À retenir »- Les Secrets natifs sont acceptables avec durcissement — chiffrement etcd, RBAC strict, audit logs et montage en volume
- La sécurité dépend aussi du nœud — un pod privilégié accède à tous les secrets du nœud
- ESO synchronise, il ne génère pas — la rotation réelle se fait côté provider (Vault, AWS, etc.)
- Le CSI Driver peut synchroniser un Secret K8s — c’est optionnel, mais nécessaire pour les variables d’environnement
- Le critère de choix principal est la consommation — env var, fichier rechargeable, sidecar, secret dynamique
- Volumes plutôt que variables d’environnement — moins d’exposition, propagation automatique