Kubernetes secrets
| Fiche express | |
|---|---|
| Objet natif | Secret (encodage base64, stocké dans etcd)
|
| CRD associées | SecretStore / ClusterSecretStore · ExternalSecret (External Secrets Operator)
|
| Source de vérité (option) | HashiCorp Vault |
| Voir aussi | Kubernetes ConfigMaps · Vault |
Cette page part du principe que la mécanique de base du Secret Kubernetes natif (création, montage en volume ou en variable d'environnement) est acquise — voir Kubernetes ConfigMaps pour ce socle. Elle se concentre sur un point souvent confondu quand un cluster est couplé à un coffre-fort externe comme HashiCorp Vault : la différence entre trois objets de niveaux différents — le Secret natif, le SecretStore et l'ExternalSecret.
Trois objets, trois niveaux
- Le Secret est l'objet Kubernetes standard, celui que les Pods consomment réellement.
- Le SecretStore (ou son équivalent cluster, le ClusterSecretStore) et l'ExternalSecret ne sont pas des objets Kubernetes natifs : ce sont des CRD apportées par l'External Secrets Operator (ESO), dont le seul rôle est de fabriquer automatiquement des Secrets natifs à partir de valeurs stockées dans Vault.
Autrement dit : Vault est la source de vérité, ESO le tuyau, et le Secret natif le résultat final consommé par les Pods.
┌─────────────────────────────┐
│ HashiCorp Vault │ Source de vérité (hors cluster idéalement)
│ secret/data/app/db ... │
└──────────────┬──────────────┘
│ authentification (Kubernetes / AppRole / Token...)
▼
┌─────────────────────────────┐
│ SecretStore / │ « OÙ est Vault et COMMENT s'y connecter »
│ ClusterSecretStore (ESO) │ → adresse, chemin, méthode d'auth
└──────────────┬──────────────┘
│ référencé par (secretStoreRef)
▼
┌─────────────────────────────┐
│ ExternalSecret (ESO) │ « QUELS secrets tirer et comment les mapper »
└──────────────┬──────────────┘
│ génère / synchronise en continu
▼
┌─────────────────────────────┐
│ Secret Kubernetes natif │ Objet standard, dans etcd
└──────────────┬──────────────┘
│ monté en volume ou injecté en variable d'env
▼
┌─────────────────────────────┐
│ Pod │
└─────────────────────────────┘
Le Secret natif et ses limites
Rappel des points importants côté objet natif :
- La donnée est encodée en base64, ce qui n'est pas du chiffrement — quiconque peut lire le Secret peut le décoder trivialement.
- Le Secret est stocké dans etcd. Sur un cluster RKE2, le chiffrement au repos (encryption at rest) peut être activé pour que les Secrets n'apparaissent pas en clair dans la base :
# État du chiffrement des secrets (RKE2)
rke2 secrets-encrypt status
# Préparer / faire tourner les clés de chiffrement
rke2 secrets-encrypt prepare
rke2 secrets-encrypt rotate
rke2 secrets-encrypt reencrypt
Un Secret natif seul montre vite ses limites à l'échelle d'un cluster de production :
- pas de rotation automatique — si le mot de passe change côté source, le Secret reste figé ;
- la valeur sensible est dupliquée dans le cluster, donc davantage de surface à protéger ;
- le base64 donne une fausse impression de sécurité à qui ne connaît pas le mécanisme ;
- la gestion devient pénible à grande échelle (copier-coller de valeurs dans des manifestes).
C'est précisément pour répondre à ces limites qu'on branche un coffre-fort externe comme Vault via ESO.
Vault, la source de vérité
Vault centralise le stockage chiffré, l'audit, la rotation et les politiques d'accès fines. L'idée est de ne plus stocker les valeurs sensibles durablement dans le cluster, mais de les laisser dans Vault et de ne les matérialiser en Secret Kubernetes qu'au moment nécessaire.
Côté terminologie Vault, on parle typiquement d'un moteur de secrets KV version 2 monté sur secret/, avec des chemins du type secret/data/app/db. Vault n'est pas un objet Kubernetes : c'est un système externe avec lequel le cluster dialogue.
Le SecretStore et le ClusterSecretStore
Le SecretStore répond à une seule question : « où se trouve le fournisseur de secrets et comment s'y authentifier ? ». C'est la configuration de connexion vers Vault — il ne contient aucune valeur de secret, uniquement les coordonnées et la méthode d'authentification.
| Objet | Portée | Utilisation typique |
|---|---|---|
SecretStore |
Namespace | Connexion Vault propre à un namespace ; un ExternalSecret ne peut référencer qu'un SecretStore de son namespace. |
ClusterSecretStore |
Cluster | Connexion Vault partagée, utilisable par les ExternalSecrets de tous les namespaces. Pratique pour mutualiser la configuration. |
Exemple de ClusterSecretStore utilisant l'authentification Kubernetes de Vault (méthode recommandée en in-cluster : Vault valide le jeton de ServiceAccount du Pod ESO) :
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.exemple.lan:8200"
path: "secret" # moteur KV monté sur secret/
version: "v2" # KV version 2
auth:
kubernetes:
mountPath: "kubernetes" # backend auth Kubernetes dans Vault
role: "app-role" # rôle Vault associé aux policies
serviceAccountRef:
name: "external-secrets"
namespace: "external-secrets"
D'autres méthodes d'authentification existent côté provider.vault.auth : appRole, tokenSecretRef (jeton statique, à éviter en production), jwt, ldap, etc.
L'ExternalSecret
L'ExternalSecret répond à l'autre question : « quels secrets tirer de Vault, et sous quelle forme les écrire dans Kubernetes ? ». Il référence un SecretStore (ou ClusterSecretStore) pour savoir où aller, puis génère et synchronise un Secret natif — on n'écrit plus le Secret à la main, ESO le fabrique à partir de Vault.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-db
namespace: app
spec:
refreshInterval: "1h" # ESO re-synchronise depuis Vault toutes les heures
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore # ou SecretStore si namespacé
target:
name: app-db # nom du Secret natif créé
creationPolicy: Owner # ESO crée et possède le Secret
data:
- secretKey: username # clé dans le Secret K8s résultant
remoteRef:
key: app/db # chemin dans Vault (secret/data/app/db)
property: username # champ à extraire
- secretKey: password
remoteRef:
key: app/db
property: password
Quelques précisions utiles :
refreshInterval— fréquence de re-synchronisation ; si la valeur change dans Vault, le Secret natif est mis à jour automatiquement (rotation).data— mappe champ par champ ; pour tout récupérer d'un coup, on utilise plutôtdataFrom.target.template(non montré ici) — permet de mettre en forme la valeur finale (par exemple générer une URL de connexion complète à partir de plusieurs champs).creationPolicy: Ownersignifie que si on supprime l'ExternalSecret, le Secret natif est supprimé aussi.
Récapitulatif des rôles
| Objet | Origine | Portée | Rôle |
|---|---|---|---|
Secret |
Kubernetes natif | Namespace | Stocke la donnée et la rend consommable par les Pods |
SecretStore |
ESO (CRD) | Namespace | Définit la connexion et l'authentification vers Vault |
ClusterSecretStore |
ESO (CRD) | Cluster | Idem, partagé entre tous les namespaces |
ExternalSecret |
ESO (CRD) | Namespace | Dit quoi tirer de Vault et génère le Secret natif |
Phrase à retenir : le SecretStore est le « comment se connecter », l'ExternalSecret le « quoi récupérer », et le Secret le « résultat consommé par les Pods ».
Cycle de vie complet
- Les valeurs sensibles sont déposées dans Vault (
secret/data/app/db). - Un
ClusterSecretStorepointant sur Vault et sa méthode d'authentification est déclaré une fois. - Pour chaque besoin, un
ExternalSecretréférençant ce store est créé. - ESO s'authentifie auprès de Vault, récupère les valeurs et crée le
Secretnatif. - Le Pod monte ce Secret comme d'habitude (volume ou variable d'environnement).
- À chaque
refreshInterval, ESO re-synchronise : Vault reste la source de vérité.
Alternatives à ESO
External Secrets Operator n'est pas la seule façon de brancher Vault sur Kubernetes :
- Vault Secrets Operator (VSO) — l'opérateur officiel de HashiCorp, logique proche d'ESO mais terminologie différente (
VaultConnection,VaultAuth,VaultStaticSecret/VaultDynamicSecret). À privilégier pour rester pleinement dans l'écosystème HashiCorp et exploiter les secrets dynamiques. - Vault Agent Injector — un sidecar injecté via annotations sur le Pod, qui écrit les secrets dans un volume partagé, sans créer de Secret natif.
- Vault CSI Provider (Secrets Store CSI Driver) — les secrets sont montés directement dans le Pod via un volume CSI, sans passer par un objet Secret.
La différence principale d'ESO/VSO par rapport aux deux autres approches : ils matérialisent un Secret Kubernetes natif, donc compatible avec tout ce qui consomme un Secret standard.
Bonnes pratiques
- Activer le chiffrement etcd au repos, même avec ESO en place : le Secret natif généré vit quand même dans etcd.
- Préférer l'authentification Kubernetes de Vault à un jeton statique pour l'authentification d'ESO.
- Mutualiser via
ClusterSecretStoreet restreindre les accès par des policies Vault fines (un rôle par application ou par namespace). - Choisir un
refreshIntervalcohérent avec la fréquence de rotation côté Vault. - Limiter les RBAC sur les Secrets natifs : la source Vault ne dispense pas de protéger le Secret généré dans le cluster.
- Versionner les manifestes
SecretStore/ExternalSecretdans Git (ils ne contiennent aucune valeur sensible), mais jamais les Secrets natifs eux-mêmes.