docs(livrables): verse le rapport collectif EC02 et le rapport de sécurisation EC04
Sources Markdown et versions figées PDF, relevés du 24/09 sur le commit gelé
9f343e9, et les quatorze preuves anonymisées du rapport de sécurisation,
chacune avec la commande qui la rejoue. Porte la déclaration d'usage de l'IA
et la section anonymisation et RGPD demandées par #154.
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
# EC02 · Management de projet : rapport collectif
|
||||
|
||||
**Groupe 3 (HEADL_015B) · Projet EnerVision.**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Fichier source** | `EADL26_EC02 - Rapport Collectif HEADL_015B-G3.md`, encodage UTF-8, aucun média externe |
|
||||
| **Version figée** | `EADL26_EC02 - Rapport Collectif HEADL_015B-G3.pdf`, produite par la chaîne Markdown → HTML → CSS de pagination → PDF |
|
||||
| **Dépôt** | Devoir Teams, à côté du ZIP du dépôt Git, **et** dans le dépôt Git lui-même (`docs/livrables/EC02/`) |
|
||||
| **Échéance** | Vendredi 25/09/2026, 9h00 (gel technique) |
|
||||
| **Relevé** | **24/09/2026 à 14h25**, sur le commit gelé `9f343e9` (`dev` = `main`) et par l'API GitHub : dépôt, tracker, board, jalons, PR et revues relevés au même instant, heures en heure locale (CEST) |
|
||||
|
||||
Chaque chiffre de ce rapport est reproductible par une commande citée en fin de document : le
|
||||
critère officiel est un compte rendu d'activité « complet et **honnête** ». Les chiffres du
|
||||
rapport du 23/09 (board du 18/09, tracker du 21/09) sont remplacés, pas complétés.
|
||||
|
||||
---
|
||||
|
||||
## 1. Organisation de l'équipe
|
||||
|
||||
Le pilotage passe par un **GitHub Project** (« EnerVision », projet n°2), avec assignation
|
||||
nominative, et par le dépôt `ProjetPiscine_EnerVision`, branche d'intégration `dev`, branche de
|
||||
production `main`.
|
||||
|
||||
### Activité par membre
|
||||
|
||||
| Membre | Compte GitHub | Commits sur `dev`, hors merges | PR mergées, auteur principal | PR mergées par lui | Board : Done / En cours / Todo | Domaines observés dans ses PR |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Johan LEROY (Tech Lead) | `JohanLeroy` | 172 | 34 | 59 | 36 / 0 / 0 | Socle backend (auth, RBAC, audit, contrat OpenAPI), moteur de règles, vues frontend, reverse proxy et certificats, déploiement continu et trois environnements, Terraform, CI unifiée, e2e et charge, supervision, Garage et rétention, tests d'intégration, dérive, documentation |
|
||||
| Dorian PESCE | `phyri0s` | 42 | 11 | 11 | 16 / 0 / 0 | Terraform k3s, endpoints, pipeline LightGBM, scoring, alertes internes, DAGs ML, DAST, en-tête CORP, réconciliation des sources |
|
||||
| Inès ZANG | `ineszang` | 54 | 4 | 5 | 5 / 2 / 0 | Terraform initial, pipeline CI, SonarCloud, administration du dépôt, procédures de déploiement |
|
||||
| Meryem EL GHAM | `Meryemel-gham` | 23 | 6 | 2 | 7 / 2 / 0 | Schéma de données, imports historique et API Mock, DAGs d'import |
|
||||
| Valentin DE FARIA RODRIGUES | `ValentinDeFaria` | 20 | 8 | 4 | 12 / 0 / 0 | Frontend et ses tests, auth frontend, audit de dépendances, supervision des capteurs, registre MLflow, amorce Garage |
|
||||
| Dependabot | - | 12 | 12 | - | - | Mises à jour de dépendances |
|
||||
| *(remontées `dev` → `main`)* | - | - | 6 | - | - | - |
|
||||
| *(non assigné)* | - | - | - | - | 1 / 0 / 1 | - |
|
||||
|
||||
Totaux : **323 commits** hors merges sur `dev` (458 avec merges), **81 PR mergées**, 69 éléments
|
||||
au board. Méthode : l'**auteur principal** d'une PR est l'auteur majoritaire des commits de sa
|
||||
branche. Deux PR ouvertes par Valentin reviennent ainsi à Johan : #73 et #164 (5 commits sur 6,
|
||||
sur une amorce de Valentin). Les six remontées de `dev` vers `main` ne sont attribuées à
|
||||
personne. Le nombre de commits mesure une activité, pas une valeur : les pratiques de découpage
|
||||
diffèrent d'un membre à l'autre.
|
||||
|
||||
**Correspondances nom / identifiant.** Établies par Git : `Dorian`, `Dorian PESCE` et
|
||||
`Phyrios` sont le compte `phyri0s` ; `ineszang` et `ineszang44` partagent la même adresse,
|
||||
`Valentin` et `valentin` aussi.
|
||||
|
||||
**Rôles principaux** (Tech Lead, Cloud/DevOps, Data & IA, Fullstack Dev, PO). Seul celui de
|
||||
Tech Lead a été nommé au départ ; les autres se lisent dans les PR de chacun :
|
||||
|
||||
| Membre | Rôle principal exercé |
|
||||
|---|---|
|
||||
| Johan LEROY | **Tech Lead** : architecture, intégration, relecture, sécurité, déploiement |
|
||||
| Dorian PESCE | **Data & IA** : modèle LightGBM, DAGs ML, scoring ; premier relecteur de l'équipe |
|
||||
| Inès ZANG | **Cloud / DevOps** : Terraform initial, pipeline CI, SonarCloud, administration du dépôt |
|
||||
| Meryem EL GHAM | **Data** : schéma de données, imports historique et API Mock, DAGs d'import |
|
||||
| Valentin DE FARIA RODRIGUES | **Fullstack Dev** : frontend et ses tests, supervision des capteurs, registre MLflow |
|
||||
|
||||
Le rôle de **PO** n'a pas eu de titulaire nommé : les arbitrages de périmètre ont été pris aux
|
||||
points d'avancement, dont la coupe du 21/09. Le formateur demandait une **rotation** des rôles
|
||||
sur les deux semaines : elle n'a pas eu lieu. Chacun est resté sur son domaine d'origine, ce qui a
|
||||
favorisé la vitesse au détriment de la polyvalence.
|
||||
|
||||
### RACI
|
||||
|
||||
Reconstitué depuis l'historique des PR mergées, chaque PR étant rattachée aux chantiers dont elle
|
||||
touche les fichiers : *Responsible* = qui écrit, *Accountable* = qui valide le merge,
|
||||
*Consulted* = qui relit (revue ou commentaire), *Informed* = toute l'équipe, par le board et les
|
||||
points d'avancement.
|
||||
|
||||
| Chantier | Responsible | Accountable | Consulted | Informed |
|
||||
|---|---|---|---|---|
|
||||
| Backend / API | `JohanLeroy`, `phyri0s`, `Meryemel-gham` | `JohanLeroy` | `phyri0s` | équipe |
|
||||
| Frontend | `JohanLeroy`, `ValentinDeFaria`, `ineszang` | `JohanLeroy` | `phyri0s` | équipe |
|
||||
| Data & ML | `phyri0s`, `Meryemel-gham`, `JohanLeroy`, `ValentinDeFaria` (registre) | `JohanLeroy` | `phyri0s`, `JohanLeroy` | équipe |
|
||||
| Infra / CI-CD | `JohanLeroy`, `ineszang`, `phyri0s`, `ValentinDeFaria` | `JohanLeroy` | `phyri0s` | équipe |
|
||||
| Sécurité | `JohanLeroy`, `phyri0s` (DAST) | `JohanLeroy` | `phyri0s` | équipe |
|
||||
|
||||
Lecture : le Tech Lead valide l'intégration de tous les chantiers, et `phyri0s` en est le premier
|
||||
relecteur. Le RACI n'a pas été posé en amont : il se lit a posteriori dans les merges et les revues.
|
||||
|
||||
---
|
||||
|
||||
## 2. Méthodologie, backlog, user stories
|
||||
|
||||
Constat factuel tiré du GitHub Project, du tracker d'issues et du dépôt :
|
||||
|
||||
- **Méthodologie** : **Kanban à jalons**, pratiqué sans avoir été nommé en amont. Le board a
|
||||
**3 colonnes** (`Todo` / `In progress` / `Done`), découpé en **2 itérations** (« Première
|
||||
semaine », « Seconde semaine ») et **6 jalons datés**.
|
||||
- **Priorisation MoSCoW** appliquée à chaque ticket, et **respectée dans les faits** : au
|
||||
24/09, **56 `Must` faits sur 56**, 5 `Should` sur 5, 4 `Could` sur 6. Au 18/09, 66 % des
|
||||
`Must` étaient faits contre 0 % des `Should` et des `Could` : aucun ticket de confort n'a été
|
||||
pris avant un ticket essentiel.
|
||||
- **Estimation en taille de tee-shirt** : 48 S, 13 M, 5 XS, 3 sans taille.
|
||||
- **Traçabilité ticket → PR → commit** : chaque ticket livré porte ses PR liées.
|
||||
- **Revue de code avant merge.** `JohanLeroy` a relu par écrit **23 PR d'autres membres** :
|
||||
15 revues formelles (8 `APPROVED`, 5 `COMMENTED`, 2 `CHANGES_REQUESTED`) et 8 revues publiées
|
||||
en commentaire. `phyri0s` a posé 17 approbations formelles, `ValentinDeFaria` 2. Au total,
|
||||
**55 des 62 PR de fonctionnalité (89 %)** ont été relues par un autre membre avant merge, par
|
||||
revue formelle ou commentaire ; les remontées de `dev` vers `main` ne portent que des PR déjà
|
||||
relues.
|
||||
- **Intégration.** **81 PR mergées** : 62 vers `dev`, 19 vers `main` (6 remontées, 1 réglage
|
||||
Sonar, 12 Dependabot). `JohanLeroy` en a mergé 59, dont 42 vers `dev` ; parmi elles, 13 PR
|
||||
écrites par d'autres membres et 12 PR Dependabot.
|
||||
- **Décisions écrites.** **20 ADR** : 17 rédigés par `JohanLeroy` (0001 à 0004, 0006 à 0010,
|
||||
0013 à 0020), 1 par `phyri0s` (0005, LightGBM), et 2 procédures de déploiement versées par
|
||||
`ineszang` (0011, 0012), qui relèvent davantage de la note d'exécution que de l'ADR.
|
||||
- **Points d'avancement** les 15, 17, 18 et 21/09, chacun terminé par une décision ; ceux du 15
|
||||
et du 18/09 sont versionnés dans `docs/dailies/`. Le compte rendu du 18/09 a été rédigé après
|
||||
coup, le 21/09.
|
||||
- **Étiquettes par domaine** sur les issues (`feature`, `backend`, `frontend`, `ml`, `infra`,
|
||||
`ci/cd`, `test`, `securite`, `pipeline ETL`, `accessibility`).
|
||||
- **User stories formalisées** (« en tant que... je veux... afin de... ») : non retrouvées
|
||||
telles quelles, le besoin fonctionnel est porté par le corps des issues.
|
||||
|
||||
---
|
||||
|
||||
## 3. Planning, jalons, gestion des risques
|
||||
|
||||
### Jalons internes du projet
|
||||
|
||||
À ne pas confondre avec la numérotation J1 à J10 du calendrier de formation : ce sont deux
|
||||
échelles différentes.
|
||||
|
||||
| Jalon projet | Échéance (API) | Fermées / total au 24/09 | État |
|
||||
|---|---|---|---|
|
||||
| J1 · Environnement et dépôt | 14/09 | 5/5 | clos le 15/09 |
|
||||
| J2 · Périmètre et choix technologiques | 15/09 | 4/4 | clos le 16/09 |
|
||||
| J3 · Ingestion et backend | 21/09 | 22/22 | tout fermé, jalon laissé ouvert |
|
||||
| J4 · Architecture, sécurité, frontend | 22/09 | 24/24 | tout fermé, jalon laissé ouvert |
|
||||
| J5 · Robustesse et livrables | 23/09 | 5/6 | reste #154 (déclaration IA et RGPD, portée par ce rapport) |
|
||||
| J6 · Amélioration possible | 28/09 | 7/9 | créé le 21/09 pour le périmètre coupé (§5) ; restent #11 et #54 |
|
||||
|
||||
Le point d'avancement du 21/09 donnait le 18/09 et le 21/09 pour J3 et J4 ; l'API donne
|
||||
aujourd'hui le 21/09 et le 22/09. L'API ne garde pas l'historique des échéances : l'écart est
|
||||
signalé, pas expliqué.
|
||||
|
||||
### Calendrier institutionnel
|
||||
|
||||
| Jalon | Contenu | Date |
|
||||
|---|---|---|
|
||||
| J1 | Rendu EC01, dossier de conception individuel | fait, 14/09 |
|
||||
| J9 | Oral EC01, 15 min + ~10 min de questions | jeudi 24/09 |
|
||||
| J10 | Gel technique 9h00, rendu EC02 à EC06, oral EC02 (15 min + ~5 min de vidéo + ~5 min de questions) | vendredi 25/09 |
|
||||
|
||||
### Risques identifiés et leur traitement
|
||||
|
||||
| Risque | Impact | Statut au 24/09 |
|
||||
|---|---|---|
|
||||
| **`main` en retard sur `dev`** | `main` est la branche par défaut et celle de la production | **Traité.** Six remontées (#125, #152, #160, #161, #163, #165) ; au gel, `main` et `dev` portent le même commit. Sept déploiements de production réussis, le dernier sur le commit gelé |
|
||||
| Tickets sans assigné (17 au 21/09) | Aucun responsable identifié | **Traité par arbitrage** : coupe du 21/09 (§5), puis assignation. Restent 2 issues ouvertes sans assigné, #55 et #154 |
|
||||
| Jalon J5 sans assigné (#41, #45, #46, #47) | Preuves attendues pour EC03 et EC04 | **Traité** : les quatre livrés et assignés, DAST (#140), tests d'intégration (#147), e2e Playwright et charge k6 (#148) |
|
||||
| Aucun scan de code dans le pipeline | Note DevSecOps EC03 / EC04 | **Traité** : SAST Bandit bloquant (#121), DAST OWASP ZAP (#140). **Restent hors CI** : Trivy et gitleaks, joués à la main pour le rapport EC04 |
|
||||
| Aucun déploiement | Attendu explicite d'EC03 et EC04 | **Traité et constaté** : trois environnements sur la machine du groupe (production sur `main`, recette sur `dev`, dev à la demande), certificats Let's Encrypt, runner auto-hébergé (ADR 0009, 0017, 0018) |
|
||||
| Montée de version majeure d'Airflow par Dependabot (#135) | Provisionnement et déploiement cassés | **Traité le jour même** (#143) |
|
||||
| Trois PR immobilisées par un quality gate mal configuré | Blocage de la chaîne de merge | **Traité le 18/09**, en configuration et non par contournement |
|
||||
| Mémoire de la machine (8 Go) insuffisante pour trois stacks | Arrêts par manque de mémoire | **Traité** : portée à 32 Go sur demande à l'école (ADR 0017) |
|
||||
| Chiffrement au repos (#42) | Données en clair sur le disque | **Partiel, découvert le 24/09** : la machine est un conteneur LXC où LUKS est impossible ; seules les archives sont chiffrées (SSE-C, ADR 0020), demande adressée à l'école |
|
||||
| Production sans approbation humaine, branches non protégées | Un push non relu part en production | **Ouvert** : annoncés par l'ADR 0009, laissés à poser par l'ADR 0014, jamais activés ; seule l'administratrice du dépôt peut le faire |
|
||||
| Services hors dépôt sur la machine : k3s et trois serveurs Vault, installés depuis une branche de travail non fusionnée | Surface exposée que le code livré ne documente pas : l'API k3s et les trois Vault écoutent sur toutes les interfaces, k3s redémarre en boucle depuis le 17/09 | **Découvert le 24/09**, à arbitrer par l'administratrice : le code livré n'en dépend pas (rapport EC04, constat 1) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Compte rendu d'activité honnête
|
||||
|
||||
### Indicateurs, depuis la baseline
|
||||
|
||||
**Baseline : 57 issues créées le 14/09**, jour 1. Série quotidienne relevée par l'API :
|
||||
|
||||
| Jour | Issues créées | Périmètre | Fermées | Cumul fermées | Ouvertes | PR mergées | Cumul PR |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 14/09 | 57 | 57 | 5 | 5 | 52 | 3 | 3 |
|
||||
| 15/09 | 1 | 58 | 10 | 15 | 43 | 7 | 10 |
|
||||
| 16/09 | 1 | 59 | 8 | 23 | 36 | 10 | 20 |
|
||||
| 17/09 | 6 | 65 | 10 | 33 | 32 | 9 | 29 |
|
||||
| 18/09 | 2 | 67 | 6 | 39 | 28 | 9 | 38 |
|
||||
| 21/09 | 5 | 72 | 16 | 55 | 17 | 11 | 49 |
|
||||
| 22/09 | 1 | 73 | 2 | 57 | 16 | 18 | 67 |
|
||||
| 23/09 | 3 | 76 | 11 | 68 | 8 | 12 | 79 |
|
||||
| 24/09 | 0 | 76 | 4 | 72 | 4 | 2 | 81 |
|
||||
|
||||
| Indicateur | 16/09 | 18/09 | 24/09 |
|
||||
|---|---|---|---|
|
||||
| Board : Done / In progress / Todo | 19 / 6 / 26 | 33 / 6 / 21 (16h) | **66 / 2 / 1** |
|
||||
| `Must` faits | 45 % | 66 % | **100 %** (56/56) |
|
||||
| `Should` et `Could` faits | 0 % | 0 % | 100 % et 67 % |
|
||||
| Issues fermées / périmètre | 23 / 59 | 39 / 67 | **72 / 76** |
|
||||
|
||||
**Deux lectures à défendre.** La priorisation est tenue dans les faits : le premier `Should`
|
||||
n'a été fermé que le 23/09 à 11h10, quand 54 des 56 `Must` l'étaient déjà. Et le périmètre a dérivé de
|
||||
57 à 76 issues (+33 %), dont 19 créées en cours de route : la dérive a été absorbée par la coupe
|
||||
du 21/09 (§5) plutôt que laissée ouverte. Le 22/09 illustre la limite du comptage : 2 issues
|
||||
fermées, mais 18 PR mergées, le déploiement continu et Terraform, les plus lourdes du projet.
|
||||
|
||||
### Livré depuis le 18/09 à 16h00
|
||||
|
||||
| PR | Mergée le | Auteur principal | Contenu |
|
||||
|---|---|---|---|
|
||||
| #113, #118 | 18 et 21/09 | `phyri0s` | Détection des alertes internes ; entraînement et scoring LightGBM orchestrés par deux DAGs |
|
||||
| #114, #124 | 21/09 | `JohanLeroy` | Moteur de règles de recommandation (ADR 0006) ; DAG d'alertes et de recommandations (ADR 0008) |
|
||||
| #112, #138, #146 | 21 et 22/09 | `Meryemel-gham` | Import depuis l'API Mock borné ; import historique, puis import horaire, orchestrés par Airflow |
|
||||
| #107, #123 | 21 et 22/09 | `ValentinDeFaria` | Supervision des capteurs par site ; enregistrement du modèle dans le registre MLflow |
|
||||
| #117, #121 | 21/09 | `JohanLeroy` | Reverse proxy Nginx et TLS (ADR 0007) ; SAST Bandit et vue CI/CD |
|
||||
| #136, #137 | 21/09 | `JohanLeroy` | Flux d'alertes du tableau de bord ; vue recommandations |
|
||||
| #139, #143, #144 | 22/09 | `JohanLeroy` | Déploiement continu, runner auto-hébergé (ADR 0009) ; réalignement Airflow 3 ; Terraform provisionne la machine (ADR 0010) |
|
||||
| #140 | 22/09 | `phyri0s` | Scan dynamique OWASP ZAP de l'API |
|
||||
| #147 | 22/09 | `JohanLeroy` | Tests d'intégration API, base et ML ; surveillance de dérive (ADR 0013) |
|
||||
| #148 | 23/09 | `JohanLeroy` | CI unifiée (ADR 0014), e2e Playwright et charge k6 (ADR 0015), supervision Prometheus, Alertmanager, Grafana (ADR 0016) |
|
||||
| #149 | 23/09 | `phyri0s` | En-tête `Cross-Origin-Resource-Policy` sur toutes les réponses |
|
||||
| #151 | 23/09 | `JohanLeroy` | Troisième environnement, `dev`, déployé à la demande (ADR 0017) |
|
||||
| #156, #159 | 23/09 | `JohanLeroy` | Noms publics, certificats Let's Encrypt par DNS-01, frontal SNI (ADR 0018) |
|
||||
| #162 | 23/09 | `phyri0s` | Réconciliation des deux sources de relevés |
|
||||
| #164 | 24/09 | `JohanLeroy` | Garage par environnement, rétention exportée des relevés, chiffrement des archives (ADR 0019, 0020) |
|
||||
| 6 remontées, #142, 12 Dependabot | 21 au 24/09 | - | Mises en production, analyse Sonar sautée sur les PR Dependabot, mises à jour de dépendances |
|
||||
|
||||
### Ce qui n'a pas été livré, et pourquoi
|
||||
|
||||
- **#11 Responsive et #54 comparateur de scénarios** : `Could`, en cours au gel, jalon J6.
|
||||
Démonstration sur poste, sans usage mobile dans le scénario du client pilote.
|
||||
- **#55 Bouton de pic fictif** : ni assigné, ni au board, rien dans le code.
|
||||
- **#43 Accessibilité** : fermée en doublon le 23/09, au motif qu'elle serait couverte par les
|
||||
tests Playwright ; **aucun test d'accessibilité n'existe**. La fermeture est à corriger dans
|
||||
l'outil, pas à défendre.
|
||||
- **#42 Chiffrement au repos** : fermée comme faite, livrée **en partie** (archives seulement),
|
||||
pour une raison d'infrastructure découverte le 24/09 (§3).
|
||||
- **Loki, Trivy et gitleaks en CI, approbation de la production** : prévus ou annoncés, non
|
||||
faits.
|
||||
- **Rotation des rôles** : elle n'a pas eu lieu (§1).
|
||||
|
||||
Lecture honnête : tout ticket assigné à quelqu'un a été mené au bout ou reste en cours au gel.
|
||||
Le retard du 21/09 n'était pas un problème d'exécution individuelle mais de **répartition** : il
|
||||
a été traité par la coupe et l'assignation, puis rattrapé. Les faiblesses restantes sont de
|
||||
rigueur de processus, pas de livraison : fermetures d'issues trop généreuses (#42, #43) et
|
||||
réglages de protection jamais activés.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. Périmètre coupé
|
||||
|
||||
**Un périmètre coupé et argumenté est un acte de management ; une issue laissée ouverte sans
|
||||
rien est un trou.** La coupe a été faite le 21/09, jour de l'échéance du J4, et tracée dans
|
||||
l'outil : un jalon **J6 « Amélioration possible »**, échéance 28/09, donc après le gel. Elle a
|
||||
servi à ordonner, pas à renoncer : une fois les `Must` faits, une partie du J6 a été livrée
|
||||
avant le gel.
|
||||
|
||||
| Issue | Sujet | Raison de la coupe au 21/09 | Au gel |
|
||||
|---|---|---|---|
|
||||
| #11 | Responsive | Démonstration sur poste, aucun usage mobile | en cours, non livré |
|
||||
| #24 | MinIO, couche bronze | La charge brute est déjà en base (`raw_data`) | **livré autrement** : Garage, jugé plus léger (ADR 0019) |
|
||||
| #26 | Monitoring Prometheus et Grafana | Classé bonus par le sujet | **livré** (#148, ADR 0016) |
|
||||
| #36 | Rétention des données | Jeu de démonstration borné | **livré** : export vers Garage puis suppression (#164) |
|
||||
| #42 | Chiffrement au repos | Données synthétiques, priorité au chiffrement en transit | **partiel** : archives chiffrées, base en clair (ADR 0020) |
|
||||
| #43 | Accessibilité | Hors des critères de notation technique | fermée en doublon, **non livrée** |
|
||||
| #54 | Comparateur de scénarios | Confort (`Could`) | en cours, non livré |
|
||||
|
||||
Les tests e2e (#46) et de charge (#47), dont la coupe était proposée le 21/09, ont finalement
|
||||
été livrés (#148). Deux sacrifices restent assumés : **Big Data**, hors de portée dans le temps
|
||||
imparti, et **RPA avancé**, au-delà de l'orchestration Airflow.
|
||||
|
||||
---
|
||||
|
||||
## 6. Écarts entre conception (EC01) et réalisation
|
||||
|
||||
Le guide d'évaluation le dit : **les écarts entre conception et réalisation font partie du
|
||||
compte rendu d'activité**. Chaque écart ci-dessous porte sa trace.
|
||||
|
||||
| # | Choix du dossier EC01 (14/09) | Réalisé au gel | Verdict |
|
||||
|---|---|---|---|
|
||||
| 1 | FastAPI / Python pour l'API | FastAPI, contrat OpenAPI versionné et testé | ✅ Tenu |
|
||||
| 2 | PostgreSQL 17 + TimescaleDB | Identique, relevés en hypertable (ADR 0001) | ✅ Tenu |
|
||||
| 3 | JWT + Argon2id, RBAC à 3 rôles | Identique, plus rotation du jeton de rafraîchissement (ADR 0002, 0003) | ✅ Tenu |
|
||||
| 4 | Une valeur nulle n'est jamais effacée | Qualité de donnée et imputation tracées, gravées par une contrainte `CHECK` | ✅ Tenu |
|
||||
| 5 | Contrats d'interface figés | Contrat OpenAPI testé, table `prediction` comme frontière ML | ✅ Tenu |
|
||||
| 6 | Docker Compose plutôt que Kubernetes | Compose, un projet par environnement (ADR 0009, 0017) | ✅ Tenu |
|
||||
| 7 | On-premise plutôt que cloud public | Machine de l'école, aucune ressource cloud | ✅ Tenu |
|
||||
| 8 | Traefik en terminaison TLS | Nginx par stack (ADR 0007) et frontal SNI (ADR 0018) | 🔁 Substitué |
|
||||
| 9 | Prophet + IsolationForest | LightGBM, un modèle global (ADR 0005), alertes internes par règles, dérive surveillée (ADR 0013) | 🔁 Substitué |
|
||||
| 10 | APScheduler pour l'ETL | Airflow, 7 DAGs : imports, entraînement, scoring, alertes, dérive, rétention | 🔁 Substitué |
|
||||
| 11 | MinIO, couche bronze | Garage par environnement (ADR 0019) | 🔁 Substitué |
|
||||
| 12 | Ansible, durcissement et déploiement | Terraform et script rejouable (ADR 0010) ; durcissement non automatisé | ⚠️ Partiel |
|
||||
| 13 | SOPS + age pour les secrets | Secrets générés sur la machine, hors de Git | ❌ Non fait |
|
||||
| 14 | Trivy, Bandit, gitleaks en CI | Bandit (#121) et un DAST ZAP non prévu (#140) ; Trivy et gitleaks hors CI | ⚠️ Partiel |
|
||||
| 15 | Prometheus, Grafana, Loki | Prometheus, Alertmanager, Grafana actifs en production (ADR 0016) ; pas de Loki | ⚠️ Partiel |
|
||||
| 16 | Runner auto-hébergé, déploiement automatique | `deploy.yml` sur runner auto-hébergé, sept déploiements de production | ✅ Tenu |
|
||||
| 17 | Deux réseaux Docker, données jamais exposées | Un seul composant exposé, le frontal ; tout le reste sur la boucle locale | 🔁 Intention tenue, autre moyen |
|
||||
|
||||
**Décompte sur les 17 choix du dossier : 8 tenus, 5 substitués, 3 partiels, 1 non fait.** Au
|
||||
23/09, il était de 8 tenus, 4 substitués, 3 partiels et 2 non faits : MinIO est passé de « non
|
||||
fait » à « substitué » par Garage.
|
||||
|
||||
**Hors de cette liste**, le dashboard était prévu en React / Vite ; il est en **Angular 22**
|
||||
depuis son initialisation le 14/09 (commit `49f4697`, PR #52). Sans effet sur le reste de
|
||||
l'architecture, puisque le frontend ne connaît que le contrat d'API. Motif : choix de Valentin, développeur
|
||||
full stack de l'équipe, qui maîtrisait Angular ; aucune objection dans le cadre du projet.
|
||||
|
||||
---
|
||||
|
||||
## 7. Usage de l'intelligence artificielle
|
||||
|
||||
Déclaration exigée par le formateur : outils utilisés, tâches réalisées, valeur ajoutée,
|
||||
limites constatées, vérifications humaines.
|
||||
|
||||
### Déclaration de Johan LEROY (Tech Lead)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Outil** | Claude Code (Anthropic), en assistant de développement dans le terminal et l'IDE |
|
||||
| **Tâches** | Aide à la rédaction de code backend, d'infrastructure et de tests, relecture de PR en amont de la revue humaine, rédaction et mise à jour de la documentation d'architecture et des ADR, analyse d'écarts entre le dépôt et les attendus, relevés chiffrés de ce rapport |
|
||||
| **Valeur ajoutée** | Vitesse sur le travail répétitif (gabarits de tests, migrations, documentation), et surtout **recoupement systématique** : détection d'incohérences entre documentation et code qu'une relecture humaine laisse passer |
|
||||
| **Limites constatées** | Des **chiffres plausibles mais faux** quand ils ne sont pas recalculés ; des **références à des fichiers inexistants** ; une tendance à **présenter comme acquis** ce qui n'est que prévu (l'approbation de la production, annoncée par deux ADR, n'a jamais été activée) ; des **horodatages en UTC recopiés comme heure locale** |
|
||||
| **Vérifications humaines** | Tout code généré passe par la CI (lint, typage, tests, couverture, SAST) et par une revue de PR. Tout chiffre publié est réobtenu par une commande (`gh`, `git log`) au moment de la rédaction. Les décisions d'architecture restent prises et signées en ADR par un humain |
|
||||
|
||||
### Déclarations des autres membres
|
||||
|
||||
Non transmises au moment du gel. Le gabarit reste celui de la déclaration ci-dessus : outil,
|
||||
tâches, valeur ajoutée, limites, vérifications.
|
||||
|
||||
---
|
||||
|
||||
## 8. Licences logicielles
|
||||
|
||||
**Licence du dépôt** : aucun fichier `LICENSE` au gel. Faute de licence explicite, le code reste
|
||||
sous le régime par défaut : tous droits réservés à ses auteurs, aucune réutilisation accordée.
|
||||
|
||||
**Dépendances, relevées le 24/09** par `pip-licenses` (verrous figés, sans dépendances de dev)
|
||||
et `license-checker --production` :
|
||||
|
||||
| Périmètre | Résultat |
|
||||
|---|---|
|
||||
| Backend, ML, Airflow (Python) | MIT, BSD, Apache 2.0 et PSF pour l'essentiel. **Aucune GPL ni AGPL embarquée.** À noter : `psycopg` (ML) sous LGPL 3.0, utilisé comme bibliothèque, sans obligation sur le code appelant ; `certifi` et `pathspec` sous MPL 2.0, copyleft limité au fichier, non modifiés ; `text-unidecode` (Airflow) sous double licence Artistic ou GPL, retenue sous Artistic |
|
||||
| Frontend, dépendances de production | 12 paquets : 10 MIT, 1 Apache 2.0, 1 0BSD |
|
||||
|
||||
**Composants exécutés à côté du produit**, chacun dans son conteneur, sans modification :
|
||||
|
||||
| Composant | Rôle | Licence |
|
||||
|---|---|---|
|
||||
| Nginx | Reverse proxy, frontal SNI | BSD 2-Clause |
|
||||
| PostgreSQL · TimescaleDB | Base, séries temporelles | PostgreSQL License · Apache 2.0 pour le cœur, Timescale License pour certaines fonctions de l'image utilisée |
|
||||
| Apache Airflow, MLflow | Orchestration, registre de modèles | Apache 2.0 |
|
||||
| Prometheus, Alertmanager, exporteurs, cAdvisor | Supervision | Apache 2.0 |
|
||||
| **Grafana, Garage, k6** | Tableaux de bord, stockage objet, tirs de charge en CI | **AGPL 3.0** |
|
||||
| acme.sh | Certificats Let's Encrypt | GPL 3.0 |
|
||||
| Mailpit | Courriel de développement et d'alerte | MIT |
|
||||
| Terraform | Infrastructure as code | **BUSL 1.1** : usage interne autorisé, redistribution concurrente interdite |
|
||||
|
||||
Les composants AGPL et GPL tournent sans modification, dans des conteneurs séparés : ils
|
||||
n'imposent rien au code du produit. Point de cohérence : l'ADR 0019 écarte MinIO en citant
|
||||
notamment sa licence AGPL, que Garage partage ; les autres raisons de l'ADR restent.
|
||||
|
||||
---
|
||||
|
||||
## 9. Anonymisation et conformité RGPD
|
||||
|
||||
- **Les données du projet sont synthétiques.** Les séries de consommation proviennent des CSV
|
||||
fournis par l'organisme de formation et de l'API Mock simulée. Aucune donnée de consommation
|
||||
réelle d'un client identifiable n'est présente dans le dépôt.
|
||||
- **Les sites sont désignés par identifiants** (`SITE001` à `SITE007`), sans raison sociale ni
|
||||
adresse.
|
||||
- **Les comptes applicatifs** de test utilisent des adresses de domaine fictif et des mots de
|
||||
passe de test. Le compte du scan DAST est un compte `lecteur` jetable.
|
||||
- **Aucun secret dans le dépôt, vérifié** : scan gitleaks sur tout l'historique le 24/09, 342
|
||||
commits hors merges, 8 constats, tous faux positifs après tri ligne à ligne ; aucun `.env`,
|
||||
clé, certificat ni state Terraform jamais commité (rapport EC04, preuve 05). C'est le ZIP
|
||||
`.git` complet qui est remis au jury : le contrôle porte donc bien sur l'historique.
|
||||
- **Adresse de la machine** : l'adresse privée de la machine du groupe apparaît dans des
|
||||
documents d'exploitation de l'historique git. Adresse non routable, joignable depuis le seul
|
||||
réseau de l'école ; elle est masquée dans l'arbre livré.
|
||||
- **Journal d'audit** : les accès sont tracés en ajout seul (ADR 0004). Cette table contient des
|
||||
identifiants d'utilisateurs applicatifs : sur un déploiement réel, elle relèverait d'une durée
|
||||
de conservation définie. Elle n'est pas fixée à ce jour : à arrêter avant tout déploiement réel.
|
||||
- **Dans ce rapport et les supports d'oral** : aucune URL, adresse IP, identifiant de connexion
|
||||
ou coordonnée personnelle n'est reproduite. Les identifiants GitHub sont des pseudonymes
|
||||
publics, conservés parce qu'ils sont la seule clé de traçabilité vérifiable.
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
Relevé du 24/09/2026 à 14h25, commit `9f343e9` :
|
||||
|
||||
- **Git** : `git rev-list --count origin/dev` et `--no-merges` · `git log origin/dev
|
||||
--no-merges --format=%an | sort | uniq -c` (alias regroupés) · `git log --no-merges
|
||||
<merge>^1..<merge>^2` (auteur principal) · `git ls-tree --name-only origin/main docs/adr/` ·
|
||||
`git rev-list --count origin/main..origin/dev`
|
||||
- **API GitHub** : `gh issue list --state all --limit 300 --json
|
||||
number,state,assignees,milestone,createdAt,closedAt` · `gh project item-list 2 --owner
|
||||
ineszang --format json` · `gh api repos/ineszang/ProjetPiscine_EnerVision/milestones?state=all`
|
||||
· `gh pr list --state all --limit 300 --json number,author,mergedBy,mergedAt,baseRefName,reviews`
|
||||
· `gh api .../deployments?environment=prod`
|
||||
- **Licences** : `uv run --frozen --no-dev --with pip-licenses pip-licenses --from=mixed` dans
|
||||
chaque projet Python · `npx license-checker@25 --production --summary` dans `apps/frontend`
|
||||
- `CONSIGNES-PROJET.md`, documents officiels 01, 02 et 05, points d'avancement des 15, 17, 18 et
|
||||
21/09, rapport de sécurisation EC04
|
||||
Binary file not shown.
@@ -0,0 +1,431 @@
|
||||
# EC04 · Rapport de sécurisation · EnerVision
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Auteur** | Johan LEROY, Groupe 3 (HEADL_015B), Tech Lead |
|
||||
| **Date du relevé** | 24/09/2026, de 14h45 à 15h15 |
|
||||
| **Référence du code** | commit gelé `9f343e9` du 24/09/2026 à 11h08, porté à la fois par `dev` et par `main` |
|
||||
| **Périmètre** | la plateforme EnerVision et sa mise en ligne sur la machine du groupe (conteneur LXC sur l'hôte Proxmox de l'école) : trois environnements Docker Compose (production, recette, dev), chacun derrière son reverse proxy Nginx, et un frontal SNI commun |
|
||||
| **Preuves** | dossier `preuves/`, quatorze fichiers numérotés, sorties brutes datées et anonymisées, chacune avec la commande qui la rejoue |
|
||||
| **Format** | source Markdown UTF-8 sans média externe, version figée PDF produite par la chaîne Markdown vers HTML vers CSS de pagination vers PDF |
|
||||
|
||||
Ce rapport compile des preuves, il ne décrit pas des intentions. Chaque affirmation porte l'un de
|
||||
trois marqueurs :
|
||||
|
||||
| Marqueur | Sens |
|
||||
|---|---|
|
||||
| **[Prouvé]** | vérifiable dans le dépôt ou par l'API GitHub, rejoué le 24/09 avec sa sortie dans `preuves/` |
|
||||
| **[Constaté]** | relevé sur la machine le 24/09 par une commande en lecture seule (`12-constats-machine.txt`) |
|
||||
| **[Absent]** | non fait, avec sa raison et son coût |
|
||||
|
||||
Anonymisation : aucune adresse IP, URL, identifiant de connexion, adresse électronique ni secret
|
||||
n'est reproduit, ici comme dans les preuves. La machine est notée « la machine du groupe », ses
|
||||
noms publics `<env>.<domaine-du-groupe>`. Le dépôt, lui, a porté l'adresse privée de la machine
|
||||
dans des documents d'exploitation : elle est masquée dans l'arbre livré, mais l'historique git la
|
||||
conserve (section 3).
|
||||
|
||||
---
|
||||
|
||||
## 1. Synthèse
|
||||
|
||||
| Axe demandé | État | Ce qui le prouve |
|
||||
|---|---|---|
|
||||
| Scans IaC et code | **[Prouvé]** onze contrôles rejoués le 24/09 sur le commit gelé, **aucune vulnérabilité haute ou critique dans le code ni dans le produit livré** ; DAST : 0 échec sur 118 règles | Bandit, pip-audit, npm audit, Checkov, gitleaks, Trivy, `terraform validate`, `nginx -t`, OWASP ZAP |
|
||||
| Gestion des secrets | **[Prouvé]** aucun secret dans l'historique git (342 commits hors merges, toutes branches) ; seize secrets générés sur la machine, jamais transmis | gitleaks, `git log` sur les fichiers sensibles, `scripts/provision-host.sh`, gardes de `config.py` |
|
||||
| Règles réseau | **[Prouvé] [Constaté]** la plateforme n'expose que le frontal SNI, en 80 et 443, tout le reste sur la boucle locale ; **[Constaté]** la machine expose en plus SSH, k3s et trois Vault, hors code livré ; **[Absent]** pare-feu hôte | ports calculés par `docker compose config` et `provision-host.sh`, relevés par `ss` sur la machine |
|
||||
| Authentification | **[Prouvé]** JWT court, rafraîchissement opaque avec rotation, Argon2id, RBAC à 3 rôles, **matrice d'accès testée** | 86 tests rejoués le 24/09, ADR 0002 et 0003 |
|
||||
| Audits d'accès et d'infrastructure | **[Prouvé]** journal d'audit en ajout seul garanti par la base ; historique des déploiements ; supervision Prometheus active en production | ADR 0004 et 0016, state Terraform, API GitHub |
|
||||
|
||||
**Sept constats à traiter**, aucun bloquant pour le produit livré, détaillés en section 2.2 :
|
||||
|
||||
1. **La machine expose plus que la plateforme** : SSH en root avec mot de passe, l'API k3s et
|
||||
trois serveurs Vault écoutent sur toutes les interfaces, sans pare-feu hôte. k3s et Vault ne
|
||||
viennent pas du code livré (`12-constats-machine.txt`).
|
||||
2. **Aucune approbation humaine avant la production**, contrairement à ce qu'annonce l'ADR 0009,
|
||||
et **aucune branche protégée** (`13-github-reglages.txt`).
|
||||
3. **Le chiffrement au repos est impossible sur cette machine** : c'est un conteneur LXC, où LUKS
|
||||
ne peut pas fonctionner. La base est en clair sur le disque ; seules les archives sont
|
||||
chiffrées (SSE-C, ADR 0020).
|
||||
4. **Une vulnérabilité MEDIUM** (CVE-2026-41016) dans une dépendance transitive d'Airflow, que
|
||||
**la CI ne peut pas voir** : son audit de dépendances ne porte que sur le verrou du backend.
|
||||
5. **Le jeton de réinitialisation de mot de passe passe dans l'URL**, et le journal d'accès du
|
||||
proxy l'enregistre en clair pendant ses quinze minutes de validité (`14-dast-zap.txt`).
|
||||
6. **Du code non relu peut tourner sur la machine de production** : toute branche d'un membre,
|
||||
par l'environnement `dev` (ADR 0017), et le workflow qu'apporterait une PR de fork, si son
|
||||
exécution n'est pas soumise à approbation.
|
||||
7. **Des fichiers sensibles dans l'arbre de travail du poste** (clé TLS locale, `.env`, state et
|
||||
variables Terraform), ignorés par git mais **qui finiraient dans un ZIP fabriqué à partir du
|
||||
dossier**. Le ZIP du rendu part donc d'un clone.
|
||||
|
||||
---
|
||||
|
||||
## 2. Scans de sécurité
|
||||
|
||||
### 2.1 Résultats
|
||||
|
||||
Tous les contrôles ont été lancés le 24/09/2026 entre 14h45 et 15h00 sur `9f343e9`. Checkov,
|
||||
Trivy config et `terraform validate` portent sur un export `git archive` du commit, pour
|
||||
qu'aucun fichier ignoré du poste ne s'y mêle ; Trivy fs porte volontairement sur l'arbre de
|
||||
travail, pour le constat 7.
|
||||
|
||||
| # | Outil | Périmètre | Résultat | Preuve |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **Bandit 1.9.4** (SAST Python) | `apps/backend/app`, `ml/enervision_ml`, `etl/airflow/dags` | **0 constat, tous niveaux**, sur 7 110 lignes (6 136 + 722 + 252) | `01` |
|
||||
| 2 | **pip-audit** (verrou figé, sans dev) | backend 50 paquets, ML 94, Airflow 128 | backend **0**, ML **0**, Airflow **1 vulnérabilité** (PYSEC-2026-24) | `02` |
|
||||
| 3 | **npm audit** (`--package-lock-only`) | frontend 511 dépendances, tests e2e 26 | **0, tous niveaux**, dans les deux verrous | `03` |
|
||||
| 4 | **Checkov 3.3.19** (IaC) | Terraform, Dockerfiles, workflows GitHub, secrets | Terraform **0 ressource évaluable** · Dockerfile 268 réussis, **3 échecs** · workflows **596 réussis, 0 échec** · secrets 0 | `04` |
|
||||
| 5 | **gitleaks** (secrets) | **tout l'historique**, toutes branches | 8 constats, **8 faux positifs** après tri ligne à ligne | `05` |
|
||||
| 6 | **Trivy config** (IaC) | dépôt entier | **3 LOW** (HEALTHCHECK), Terraform propre | `06` |
|
||||
| 7 | **Trivy fs** (dépendances et secrets) | arbre de travail du poste | la même CVE Airflow, et la clé TLS locale (fichier ignoré par git) | `07` |
|
||||
| 8 | Tests d'accès du backend | matrice rôle × route, protection des routes, durcissement, JWT | **86 réussis**, 5 tests d'intégration désélectionnés (joués en CI) | `08` |
|
||||
| 9 | `terraform fmt` et `validate` | les deux racines | **formatage conforme, deux configurations valides** | `09` |
|
||||
| 10 | `nginx -t` et ports fusionnés | proxy de stack, frontal SNI, `docker-compose.prod.yml` | **deux configurations valides**, un seul composant exposé | `10` |
|
||||
| 11 | State Terraform local | racine `vm-eni` | 3 ressources appliquées sur 4 déclarées | `11` |
|
||||
| 13 | Réglages GitHub | environnements, branches, secrets, runs | voir constat 2 | `13` |
|
||||
| 14 | **OWASP ZAP 2.17.0** (DAST) | l'API en fonctionnement, authentifiée | **0 échec, 0 avertissement, 118 règles passées**, 4 alertes informatives | `14` |
|
||||
|
||||
**Ce que la CI rejoue**, sur chaque PR et chaque push vers `dev` ou `main`, filtré par chemins,
|
||||
derrière le check unique « CI ok » (ADR 0014) : Bandit bloquant à partir de MEDIUM sur le backend
|
||||
et le ML, `pip-audit` sur le verrou du backend, `npm audit --audit-level=high` sur le frontend,
|
||||
`terraform fmt` et `validate`, actionlint et shellcheck sur les workflows, validation des
|
||||
fichiers Compose, `nginx -t` du frontal, `promtool` et `amtool` sur la supervision, une fumée S3
|
||||
sur Garage avec chiffrement SSE-C, les parcours Playwright et deux tirs k6 (fumée et contrôle du
|
||||
429 par le proxy), et une analyse SonarCloud. Dependabot suit sept entrées chaque semaine. Le
|
||||
DAST tourne chaque lundi, à la demande, et sur les PR qui modifient son propre workflow.
|
||||
**Ne sont pas en CI** : Checkov, Trivy, gitleaks, `pip-audit` sur les verrous ML et Airflow,
|
||||
`npm audit` sur le verrou des tests e2e. Ils ont été joués pour ce rapport.
|
||||
|
||||
### 2.2 Lecture des constats
|
||||
|
||||
**1. La machine expose plus que la plateforme.** Relevé par `ss` le 24/09 : outre le frontal en
|
||||
80 et 443, écoutent sur toutes les interfaces SSH (connexion root et mot de passe acceptés),
|
||||
l'API **k3s** en 6443 et **trois serveurs Vault** 2.1.1 en 8200 à 8205, initialisés et
|
||||
descellés. Aucun pare-feu ne filtre : la table nftables est vide, en politique `accept`. Ni k3s
|
||||
ni Vault ne viennent du code livré : `git grep vault` ne trouve rien sur le commit gelé. Vault
|
||||
est visé par le Terraform d'une branche de travail non fusionnée. k3s, installé
|
||||
le 17/09, redémarre en boucle depuis (51 678 redémarrages) sans porter aucun pod. Correctif, sans
|
||||
commit : arrêter k3s, restreindre Vault à la boucle locale ou l'arrêter, puis un pare-feu
|
||||
n'ouvrant que 22, 80 et 443, et SSH par clé seule. C'est l'infrastructure de l'administratrice
|
||||
du dépôt : la décision lui revient.
|
||||
|
||||
**2. Production sans approbation, branches sans protection.** L'environnement GitHub `prod` n'a
|
||||
qu'une règle : il n'accepte que la branche `main`. Aucun relecteur n'est requis : le dernier
|
||||
déploiement est passé de l'attente à l'exécution en une seconde. `main` et `dev` ne sont pas protégées, aucun
|
||||
ruleset n'existe : trois commits ont été poussés directement sur `dev` (`c2f360c`, `cbbfaf4`,
|
||||
`6c09bee`), relus ensuite seulement par les PR de remontée #161 et #163. L'ADR 0009 annonçait une
|
||||
production « après approbation » ; l'ADR 0014 en faisait un réglage restant à poser par
|
||||
l'administratrice. Il ne l'a jamais été : les deux ADR portent désormais une note datée du
|
||||
24/09. Correctif : deux réglages, que seule l'administratrice du dépôt peut activer.
|
||||
|
||||
**3. Chiffrement au repos.** La commande `systemd-detect-virt` répond `lxc` : sans
|
||||
device-mapper ni périphérique loop, LUKS est impossible. `scripts/coffre-luks.sh` le détecte et
|
||||
refuse de démarrer (ADR 0020). La base TimescaleDB et les métadonnées de Garage sont donc en
|
||||
clair sur le disque du conteneur. Ce qui est chiffré dès aujourd'hui : les archives exportées
|
||||
vers Garage par le DAG `retention`, en SSE-C, avec une clé que Garage ne conserve pas. Le
|
||||
chiffrement du disque relève de l'hôte Proxmox, donc de l'administrateur de l'école, à qui la
|
||||
demande est adressée.
|
||||
|
||||
**4. CVE-2026-41016, MEDIUM, `apache-airflow-providers-smtp` 2.3.2.** Le `SmtpHook` d'Airflow
|
||||
négocie STARTTLS sans valider le certificat. Dépendance **transitive** d'`apache-airflow` 3.3.2.
|
||||
**Exposition actuelle : nulle**, aucun DAG n'envoie de courriel et aucune connexion SMTP n'est
|
||||
déclarée dans Airflow. Correctif : `providers-smtp` 3.0.0 ou plus. **Le vrai constat est
|
||||
ailleurs** : la CI audite le verrou du backend et pas ceux du ML ni d'Airflow, qui portent 222
|
||||
paquets, et Dependabot ne suit en `uv` que le backend. La CVE était déjà relevée le 23/09 et elle
|
||||
est toujours là : c'est exactement ce que produit un angle mort.
|
||||
|
||||
**5. Jeton de réinitialisation dans l'URL** (ZAP 10024, informatif). `GET
|
||||
/api/v1/auth/reset-password/validate?token=…` : le jeton est stocké haché, valable quinze
|
||||
minutes, à usage unique, caviardé des journaux de l'API, et `Referrer-Policy: no-referrer` est
|
||||
posé. Mais le journal d'accès du proxy enregistre la requête complète, donc le jeton en clair,
|
||||
lisible par qui administre la machine pendant sa validité. Correctif : passer la validation en
|
||||
`POST`, ou journaliser `$uri` sans ses paramètres sur cette route.
|
||||
|
||||
**6. Du code non relu peut tourner sur la machine de production.** Le troisième environnement,
|
||||
`dev`, se déploie par `workflow_dispatch` depuis n'importe quelle branche (ADR 0017). Il vit sur
|
||||
la même machine et le même démon Docker que la production : tout membre qui a le droit
|
||||
d'écriture peut y exécuter du code non relu. `deploy.yml` n'a jamais de déclencheur
|
||||
`pull_request`, mais cela ne suffit pas, l'ADR 0014 le dit : une PR de fork peut apporter son
|
||||
propre workflow qui cible le runner. La seule protection est alors l'approbation des workflows
|
||||
des contributeurs externes, un réglage que l'API refuse de lire avec les droits d'un membre
|
||||
(403) : non vérifié. Risque accepté pour une équipe de cinq, à fermer avant tout contributeur
|
||||
extérieur.
|
||||
|
||||
**7. Fichiers sensibles du poste.** Trivy fs trouve la clé du certificat auto-signé local, et le
|
||||
poste porte aussi, ignorés : deux `.env`, le state et les variables Terraform, les journaux de
|
||||
session. Tous sont ignorés par git et n'ont jamais été versionnés (`05`). **Conséquence
|
||||
opérationnelle pour vendredi : le ZIP du dépôt, `.git` inclus, se fabrique à partir d'un clone**,
|
||||
jamais en compressant le dossier de travail.
|
||||
|
||||
**HEALTHCHECK absent** (Checkov CKV_DOCKER_2, Trivy DS-0026, LOW) sur les images frontend,
|
||||
Airflow et ML. Le backend en porte un, et c'est lui que le proxy attend avant de démarrer ; la
|
||||
base, l'API Airflow et Garage ont le leur dans `docker-compose.yml`. Reste le frontend, un nginx
|
||||
statique qui tomberait sans être signalé. Impact faible.
|
||||
|
||||
**Terraform : Checkov n'évalue aucune ressource.** Les six ressources du dépôt sont des
|
||||
`null_resource` qui agissent par SSH, pour lesquelles Checkov n'a aucune politique. Ce scan ne
|
||||
prouve rien, dans un sens comme dans l'autre. Les garanties réelles sont ailleurs : validation en
|
||||
CI, clé SSH seule, jeton du runner en variable `sensitive` et hors des triggers (`11`, ADR 0010).
|
||||
|
||||
**gitleaks : huit faux positifs.** Six viennent de la règle `generic-api-key` qui prend
|
||||
`api_history` et `api_current`, deux valeurs de la colonne `source` des relevés, pour des clés.
|
||||
Les deux nouveaux sont des valeurs de test : un identifiant S3 factice (`settings_s3()`) et le mot
|
||||
de passe provisoire simulé d'un test Angular.
|
||||
|
||||
---
|
||||
|
||||
## 3. Gestion des secrets
|
||||
|
||||
| Mesure | Preuve | État |
|
||||
|---|---|---|
|
||||
| Aucun secret dans l'historique git, toutes branches ; aucun `.env`, `.pem`, `.key`, `.tfvars`, `.tfstate` ni jeton DNS jamais commité | `05` | [Prouvé] |
|
||||
| `.env`, `*.pem`, `*.tfvars`, `*.tfstate` ignorés par git ; seuls les `*.example` sont versionnés ; `infra/garage/garage.toml`, versionné, ne porte aucun secret | `.gitignore`, `05` | [Prouvé] |
|
||||
| **Seize secrets générés sur la machine** par `openssl rand` (base, API, jeton des métriques, cinq pour Airflow dont sa clé Fernet, six pour Garage dont la clé SSE-C, Grafana, rôle de supervision), `.env` écrit sous `umask 077` puis en `600`. Les secrets existants sont conservés ; le reste du fichier est réécrit à chaque passage pour réaligner hôte, ports et profils | `scripts/provision-host.sh` | [Prouvé] le script · [Constaté] les droits |
|
||||
| Refus d'écrire le `.env` si une valeur d'exemple `change_me` subsiste, hors identifiants de l'API Mock posés à la main | `provision-host.sh`, fonction `preparer` | [Prouvé] |
|
||||
| L'API **refuse de démarrer** si `APP_SECRET_KEY` fait moins de 32 caractères ou vaut une sentinelle, si `APP_DEBUG` est vrai hors local, ou si les origines CORS sont en joker ou absentes | `apps/backend/app/core/config.py` | [Prouvé] |
|
||||
| Secrets typés `SecretStr`, clés S3 et SSE-C comprises, donc jamais journalisés ; jetons, mots de passe et cookies caviardés dans les journaux | `config.py`, `app/core/logging.py` | [Prouvé] |
|
||||
| Compose exige par `${VAR:?}` les secrets de la base et de l'API ; ceux d'Airflow, de Garage et de la supervision sont gardés par `airflow-init` et par les gardes du `Makefile` avant tout démarrage | `docker-compose.yml`, `Makefile` | [Prouvé] |
|
||||
| Clé de signature d'Airflow **distincte** de celle de l'API | `docker-compose.yml` | [Prouvé] |
|
||||
| Côté GitHub, **un seul secret** (`SONAR_TOKEN`) ; le déploiement n'en consomme aucun | `13` | [Prouvé] |
|
||||
| Terraform : clé SSH seule, jeton du runner en variable `sensitive`, absent des triggers | `11`, ADR 0010 | [Prouvé] |
|
||||
| Jeton du scan DAST éphémère et caviardé des journaux publiés | `dast.yml`, `scripts/dast-token.sh` | [Prouvé] |
|
||||
|
||||
**Ce qui manque.** Le dossier EC01 prévoyait **SOPS + age** : non fait. Les secrets vivent en
|
||||
clair sur le disque de la machine, protégés par les droits du fichier seulement, et la rotation
|
||||
est manuelle. Le 23/09, ce rapport jugeait la situation acceptable pour deux environnements,
|
||||
mais plus pour trois : **le seuil est franchi**, avec trois `.env`, un jeton DNS et une clé SSE-C dont
|
||||
la perte rendrait les archives illisibles. L'ADR 0019 demande de sauvegarder cette clé hors de la
|
||||
machine : c'est une procédure, rien ne le vérifie. Enfin, l'adresse privée de la machine figure
|
||||
dans l'historique git (ADR d'exploitation du 21 et du 22/09) : adresse non routable, joignable
|
||||
seulement depuis le réseau de l'école, masquée dans l'arbre livré.
|
||||
|
||||
---
|
||||
|
||||
## 4. Règles réseau
|
||||
|
||||
### 4.1 Surface exposée
|
||||
|
||||
Sur la machine, `provision-host.sh` ramène tous les ports des trois stacks sur la boucle locale
|
||||
(`10`) :
|
||||
|
||||
| Composant | Publication sur la machine | Joignable depuis |
|
||||
|---|---|---|
|
||||
| **Frontal SNI** (`infra/front`, nginx `stream`) | réseau de l'hôte, **80 et 443** | le réseau |
|
||||
| Proxy Nginx de chaque stack | `127.0.0.1` : 10443, 8443, 9443 (HTTPS) et l'écouteur PROXY protocol du frontal | la machine seule |
|
||||
| Base, Mailpit, API Airflow | `127.0.0.1`, un port par environnement | la machine, donc par tunnel SSH |
|
||||
| Garage (S3 et administration) | `127.0.0.1`, un port par environnement ; pas encore en `dev`, resté sur le commit du 23/09 | la machine seule |
|
||||
| Prometheus, Alertmanager, Grafana (production) | `127.0.0.1` | la machine seule |
|
||||
| Backend, frontend, scheduler et processeur Airflow, exporteurs | aucune | le réseau interne de Compose |
|
||||
| *Hors plateforme* : SSH, API k3s, trois Vault | toutes les interfaces : 22, 6443, 8200 à 8205 | **le réseau** (constat 1) |
|
||||
|
||||
**Un seul composant exposé par la plateforme.** Le frontal lit le nom demandé (SNI) sans
|
||||
déchiffrer et relaie la connexion, en PROXY protocol, vers le proxy de la stack visée : l'adresse
|
||||
réelle du client arrive jusqu'à la limitation de débit. Les trois environnements sont trois
|
||||
projets Compose distincts, sans réseau ni volume partagé (ADR 0009, 0017, 0018). Sur la
|
||||
machine, `ss` confirme les ports de la plateforme, mais relève aussi les services hors plateforme
|
||||
du constat 1. [Constaté, `12`]
|
||||
|
||||
### 4.2 Reverse proxy et TLS
|
||||
|
||||
| Directive | Valeur | Effet |
|
||||
|---|---|---|
|
||||
| Certificats | **Let's Encrypt par défi DNS-01** (acme.sh, domaine dynv6), vérifiés chaque nuit par une tâche cron et renouvelés à échéance ; l'auto-signé ne sert plus qu'au poste et aux tests e2e | chaîne de confiance publique, sans port 80 ouvert pour le défi (ADR 0018) |
|
||||
| Protocoles | TLS 1.2 et 1.3, tickets de session désactivés | pas de protocole obsolète |
|
||||
| Redirection | 80 vers 443 | pas de trafic applicatif en clair |
|
||||
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | le navigateur refuse ensuite le HTTP |
|
||||
| `Content-Security-Policy` | `default-src 'self'`, `frame-ancestors 'none'`... | limite l'injection de script et le clickjacking |
|
||||
| En-têtes de l'API | `nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`, `Cross-Origin-Resource-Policy: same-origin` | défense en profondeur si le proxy manquait |
|
||||
| Limitation de débit | `api` 20 req/s, `auth` 30 req/min sur les routes qui vérifient un secret ; contrôlée par un tir k6 en CI | freine la force brute sans pénaliser la navigation derrière le NAT de l'école |
|
||||
| `server_tokens off`, corps limité à 2 Mo | | pas de version publiée, pas de requête démesurée |
|
||||
|
||||
Les deux configurations sont validées par `nginx -t` sur l'image de production (`10`).
|
||||
Certificats servis : Let's Encrypt (émetteur YE1) sur les trois noms, échéance au 22/12/2026, et
|
||||
les trois sondes de santé répondent 200 par le nom public avec un certificat vérifié, sans `-k`.
|
||||
[Constaté, `12`]
|
||||
|
||||
### 4.3 Accès à la machine et déploiement
|
||||
|
||||
- Le **runner GitHub Actions** auto-hébergé initie lui-même la connexion vers GitHub : **aucun
|
||||
port entrant** n'est ouvert pour déployer. [Prouvé]
|
||||
- `deploy.yml` ne se déclenche **jamais sur `pull_request`** : sur un dépôt public, une PR venue
|
||||
d'un fork exécuterait sinon son code sur la machine. Il n'est appelé qu'après une CI verte sur
|
||||
un push, et refuse de revenir à un commit plus ancien que celui déployé. [Prouvé]
|
||||
- La production n'accepte que `main`, **sans approbation humaine** (constat 2). [Prouvé, `13`]
|
||||
- Terraform se connecte par **clé SSH**, jamais par mot de passe. [Prouvé]
|
||||
|
||||
**Ce qui manque.** **Aucun pare-feu hôte** n'est configuré ni documenté : la restriction repose
|
||||
sur la publication des ports et sur le réseau de l'école ; la table nftables est vide, en
|
||||
politique `accept` [Constaté, `12`]. **SSH n'est pas durci** : connexion root et authentification
|
||||
par mot de passe acceptées [Constaté, `12`] ; Ansible était prévu au dossier EC01 et n'a pas été
|
||||
fait. Pas de
|
||||
réseaux Docker nommés : l'intention du dossier EC01 (la base jamais exposée) est tenue par
|
||||
l'absence de publication, plus fragile à la relecture qu'une politique explicite. Azure n'est pas
|
||||
utilisé, par choix d'architecture on-premise : il n'y a ni NSG ni Application Gateway à auditer.
|
||||
|
||||
---
|
||||
|
||||
## 5. Authentification et autorisation
|
||||
|
||||
| Mécanisme | Détail | Trace |
|
||||
|---|---|---|
|
||||
| Jeton d'accès | JWT HS256, **15 minutes**, algorithme épinglé, `aud`, `iss` et `typ` vérifiés, `alg: none` rejeté, gardé en mémoire côté navigateur | ADR 0002, `app/core/security.py` |
|
||||
| Jeton de rafraîchissement | chaîne **opaque de 256 bits**, stockée hachée, **rotation à chaque usage et détection de réutilisation** ; cookie `HttpOnly`, `Secure`, `SameSite=Strict`, chemin restreint | ADR 0002, `app/services/auth.py` |
|
||||
| Mots de passe | **Argon2id** (m=19 456 Kio, t=2, p=1), re-hachage passif si les paramètres changent | `app/core/hashing.py` |
|
||||
| Réinitialisation | jeton haché, 15 minutes, usage unique ; voir le constat 5 | `app/models/password_reset_token.py` |
|
||||
| Force brute | limitation à fenêtre glissante sur trois clés, **évaluée avant le hachage** ; pas de verrouillage de compte, qui serait un déni de service | ADR 0002, `login_attempt` |
|
||||
| Énumération de comptes | message et temps de réponse identiques quelle que soit la cause | `app/services/auth.py` |
|
||||
| Autorisation | **RBAC à trois rôles ordonnés** (`lecteur`, `operateur`, `admin`), décision prise sur **la ligne en base relue à chaque requête**, jamais sur le claim | ADR 0003, `app/api/deps.py` |
|
||||
| Révocation | immédiate : compte désactivé ou mot de passe changé invalide les jetons antérieurs | `credentials_changed_at` |
|
||||
| Refus par défaut | **16 routes sous rôle**, 3 authentifiées sans rôle, 1 par cookie, 8 publiques listées nommément (dont `/metrics`, gardée par son propre jeton) ; un test appelle réellement chaque route sans jeton | `tests/api/acces.py`, `test_route_protection.py` |
|
||||
| Matrice d'accès | chaque route gardée croisée avec les trois rôles, sur les routes réelles, puis rejouée avec de vrais jetons contre une vraie base en CI | `test_matrice_acces.py` |
|
||||
| Garde-fous d'administration | refus de rétrograder ou désactiver le dernier administrateur ; premier administrateur créé hors dépôt | `app/services/user.py`, `app/cli.py` |
|
||||
|
||||
**Preuve d'exécution** : 86 tests d'accès, de protection des routes, de durcissement et de JWT,
|
||||
réussis le 24/09 (`08`) ; le DAST authentifié ne relève ni échec ni avertissement (`14`).
|
||||
|
||||
**Ce qui reste ouvert, et c'est écrit dans le dépôt** (`owasp-traceabilite.md`) : **pas
|
||||
d'autorisation par objet** (OWASP API1). Les rôles sont globaux, un compte `lecteur` lit tous les
|
||||
sites. Sans conséquence tant que les routes métier sont en lecture seule ; à corriger par une table
|
||||
d'affectation compte-site avant la première route d'écriture.
|
||||
|
||||
---
|
||||
|
||||
## 6. Audits d'accès et d'infrastructure
|
||||
|
||||
### 6.1 Traçabilité applicative
|
||||
|
||||
- **Journal d'audit en ajout seul, garanti par PostgreSQL** : deux déclencheurs refusent
|
||||
`UPDATE`, `DELETE` et `TRUNCATE` sur `audit_log`. Les champs de détail passent par une liste
|
||||
blanche ; l'acteur est dénormalisé pour survivre à la suppression d'un compte (ADR 0004).
|
||||
[Prouvé] par les tests d'intégration · en production, 5 créations de compte et 3 changements
|
||||
de mot de passe [Constaté, `12`]
|
||||
- **Tentatives de connexion** journalisées à part (`login_attempt`). [Prouvé]
|
||||
- **Limite assumée** : les déclencheurs arrêtent l'accident, pas un compte qui détient
|
||||
`ALTER TABLE`. Le journal n'est pas une preuve de non-répudiation.
|
||||
|
||||
### 6.2 Traçabilité de l'infrastructure
|
||||
|
||||
- **Provisionnement** : le state Terraform porte trois ressources appliquées (Docker, les trois
|
||||
environnements, le runner). La quatrième, le coffre LUKS, n'a jamais été appliquée (`11`,
|
||||
constat 3).
|
||||
- **Déploiements** : chaque déploiement est un run de `deploy.yml` rattaché à un environnement
|
||||
GitHub. Sept déploiements de production entre le 23/09 11h37 et le 24/09 11h12, le dernier sur
|
||||
le commit gelé (`13`). [Prouvé]
|
||||
- **Dépôt** : une seule administratrice, aucune branche protégée (constat 2).
|
||||
|
||||
### 6.3 Supervision
|
||||
|
||||
**[Prouvé]** dans le dépôt, active en production (profil Compose `monitoring`, ADR 0016) :
|
||||
Prometheus lit `/metrics` avec son jeton, ainsi que la base, l'hôte, les conteneurs et Garage.
|
||||
Neuf règles d'alerte sont testées par `promtool` en CI : API indisponible, erreurs serveur,
|
||||
latence, base indisponible ou saturée, mémoire, disque et CPU de l'hôte, cible injoignable.
|
||||
Alertmanager les envoie à Mailpit. Trois tableaux Grafana couvrent l'API, les données et
|
||||
l'infrastructure. En production, les sept cibles sont `up` et les neuf règles chargées
|
||||
[Constaté, `12`].
|
||||
|
||||
**Ce qui manque** : aucune règle sur des **événements de sécurité** (pic de 401, de 403 ou de
|
||||
429) ; des alertes qui restent dans Mailpit et ne réveillent personne ; pas de Loki, donc aucune
|
||||
centralisation des journaux ; aucune alerte sur l'échec d'un DAG.
|
||||
|
||||
---
|
||||
|
||||
## 7. Écarts avec le dossier EC01, et leur coût
|
||||
|
||||
| Prévu au dossier EC01 | Livré | Coût |
|
||||
|---|---|---|
|
||||
| Traefik en terminaison TLS | **Nginx** par stack (ADR 0007), plus un **frontal SNI** (ADR 0018) | configuration écrite à la main, mais explicite et validée en CI |
|
||||
| Trivy, Bandit, gitleaks en CI | **Bandit bloquant en CI** ; Trivy, gitleaks et Checkov joués pour ce rapport | un secret commité demain ne serait vu qu'au prochain passage manuel |
|
||||
| SOPS + age | secrets générés sur la machine, jamais transmis | secrets en clair sur disque, sans sauvegarde vérifiée |
|
||||
| Ansible pour le durcissement | rien | pare-feu et SSH non durcis de façon reproductible |
|
||||
| Deux réseaux Docker | un seul composant exposé | propriété portée par une absence, fragile à la relecture |
|
||||
| Prometheus, Grafana, Loki | **Prometheus, Alertmanager, Grafana** actifs en production ; pas de Loki | journaux dispersés, aucune alerte de sécurité |
|
||||
| Scan d'image de conteneur | aucun | les images construites sur la machine ne sont pas analysées |
|
||||
| Chiffrement au repos | **SSE-C des archives** ; LUKS écrit mais impossible sur LXC (ADR 0020) | base en clair sur le disque du conteneur |
|
||||
|
||||
---
|
||||
|
||||
## 8. Plan d'action
|
||||
|
||||
### 8.1 Avant le gel du 25/09, 9h00
|
||||
|
||||
1. **Fabriquer le ZIP du dépôt depuis un clone**, `.git` inclus, et vérifier qu'il ne contient
|
||||
ni `.env`, ni `*.pem`, ni `*.tfvars`, ni `*.tfstate`.
|
||||
2. Activer, par l'administratrice : un relecteur requis sur l'environnement `prod`, et la
|
||||
protection de `main`. Deux réglages, sans commit.
|
||||
3. Arrêter k3s, qui redémarre en boucle, et restreindre les trois Vault à la boucle locale
|
||||
(constat 1). Sans commit, sur décision de l'administratrice.
|
||||
|
||||
### 8.2 Après le gel, par ordre de valeur
|
||||
|
||||
1. Pare-feu hôte n'ouvrant que 22, 80 et 443, et SSH par clé seule, dans un script rejouable.
|
||||
2. `pip-audit` sur les trois verrous, gitleaks et Trivy en CI ; montée de `providers-smtp`.
|
||||
3. Validation du jeton de réinitialisation en `POST`, ou journal d'accès sans paramètres.
|
||||
4. Chiffrement du disque par l'hôte Proxmox, ou une vraie machine virtuelle pour dérouler le
|
||||
coffre LUKS déjà écrit.
|
||||
5. Environnement `dev` sur une autre machine, ou limité aux branches relues.
|
||||
6. Règles d'alerte de sécurité et Loki ; seuil bloquant sur le DAST.
|
||||
7. Autorisation par site (API1) ; SOPS + age ; HEALTHCHECK du frontend.
|
||||
|
||||
---
|
||||
|
||||
## 9. Contribution personnelle
|
||||
|
||||
Attribution vérifiée par `git log` sur chaque fichier cité.
|
||||
|
||||
| Sujet | Auteur principal |
|
||||
|---|---|
|
||||
| Authentification, RBAC, journal d'audit, gardes de configuration, caviardage des journaux, ADR 0002 à 0004 | **Johan** |
|
||||
| Matrice d'accès et protection des routes, et leurs tests | **Johan** |
|
||||
| Reverse proxy Nginx et TLS, `docker-compose.prod.yml`, ADR 0007 | **Johan** |
|
||||
| Trois environnements, `provision-host.sh`, `deploy.yml`, Terraform `vm-eni`, ADR 0009, 0010 et 0017 | **Johan** |
|
||||
| CI unifiée (`ci.yml`), e2e et k6, supervision, ADR 0014 à 0016 | **Johan** |
|
||||
| Certificats DNS-01 et frontal SNI, ADR 0018 | **Johan** |
|
||||
| Garage par environnement, rétention, SSE-C, coffre LUKS, ADR 0019 et 0020 | **Johan**, sur une amorce de Valentin |
|
||||
| Scan DAST OWASP ZAP (`dast.yml`, `dast-token.sh`) | Dorian ; ma part est la revue, trois points bloquants dont une fuite du jeton dans les artefacts |
|
||||
| En-tête `Cross-Origin-Resource-Policy`, module Terraform k3s | Dorian |
|
||||
| Workflow frontend, configuration SonarCloud, administration du dépôt | Inès |
|
||||
| Ce rapport et les contrôles du 24/09 | **Johan** |
|
||||
|
||||
---
|
||||
|
||||
## 10. Usage de l'IA
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Outil** | Claude Code (Anthropic), en assistant dans le terminal |
|
||||
| **Tâches** | lancement des scans et mise en forme de leurs sorties, recoupement entre la documentation, le code et l'API GitHub, première rédaction de ce rapport |
|
||||
| **Vérifications humaines** | chaque chiffre est lu dans une sortie de `preuves/`, rejouable par la commande en tête du fichier ; les huit constats gitleaks et les quatre alertes ZAP ont été triés ligne à ligne ; les constats sur la machine viennent de commandes lancées par l'auteur |
|
||||
| **Limite constatée** | l'outil tend à présenter comme acquis ce qui n'est que prévu, et à conclure avant d'avoir vérifié (une première lecture de l'alerte ZAP citait des codes HTTP non relevés) : d'où les trois marqueurs et le tri ligne à ligne |
|
||||
|
||||
## 11. Licences
|
||||
|
||||
Outils de ce rapport : Bandit, pip-audit, Checkov, Trivy et OWASP ZAP sous licence Apache 2.0,
|
||||
gitleaks sous licence MIT. Aucun n'est embarqué dans le produit. Composants ajoutés depuis le
|
||||
dossier EC01 : Prometheus, Alertmanager, les exporteurs, cAdvisor, Playwright et boto3 sous Apache
|
||||
2.0 ; acme.sh sous GPL 3.0 ; **Garage, Grafana et k6 sous AGPL 3.0**. Ils sont utilisés sans
|
||||
modification, chacun dans son propre conteneur, ce qui n'emporte aucune obligation de publication.
|
||||
L'ADR 0019 écarte pourtant MinIO en citant notamment sa licence AGPL, que Garage partage :
|
||||
l'argument ne tient pas, les autres raisons de l'ADR restent. Le recensement complet est dans le
|
||||
rapport collectif EC02.
|
||||
|
||||
---
|
||||
|
||||
## Annexe · Index des preuves
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| `01-bandit.txt` | Bandit, seuil de la CI puis tous niveaux, trois modules |
|
||||
| `02-pip-audit.txt` | pip-audit sur les trois verrous Python |
|
||||
| `03-npm-audit.txt` | npm audit, frontend et tests e2e |
|
||||
| `04-checkov.txt` | Checkov, quatre frameworks |
|
||||
| `05-gitleaks.txt` | gitleaks sur tout l'historique, constats caviardés et tri |
|
||||
| `06-trivy-config.txt` | Trivy config sur le commit |
|
||||
| `07-trivy-fs.txt` | Trivy fs sur l'arbre de travail, clé retirée |
|
||||
| `08-tests-acces.txt` | tests d'accès et d'authentification du backend |
|
||||
| `09-terraform-validate.txt` | `terraform fmt` et `validate` sur les deux racines |
|
||||
| `10-proxy-tls.txt` | `nginx -t` du proxy et du frontal, directives, ports |
|
||||
| `11-terraform-state.txt` | ressources appliquées sur la machine |
|
||||
| `12-constats-machine.txt` | relevés en lecture seule sur la machine |
|
||||
| `13-github-reglages.txt` | environnements, protection des branches, secrets, déploiements |
|
||||
| `14-dast-zap.txt` | dernier rapport OWASP ZAP et tri des alertes |
|
||||
|
||||
Chaque fichier donne en tête sa date, le commit analysé et la commande exacte pour le rejouer.
|
||||
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
# Bandit 1.9.4 · 2026-09-24 14:45 CEST · commit 9f343e9 (dev = main)
|
||||
|
||||
## apps/backend/app · seuil CI (MEDIUM, confiance MEDIUM)
|
||||
Run started:2026-09-24 12:45:08.896999+00:00
|
||||
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 6136
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
exit=0
|
||||
|
||||
## apps/backend/app · tous niveaux
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 6136
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
|
||||
## ml/enervision_ml · seuil CI (MEDIUM, confiance MEDIUM)
|
||||
Run started:2026-09-24 12:45:09.551911+00:00
|
||||
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 722
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
exit=0
|
||||
|
||||
## ml/enervision_ml · tous niveaux
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 722
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
|
||||
## etl/airflow/dags · seuil CI (MEDIUM, confiance MEDIUM)
|
||||
Run started:2026-09-24 12:45:09.903724+00:00
|
||||
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 252
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
exit=0
|
||||
|
||||
## etl/airflow/dags · tous niveaux
|
||||
Test results:
|
||||
No issues identified.
|
||||
|
||||
Code scanned:
|
||||
Total lines of code: 252
|
||||
Total lines skipped (#nosec): 0
|
||||
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
|
||||
|
||||
Run metrics:
|
||||
Total issues (by severity):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Total issues (by confidence):
|
||||
Undefined: 0
|
||||
Low: 0
|
||||
Medium: 0
|
||||
High: 0
|
||||
Files skipped (0):
|
||||
@@ -0,0 +1,27 @@
|
||||
# pip-audit (méthode du job security-audit de backend.yml : verrou figé, sans dépendances de dev) · 2026-09-24 14:45 CEST · commit 9f343e9
|
||||
|
||||
## apps/backend/uv.lock
|
||||
paquets figés : 50
|
||||
Installed 28 packages in 17ms
|
||||
WARNING:pip_audit._cli:--no-deps is supported, but users are encouraged to fully hash their pinned dependencies
|
||||
WARNING:pip_audit._cli:Consider using a tool like `pip-compile`: https://pip-tools.readthedocs.io/en/latest/#using-hashes
|
||||
No known vulnerabilities found
|
||||
exit=0
|
||||
|
||||
## ml/uv.lock
|
||||
paquets figés : 94
|
||||
WARNING:pip_audit._cli:--no-deps is supported, but users are encouraged to fully hash their pinned dependencies
|
||||
WARNING:pip_audit._cli:Consider using a tool like `pip-compile`: https://pip-tools.readthedocs.io/en/latest/#using-hashes
|
||||
No known vulnerabilities found
|
||||
exit=0
|
||||
|
||||
## etl/airflow/uv.lock
|
||||
paquets figés : 128
|
||||
WARNING:pip_audit._cli:--no-deps is supported, but users are encouraged to fully hash their pinned dependencies
|
||||
WARNING:pip_audit._cli:Consider using a tool like `pip-compile`: https://pip-tools.readthedocs.io/en/latest/#using-hashes
|
||||
Found 2 known vulnerabilities in 1 package
|
||||
Name Version ID Fix Versions
|
||||
----------------------------- ------- ------------- ------------
|
||||
apache-airflow-providers-smtp 2.3.2 PYSEC-2026-24 3.0.0
|
||||
apache-airflow-providers-smtp 2.3.2 PYSEC-2026-24 3.0.0
|
||||
exit=1
|
||||
@@ -0,0 +1,15 @@
|
||||
# npm audit (méthode du job security-audit de frontend.yml : --package-lock-only) · 2026-09-24 14:45 CEST · commit 9f343e9
|
||||
|
||||
## apps/frontend · seuil CI : --audit-level=high
|
||||
found 0 vulnerabilities
|
||||
exit=0
|
||||
## apps/frontend · synthèse tous niveaux (metadata)
|
||||
vulnerabilites: {"info": 0, "low": 0, "moderate": 0, "high": 0, "critical": 0, "total": 0}
|
||||
dependances: {"prod": 13, "dev": 499, "optional": 150, "peer": 0, "peerOptional": 0, "total": 511}
|
||||
|
||||
## tests/e2e · seuil CI : --audit-level=high
|
||||
found 0 vulnerabilities
|
||||
exit=0
|
||||
## tests/e2e · synthèse tous niveaux (metadata)
|
||||
vulnerabilites: {"info": 0, "low": 0, "moderate": 0, "high": 0, "critical": 0, "total": 0}
|
||||
dependances: {"prod": 1, "dev": 26, "optional": 20, "peer": 0, "peerOptional": 0, "total": 26}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Checkov 3.3.19 · 2026-09-24 14:47 CEST · commit 9f343e9, sur un export git archive du commit (aucun fichier ignoré du poste)
|
||||
# Option --quiet : seuls les contrôles en échec sont listés, le résumé JSON donne les réussis.
|
||||
|
||||
## 1. infra/terraform, framework terraform
|
||||
-- résumé : (aucune ressource) {'passed': 0, 'failed': 0, 'skipped': 0, 'parsing_errors': 0, 'resource_count': 0, 'checkov_version': '3.3.19'}
|
||||
Lecture : resource_count 0. Les 6 ressources Terraform du dépôt sont des null_resource (provisioners SSH), pour lesquelles Checkov n'a aucune politique : ce scan ne prouve rien sur ce Terraform, dans un sens comme dans l'autre.
|
||||
|
||||
## 2. framework dockerfile, dépôt entier
|
||||
dockerfile scan results:
|
||||
Passed checks: 268, Failed checks: 3, Skipped checks: 0
|
||||
Check: CKV_DOCKER_2: "Ensure that HEALTHCHECK instructions have been added to container images"
|
||||
FAILED for resource: /ml/Dockerfile.
|
||||
File: /ml/Dockerfile:1-7
|
||||
Check: CKV_DOCKER_2: "Ensure that HEALTHCHECK instructions have been added to container images"
|
||||
FAILED for resource: /etl/airflow/Dockerfile.
|
||||
File: /etl/airflow/Dockerfile:1-50
|
||||
Check: CKV_DOCKER_2: "Ensure that HEALTHCHECK instructions have been added to container images"
|
||||
FAILED for resource: /apps/frontend/Dockerfile.
|
||||
File: /apps/frontend/Dockerfile:1-47
|
||||
-- résumé : dockerfile {'passed': 268, 'failed': 3, 'skipped': 0, 'parsing_errors': 0, 'resource_count': 4, 'checkov_version': '3.3.19'}
|
||||
|
||||
## 3. framework github_actions, dépôt entier
|
||||
github_actions scan results:
|
||||
Passed checks: 596, Failed checks: 0, Skipped checks: 0
|
||||
-- résumé : github_actions {'passed': 596, 'failed': 0, 'skipped': 0, 'parsing_errors': 0, 'resource_count': 0, 'checkov_version': '3.3.19'}
|
||||
|
||||
## 4. framework secrets, dépôt entier
|
||||
-- résumé : (aucune ressource) {'passed': 0, 'failed': 0, 'skipped': 0, 'parsing_errors': 0, 'resource_count': 0, 'checkov_version': '3.3.19'}
|
||||
@@ -0,0 +1,23 @@
|
||||
# gitleaks (image zricethezav/gitleaks:latest, sha256:c00b6bd0aeb3) · 2026-09-24 14:48 CEST
|
||||
# Commande : docker run --rm -v <depot>:/repo:ro zricethezav/gitleaks:latest git /repo --log-opts="--all" --redact --report-format json
|
||||
# Périmètre : toutes les branches locales et distantes, 342 commits hors merges (487 avec merges), commit de tête 9f343e9
|
||||
# Champs Author et Email retirés de cette copie (anonymisation). Valeurs caviardées par --redact.
|
||||
|
||||
Résultat brut : 339 commits scannés, ~3,43 Mo, 8 constats, tous de la règle generic-api-key.
|
||||
|
||||
- generic-api-key · apps/backend/tests/etl/test_reading_retention.py:74 · commit fa815f49 · 2026-09-24 · extrait caviardé : s3_access_key": "REDACTED"
|
||||
- generic-api-key · apps/frontend/src/app/features/auth/change-password/change-password.spec.ts:90 · commit c2f360c5 · 2026-09-23 · extrait caviardé : current_password: 'REDACTED'
|
||||
- generic-api-key · apps/backend/tests/repositories/test_drift.py:110 · commit 9bf2f271 · 2026-09-22 · extrait caviardé : api_current", REDACTED
|
||||
- generic-api-key · apps/backend/tests/repositories/test_drift.py:113 · commit 9bf2f271 · 2026-09-22 · extrait caviardé : api_history", REDACTED
|
||||
- generic-api-key · apps/backend/tests/repositories/test_reading.py:202 · commit c059f838 · 2026-09-18 · extrait caviardé : api_history", REDACTED
|
||||
- generic-api-key · apps/backend/tests/repositories/test_reading.py:205 · commit c059f838 · 2026-09-18 · extrait caviardé : api_current", REDACTED
|
||||
- generic-api-key · apps/backend/tests/repositories/test_reading.py:98 · commit b433e01f · 2026-09-18 · extrait caviardé : api_history", REDACTED
|
||||
- generic-api-key · apps/backend/tests/repositories/test_reading.py:101 · commit b433e01f · 2026-09-18 · extrait caviardé : api_current", REDACTED
|
||||
|
||||
Tri manuel : les 8 constats sont des faux positifs.
|
||||
- 6 constats (test_drift.py, test_reading.py), déjà triés le 23/09 : la règle generic-api-key prend pour une clé la valeur qui suit un identifiant commençant par « api_ ». Les lignes visées sont des appels de fabrique de test du type creer_lecture(..., source="api_history", consumption_kwh=20.0) : « api_history » et « api_current » sont deux des trois valeurs admises pour la colonne source de reading (contrainte ck_reading_source, apps/backend/app/models/energy.py), pas des secrets.
|
||||
- apps/backend/tests/etl/test_reading_retention.py:74 : identifiant de clé S3 factice (« GK » suivi de 10 chiffres) construit par la fabrique de test settings_s3(), à côté de SecretStr("un-secret-garage") ; aucun Garage réel ne l'accepte, les vraies clés sont générées sur la machine par scripts/provision-host.sh.
|
||||
- apps/frontend/src/app/features/auth/change-password/change-password.spec.ts:90 : mot de passe provisoire simulé d'un test unitaire Angular (valeur attendue par un mock de AuthService), sans compte réel derrière.
|
||||
|
||||
Contrôle complémentaire : git log --all -- .env apps/backend/.env ml/.env etl/airflow/.env '*.pem' '*.key' '*terraform.tfvars' '*.tfstate' '*.tfstate.backup' dns.token renvoie 0 commit : aucun fichier de secrets, certificat, clé, state Terraform ni jeton DNS n'a jamais été versionné.
|
||||
Seul fichier de configuration sensible versionné : infra/garage/garage.toml (commit e53c7e4, 24/09), par conception : relu ligne à ligne, il ne porte aucun secret, Garage lit GARAGE_RPC_SECRET, GARAGE_ADMIN_TOKEN et GARAGE_METRICS_TOKEN dans son environnement (ADR 0019).
|
||||
@@ -0,0 +1,66 @@
|
||||
# Trivy config (image aquasec/trivy:latest, sha256:62b1e65e8869) · 2026-09-24 14:46 CEST · commit 9f343e9, sur un export git archive du commit
|
||||
# Commande : docker run --rm -v <export>:/repo:ro aquasec/trivy:latest config /repo
|
||||
|
||||
Report Summary
|
||||
|
||||
┌────────────────────────────────────────┬────────────┬───────────────────┐
|
||||
│ Target │ Type │ Misconfigurations │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ apps/backend/Dockerfile │ dockerfile │ 0 │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ apps/frontend/Dockerfile │ dockerfile │ 1 │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ etl/airflow/Dockerfile │ dockerfile │ 1 │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ infra/terraform/environments/k3s-cible │ terraform │ 0 │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ infra/terraform/environments/vm-eni │ terraform │ 0 │
|
||||
├────────────────────────────────────────┼────────────┼───────────────────┤
|
||||
│ ml/Dockerfile │ dockerfile │ 1 │
|
||||
└────────────────────────────────────────┴────────────┴───────────────────┘
|
||||
Legend:
|
||||
- '-': Not scanned
|
||||
- '0': Clean (no security findings detected)
|
||||
|
||||
|
||||
apps/frontend/Dockerfile (dockerfile)
|
||||
=====================================
|
||||
Tests: 27 (SUCCESSES: 26, FAILURES: 1)
|
||||
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
|
||||
|
||||
DS-0026 (LOW): Add HEALTHCHECK instruction in your Dockerfile
|
||||
════════════════════════════════════════
|
||||
You should add HEALTHCHECK instruction in your docker container images to perform the health check on running containers.
|
||||
|
||||
See https://avd.aquasec.com/misconfig/ds-0026
|
||||
────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
etl/airflow/Dockerfile (dockerfile)
|
||||
===================================
|
||||
Tests: 27 (SUCCESSES: 26, FAILURES: 1)
|
||||
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
|
||||
|
||||
DS-0026 (LOW): Add HEALTHCHECK instruction in your Dockerfile
|
||||
════════════════════════════════════════
|
||||
You should add HEALTHCHECK instruction in your docker container images to perform the health check on running containers.
|
||||
|
||||
See https://avd.aquasec.com/misconfig/ds-0026
|
||||
────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
ml/Dockerfile (dockerfile)
|
||||
==========================
|
||||
Tests: 27 (SUCCESSES: 26, FAILURES: 1)
|
||||
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
|
||||
|
||||
DS-0026 (LOW): Add HEALTHCHECK instruction in your Dockerfile
|
||||
════════════════════════════════════════
|
||||
You should add HEALTHCHECK instruction in your docker container images to perform the health check on running containers.
|
||||
|
||||
See https://avd.aquasec.com/misconfig/ds-0026
|
||||
────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Trivy fs, scanners vuln et secret (image aquasec/trivy:latest, sha256:62b1e65e8869) · 2026-09-24 14:48 CEST · commit 9f343e9
|
||||
# Commande : docker run --rm -v <depot>:/repo:ro aquasec/trivy:latest fs --scanners vuln,secret --skip-dirs node_modules --skip-dirs .venv --skip-dirs .angular /repo
|
||||
# Périmètre : arbre de travail du poste, donc fichiers ignorés par git compris. Le corps de toute clé détectée est retiré de cette copie.
|
||||
|
||||
Report Summary
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────────┬──────┬─────────────────┬─────────┐
|
||||
│ Target │ Type │ Vulnerabilities │ Secrets │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ apps/backend/uv.lock │ uv │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ apps/frontend/package-lock.json │ npm │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ etl/airflow/uv.lock │ uv │ 1 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-07209b6bfc7a4edfa1c8a8b1b4577a34/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-37380043d1da4d578858f40853a7f72d/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-39f8a317ffed49288cbdffb66046ecd4/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-3a8d4c6d896b4ca2b0645b323adfb857/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-57dd632c169a4c43b5777f9fe1a52958/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/mlruns/1/models/m-d1cbcc5b82a1400ea438c01c12e73536/artifacts/requirements.txt │ pip │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ ml/uv.lock │ uv │ 0 │ - │
|
||||
├──────────────────────────────────────────────────────────────────────────────────┼──────┼─────────────────┼─────────┤
|
||||
│ infra/proxy/tls/privkey.pem │ text │ - │ 1 │
|
||||
└──────────────────────────────────────────────────────────────────────────────────┴──────┴─────────────────┴─────────┘
|
||||
Legend:
|
||||
- '-': Not scanned
|
||||
- '0': Clean (no security findings detected)
|
||||
|
||||
|
||||
etl/airflow/uv.lock (uv)
|
||||
========================
|
||||
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
|
||||
|
||||
┌───────────────────────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐
|
||||
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
|
||||
├───────────────────────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ apache-airflow-providers-smtp │ CVE-2026-41016 │ MEDIUM │ fixed │ 2.3.2 │ 3.0.0 │ apache-airflow-providers-smtp: No certificate validation on │
|
||||
│ │ │ │ │ │ │ SMTP STARTTLS connections in SMTP provider │
|
||||
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-41016 │
|
||||
└───────────────────────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────────┘
|
||||
|
||||
infra/proxy/tls/privkey.pem (secrets)
|
||||
=====================================
|
||||
Total: 1 (HIGH: 1) · AsymmetricPrivateKey (private-key) · infra/proxy/tls/privkey.pem:2-27 · [contenu retiré]
|
||||
|
||||
Lecture :
|
||||
- CVE-2026-41016 (= PYSEC-2026-24 de pip-audit) : dépendance transitive d'apache-airflow 3.3.2, SmtpHook sans validation de certificat au STARTTLS. Aucun DAG n'envoie de courriel et aucune connexion SMTP n'est déclarée dans Airflow : pas d'exposition aujourd'hui. Correctif : providers-smtp >= 3.0.0. Inchangée depuis le 23/09 : le verrou etl/airflow/uv.lock n'a pas bougé et n'est audité ni par la CI ni par Dependabot.
|
||||
- privkey.pem : clé du certificat auto-signé du poste, générée par make tls-selfsigned pour le développement local ; en recette et en production, le certificat vient de Let's Encrypt (ADR 0018). Ignorée par git (.gitignore, motif infra/proxy/tls/*.pem), jamais commitée (git log --all -- '*.pem' vide, cf. 05-gitleaks.txt). Risque réel : la retrouver dans un ZIP fabriqué à partir de l'arbre de travail au lieu d'un clone.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Tests d'accès et d'authentification du backend · 2026-09-24 14:50 CEST · commit 9f343e9
|
||||
# Commande : uv run --frozen pytest tests/api/test_matrice_acces.py tests/api/test_route_protection.py tests/api/test_authorization.py tests/api/test_hardening.py tests/api/test_auth.py tests/core/test_security.py -m 'not integration' --no-cov
|
||||
# Les tests marqués integration (mêmes matrices avec de vrais jetons et une vraie base) tournent en CI sur TimescaleDB, pas ici.
|
||||
|
||||
........................................................................ [ 83%]
|
||||
.............. [100%]
|
||||
86 passed, 5 deselected in 3.36s
|
||||
@@ -0,0 +1,16 @@
|
||||
# Terraform fmt et validate (mêmes étapes que .github/workflows/infra.yml) · 2026-09-24 14:50 CEST · commit 9f343e9, sur un export git archive · Terraform v1.16.3
|
||||
|
||||
## terraform fmt -check -recursive infra/terraform
|
||||
exit=0
|
||||
|
||||
## infra/terraform/environments/k3s-cible/ : init -backend=false puis validate
|
||||
init exit=0
|
||||
Success! The configuration is valid.
|
||||
|
||||
exit=0
|
||||
|
||||
## infra/terraform/environments/vm-eni/ : init -backend=false puis validate
|
||||
init exit=0
|
||||
Success! The configuration is valid.
|
||||
|
||||
exit=0
|
||||
@@ -0,0 +1,78 @@
|
||||
# Reverse proxy, frontal SNI et ports publiés · 2026-09-24 14:51 CEST · commit 9f343e9, configuration lue sur un export git archive
|
||||
|
||||
## nginx -t du proxy de stack (image nginx:1.31-alpine, celle de docker-compose.prod.yml, configuration montée en lecture seule)
|
||||
# --add-host backend et frontend : nginx résout les upstream au test, hors du réseau Compose. Certificat : celui du poste, monté en lecture seule.
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
|
||||
## nginx -t du frontal SNI de la machine (même commande que infra.yml)
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
|
||||
## Directives de sécurité du proxy (grep sur infra/proxy)
|
||||
infra/proxy/nginx.conf:1:# Contrainte : les directives `limit_req_zone` ne sont valides que dans le bloc `http`.
|
||||
infra/proxy/nginx.conf:16: server_tokens off;
|
||||
infra/proxy/nginx.conf:26: client_max_body_size 2m;
|
||||
infra/proxy/nginx.conf:35: limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
|
||||
infra/proxy/nginx.conf:36: limit_req_zone $binary_remote_addr zone=auth:10m rate=30r/m;
|
||||
infra/proxy/nginx.conf:37: limit_req_status 429;
|
||||
infra/proxy/conf.d/enervision.conf:14: listen 80 default_server;
|
||||
infra/proxy/conf.d/enervision.conf:22: return 301 https://$host$request_uri;
|
||||
infra/proxy/conf.d/enervision.conf:27: listen 443 ssl default_server;
|
||||
infra/proxy/conf.d/enervision.conf:28: listen 4443 ssl proxy_protocol default_server;
|
||||
infra/proxy/conf.d/enervision.conf:34: real_ip_header proxy_protocol;
|
||||
infra/proxy/conf.d/enervision.conf:40: ssl_protocols TLSv1.2 TLSv1.3;
|
||||
infra/proxy/conf.d/enervision.conf:44: ssl_session_tickets off;
|
||||
infra/proxy/conf.d/enervision.conf:48: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
infra/proxy/conf.d/enervision.conf:49: add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;
|
||||
infra/proxy/conf.d/enervision.conf:61: limit_req zone=auth burst=20 nodelay;
|
||||
infra/proxy/conf.d/enervision.conf:67: limit_req zone=api burst=40 nodelay;
|
||||
|
||||
## En-têtes posés par l'API elle-même (apps/backend/app/api/middleware.py)
|
||||
apps/backend/app/api/middleware.py:16: "X-Content-Type-Options": "nosniff",
|
||||
apps/backend/app/api/middleware.py:17: "X-Frame-Options": "DENY",
|
||||
apps/backend/app/api/middleware.py:18: "Referrer-Policy": "no-referrer",
|
||||
apps/backend/app/api/middleware.py:21: "Cross-Origin-Resource-Policy": "same-origin",
|
||||
apps/backend/app/api/middleware.py:37: response.headers["Cache-Control"] = "no-store"
|
||||
|
||||
## Frontal SNI (infra/front/nginx.conf) : aiguillage TCP sans déchiffrement
|
||||
map $ssl_preread_server_name $stack {
|
||||
~^rec\. 127.0.0.1:8444;
|
||||
~^dev\. 127.0.0.1:9444;
|
||||
default 127.0.0.1:10444;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
ssl_preread on;
|
||||
proxy_pass $stack;
|
||||
proxy_protocol on;
|
||||
proxy_connect_timeout 5s;
|
||||
Réseau : network_mode host (infra/front/compose.yml), ports 80 et 443 de la machine. Il lit le nom demandé (SNI), sans déchiffrer, et relaie en PROXY protocol vers l'écouteur 4443 du proxy de la stack visée, sur 127.0.0.1.
|
||||
|
||||
## Ports publiés après fusion (docker compose 5.5.1, --env-file .env.example, profil monitoring, config --format json)
|
||||
# Commande : docker compose --env-file .env.example -f docker-compose.yml -f docker-compose.prod.yml --profile monitoring config --format json, puis extraction des seuls champs ports (aucune variable recopiée)
|
||||
airflow-apiserver 127.0.0.1:8080->8080
|
||||
airflow-dag-processor aucun port publié
|
||||
airflow-init aucun port publié
|
||||
airflow-scheduler aucun port publié
|
||||
alertmanager 127.0.0.1:9093->9093
|
||||
backend aucun port publié
|
||||
cadvisor aucun port publié
|
||||
db 127.0.0.1:5433->5432
|
||||
frontend aucun port publié
|
||||
garage 127.0.0.1:3900->3900, 127.0.0.1:3903->3903
|
||||
grafana 127.0.0.1:3001->3000
|
||||
mailpit 127.0.0.1:8025->8025
|
||||
node-exporter aucun port publié
|
||||
postgres-exporter aucun port publié
|
||||
prometheus 127.0.0.1:9090->9090
|
||||
proxy 0.0.0.0:80->80, 0.0.0.0:443->443, 127.0.0.1:<aléatoire>->4443
|
||||
|
||||
## Ports réellement posés sur la machine par scripts/provision-host.sh (l. 212-214, colonnes https, http, front)
|
||||
prod 127.0.0.1:10443 127.0.0.1:10080 127.0.0.1:10444
|
||||
rec 127.0.0.1:8443 127.0.0.1:8081 127.0.0.1:8444
|
||||
dev 127.0.0.1:9443 127.0.0.1:8083 127.0.0.1:9444
|
||||
Base, Mailpit, Airflow, Garage et supervision décalés par environnement, tous sur 127.0.0.1.
|
||||
|
||||
Lecture : les défauts du dépôt publient le proxy en 80 et 443 pour un poste de développement. Sur la machine, provision-host.sh ramène tous les ports des trois stacks sur la boucle locale : le seul composant joignable depuis le réseau est le frontal SNI, en 80 et 443. Backend, frontend, scheduler, dag-processor et exporteurs ne publient rien. Le port SMTP 1025 de Mailpit, publié en développement, disparaît (!override). L'état effectif de la machine est relevé dans 12-constats-machine.txt.
|
||||
@@ -0,0 +1,11 @@
|
||||
# State Terraform local de la racine vm-eni · relevé 2026-09-24 14:50 CEST · commit 9f343e9
|
||||
# Commande : terraform -chdir=infra/terraform/environments/vm-eni state list (seuls les noms de ressources, aucun attribut recopié)
|
||||
|
||||
null_resource.docker_engine
|
||||
null_resource.environnements
|
||||
null_resource.runner_github
|
||||
|
||||
Dernière écriture du state : 2026-09-23 12:12:09
|
||||
serial 12 · terraform 1.16.3 · 3 ressources appliquées sur 4 déclarées dans vm-eni/main.tf
|
||||
|
||||
Lecture : depuis le relevé du 23/09 9h05 (serial 3, 2 ressources), les applies du 22/09 au 23/09 ont installé Docker, préparé les trois environnements (clones, .env générés sur la machine, certificats) et enregistré le runner GitHub (null_resource.runner_github). La quatrième ressource, null_resource.coffre (volumes Docker dans un coffre LUKS), est désactivée tant que la variable coffre_taille est vide (count = 0), et elle l'est restée : la machine est un conteneur LXC, où LUKS est impossible (ADR 0020), elle n'a donc jamais été appliquée. Clés des triggers relevées (valeurs non recopiées) : docker_engine [hote, user], environnements [depot, domaine, racine, script], runner_github [dossier, labels, nom, version]. L'adresse de la machine et le compte SSH y figurent, mais ni le jeton d'enregistrement du runner ni la clé SSH : le jeton est une variable sensitive hors des triggers (ADR 0010).
|
||||
@@ -0,0 +1,99 @@
|
||||
# Constats sur la machine du groupe, en lecture seule · 24/09/2026 15h20 à 15h30 CEST · commit déployé en production 9f343e9
|
||||
# Commande : ssh root@<IP-VM-G3> 'bash -s' < constats-machine-enervision.sh, puis constats-complement-enervision.sh, lancés par l'auteur
|
||||
# Anonymisation : adresse et nom de la machine masqués, identifiants de cluster Vault et adresses internes des réseaux Docker retirés.
|
||||
|
||||
## Virtualisation (systemd-detect-virt)
|
||||
lxc
|
||||
|
||||
## Ports TCP en écoute (ss -tlnpH, adresse locale et processus)
|
||||
Sur toutes les interfaces :
|
||||
0.0.0.0:22 et [::]:22 sshd
|
||||
0.0.0.0:80, 0.0.0.0:443 frontal SNI (enervision-front)
|
||||
*:6443 k3s-server
|
||||
0.0.0.0:8200, *:8201 vault
|
||||
0.0.0.0:8202, *:8203 vault
|
||||
0.0.0.0:8204, *:8205 vault
|
||||
Sur la boucle locale seulement :
|
||||
proxys des stacks 127.0.0.1:10080, 10443, 10444 (prod) · 8081, 8443, 8444 (rec) · 8083, 9443, 9444 (dev)
|
||||
bases des stacks 127.0.0.1:5433 (prod), 5434 (rec), 5435 (dev)
|
||||
Mailpit 127.0.0.1:8025, 8026, 8027
|
||||
API Airflow 127.0.0.1:8080, 8082, 8084
|
||||
Garage (S3, admin) 127.0.0.1:3900, 3903 (prod) · 3910, 3913 (rec) · aucun pour dev
|
||||
Grafana, Prometheus, Alertmanager (prod) 127.0.0.1:3001, 9090, 9093
|
||||
PostgreSQL 18 de l'hôte 127.0.0.1:5432, [::1]:5432
|
||||
Postfix de l'hôte 127.0.0.1:25, [::1]:25
|
||||
k3s (interne) 127.0.0.1:6444
|
||||
containerd 127.0.0.1:34303
|
||||
résolveur systemd 127.0.0.53:53, 127.0.0.54:53
|
||||
|
||||
## Pare-feu (nft list ruleset, iptables -S)
|
||||
table inet filter : chaînes input, forward et output vides, politique accept.
|
||||
table ip raw : 24 règles posées par Docker, qui rejettent tout paquet destiné à un port publié sur 127.0.0.1 (ou à un conteneur) s'il n'arrive pas par lo (ou par le pont Docker du projet).
|
||||
iptables : -P INPUT ACCEPT, -P FORWARD DROP, -P OUTPUT ACCEPT, et les chaînes DOCKER, DOCKER-USER, DOCKER-FORWARD de Docker.
|
||||
|
||||
## SSH effectif (sshd -T)
|
||||
permitrootlogin yes
|
||||
pubkeyauthentication yes
|
||||
passwordauthentication yes
|
||||
kbdinteractiveauthentication no
|
||||
|
||||
## Stacks (docker ps par projet Compose)
|
||||
### enervision-prod : 15 services démarrés, airflow-init terminé (0)
|
||||
airflow-apiserver (healthy), airflow-dag-processor, airflow-scheduler, alertmanager, backend (healthy), cadvisor (healthy), db (healthy), frontend, garage (healthy), grafana, mailpit (healthy), node-exporter, postgres-exporter, prometheus, proxy
|
||||
### enervision-rec : 9 services démarrés, airflow-init terminé (0)
|
||||
airflow-apiserver (healthy), airflow-dag-processor, airflow-scheduler, backend (healthy), db (healthy), frontend, garage (healthy), mailpit (healthy), proxy
|
||||
### enervision-dev : 8 services démarrés, airflow-init terminé (0), pas de Garage
|
||||
airflow-apiserver (healthy), airflow-dag-processor, airflow-scheduler, backend (healthy), db (healthy), frontend, mailpit (healthy), proxy
|
||||
### enervision-front
|
||||
front, démarré depuis 26 heures
|
||||
### Hors projets EnerVision
|
||||
19 conteneurs arrêtés entre le 16 et le 21/09 ou jamais démarrés, dont 3 hello-world ; aucun en fonctionnement.
|
||||
|
||||
## Sondes de santé par le nom public, certificat vérifié (curl sans -k)
|
||||
prod http=200 verification_tls=0
|
||||
rec http=200 verification_tls=0
|
||||
dev http=200 verification_tls=0
|
||||
|
||||
## Certificats servis par le frontal (openssl s_client -servername)
|
||||
prod : issuer=C = US, O = Let's Encrypt, CN = YE1 · notAfter=Dec 22 09:40:38 2026 GMT
|
||||
rec : issuer=C = US, O = Let's Encrypt, CN = YE1 · notAfter=Dec 22 09:11:04 2026 GMT
|
||||
dev : issuer=C = US, O = Let's Encrypt, CN = YE1 · notAfter=Dec 22 09:17:54 2026 GMT
|
||||
|
||||
## En-têtes servis en production sur / (curl -sI)
|
||||
server: nginx
|
||||
strict-transport-security: max-age=31536000; includeSubDomains
|
||||
content-security-policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
|
||||
|
||||
## Droits des secrets sur disque (stat)
|
||||
600 enervision /srv/enervision/dev/.env
|
||||
600 enervision /srv/enervision/prod/.env
|
||||
600 enervision /srv/enervision/rec/.env
|
||||
600 enervision /srv/enervision/dns.token
|
||||
|
||||
## Runner GitHub Actions (systemctl)
|
||||
actions.runner.<depot>.<machine-G3>.service loaded active running
|
||||
|
||||
## Journal d'audit de la production (select action, count(*) from audit_log group by action)
|
||||
user.created|5
|
||||
user.password_changed|3
|
||||
|
||||
## Supervision de la production (API Prometheus, 127.0.0.1:9090)
|
||||
cibles : alertmanager, backend, cadvisor, garage, node, postgres, prometheus · toutes health=up
|
||||
règles chargées : ApiErreursServeur, ApiIndisponible, ApiLatenceElevee, BaseConnexionsSaturees, BaseIndisponible, CibleInjoignable, HoteCpuSature, HoteDisquePlein, HoteMemoireSaturee (groupes api, base, hote, supervision)
|
||||
|
||||
## Services hors plateforme
|
||||
k3s : binaire /usr/local/bin/k3s du 17/09/2026 14:07 UTC · systemctl show k3s : ActiveState=activating, SubState=start, NRestarts=51678 · k3s kubectl get pods -A : No resources found, API refusée pendant un redémarrage
|
||||
vault.service : active (running), « HashiCorp Vault »
|
||||
Vault, seal-status sans authentification (champs utiles) :
|
||||
8200 : version 2.1.1, shamir t=2 n=5, initialized=true, sealed=false, storage_type=file, démarré le 22/09 12:28 UTC
|
||||
8202 : version 2.1.1, shamir t=3 n=5, initialized=true, sealed=false, storage_type=file, démarré le 22/09 07:27 UTC
|
||||
8204 : version 2.1.1, shamir t=4 n=5, initialized=true, sealed=false, storage_type=file, démarré le 22/09 07:28 UTC
|
||||
postgresql@18-main : active (running), boucle locale seulement
|
||||
postfix@- : active (running), boucle locale seulement
|
||||
|
||||
Lecture :
|
||||
- La plateforme livrée tient sa promesse : de ses composants, seul le frontal SNI écoute hors de la boucle locale, en 80 et 443. Les trois noms publics répondent 200 avec un certificat Let's Encrypt vérifié, HSTS et CSP sont servis, les .env sont en 600, le runner tourne, les sept cibles Prometheus sont up.
|
||||
- La machine, elle, expose davantage : SSH en root avec mot de passe accepté, l'API k3s sur 6443 et trois serveurs Vault descellés sur 8200 à 8205, tous sur toutes les interfaces, sans pare-feu hôte (table nftables vide, politique accept).
|
||||
- k3s et Vault ne viennent pas du code livré : git grep vault ne trouve rien sur le commit gelé. Vault est visé par le Terraform d'une branche de travail non fusionnée (provider vault, adresse de la machine en 8200). k3s redémarre en boucle depuis son installation du 17/09 (51 678 redémarrages) et ne porte aucun pod : il consomme sans servir.
|
||||
- L'environnement dev tourne encore le commit du 23/09 : pas de Garage. Il suit la dernière branche lancée à la main (ADR 0017), et aucune ne l'a été depuis.
|
||||
- Le journal d'audit ne compte que les événements d'administration des comptes (5 créations, 3 changements de mot de passe) : les connexions vont à login_attempt.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Réglages GitHub du dépôt, lus par l'API (gh api, lecture seule) · 2026-09-24 14:54 CEST · commit 9f343e9
|
||||
# Heures converties en heure locale (CEST). URL des environnements non reproduites (anonymisation).
|
||||
|
||||
## Dépôt
|
||||
visibilité : public · branche par défaut : main · licence : aucune · fork autorisé : oui
|
||||
|
||||
## Protection des branches (gh api repos/<depot>/branches/<b> --jq .protected ; gh api repos/<depot>/rulesets)
|
||||
main : protected=false
|
||||
dev : protected=false
|
||||
rulesets : 0
|
||||
|
||||
## Environnements de déploiement (gh api repos/<depot>/environments/<env>)
|
||||
prod : 1 règle, branch_policy, restreint à la branche main ; relecteurs requis : 0 ; délai : aucun
|
||||
rec : aucune règle
|
||||
dev : aucune règle
|
||||
|
||||
## Approbation des workflows des contributeurs externes (gh api repos/<depot>/actions/permissions/workflow)
|
||||
refusé avec les droits d'un membre (HTTP 403) : réglage non vérifiable, seule l'administratrice le lit
|
||||
|
||||
## Secrets d'Actions (gh secret list, noms seulement)
|
||||
SONAR_TOKEN
|
||||
|
||||
## Derniers runs de ci.yml (gh run list --workflow ci.yml)
|
||||
24/09 11:51 dev 9f343e9 push success
|
||||
24/09 11:08 main 9f343e9 push success
|
||||
24/09 11:08 dev 5a7e2a9 pull_request success
|
||||
24/09 10:59 dev 5a7e2a9 push success
|
||||
|
||||
## Déploiements de l'environnement prod (gh api repos/<depot>/deployments?environment=prod)
|
||||
24/09 11:12 9f343e9
|
||||
23/09 15:56 5afe2fc
|
||||
23/09 14:56 32f1bef
|
||||
23/09 12:49 7f4364d
|
||||
23/09 12:02 84969d3
|
||||
23/09 12:02 93d5cad
|
||||
23/09 11:37 b786f27
|
||||
|
||||
Lecture :
|
||||
- La production n'attend aucune approbation humaine : la seule garde est la restriction à main. L'ADR 0009 annonce une production après approbation, l'ADR 0014 laisse ce réglage à poser par l'administratrice : il ne l'est pas.
|
||||
- Aucune branche protégée : un membre en écriture peut pousser directement sur dev ou main, ce qui est arrivé trois fois sur dev (c2f360c, cbbfaf4, 6c09bee), relus ensuite par les PR de remontée #161 et #163.
|
||||
- Un seul secret côté GitHub, pour SonarCloud : le déploiement n'en consomme aucun, le runner auto-hébergé travaille dans le dossier qui porte déjà le .env.
|
||||
- Chaque push sur main depuis le 23/09 11h37 a produit un déploiement ; le dernier porte le commit gelé 9f343e9.
|
||||
@@ -0,0 +1,21 @@
|
||||
# DAST OWASP ZAP, dernier passage publié en artefact zap-report (run 35840107213, dast.yml) · 23/09/2026 10:59 CEST · commit fe0d422 (PR de remontée dev vers main)
|
||||
# Récupération : gh run download 35840107213 -n zap-report, puis extraction des alertes du JSON et du bilan de zap-stdout.log
|
||||
# Cible : l'API en fonctionnement dans le job (backend seul, sans TLS ni proxy), authentifiée par un compte lecteur jetable
|
||||
|
||||
## Bilan des règles (zap-stdout.log)
|
||||
FAIL-NEW: 0 FAIL-INPROG: 0 WARN-NEW: 0 WARN-INPROG: 0 INFO: 0 IGNORE: 0 PASS: 118
|
||||
|
||||
## Alertes du rapport (ZAP 2.17.0)
|
||||
- Informational (High) · A Client Error response code was returned by the server [100000] · 65 occurrence(s)
|
||||
- Informational (Medium) · Non-Storable Content [10049] · 5 occurrence(s)
|
||||
- Informational (High) · Authentication Request Identified [10111] · 1 occurrence(s)
|
||||
exemple : POST /api/v1/auth/login · paramètre email
|
||||
- Informational (Medium) · Information Disclosure - Sensitive Information in URL [10024] · 1 occurrence(s)
|
||||
exemple : GET /api/v1/auth/reset-password/validate?token=token · paramètre token
|
||||
|
||||
Lecture :
|
||||
- 0 échec, 0 avertissement, 118 règles passées. Les 4 alertes restantes sont informatives.
|
||||
- « Client Error response code » (65) : 42 × 404, 16 × 403, 3 × 405, 2 × 422, 2 × 401, les refus attendus d'un scanner qui sonde des routes inexistantes, gardées par rôle ou appelées avec une charge invalide. Aucun 5xx.
|
||||
- « Non-Storable Content » (5) : login, logout et refresh marqués Cache-Control: no-store par l'API, voulu ; les deux sondes de santé, que ZAP juge non mémorisables parce qu'appelées avec un en-tête Authorization.
|
||||
- « Authentication Request Identified » : repérage de la route de connexion, sans défaut associé.
|
||||
- « Sensitive Information in URL » : le jeton de réinitialisation voyage en paramètre de GET /api/v1/auth/reset-password/validate. Mitigations en place : jeton stocké haché (token_hash), valable 15 minutes (password_reset_ttl_seconds = 900), à usage unique (consumed_at), caviardé des journaux de l'API (app/core/logging.py, motif token=), Referrer-Policy: no-referrer. Résidu réel : le journal d'accès du proxy (log_format enervision, champ "$request", infra/proxy/nginx.conf:18) enregistre l'URL complète, donc le jeton en clair pendant sa durée de validité, lisible par qui administre la machine.
|
||||
Reference in New Issue
Block a user