Sprint 7 Report — Remédiation & Fiabilité¶
Période : 2026-09-01 à 2026-09-14 (premières livraisons dès le 2026-08-17, voir Vélocité)
Release : merge request develop → main sans squash, ouverte à la clôture
Équipe : 1 ingénieur DevOps (Youssef Kadi)
Repo : gitlab.com/yk-devops/fastapi-eks-project
Executive Summary¶
À l'entrée du Sprint 7, la plateforme multi-environnements du Sprint 6 tournait, mais un audit DevSecOps du compte AWS, du cluster et de la chaîne CI venait d'en mesurer les failles. Ses constats ont été versés au backlog le 27/08, avec des priorités adossées aux gravités que le rapport attribue lui-même. Le milestone a été recadré le 29/08 : sept issues antérieures à l'audit en sont sorties, pour laisser la place à la remédiation.
Le sprint se clôt à sa date, à 35 issues fermées sur 35, portées par 70 merge requests. Aucun reliquat. Les quatre constats critical sont fermés : MFA obligatoire sur l'administrateur, clés de la CI séparées, politique IAM de la CI réduite aux actions utilisées, clés de la CI sorties du state Terraform.
La signature du sprint est une découverte que l'audit n'avait pas faite. Les secrets de production étaient en clair dans le state Terraform, et la CI pouvait le lire (SEC-008). Le constat ne figure dans aucun des 48 points du rapport. Il a demandé quatre ADR pour être soldé (033, 034, 037, 038) : Terraform déclare désormais les secrets sans en fabriquer la valeur, l'historique du bucket de state est purgé et borné à 30 jours, et le mot de passe master RDS passe en écriture seule.
L'autre fil du sprint est celui des versions que personne ne surveillait. Le cluster était épinglé en 1.32 depuis mai, en support étendu, pour 36,15 $ de surcoût mesuré, soit 83 % de la facture EKS de mai et juin. Rien ne l'avait signalé. Il monte désormais en 1.35, et la compatibilité EKS / Cilium est refusée en MR comme au montage si elle sort de l'intervalle testé.
Neuf incidents ont été capitalisés : SEC-008 et INC-069 à INC-076. Quatre d'entre eux (INC-069, INC-071, INC-073, INC-076) partagent un même mécanisme : un contrôle qui rend vert sans avoir vérifié ce qu'on lui prête.
Bilan des actions de la rétrospective Sprint 6¶
On rend compte des 3 actions que la rétro du Sprint 6 s'était engagée à mener.
| Action Sprint 6 | Verdict | Détail |
|---|---|---|
| Clore le Sprint 7 à sa date, avec un point d'étape à mi-parcours | ⚠️ À moitié | La date est tenue : clôture le 14/09, contre six semaines pour deux au Sprint 6. Aucun reliquat : les issues hors de portée étaient sorties dès le 29/08, au lieu de prolonger la fenêtre. En revanche, aucune trace écrite d'une revue de mi-sprint datée du 08/09 : aucun compte rendu, aucune session ce jour-là. Le recadrage réel du périmètre a eu lieu le 29/08, avant l'ouverture. |
| Solder les gardes-fous qui n'en sont pas : #174 et #178, vérifiés par test négatif | ✅ Fait, avec une nuance sur #174 | #178 fermée le 17/08 (INC-069) : les analyses tournent enfin sur les merge requests, le SAST passe de 19 666 fichiers analysés à 327, et un gate SAST bloquant est livré (!322). #174 fermée le 09/09 par l'ADR 031, avec un exercice réel le 04/09. Mais le garde-fou prévient à 23 h, il ne détruit plus rien : aucun mécanisme automatique de destruction n'existe, et sa propre panne ne se signale pas (#196). |
| Livrer #141, l'alerte sur les pannes silencieuses | ❌ Non fait | Sortie du sprint le 29/08, sans milestone depuis. Troisième report consécutif. Le sprint en a pourtant fourni un nouvel exemple : le scan de registre de 8 h a échoué 15 matins de suite avant d'être lu (#197). |
Stories livrées¶
35 issues fermées sur 35, portées par 70 merge requests rattachées au milestone (!321 → !416).
Identités et droits IAM¶
| Issue | Livré |
|---|---|
| #183 | MFA obligatoire sur iamadmin par politique IAM (audit AW-9, critical). INC-070 |
| #184 | Privilèges des clés AWS de la CI séparés, variables CI de 16 à 15 (audit AW-2, EK-16, critical). ADR 032 |
| #185 | Politique IAM de la CI réduite aux actions utilisées, de v7 à v11 (audit AW-2, critical). INC-076 |
| #188 | Durcissement IAM du compte, découpée le 29/08 en #192, #193 et #194 |
| #192 | Politique de mot de passe au niveau du compte AWS (audit AW-7) |
| #193 | Politique de ressource deny-by-default étendue aux sept secrets (audit AW-10) |
| #203 | Escalade de privilèges fermée : iam:PassRole borné aux trois services attendus, prouvé par une sonde refusée |
| #223 | Accès cluster du user CI retiré (audit EK-10) : access entry AmazonEKSEditPolicy et deux Roles supprimés. Prouvé au montage du 11/09, fermée le 14/09 sur la première pipeline applicative complète, write-back compris |
| #190 | EK-15 accepté par l'ADR 036 : GitLab Free ne sait pas exprimer la règle de push demandée. INC-071 |
Secrets hors du state Terraform¶
| Issue | Livré |
|---|---|
| #198 | SEC-008 : Terraform déclare les sept secrets sans en fabriquer la valeur, la CI perd l'accès au state persistent. ADR 033 |
| #202 | Terraform ne fabrique plus les clés d'accès de la CI (critical). ADR 034 |
| #199 | Les copies historiques du state : trois valeurs encore vivantes tournées, puis le stock purgé. ADR 037 |
| #218 | Historique du bucket de state borné à 30 jours, recompté à 309 objets le 11/09 |
| #217 | seed-secrets.sh : l'auto-vérification ne pouvait pas réassumer le rôle déjà assumé |
| #219 | Mot de passe master RDS en écriture seule (password_wo), absent du state prouvé au montage du 11/09. ADR 038 |
Infrastructure, traçabilité et coût¶
| Issue | Livré |
|---|---|
| #186 | Cluster monté en 1.35 au lieu de 1.32, en support étendu depuis le 23/03 |
| #187 | Socle de trace AWS : CloudTrail multi-région et rétention de 90 jours du journal du plan de contrôle |
| #189 | Durcissement Terraform : flow logs VPC, chiffrement EBS, HTTPS sur le bucket de state. NACL large accepté par l'ADR 035. INC-074 |
| #195 | Détection des anomalies de coût, par un budget filtré sur la ligne extendedSupport. INC-072 |
| #222 | RBAC d'Alloy restreint à la lecture des pods : le chart donnait secrets et pods/log sur tout le cluster (audit C-Q) |
Versions et dépendances¶
| Issue | Livré |
|---|---|
| #204 | Renovate signale une version d'EKS périmée, sans cluster |
| #210 | scripts/check-version-compat.sh refuse une paire EKS / Cilium non testée, en MR et dans le before_script d'infra-start |
| #211 | Cilium 1.19.5 → 1.20.1, prérequis à toute montée d'EKS |
| #212 | Renovate surveille Cilium, et un bump qui quitte la mineure sans relever les bornes est refusé |
| #213 | Outillage du devcontainer épinglé, kubectl ramené dans l'écart de version supporté |
| #214 | Helm qui déploie le cluster épinglé en CI, divergence avec le devcontainer refusée |
| #205 | Renovate écrivait dans requirements.txt, généré par pip-compile. Première mise à jour Python depuis 69 jours |
| #206 | .gitlab-ci-infra.yml échappait à Renovate : l'image Terraform qui applique n'était jamais bumpée |
| #209 | Renovate ne créait que 2 MR par semaine, la file ne se vidait jamais |
| #224 | 8 CVE Debian corrigeables (7 HIGH, 1 CRITICAL) dans une base épinglée par digest que l'amont n'a pas reconstruite. Trouvées, corrigées, promues en prod et prouvées le jour de la clôture |
Chaîne CI/CD¶
| Issue | Livré |
|---|---|
| #178 | INC-069 : les analyses de sécurité ne couvraient aucune merge request, dependency_scanning n'avait jamais tourné |
| #174 | Garde-fou de teardown qui prévient au lieu de détruire (ADR 031), et scripts/check-teardown.sh en huit points |
| #201 | INC-073 : le tag de sast-gate ne retirait aucun SPOF et en ajoutait un second |
| #208 | Le write-back GitOps ordonne les pipelines au lieu d'échouer sur tout commit concurrent |
| #207 | Le quirk du write-back était formulé trop étroitement dans CLAUDE.md et .gitlab-ci.yml |
Stories non livrées¶
Aucune. Toutes les issues du milestone sont fermées, chacune sur sa preuve.
Sortis du sprint le 29/08, à replanifier¶
Sept issues antérieures à l'audit ont quitté le milestone pour laisser la place à la remédiation : #140 (tests E2E), #141 (alerte sur les pannes silencieuses), #163 (Karpenter), #165 (dimensionnement du runner), #179 (découpage du .gitlab-ci.yml), #181 (fermée depuis, le 10/09) et #182 (pipelines parent-enfant).
Nés pendant le sprint, hors milestone¶
| Issue | Sujet |
|---|---|
| #194 | Retirer dev, staging et prod de la politique du rôle contrôleur ESO (audit AW-11, high). Bloquée par l'action 29 de l'audit |
| #196 | Le garde-fou de teardown ne signale pas sa propre panne |
| #215 | Le frontend n'est suivi par aucun manager Renovate, ses dépendances datent de sa création |
| #216 | Helm 4 bloqué, le module Ansible qui pilote Helm ne le déclare pas |
| #220 | Les VPC Flow Logs sont supprimés à chaque teardown, la rétention de 90 jours n'est jamais atteinte |
| #221 | Permissions boundary sur les rôles créés par la CI. Un ARN faux tue tous les CreateRole, validation au montage seulement |
Vélocité¶
Baseline Sprint 6 = 36 éléments livrés sur six semaines. Sprint 7 = 35 éléments sur le timebox annoncé, portés par 70 merge requests au milestone. S'y ajoutent 19 merge requests hors milestone : 13 mises à jour Renovate et 6 changements de sécurité, de CI et de doc liés aux issues du sprint (!356 → !361).
| Type | Nb |
|---|---|
type::security |
16 |
type::ci-cd |
8 |
type::infra |
7 |
type::dependencies |
2 |
type::fix |
1 |
type::docs |
1 |
| Total | 35 |
| Priorité | Nb |
|---|---|
priority::critical |
4 |
priority::high |
10 |
priority::medium |
18 |
priority::low |
3 |
Scope né en cours de sprint : 22 issues sur 35, soit 63 %, contre 77 % au Sprint 6. À l'ouverture du 01/09, le milestone portait 13 issues : #174 et #178 du reliquat, et 11 issues créées du 27 au 30/08, pour l'essentiel issues de l'audit. Les 22 autres sont nées entre le 01/09 et le 14/09.
Lecture honnête de ces chiffres. Le timebox est tenu, mais le sprint n'a pas commencé le 01/09. #178 est fermée le 17/08, et une première vague de remédiation d'audit a été livrée les 29 et 30/08 (#183, #184, #188, #195). La fenêtre réelle de travail va du 27/08 au 14/09, soit près de trois semaines. L'écart avec le Sprint 6 reste net, mais la clôture à date doit une partie de sa réussite à ce démarrage anticipé.
Incidents majeurs et résolutions¶
Le sprint a capitalisé 9 incidents : 1 de sécurité (SEC-008) et 8 techniques (INC-069 à INC-076). Détail complet dans sprint7.md.
L'incident de sécurité¶
SEC-008 a été trouvé en lisant le code, à la fin d'une session consacrée à autre chose. Terraform ne créait pas seulement l'emplacement des secrets : il générait leur valeur, la poussait dans Secrets Manager, et en gardait une copie en clair dans le state. L'attribut sensitive masque l'affichage, jamais le stockage. La CI pouvait lire ce state.
La remédiation a demandé un ordre précis. La rotation vient après la parade : tant que Terraform fabrique la valeur, la tourner la fait revenir dans le state au plan suivant. D'où la séquence ADR 033 (déclarer sans fabriquer), ADR 034 (même chose pour les clés IAM), ADR 037 (tourner puis purger l'historique) et ADR 038 (master RDS en écriture seule).
Leçon : un commentaire n'est pas un contrôle. Le dépôt décrivait une propriété de sécurité que le code ne portait pas.
Les incidents techniques¶
| Incident | Sujet | Leçon retenue |
|---|---|---|
| INC-069 | La couverture de sécurité annoncée par la CI ne couvrait presque rien | Un dispositif de sécurité se vérifie par ce qu'il a analysé, pas par sa présence dans un fichier de configuration |
| INC-070 | Deux pièges sur la route du MFA obligatoire, dont un qui verrouille dehors | Une politique de refus se relit par sa porte de sortie. Vus avant l'apply, aucun dégât |
| INC-071 | Deux limites de plan GitLab en une soirée | Une limite de plan peut fermer la remédiation d'un constat. Et auteur, committer et pousseur sont trois identités distinctes |
| INC-072 | Quatre alarmes, dont une qui a sonné pour rien, et six fois le tarif pendant cinq mois | Une supervision n'a de valeur que si elle produit une action. Correctif trouvé au troisième essai |
| INC-073 | Une parade contre un SPOF qui n'en retirait aucun, et en ajoutait un second | Un job ne peut pas être plus disponible que ce dont il dépend |
| INC-074 | Le montage bloqué par une permission que le code exigeait depuis #189 | Une ressource ajoutée à une stack est aussi une permission ajoutée à l'identité qui l'applique |
| INC-075 | Un apply de la CI aurait rendu le garde-fou de teardown muet |
Un plan se lit en entier, y compris les lignes qui ne parlent pas du changement en cours |
| INC-076 | La politique CI réduite laisse le cluster naître sans un seul nœud | AWS évalue les droits de l'appelant pour des actions que le service exécute lui-même. Seul un montage complet discrimine |
Décisions d'architecture (ADR)¶
| ADR | Décision |
|---|---|
| 031 | Le garde-fou de teardown prévient au lieu de détruire |
| 032 | Privilèges des clés AWS de la CI. La clé ECR n'est pas dédoublée : IAM ne sait poser aucune condition sur le nom d'un tag d'image |
| 033 | Terraform déclare les secrets, il ne les fabrique plus |
| 034 | Terraform déclare les identités CI, il n'en fabrique plus les clés |
| 035 | Le NACL par défaut reste large en entrée, le FAIL CIS est accepté |
| 036 | Le push direct sur develop reste ouvert aux Maintainers, EK-15 est accepté |
| 037 | Trois valeurs sont tournées, le stock du bucket de state est purgé et borné |
| 038 | Le mot de passe master RDS passe en écriture seule, le state ephemeral ne le porte plus |
Huit ADR en un sprint, dont trois qui acceptent un risque (031, 035, 036). C'est un changement de nature par rapport aux sprints précédents : un constat d'audit peut se fermer par une décision écrite et argumentée, pas seulement par un correctif. Chaque acceptation nomme ce qu'elle ne couvre pas.
Rétrospective¶
Ce qui a bien fonctionné¶
- Le timebox tenu. Deux semaines annoncées, clôture à la date, reliquat sorti au lieu de prolonger la fenêtre.
- Des priorités qui ne sont pas au jugé. Les priorités des issues d'audit reprennent les gravités du rapport. Les quatre
criticalsont toutes fermées entre le 29/08 et le 06/09, dans la première moitié du sprint. - Fermer sur preuve, pas sur merge. Plusieurs issues sont restées ouvertes après le merge, en
Refsplutôt qu'enCloses, jusqu'à leur preuve réelle : #186, #219, #222, #223 et #224. Pour #224, le scan de registre a été relancé à la main plutôt que d'attendre le lendemain 8 h. #203 est fermée sur une sonde réelle refusée, #189 sur les appels CloudTrail du service. - Un montage entier consacré aux preuves. Le 11/09, un seul montage a validé tout ce qui avait été mergé depuis le 06/09, puis a été détruit à 16:24, avec
check-teardown.shà 8/8. - Des recommandations retournées sur mesure. Sur #199, la recommandation initiale était « purger sans tourner ». La comparaison d'empreintes a montré que 3 valeurs du stock étaient encore vivantes, et la décision s'est inversée avant d'agir.
Ce qui a bloqué ou coûté du temps¶
- Des comptes plausibles et faux. Trois en deux jours sur le bucket de state : un préfixe qui matchait aussi les fichiers de verrou, une requête évaluée page par page, puis un
total: 0rendu sur des credentials expirés. Aucun message n'annonce un compte partiel. - Des tests qui annulaient ce qu'ils mesuraient. Quatre cas le 07/09, dont des dry-runs Renovate qui forçaient
schedule: nullet masquaient justement la cause cherchée : un créneau horaire natif que personne n'avait écrit. - Le montage cassé du 06/09 (INC-076) : la politique réduite depuis CloudTrail ne couvrait pas les appels faits par le service au nom de la CI. Cluster facturé, zéro nœud, réparé dans la journée.
- Le runner
.112reste un SPOF. Éteint au réveil du 07/09, deux pipelines bloqués deux heures. Éteint du 11/09 au 14/09, les pipelines Renovate de la nuit ont échoué faute de runner. - Une promotion en prod sans gate. Pour #224, l'image est passée de dev à prod le même jour. Le palier staging est resté formel, le cluster étant détruit, et aucun test ne lance l'image livrée ni ne vérifie l'app déployée (#140). Ce que la CI teste réellement : 41 tests
pytestà 93 % de couverture, contre une image Python générique. - Des alarmes en place et non lues. Le scan de registre a échoué 15 matins de suite, et le surcoût du support étendu a couru cinq mois alors que la ligne s'appelait littéralement
extendedSupportdans la facture.
3 actions concrètes pour Sprint 8¶
-
Tenir et tracer la revue de mi-sprint. Vérifiable : un compte rendu daté, avec l'état du milestone ce jour-là et ce qui en sort. Le Sprint 7 a tenu sa date, mais le point d'étape n'a laissé aucune trace.
-
Livrer enfin #141, ou la fermer par une décision écrite. Quatrième inscription sinon. Vérifiable : couper volontairement le composant surveillé et recevoir l'alerte. Si elle n'a toujours pas sa place, un ADR qui accepte le risque vaut mieux qu'un report silencieux de plus.
-
Rendre l'absence du runner visible avant qu'elle bloque une merge request. Vérifiable : couper le
.112et constater qu'un signal arrive avant le premierstuck_pending_no_matching_runners. Aujourd'hui, c'est la pipeline bloquée qui fait office d'alerte (#159).
Validation end-to-end¶
Dernière validation complète en conditions réelles : 2026-09-11, pipeline 2840903511 (infra-start 721 s, bootstrap 603 s), avant le teardown du cluster.
- Garde-fou EKS / Cilium exercé en live pour la première fois : Kubernetes 1.35 accepté dans l'intervalle testé par Cilium 1.20.1
- Provider AWS v6 appliqué pour la première fois (
v6.64.0), plan à 46 ressources contre 48 le 06/09 : l'écart vaut exactement les deux ressources retirées par #223 - Helm épinglé vu dans le vrai job
bootstrap:v3.21.4 - Cilium 1.20.1 seul CNI actif,
aws-nodeetkube-proxyabsents - ESO 7/7
SecretSynced, 15/15 applications ArgoCDSyncedetHealthy, 9 routesAccepted, DNS et HTTPS OK - Master RDS absent du state (#219), avec témoin : le state du 06/09 le portait encore. Pods API
Readydans les trois environnements - Alloy :
can-i list secretsàno, avec le contrôle positiflist podsàyesqui rend cenovalable - Rotations du 09/09 validées de bout en bout : admin Grafana, SSO GitLab d'ArgoCD et de Grafana, webhook Slack
- Teardown à 16:24,
check-teardown.shà 8/8, vérifié côté AWS et non au statut du pipeline
Le jour de la clôture. Le scan de registre de 8 h est rouge les 13 et 14/09 : 8 CVE corrigeables (7 HIGH, 1 CRITICAL) sur l'image de prod, dans perl-base, libsqlite3-0, libpcre2-8-0 et gzip. Un simple rebuild ne suffisait pas : la base python:3.12-slim est épinglée par digest (la leçon d'INC-068), et l'amont ne l'a pas reconstruite depuis le 01/09. Le même constat bloquait trivy-image-scan sur toute merge request qui reconstruit l'image.
- Correctif (
!414) : les quatre paquets ajoutés au bloc--only-upgradedu Dockerfile. 0 CVE en local, contre 8 sur un témoin non patché - Promotion :
!415(staging) puis!416(prod), sans rebuild - Preuve : scan de registre relancé à la main (pipeline
2846796567), imagedeployed-913bcff…, 87 paquets analysés,Aucune nouvelle CVE fixable
Limite acceptée : la prod n'a jamais fait tourner cette image. Elle se valide en live au prochain montage.
Documentation publique : doc.devopsyouss.com
Sprint 7 clôturé le 2026-09-14 à 35 issues sur 35. #220 et #221 ouvrent le Sprint 8.