← Retour au blog

Tutoriel External Secrets Operator : Synchroniser vos secrets Kubernetes

15/12/2025

Chargement…

GitOps : Sécurisez vos secrets Kubernetes avec External Secrets Operator (ESO)

INTRODUCTION

La gestion des secrets est souvent le talon d'Achille d'une approche GitOps. Stocker des fichiers chiffrés dans Git (via des outils comme Sealed Secrets) complexifie la rotation des clés et manque de souplesse.

External Secrets Operator (ESO) résout ce problème en inversant le paradigme : il ne stocke rien, il synchronise. Il récupère les secrets natifs depuis un gestionnaire externe (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, Google Secret Manager) et les injecte directement en tant que secrets Kubernetes standards.

Dans ce tutoriel, nous allons voir comment implémenter une architecture robuste de gestion des secrets sur Kubernetes couplée à AWS.


Prérequis

Pour suivre ce guide, vous aurez besoin de :

  • Un cluster Kubernetes (v1.24+).
  • Helm (v3+) installé sur votre poste.
  • kubectl configuré avec les droits d'administration (cluster-admin).
  • Un compte AWS avec accès à Secrets Manager et IAM.

Tutoriel pas à pas

1. Installation de l'opérateur (ESO)

Nous utilisons Helm pour déployer l'opérateur dans un namespace dédié, garantissant une isolation propre des composants système.

# 1. Ajouter le dépôt Helm officiel
helm repo add external-secrets https://charts.external-secrets.io

# 2. Mettre à jour les index
helm repo update

# 3. Installer l'opérateur
helm install external-secrets external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace \
    --set installCRDs=true

Vérification : Assurez-vous que les pods sont en statut Running : kubectl get pods -n external-secrets

2. Configuration de l'accès IAM (AWS)

Pour que le cluster puisse lire dans AWS Secrets Manager, une identité IAM est requise.

A. La politique IAM (Policy)

Voici une politique respectant le principe de moindre privilège. Elle limite l'accès à des secrets spécifiques.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:<AWS_REGION>:<AWS_ACCOUNT_ID>:secret:prod/my-app/*"
    }
  ]
}

B. Authentification (Méthode simple vs Production)

Pour ce tutoriel, nous utiliserons une paire de clés (Access Key/Secret Key).

⚠️ Mise en garde Production (IRSA) : En production, n'utilisez jamais de clés statiques (long-term credentials). Privilégiez IAM Roles for Service Accounts (IRSA) via OIDC pour une authentification temporaire et sécurisée.

Créez un secret Kubernetes contenant vos identifiants IAM :

kubectl create secret generic aws-creds \
    --from-literal=access-key=<VOTRE_ACCESS_KEY> \
    --from-literal=secret-access-key=<VOTRE_SECRET_KEY> \
    -n default

3. Création du SecretStore

Le SecretStore est la ressource qui fait le pont entre Kubernetes et votre fournisseur (ici AWS). Il est scopé au namespace (contrairement au ClusterSecretStore).

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-store
  namespace: default
spec:
  provider:
    aws:
      service: SecretsManager
      region: eu-west-3 # Remplacez par votre région
      auth:
        secretRef:
          accessKeyIDSecretRef:
            name: aws-creds
            key: access-key
          secretAccessKeySecretRef:
            name: aws-creds
            key: secret-access-key

4. Création de l'ExternalSecret

C'est ici que la magie opère. L'ExternalSecret déclare quel secret distant récupérer et comment le transformer en secret Kubernetes.

Imaginez un secret dans AWS nommé prod/db-credentials contenant le JSON : {"username":"admin", "password":"supersecurepassword"}.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-db-secret
  namespace: default
spec:
  refreshInterval: 1h # Vérifie les changements côté AWS toutes les heures
  secretStoreRef:
    name: aws-secrets-store
    kind: SecretStore
  target:
    name: db-credentials-k8s  # Nom du secret final généré dans K8s
    creationPolicy: Owner     # Supprime le secret K8s si l'ExternalSecret est supprimé
  data:
  - secretKey: db_user        # Clé dans le secret K8s
    remoteRef:
      key: prod/db-credentials
      property: username      # Clé du JSON AWS
  - secretKey: db_pass
    remoteRef:
      key: prod/db-credentials
      property: password

Appliquez le fichier : kubectl apply -f external-secret.yaml


Sécurité 🔐

  1. Least Privilege : Bannissez les wildcards (*) dans vos policies IAM. Ciblez toujours des ARNs précis.
  2. ClusterSecretStore vs SecretStore :
    • Utilisez SecretStore pour isoler les tenants (chaque équipe gère son auth).
    • Réservez ClusterSecretStore (global) uniquement si tous les secrets proviennent du même compte AWS de manière centralisée.
  3. Validation OPA/Kyverno : Implémentez des règles pour empêcher un namespace de développement de référencer des secrets de production (en filtrant sur le nom ou les tags).

Observabilité et Monitoring 📈

L'opérateur expose des métriques Prometheus sur le port 8080. Il est crucial d'alerter sur :

  • externalsecret_sync_calls_error : Indique un problème IAM, réseau, ou une clé manquante.
  • Status des CRDs : Un ExternalSecret doit toujours avoir la condition Ready à True.

Commande de diagnostic rapide :

kubectl get externalsecrets
# Le STATUS doit être 'SecretSynced'
# Le READY doit être 'True'

Tests / Validation ✅

  1. Vérification de la présence :
    kubectl get secret db-credentials-k8s -o jsonpath='{.data.db_pass}' | base64 -d
    
  2. Linting CI/CD : Intégrez kubeconform dans votre pipeline GitOps pour valider la syntaxe YAML avant le déploiement.
  3. Test de rotation : Modifiez une valeur dans AWS Secrets Manager. Attendez le délai du refreshInterval (ou forcez-le via une annotation) et vérifiez que Kubernetes a bien la nouvelle valeur.

Erreurs fréquentes & diagnostics 🛠️

  • Erreur : SecretSyncedError / AccessDeniedException

    • Cause : Le rôle IAM n'a pas les droits sur l'ARN exact.
    • Solution : Vérifiez la policy AWS et assurez-vous que la région dans le SecretStore est correcte.
  • Erreur : Key not found

    • Cause : La propriété JSON demandée n'existe pas dans le secret AWS.
    • Solution : Attention à la casse ! Password est différent de password.

Bonus : Rotation automatique des Pods 🔄

ESO met à jour le secret Kubernetes, mais vos applications (Pods) ne rechargent pas forcément cette configuration à chaud.

Pour une solution complète, couplez ESO avec Reloader (de Stakater). Cet outil surveille les changements sur les Secrets et ConfigMaps et redémarre automatiquement (Rolling Update) les déploiements associés.

Ajoutez simplement cette annotation sur votre Deployment :

metadata:
  annotations:
    reloader.stakater.com/auto: "true"

Conclusion

L'utilisation d'External Secrets Operator renforce considérablement la sécurité de votre chaîne GitOps en découplant le cycle de vie des secrets de celui du code applicatif. Vous obtenez le meilleur des deux mondes : la puissance de gestion d'un Vault/AWS et la simplicité native de Kubernetes.

Prêt à passer en prod ? Assurez-vous d'abord de migrer vers l'authentification IRSA !