Skip to content

ADR 009 — External Secrets Operator + IRSA (2026-05-29)

Contexte

Gap C5 de l'audit du 2026-05-20 : External Secrets Operator (ESO) est annoncé dans le design du projet mais n'a jamais été installé. Les secrets applicatifs (DB_PASSWORD, SECRET_KEY) sont aujourd'hui injectés de façon impérative par le job deploy du pipeline :

kubectl create secret generic fastapi-secrets \
  --from-literal=SECRET_KEY="$SECRET_KEY" \
  --from-literal=DB_PASSWORD="$TF_VAR_db_password" \
  -n fastapi --dry-run=client -o yaml | kubectl apply -f -

ProblÚmes : - Les valeurs vivent dans des variables CI GitLab, dupliquées et gérées à la main. - Le Secret Kubernetes n'a pas de source de vérité unique ni de rotation. - Incohérence design : le ConfigMap et le Deployment documentent déjà « variables sensibles gérées par ESO », mais ESO est absent.

Objectif (issue #33) : AWS Secrets Manager devient la source de vérité, ESO synchronise automatiquement le Secret Kubernetes fastapi-secrets, et le job deploy ne crée plus de secret impérativement.


DĂ©cision 1 — External Secrets Operator comme mĂ©canisme de synchronisation

ESO est retenu pour matérialiser un Secret Kubernetes à partir d'AWS Secrets Manager.

Alternatives écartées : - Secret impératif en CI (existant) : pas de source unique, pas de rotation, valeur dupliquée. - Sealed Secrets : chiffre un Secret dans Git, mais la source de vérité reste Git, pas un coffre managé. Pas d'intégration native AWS. - Secrets Store CSI Driver : monte les secrets en volume, mais ne crée pas un objet Secret Kubernetes natif. Le Deployment consomme déjà envFrom.secretRef, ESO colle mieux sans refonte du PodSpec.


DĂ©cision 2 — IRSA (OIDC) comme mĂ©thode d'authentification vers AWS

Le contrÎleur ESO s'authentifie auprÚs de Secrets Manager via IRSA (IAM Roles for Service Accounts) : le ServiceAccount du contrÎleur est annoté avec l'ARN d'un rÎle IAM dont la trust policy est liée au provider OIDC du cluster.

Alternative écartée : credentials IAM statiques (access key/secret key stockés dans un Secret Kubernetes). Plus simple, mais introduit un secret long-lived dans le cluster pour gérer les secrets, ce qui est contradictoire et affaiblit la posture.

Le rÎle IRSA d'ESO est scopé en moindre privilÚge : secretsmanager:GetSecretValue + DescribeSecret sur le seul ARN du secret applicatif.


DĂ©cision 3 — Le secret Secrets Manager vit dans le stack persistent

Le secret fastapi-eks/app (payload JSON DB_PASSWORD + SECRET_KEY) est créé dans le stack Terraform persistent, pas ephemeral.

Raisons : - Le secret survit au terraform destroy quotidien du stack ephemeral. - On Ă©vite la fenĂȘtre de rĂ©cupĂ©ration AWS (7 Ă  30 jours) qui complique un delete/recreate journalier. recovery_window_in_days = 0 permet malgrĂ© tout un recreate propre si le stack persistent est un jour dĂ©truit. - SECRET_KEY est gĂ©nĂ©rĂ© par random_password (Terraform en est propriĂ©taire, aucune valeur humaine Ă  gĂ©rer) et reste stable entre les cycles ephemeral. Le mettre dans ephemeral le rĂ©gĂ©nĂ©rerait chaque matin, invalidant tous les JWT.

DB_PASSWORD partage la valeur de var.db_password (mĂȘme TF_VAR_db_password que le module RDS). ConsĂ©quence : une rotation du mot de passe RDS exige de rĂ©-appliquer les deux stacks (persistent pour le secret, ephemeral pour RDS). Rotation rare, coĂ»t acceptĂ©.


DĂ©cision 4 — Le provider OIDC et le rĂŽle IRSA vivent dans le stack ephemeral

Le cluster EKS est ephemeral : son issuer OIDC change à chaque recréation. Le aws_iam_openid_connect_provider et le rÎle IRSA d'ESO (dont la trust policy référence cet issuer) sont donc créés dans le stack ephemeral, recréés à chaque aws-start.

Le nom du rÎle est fixe, donc son ARN est stable entre les cycles : l'annotation IRSA du ServiceAccount ESO (posée par le bootstrap Ansible) n'a pas à changer.


Conséquences et découpage

Le terraform_ci policy (stack persistent, module IAM) est Ă©largi pour que l'apply ephemeral puisse gĂ©rer le provider OIDC, la policy inline du rĂŽle ESO, et lire la metadata du secret. Le stack persistent doit ĂȘtre appliquĂ© (par un admin) avant le premier apply ephemeral qui crĂ©e l'IRSA.

Implémentation étalée sur plusieurs MR (discipline « petit et ciblé ») :

MR PérimÚtre
MR-A Stack persistent : secret Secrets Manager + élargissement terraform_ci
MR-B Stack ephemeral : output issuer OIDC + provider OIDC + rĂŽle IRSA ESO
MR-C Ansible : install ESO via Helm + annotation IRSA + uninstall teardown
MR-D Manifests SecretStore + ExternalSecret, retrait du Secret impératif du deploy, validation end-to-end

Date : 2026-05-29 Sprint : 3 Issue : #33