TP18 : Gestion des secrets (Vault, External Secrets Operator, Sealed Secrets, rotation)

Un Secret Kubernetes n’est qu’un objet encodé en base64 : quiconque peut lire les Secrets du namespace ou accéder à etcd voit les valeurs, et on ne peut pas le versionner dans Git. Ce TP présente les trois approches courantes pour faire mieux : un coffre-fort central (HashiCorp Vault) synchronisé vers Kubernetes par External Secrets Operator, des secrets chiffrés versionnables (Sealed Secrets), et la rotation des secrets sans redéploiement manuel.


Fichiers de départ : manifests.zip, vault.zip

1. Objectifs pédagogiques

À l’issue du TP, vous serez capable de :

  • expliquer pourquoi un Secret Kubernetes ne suffit pas et comparer trois approches de gestion ;
  • utiliser Vault : moteur KV v2, policies, méthode d’authentification Kubernetes ;
  • synchroniser un secret de Vault (puis de Secrets Manager via Floci) vers Kubernetes avec External Secrets Operator ;
  • chiffrer un secret pour Git avec Sealed Secrets ;
  • réaliser une rotation et comprendre son effet sur les variables d’environnement et sur les fichiers montés.

Les images de cette page sont des schémas explicatifs, pas des captures d’écran.

2. Environnement du TP

  • Le cluster K3s du TP14, kubectl et Helm (TP15).
  • Le CLI vault sur la VM de lab (installation : même dépôt APT HashiCorp que Terraform au TP6).
  • Floci et AWS CLI (TP13) pour la partie 4.

Trois façons de gérer un secret Trois façons de gérer un secret

3. Partie 1 : Installer Vault

  • Installer Vault en mode dev (stockage en mémoire, déverrouillé, jeton root root) : uniquement pour un lab.

    helm repo add hashicorp https://helm.releases.hashicorp.com && helm repo update
    helm install vault hashicorp/vault -n vault --create-namespace \
      --set server.dev.enabled=true --set server.dev.devRootToken=root --set injector.enabled=false
    kubectl -n vault get pods
    kubectl -n vault port-forward svc/vault 8200:8200 &
    export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN=root
    vault status

    En production, Vault démarre scellé (sealed) : il faut le déverrouiller avec plusieurs clés (Shamir) ou un auto-unseal KMS, et le stockage est persistant (Raft). Le mode dev supprime tout cela.

  • Écrire et relire un secret dans le moteur KV v2 monté sur secret/ :

    vault kv put secret/tp18/api api-key=v1-initial-value
    vault kv get secret/tp18/api
    vault kv metadata get secret/tp18/api      # KV v2 conserve les versions

4. Partie 2 : Policies et authentification Kubernetes

  • Compléter vault/app-policy.hcl (lecture seule sur secret/data/tp18/*) et la charger :

    vault policy write tp18-app vault/app-policy.hcl
  • Activer la méthode d’authentification Kubernetes : Vault vérifie le jeton du ServiceAccount d’un Pod auprès de l’API Kubernetes.

    vault auth enable kubernetes
    vault write auth/kubernetes/config kubernetes_host="https://kubernetes.default.svc:443"
    vault write auth/kubernetes/role/tp18-app \
      bound_service_account_names=app-sa bound_service_account_namespaces=tp18 \
      policies=tp18-app ttl=1h
  • Expliquer : quel Pod peut obtenir un jeton Vault avec ce rôle ? Que se passe-t-il pour un Pod d’un autre namespace ?

5. Partie 3 : External Secrets Operator

Vault et ESO : le Secret Kubernetes est créé et mis à jour à partir de Vault Vault et ESO : le Secret Kubernetes est créé et mis à jour à partir de Vault

  • Installer ESO :

    helm repo add external-secrets https://charts.external-secrets.io && helm repo update
    helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace
  • Appliquer le namespace et le ServiceAccount (manifests/00-app.yaml : le Pod restera en attente tant que le Secret n’existe pas), puis compléter et appliquer 10-secretstore.yaml et 20-externalsecret.yaml.

    Si la ressource n’est pas reconnue, vérifiez la version de l’API proposée par votre version d’ESO (kubectl api-resources | grep external-secrets) : v1 dans les versions récentes, v1beta1 dans les plus anciennes.

  • Vérifier la synchronisation :

    kubectl -n tp18 get secretstore,externalsecret      # STATUS SecretSynced, READY True
    kubectl -n tp18 get secret api-credentials -o jsonpath='{.data.api-key}' | base64 -d
    kubectl -n tp18 logs deploy/secret-reader -f

6. Partie 4 : Rotation

  • Changer la valeur dans Vault, puis observer les logs de secret-reader pendant une minute :

    vault kv put secret/tp18/api api-key=v2-rotated-value
    kubectl -n tp18 logs deploy/secret-reader -f
  • Constater : le fichier monté finit par afficher la nouvelle valeur (le kubelet met à jour les volumes Secret), mais la variable d’environnement garde l’ancienne valeur jusqu’au redémarrage du Pod.

  • Rendre le redémarrage automatique, au choix : kubectl rollout restart déclenché par la CI, ou l’opérateur Reloader avec l’annotation reloader.stakater.com/auto: "true".

  • Revenir à la version précédente (vault kv rollback -version=1 secret/tp18/api) : que montre vault kv metadata get ?

7. Partie 5 : Secrets Manager (Floci) comme seconde source

  • Créer un secret dans Floci (aws secretsmanager create-secret --name tp18/db-password --secret-string 's3cr3t-lab'), Floci étant joignable depuis le cluster à http://<IP-VM-lab>:4566.

  • Créer un Secret Kubernetes floci-creds contenant les identifiants fictifs (access-key=test, secret-access-key=test) et un SecretStore de type aws (service: SecretsManager, region: us-east-1, auth.secretRef).

  • Pointer ESO vers Floci en ajoutant au déploiement du contrôleur la variable d’environnement de endpoint personnalisé décrite dans la documentation ESO (AWS, custom endpoints), par exemple :

    helm upgrade external-secrets external-secrets/external-secrets -n external-secrets \
      --reuse-values --set "extraEnv[0].name=AWS_SECRETSMANAGER_ENDPOINT" \
      --set "extraEnv[0].value=http://<IP-VM-lab>:4566"
  • Synchroniser tp18/db-password dans un Secret db-credentials et vérifier sa valeur.

8. Partie 6 : Sealed Secrets (GitOps)

  • Installer le contrôleur et le CLI kubeseal :

    helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets && helm repo update
    helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system
    # CLI : binaire kubeseal depuis https://github.com/bitnami-labs/sealed-secrets/releases
  • Chiffrer un secret sans jamais l’appliquer en clair :

    kubectl -n tp18 create secret generic smtp --from-literal=password='lab-smtp' \
      --dry-run=client -o yaml \
    | kubeseal --controller-name sealed-secrets --controller-namespace kube-system -o yaml > smtp-sealed.yaml
    cat smtp-sealed.yaml                    # valeur chiffrée : peut être commitée
    kubectl apply -f smtp-sealed.yaml && kubectl -n tp18 get secret smtp
  • Copier smtp-sealed.yaml en changeant le namespace en default puis l’appliquer : pourquoi le déchiffrement échoue-t-il (portée strict) ?

  • Que se passe-t-il si la clé privée du contrôleur est perdue ? Comment la sauvegarder ?

9. Partie 7 : Synthèse

  • Compléter le tableau comparatif :
Critère Secret Kubernetes Sealed Secrets Vault + ESO
Versionnable dans Git
Source de vérité
Rotation
Audit des accès
Complexité d’exploitation
Adapté à GitOps

Défi

  1. Activer le moteur database de Vault pour générer des identifiants PostgreSQL dynamiques à durée de vie limitée (ttl=1h) pour le PostgreSQL du TP14.
  2. Activer le chiffrement des Secrets au repos dans K3s (--secrets-encryption) et vérifier avec k3s secrets-encrypt status.

10. Livrables

Captures à déposer dans captures/ :

Fichier Contenu attendu
vault-kv.png vault kv get et vault kv metadata get (versions)
vault-k8s-auth.png Rôle Kubernetes et policy tp18-app
eso-synced.png ExternalSecret en état SecretSynced
rotation.png Logs : le fichier change, la variable d’environnement non
eso-floci.png Secret synchronisé depuis Secrets Manager (Floci)
sealed-secret.png SealedSecret chiffré et Secret déchiffré dans le cluster

Ainsi que : la policy Vault, les manifests ESO, smtp-sealed.yaml et le tableau comparatif. Aucun secret en clair dans Git.