Files
ENI-projet-piscine/docs/livrables/EC04/EADL26_EC04 - Rapport - LEROY Johan (G3).md
T
Johan LEROY 182a2f4a6c 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.
2026-09-24 15:55:17 +02:00

32 KiB
Raw Blame History

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.