Dette passe 1, lot L7 : builds reproductibles, dépendances, durcissement des conteneurs #7

Open
Thomas wants to merge 7 commits from pr/dette-l7 into pr/dette-l4bis
Member

Objet

Lot L7 de la passe 1 de dette : builds reproductibles, dépendances à jour et auditées, durcissement des conteneurs.

Contenu de la branche

  • Fusion du lot L7 : le lot a été fait en parallèle sur fix/dette-pass-1-l7, partie de 74df6f6 (tête de L5). Ses cinq commits (d2436fd, 112bfc5, 2e044ee, e9a63e1, f257e0a) sont fusionnés au-dessus de L4-bis par le commit de fusion 1795d14.
  • Commit a7e2e07 du fil principal, après la fusion : le lot L7 avait remonté le profil release du boîtier à la racine du workspace, panic = "abort" compris, ce qui l'appliquait aussi au serveur. Désormais :
    • le serveur garde le déroulement de pile sur panic ([profile.release] sans panic) : une tâche en panique est absorbée par tokio::spawn au lieu d'arrêter tout le processus pendant une écriture ;
    • le boîtier est construit avec le profil release-embedded (inherits = "release", panic = "abort"), comme son crate le prévoyait ; aquaboitier-embedded/Dockerfile construit et copie depuis target/release-embedded/.

Dettes traitées et preuves

Dette Correction Preuve
N16, builds reproductibles Cargo.lock copié et --locked dans les deux Dockerfiles, cargo generate-lockfile supprimé ; images épinglées par version et empreinte @sha256: (builder serveur rust:1.99.0-slim-bookworm, runtime debian:bookworm-20261005-slim, builder boîtier rust:1.99.0-alpine3.24, runtime alpine:3.24.2, composes mariadb:10.11.19, eclipse-mosquitto:2.0.22, webdb/app:2025.06.10) ; rust-toolchain.toml en 1.99.0 aquaserveur/Dockerfile, aquaboitier-embedded/Dockerfile, composes, rust-toolchain.toml
N17 étape tester du Dockerfile boîtier supprimée ; Dockerfile.prod supprimé (aucune référence hors README, mis à jour) aquaboitier-embedded/, README.md
N18 RUN_MIGRATIONS et mode migrate retirés ; libpq-dev, libssl-dev, pkg-config, netcat-openbsd retirés (attente de la base par /dev/tcp de bash) aquaserveur/Dockerfile, entrypoint.sh, ENV_SETUP.md
.dockerignore racine versionné ; contexte de build serveur 855 kB (environ 6 Go avant d'après le rapport 02) .dockerignore, .gitignore
N19, composes durcis ports de docker-compose.base.yml sur 127.0.0.1 ; no-new-privileges et cap_drop: [ALL] sur tous les services sauf rust-test, capacités rendues une par une après essai (mariadb CHOWN, DAC_OVERRIDE, SETGID, SETUID ; mosquitto et backend CHOWN, SETGID, SETUID ; boîtier et webdb aucune) ; read_only: true avec tmpfs pour mariadb, mosquitto, backend, boîtier ; volumes boitier_state et boitier_conf ; MariaDB de base 10.6 vers 10.11.19 docker-compose.base.yml, docker-compose.dev.yml, docker-compose.prod.yml
N22, configuration d'audit audit.toml racine supprimé, .cargo/audit.toml seul fichier, lu par cargo audit .cargo/audit.toml
N23, N24, dépendances cargo update compatible, reqwest retiré : 336 paquets avant, 263 après ; résolus : h2, rustls, rustls-webpki 0.103, anyhow, event-listener, rand, spin Cargo.lock, Cargo.toml
N20, N21 partie code 19 sqlx-no-format-in-query (ERROR) des tests corrigés : SQL construit avant l'appel ; 2 unwrap de production de config_sync.rs remplacés par un repli journalisé aquaserveur/tests/*, aquaserveur/src/config_sync.rs
.env.gitcheck chemins de l'ancien dépôt retirés, contrôle des valeurs longues réécrit (couvre .env.prod, n'affiche pas les valeurs) .env.gitcheck

Exceptions cargo audit justifiées

Six exceptions dans .cargo/audit.toml, décidées le 2026-10-07 avec la base d'avis du jour, chacune avec sa justification et sa condition de retrait. Réexamen à chaque mise à jour de rumqttc et de sqlx, et au plus tard le 2027-01-07.

Avis Paquet Justification Retrait
RUSTSEC-2023-0071 rsa 0.9.10 (via sqlx-mysql) aucun correctif publié ; le serveur ne détient aucune clé privée RSA dès qu'une version corrigée existe
RUSTSEC-2026-0049, 0098, 0099, 0104 rustls-webpki 0.102.8 (imposé par rumqttc 0.25.1, dernière version) aucune connexion TLS vers MQTT dans le code (aucun Transport::tls), donc pas de vérification de certificats par ce chemin quand rumqttc dépend de rustls-webpki >= 0.103.10, ou avant d'activer TLS sur MQTT
RUSTSEC-2025-0134 rustls-pemfile 2.2.0 (non maintenu, avertissement) lecture PEM inutilisée sans TLS avec la mise à jour de rumqttc

Résultat attendu : cargo audit sans vulnérabilité hors de cette liste.

Portes (compte rendu du lot)

Porte Résultat
cargo fmt --all -- --check, cargo clippy ... -D warnings OK
cargo test --workspace OK, 0 échec
Tests ignorés sur MariaDB 10.11.19 et deux Mosquitto 2.0.22 jetables, durcis comme les composes 68 passés, 0 échec, deux passages (sur la base L5 du lot)
Semgrep .semgrep/rules 0 ERROR
Build aquaserveur/Dockerfile OK, 89,3 Mo
Build aquaboitier-embedded/Dockerfile OK, 11,9 Mo (avant a7e2e07)

Écarts et points à valider

  • MariaDB de docker-compose.base.yml : 10.6 vers 10.11.19.
  • rust-test (dev) : no-new-privileges seulement, sans cap_drop (root sur les sources montées).
  • Nouveaux volumes du boîtier dev : à la prochaine recréation, l'identité et l'état de la couche inscriptible sont perdus ; BOITIER_DEV_MODE=1 attend alors une identité injectée.
  • Commande ARM64 du README (docker buildx build --platform linux/arm64 -f aquaboitier-embedded/Dockerfile) non exécutée.
  • webdb/app : image tierce, seul tag daté disponible.
  • aquaboitier-embedded/.dockerignore non suivi du dépôt principal : à supprimer à la main.

Hors périmètre

N21 hors code (règle no-unwrap-in-production sans exclusion des tests, configuration Semgrep et CI), N25 à N28, authentification et ACL Mosquitto et healthchecks réels (L6).

Retour arrière

Revenir a7e2e07 puis la fusion 1795d14 (git revert -m 1), reconstruire les images. Les volumes ajoutés au boîtier dev peuvent être laissés.


Pile de PR

Base de cette PR : pr/dette-l4bis. Fusion dans l'ordre de la pile ; apres chaque fusion, vigie.py retarget rebase la PR suivante sur main.

  1. #2 pr/dette-l2 : Dette passe 1, lot L2 : contrats partagés dans aquashared et client MQTT du boîtier
  2. #3 pr/dette-l3 : Dette passe 1, lot L3 : provisioning non destructif et identifiant validé à l'entrée MQTT
  3. #4 pr/dette-l4 : Dette passe 1, lot L4 : mesures, alertes et déduplication
  4. #5 pr/dette-l5 : Dette passe 1, lot L5 : boucle MQTT du serveur, livraison de la configuration et journaux
  5. #6 pr/dette-l4bis : Dette passe 1, lot L4-bis : alertes même si l'écriture échoue, délai unique de 600 s, typage des capteurs, ordre des accusés MQTT
  6. #7 pr/dette-l7 : Dette passe 1, lot L7 : builds reproductibles, dépendances, durcissement des conteneurs (cette PR)
  7. #8 pr/dette-l6 : Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée
  8. #9 pr/dette-l8 : Dette passe 1, lot L8 : tests non destructifs, fixtures synthétiques, script de tests sur base jetable

Commits du lot

  • d2436fd chore: pin rust 1.99.0, update dependencies, drop reqwest, single audit config
  • 112bfc5 build: reproducible pinned images, locked builds, root dockerignore
  • 2e044ee build: harden compose services and bind base ports to loopback
  • e9a63e1 fix: build test SQL outside sqlx calls and drop panics from config payloads
  • f257e0a chore: fix gitcheck paths for the current repository layout
  • 1795d14 Merge branch 'fix/dette-pass-1-l7' into fix/dette-pass-1-int
  • a7e2e07 build: keep unwinding on the server, abort only in the boitier profile
## Objet Lot L7 de la passe 1 de dette : builds reproductibles, dépendances à jour et auditées, durcissement des conteneurs. ## Contenu de la branche - **Fusion du lot L7** : le lot a été fait en parallèle sur `fix/dette-pass-1-l7`, partie de `74df6f6` (tête de L5). Ses cinq commits (`d2436fd`, `112bfc5`, `2e044ee`, `e9a63e1`, `f257e0a`) sont fusionnés au-dessus de L4-bis par le commit de fusion `1795d14`. - **Commit `a7e2e07` du fil principal**, après la fusion : le lot L7 avait remonté le profil release du boîtier à la racine du workspace, `panic = "abort"` compris, ce qui l'appliquait aussi au serveur. Désormais : - le serveur garde le déroulement de pile sur panic (`[profile.release]` sans `panic`) : une tâche en panique est absorbée par `tokio::spawn` au lieu d'arrêter tout le processus pendant une écriture ; - le boîtier est construit avec le profil `release-embedded` (`inherits = "release"`, `panic = "abort"`), comme son crate le prévoyait ; `aquaboitier-embedded/Dockerfile` construit et copie depuis `target/release-embedded/`. ## Dettes traitées et preuves | Dette | Correction | Preuve | |---|---|---| | N16, builds reproductibles | `Cargo.lock` copié et `--locked` dans les deux Dockerfiles, `cargo generate-lockfile` supprimé ; images épinglées par version et empreinte `@sha256:` (builder serveur `rust:1.99.0-slim-bookworm`, runtime `debian:bookworm-20261005-slim`, builder boîtier `rust:1.99.0-alpine3.24`, runtime `alpine:3.24.2`, composes `mariadb:10.11.19`, `eclipse-mosquitto:2.0.22`, `webdb/app:2025.06.10`) ; `rust-toolchain.toml` en 1.99.0 | `aquaserveur/Dockerfile`, `aquaboitier-embedded/Dockerfile`, composes, `rust-toolchain.toml` | | N17 | étape `tester` du Dockerfile boîtier supprimée ; `Dockerfile.prod` supprimé (aucune référence hors README, mis à jour) | `aquaboitier-embedded/`, `README.md` | | N18 | `RUN_MIGRATIONS` et mode `migrate` retirés ; `libpq-dev`, `libssl-dev`, `pkg-config`, `netcat-openbsd` retirés (attente de la base par `/dev/tcp` de bash) | `aquaserveur/Dockerfile`, `entrypoint.sh`, `ENV_SETUP.md` | | `.dockerignore` racine | versionné ; contexte de build serveur 855 kB (environ 6 Go avant d'après le rapport 02) | `.dockerignore`, `.gitignore` | | N19, composes durcis | ports de `docker-compose.base.yml` sur `127.0.0.1` ; `no-new-privileges` et `cap_drop: [ALL]` sur tous les services sauf `rust-test`, capacités rendues une par une après essai (mariadb CHOWN, DAC_OVERRIDE, SETGID, SETUID ; mosquitto et backend CHOWN, SETGID, SETUID ; boîtier et webdb aucune) ; `read_only: true` avec tmpfs pour mariadb, mosquitto, backend, boîtier ; volumes `boitier_state` et `boitier_conf` ; MariaDB de base 10.6 vers 10.11.19 | `docker-compose.base.yml`, `docker-compose.dev.yml`, `docker-compose.prod.yml` | | N22, configuration d'audit | `audit.toml` racine supprimé, `.cargo/audit.toml` seul fichier, lu par `cargo audit` | `.cargo/audit.toml` | | N23, N24, dépendances | `cargo update` compatible, `reqwest` retiré : 336 paquets avant, 263 après ; résolus : h2, rustls, rustls-webpki 0.103, anyhow, event-listener, rand, spin | `Cargo.lock`, `Cargo.toml` | | N20, N21 partie code | 19 `sqlx-no-format-in-query` (ERROR) des tests corrigés : SQL construit avant l'appel ; 2 `unwrap` de production de `config_sync.rs` remplacés par un repli journalisé | `aquaserveur/tests/*`, `aquaserveur/src/config_sync.rs` | | `.env.gitcheck` | chemins de l'ancien dépôt retirés, contrôle des valeurs longues réécrit (couvre `.env.prod`, n'affiche pas les valeurs) | `.env.gitcheck` | ## Exceptions `cargo audit` justifiées Six exceptions dans `.cargo/audit.toml`, décidées le 2026-10-07 avec la base d'avis du jour, chacune avec sa justification et sa condition de retrait. **Réexamen à chaque mise à jour de `rumqttc` et de `sqlx`, et au plus tard le 2027-01-07.** | Avis | Paquet | Justification | Retrait | |---|---|---|---| | RUSTSEC-2023-0071 | `rsa` 0.9.10 (via `sqlx-mysql`) | aucun correctif publié ; le serveur ne détient aucune clé privée RSA | dès qu'une version corrigée existe | | RUSTSEC-2026-0049, 0098, 0099, 0104 | `rustls-webpki` 0.102.8 (imposé par `rumqttc` 0.25.1, dernière version) | aucune connexion TLS vers MQTT dans le code (aucun `Transport::tls`), donc pas de vérification de certificats par ce chemin | quand `rumqttc` dépend de `rustls-webpki` >= 0.103.10, ou avant d'activer TLS sur MQTT | | RUSTSEC-2025-0134 | `rustls-pemfile` 2.2.0 (non maintenu, avertissement) | lecture PEM inutilisée sans TLS | avec la mise à jour de `rumqttc` | Résultat attendu : `cargo audit` sans vulnérabilité hors de cette liste. ## Portes (compte rendu du lot) | Porte | Résultat | |---|---| | `cargo fmt --all -- --check`, `cargo clippy ... -D warnings` | OK | | `cargo test --workspace` | OK, 0 échec | | Tests ignorés sur MariaDB 10.11.19 et deux Mosquitto 2.0.22 jetables, durcis comme les composes | 68 passés, 0 échec, deux passages (sur la base L5 du lot) | | Semgrep `.semgrep/rules` | 0 ERROR | | Build `aquaserveur/Dockerfile` | OK, 89,3 Mo | | Build `aquaboitier-embedded/Dockerfile` | OK, 11,9 Mo (avant `a7e2e07`) | ## Écarts et points à valider - MariaDB de `docker-compose.base.yml` : 10.6 vers 10.11.19. - `rust-test` (dev) : `no-new-privileges` seulement, sans `cap_drop` (root sur les sources montées). - Nouveaux volumes du boîtier dev : à la prochaine recréation, l'identité et l'état de la couche inscriptible sont perdus ; `BOITIER_DEV_MODE=1` attend alors une identité injectée. - Commande ARM64 du README (`docker buildx build --platform linux/arm64 -f aquaboitier-embedded/Dockerfile`) non exécutée. - `webdb/app` : image tierce, seul tag daté disponible. - `aquaboitier-embedded/.dockerignore` non suivi du dépôt principal : à supprimer à la main. ## Hors périmètre N21 hors code (règle `no-unwrap-in-production` sans exclusion des tests, configuration Semgrep et CI), N25 à N28, authentification et ACL Mosquitto et healthchecks réels (L6). ## Retour arrière Revenir `a7e2e07` puis la fusion `1795d14` (`git revert -m 1`), reconstruire les images. Les volumes ajoutés au boîtier dev peuvent être laissés. --- ### Pile de PR Base de cette PR : `pr/dette-l4bis`. Fusion dans l'ordre de la pile ; apres chaque fusion, `vigie.py retarget` rebase la PR suivante sur `main`. 1. #2 `pr/dette-l2` : Dette passe 1, lot L2 : contrats partagés dans aquashared et client MQTT du boîtier 2. #3 `pr/dette-l3` : Dette passe 1, lot L3 : provisioning non destructif et identifiant validé à l'entrée MQTT 3. #4 `pr/dette-l4` : Dette passe 1, lot L4 : mesures, alertes et déduplication 4. #5 `pr/dette-l5` : Dette passe 1, lot L5 : boucle MQTT du serveur, livraison de la configuration et journaux 5. #6 `pr/dette-l4bis` : Dette passe 1, lot L4-bis : alertes même si l'écriture échoue, délai unique de 600 s, typage des capteurs, ordre des accusés MQTT 6. #7 `pr/dette-l7` : Dette passe 1, lot L7 : builds reproductibles, dépendances, durcissement des conteneurs (cette PR) 7. #8 `pr/dette-l6` : Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée 8. #9 `pr/dette-l8` : Dette passe 1, lot L8 : tests non destructifs, fixtures synthétiques, script de tests sur base jetable ### Commits du lot - `d2436fd` chore: pin rust 1.99.0, update dependencies, drop reqwest, single audit config - `112bfc5` build: reproducible pinned images, locked builds, root dockerignore - `2e044ee` build: harden compose services and bind base ports to loopback - `e9a63e1` fix: build test SQL outside sqlx calls and drop panics from config payloads - `f257e0a` chore: fix gitcheck paths for the current repository layout - `1795d14` Merge branch 'fix/dette-pass-1-l7' into fix/dette-pass-1-int - `a7e2e07` build: keep unwinding on the server, abort only in the boitier profile <!-- vigie:stack -->
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
build: keep unwinding on the server, abort only in the boitier profile
All checks were successful
aquaprocess/revue-statique succes : 9 verifications, 0 bloquant
a7e2e073fd
The workspace release profile had panic = "abort" for every binary.
A panicking server task would then stop the whole process in the middle
of a write. The server keeps unwinding; the boitier image builds with a
dedicated release-embedded profile that aborts, as its crate intended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Thomas left a comment

Revue statique Vigie : PR #7

Tete a7e2e073fd, base pr/dette-l4bis (dfd85f78db), 7 commit(s), 27 fichier(s) ajoutes ou modifies.

Statut aquaprocess/revue-statique : failure (format des commits)

Constats : 0 bloquant, 2 majeur, 4 mineur, 0 style. Commentaires en ligne : 2 (mode important, plafond 10).

Verifications automatiques

Verification Resultat Detail
cargo fmt --check success aucune difference
cargo clippy -D warnings success aucun avertissement
cargo test --workspace success 350 passes, 0 echec(s), 76 ignores
tests ignores (base jetable) success 76 passes, 0 echec(s) ; brokers MQTT : mosquitto, mosquitto-nolimit ; dump charge en 42 s ; 1 fichier(s) de fixtures ; migrations du depot : 001_mesure_empreinte.sql, 002_config_delivery.sql, 003_capteur_famille.sql
cargo audit success 0 vulnerabilite, 0 avertissement(s), 6 ignoree(s) par .cargo/audit.toml
semgrep (regles du depot) success 9 fichier(s) ; lignes ajoutees : 0 resultat(s) hors tests, tests : no-unwrap-in-production x14 ; 154 sur lignes inchangees
gitleaks (plage de commits) success 6 commit(s) analyses, plus 1 fusion(s) contre leur premier parent, aucune fuite
format des commits failure 1 commit(s) sur 7 : 1795d14 : sujet hors format type: description, ligne Co-Authored-By: absente, ligne Claude-Session: absente
emoji dans le code ajoute success 682 ligne(s) ajoutee(s), aucune

Analyse qualitative

Verdict : le lot L7 tient ses promesses principales (images épinglées par empreinte, builds --locked, composes durcis, dépendances à jour, audit propre). Statut failure pour une raison de forme seulement : le commit de fusion 1795d14 porte le message par défaut de git (format type: description et lignes de signature absents). 2 majeurs : la politique de panique du serveur après a7e2e07, et cinq exceptions d'audit évitables. Le reste en mineurs.

Plage revue : dfd85f7..a7e2e07 (les 5 commits de L7, la fusion 1795d14, le commit a7e2e07), sans L4-bis ni les lots précédents.

cargo audit : vérifié, aucune vulnérabilité non justifiée

  • Avec la configuration du dépôt (.cargo/audit.toml, lue par Vigie) : 0 vulnérabilité, 0 avertissement, 6 avis ignorés.
  • Sans aucune configuration, sur le Cargo.lock de a7e2e07 (263 paquets) : 5 vulnérabilités et 1 avertissement, exactement les 6 avis listés : RUSTSEC-2023-0071 (rsa 0.9.10), RUSTSEC-2026-0049, 0098, 0099, 0104 (rustls-webpki 0.102.8), RUSTSEC-2025-0134 (rustls-pemfile 2.2.0, non maintenu). Aucun autre avis.
  • Chaque exception porte sa justification, sa condition de retrait, la date de décision (2026-10-07) et le réexamen au plus tard le 2027-01-07.
  • Les avis encore présents sur la base de #6 et non justifiés, RUSTSEC-2026-0258 (h2) et RUSTSEC-2026-0285 (rustls), disparaissent avec la mise à jour.
  • Amélioration possible (majeur .cargo/audit.toml:21) : sans la fonctionnalité par défaut use-rustls de rumqttc, cinq des six exceptions deviennent inutiles ; vérifié par cargo tree et cargo check.

Ce que cette PR corrige parmi les constats des PR #2 à #5

Constat d'origine État
#2 mineur Dockerfile.prod:73 : /data et /app/conf présentés comme persistants sans volume, identité perdue à la recréation corrigé : Dockerfile.prod supprimé, volumes boitier_state et boitier_conf dans le compose de dev
cargo audit en attention sur #3 à #6 (h2, rustls, rustls-webpki) corrigé pour h2 et rustls ; rustls-webpki justifié (voir plus haut)

Déjà relevé dans la pile, toujours présent (non repris en constat ici)

  • #5 bloquant worker.rs:183 (désormais :179) : fragment de payload d'une trame refusée recopié dans les journaux. Non touché par L7.
  • #6, contrôle semgrep bloquant provisioning/handler.rs:456 : identique à cette tête, la fusion ne reprend pas la méthode de L7 pour le code de L4-bis (voir aquaserveur/tests/e2e_t3.rs:98).
  • #5 majeur server.rs:282 : message refusé qui garde sa place inflight. C'est le mécanisme par lequel une panique de worker devient un arrêt de la réception (voir Cargo.toml:54).
  • #5 majeur mosquitto.conf:27 (max_queued_messages sans max_queued_bytes ni expiration) : renvoyé à L6, non touché.
  • #4 style main.rs:52 (émojis dans les messages) : entrypoint.sh en garde aussi (lignes 22 et 51, inchangées).

Commit de fusion

1795d14 (« Merge branch 'fix/dette-pass-1-l7' into fix/dette-pass-1-int ») ne respecte pas la convention du dépôt. Sa seule résolution manuelle est dans Cargo.lock (ajout de flume, retrait de reqwest), correcte. Deux voies, au choix de l'auteur : reconstruire une branche linéaire (L7 rebasé sur L4-bis, puis a7e2e07) et la proposer à la place de celle-ci, ou admettre les commits de fusion dans la convention. Vigie ne pousse rien en force.

Correction et sécurité

  • Builds : toutes les images FROM et celles des composes sont épinglées par version et empreinte ; Cargo.lock copié et --locked dans les deux Dockerfiles ; rust-toolchain.toml aligné sur l'image. Plus de generate-lockfile, plus d'OpenSSL ni de netcat dans l'image serveur.
  • Profils (a7e2e07) : la séparation release (serveur) et release-embedded (boîtier, panic = "abort") est propre, et le Dockerfile du boîtier construit et copie bien depuis target/release-embedded/. Mais le serveur qui déroule la pile n'a aucune surveillance de ses tâches : une panique devient un arrêt silencieux de la réception (Cargo.toml:54).
  • Composes : ports de docker-compose.base.yml sur 127.0.0.1, no-new-privileges partout, cap_drop: ALL partout sauf rust-test (justifié), capacités rendues minimales et cohérentes avec les entrypoints (gosu, chown), read_only avec tmpfs. Mise à niveau de MariaDB 10.6 vers 10.11.19 sur le volume existant à vérifier (docker-compose.base.yml:8).
  • .dockerignore : contexte réduit à 855 kB d'après le compte rendu, mais motifs ancrés à la racine seulement (.dockerignore:9).
  • config_sync.rs : expect remplacés par un repli {} journalisé, sur un chemin inatteignable en pratique ; acceptable.
  • .env.gitcheck : chemins corrigés, valeurs jamais affichées, contrôle des valeurs longues réécrit et fonctionnel (seuil de 32 caractères, heuristique).
  • entrypoint.sh : attente de la base sans délai de connexion (entrypoint.sh:34).

Cohérence avec le compte rendu

Vérifié à la tête a7e2e07 : empreintes @sha256 dans les deux Dockerfiles et les trois composes, --locked, reqwest retiré (263 paquets dans le verrou), RUN_MIGRATIONS retiré de l'entrypoint, du Dockerfile, de ENV_SETUP.md et du README, .dockerignore versionné, ports en 127.0.0.1, capacités telles que décrites, MariaDB 10.11.19. Résultats reproduits par Vigie : cargo test 350 passés, 76 ignorés ; 76 tests ignorés passés sur base et brokers jetables ; cargo audit comme ci-dessus.

Écarts :

  • « Semgrep : 0 ERROR » : vrai pour la branche L7 seule, faux à cette tête (1 ERROR en production et 9 dans les tests venus de L4-bis).
  • panic = "abort" du serveur, point à valider du compte rendu : traité par a7e2e07, avec la réserve du majeur Cargo.toml:54.
  • Builds Docker et commande ARM64 non rejoués par Vigie (analyse statique et tests Rust seulement).

Tests

Le durcissement des composes est prouvé par des essais manuels décrits dans le compte rendu (démarrages à vide et sur volume existant, CapEff, NoNewPrivs), sans test automatisé rejouable. Aucun test ne couvre une panique de worker en profil release.

Questions ouvertes

  • Serveur : panic = "abort" avec redémarrage par le compose, ou déroulement avec surveillance des tâches et arrêt explicite ? Les deux sont défendables, pas le déroulement sans surveillance.
  • Retirer use-rustls de rumqttc maintenant, quitte à réactiver une fonctionnalité TLS explicite avec L6 ?
  • Branche linéaire ou fusion admise pour cette PR ?

Outil

Vigie lit désormais .cargo/audit.toml (en plus de l'ancien audit.toml racine) et analyse avec gitleaks le contenu des fusions contre leur premier parent, git log -p ne montrant pas le diff d'une fusion.

Autres constats (non publies en ligne)

Gravite Emplacement Source Constat
mineur .dockerignore:9 revue Les motifs de .dockerignore sont ancrés à la racine du contexte : .env, .env.*, *.md et *.sh n'excluent que les fichiers de la racine. Un aquaserveur/.env ou aquaboitier-embedded/.env.local (fichiers locaux courants) serait envoyé au démon puis copié dans l'étage de build par COPY aquaserveur ./aquaserveur, donc présent dans le cache de build et les couches intermédiaires. Aucun fichier de ce type n'existe aujourd'hui. Piste : **/.env, **/.env.*, !**/.env.example (même chose pour **/*.log).
mineur aquaserveur/entrypoint.sh:34 revue (exec 3<>/dev/tcp/host/port) n'a pas de délai : si l'hôte de la base ne répond pas (paquets ignorés, pas de RST), chaque essai attend le délai de connexion TCP du noyau, de l'ordre de deux minutes, et les 30 essais peuvent retarder le démarrage de plus d'une heure. nc -z avait le même défaut. Piste : timeout 2 bash -c 'exec 3<>/dev/tcp/...' (timeout est dans coreutils, présent dans debian:bookworm-slim).
mineur aquaserveur/tests/e2e_t3.rs:98 revue La correction des 19 sqlx-no-format-in-query consiste à sortir le format! dans une variable : la règle ne voit plus le motif, mais la requête est construite de la même façon. C'est acceptable ici (noms de tables dérivés d'entiers ou de constantes), mais ce n'est pas une correction : la règle reste contournable et l'écart n'est documenté nulle part. De plus, à la tête de cette PR, le code de L4-bis fusionné garde le motif d'origine : 1 résultat ERROR en production (provisioning/handler.rs:456) et 9 dans les tests ajoutés par L4-bis (data_alert_rule.rs, sensor_typing.rs, ... ; compte de la revue de #6), ce qui contredit « Semgrep : 0 ERROR » du compte rendu. Piste : une fonction boitier_table(id: BoitierId, suffixe) qui rend un identifiant validé, utilisée partout, et une exception semgrep documentée sur ce seul point.
mineur docker-compose.base.yml:8 revue Passage de MariaDB 10.6 à 10.11.19 sur le volume mariadb_data existant : à vérifier, l'image officielle ne lance mariadb-upgrade au démarrage que si MARIADB_AUTO_UPGRADE est renseigné (variable absente ici) ; sinon les tables système restent au format 10.6 avec un avertissement. Piste : ajouter MARIADB_AUTO_UPGRADE: "1" ou documenter la commande de mise à niveau dans le README.

Limites

Revue publiee en COMMENT : l'auteur des PR et le relecteur sont le meme compte, Forgejo refuse APPROVE et REQUEST_CHANGES. Le blocage s'exprime par le statut de commit aquaprocess/revue-statique. Analyse statique et tests automatises seulement, sans fusion ni deploiement.

## Revue statique Vigie : PR #7 Tete `a7e2e073fd`, base `pr/dette-l4bis` (`dfd85f78db`), 7 commit(s), 27 fichier(s) ajoutes ou modifies. **Statut `aquaprocess/revue-statique` : failure** (format des commits) Constats : 0 bloquant, 2 majeur, 4 mineur, 0 style. Commentaires en ligne : 2 (mode `important`, plafond 10). ### Verifications automatiques | Verification | Resultat | Detail | |---|---|---| | cargo fmt --check | success | aucune difference | | cargo clippy -D warnings | success | aucun avertissement | | cargo test --workspace | success | 350 passes, 0 echec(s), 76 ignores | | tests ignores (base jetable) | success | 76 passes, 0 echec(s) ; brokers MQTT : mosquitto, mosquitto-nolimit ; dump charge en 42 s ; 1 fichier(s) de fixtures ; migrations du depot : 001_mesure_empreinte.sql, 002_config_delivery.sql, 003_capteur_famille.sql | | cargo audit | success | 0 vulnerabilite, 0 avertissement(s), 6 ignoree(s) par .cargo/audit.toml | | semgrep (regles du depot) | success | 9 fichier(s) ; lignes ajoutees : 0 resultat(s) hors tests, tests : no-unwrap-in-production x14 ; 154 sur lignes inchangees | | gitleaks (plage de commits) | success | 6 commit(s) analyses, plus 1 fusion(s) contre leur premier parent, aucune fuite | | format des commits | failure | 1 commit(s) sur 7 : `1795d14` : sujet hors format `type: description`, ligne `Co-Authored-By:` absente, ligne `Claude-Session:` absente | | emoji dans le code ajoute | success | 682 ligne(s) ajoutee(s), aucune | ### Analyse qualitative Verdict : le lot L7 tient ses promesses principales (images épinglées par empreinte, builds `--locked`, composes durcis, dépendances à jour, audit propre). Statut `failure` pour une raison de forme seulement : le commit de fusion `1795d14` porte le message par défaut de git (format `type: description` et lignes de signature absents). 2 majeurs : la politique de panique du serveur après `a7e2e07`, et cinq exceptions d'audit évitables. Le reste en mineurs. Plage revue : `dfd85f7..a7e2e07` (les 5 commits de L7, la fusion `1795d14`, le commit `a7e2e07`), sans L4-bis ni les lots précédents. ### `cargo audit` : vérifié, aucune vulnérabilité non justifiée - Avec la configuration du dépôt (`.cargo/audit.toml`, lue par Vigie) : **0 vulnérabilité, 0 avertissement, 6 avis ignorés**. - Sans aucune configuration, sur le `Cargo.lock` de `a7e2e07` (263 paquets) : 5 vulnérabilités et 1 avertissement, **exactement les 6 avis listés** : RUSTSEC-2023-0071 (`rsa` 0.9.10), RUSTSEC-2026-0049, 0098, 0099, 0104 (`rustls-webpki` 0.102.8), RUSTSEC-2025-0134 (`rustls-pemfile` 2.2.0, non maintenu). Aucun autre avis. - Chaque exception porte sa justification, sa condition de retrait, la date de décision (2026-10-07) et le réexamen au plus tard le **2027-01-07**. - Les avis encore présents sur la base de #6 et non justifiés, RUSTSEC-2026-0258 (`h2`) et RUSTSEC-2026-0285 (`rustls`), disparaissent avec la mise à jour. - Amélioration possible (majeur `.cargo/audit.toml:21`) : sans la fonctionnalité par défaut `use-rustls` de `rumqttc`, cinq des six exceptions deviennent inutiles ; vérifié par `cargo tree` et `cargo check`. ### Ce que cette PR corrige parmi les constats des PR #2 à #5 | Constat d'origine | État | |---|---| | #2 mineur `Dockerfile.prod:73` : `/data` et `/app/conf` présentés comme persistants sans volume, identité perdue à la recréation | **corrigé** : `Dockerfile.prod` supprimé, volumes `boitier_state` et `boitier_conf` dans le compose de dev | | `cargo audit` en `attention` sur #3 à #6 (`h2`, `rustls`, `rustls-webpki`) | **corrigé** pour `h2` et `rustls` ; `rustls-webpki` justifié (voir plus haut) | ### Déjà relevé dans la pile, toujours présent (non repris en constat ici) - **#5 bloquant `worker.rs:183`** (désormais `:179`) : fragment de payload d'une trame refusée recopié dans les journaux. Non touché par L7. - **#6, contrôle semgrep bloquant `provisioning/handler.rs:456`** : identique à cette tête, la fusion ne reprend pas la méthode de L7 pour le code de L4-bis (voir `aquaserveur/tests/e2e_t3.rs:98`). - #5 majeur `server.rs:282` : message refusé qui garde sa place inflight. C'est le mécanisme par lequel une panique de worker devient un arrêt de la réception (voir `Cargo.toml:54`). - #5 majeur `mosquitto.conf:27` (`max_queued_messages` sans `max_queued_bytes` ni expiration) : renvoyé à L6, non touché. - #4 style `main.rs:52` (émojis dans les messages) : `entrypoint.sh` en garde aussi (lignes 22 et 51, inchangées). ### Commit de fusion `1795d14` (« Merge branch 'fix/dette-pass-1-l7' into fix/dette-pass-1-int ») ne respecte pas la convention du dépôt. Sa seule résolution manuelle est dans `Cargo.lock` (ajout de `flume`, retrait de `reqwest`), correcte. Deux voies, au choix de l'auteur : reconstruire une branche linéaire (L7 rebasé sur L4-bis, puis `a7e2e07`) et la proposer à la place de celle-ci, ou admettre les commits de fusion dans la convention. Vigie ne pousse rien en force. ### Correction et sécurité - Builds : toutes les images `FROM` et celles des composes sont épinglées par version et empreinte ; `Cargo.lock` copié et `--locked` dans les deux Dockerfiles ; `rust-toolchain.toml` aligné sur l'image. Plus de `generate-lockfile`, plus d'OpenSSL ni de `netcat` dans l'image serveur. - Profils (`a7e2e07`) : la séparation `release` (serveur) et `release-embedded` (boîtier, `panic = "abort"`) est propre, et le Dockerfile du boîtier construit et copie bien depuis `target/release-embedded/`. Mais le serveur qui déroule la pile n'a aucune surveillance de ses tâches : une panique devient un arrêt silencieux de la réception (`Cargo.toml:54`). - Composes : ports de `docker-compose.base.yml` sur `127.0.0.1`, `no-new-privileges` partout, `cap_drop: ALL` partout sauf `rust-test` (justifié), capacités rendues minimales et cohérentes avec les entrypoints (`gosu`, `chown`), `read_only` avec tmpfs. Mise à niveau de MariaDB 10.6 vers 10.11.19 sur le volume existant à vérifier (`docker-compose.base.yml:8`). - `.dockerignore` : contexte réduit à 855 kB d'après le compte rendu, mais motifs ancrés à la racine seulement (`.dockerignore:9`). - `config_sync.rs` : `expect` remplacés par un repli `{}` journalisé, sur un chemin inatteignable en pratique ; acceptable. - `.env.gitcheck` : chemins corrigés, valeurs jamais affichées, contrôle des valeurs longues réécrit et fonctionnel (seuil de 32 caractères, heuristique). - `entrypoint.sh` : attente de la base sans délai de connexion (`entrypoint.sh:34`). ### Cohérence avec le compte rendu Vérifié à la tête `a7e2e07` : empreintes `@sha256` dans les deux Dockerfiles et les trois composes, `--locked`, `reqwest` retiré (263 paquets dans le verrou), `RUN_MIGRATIONS` retiré de l'entrypoint, du Dockerfile, de `ENV_SETUP.md` et du README, `.dockerignore` versionné, ports en `127.0.0.1`, capacités telles que décrites, MariaDB 10.11.19. Résultats reproduits par Vigie : `cargo test` 350 passés, 76 ignorés ; 76 tests ignorés passés sur base et brokers jetables ; `cargo audit` comme ci-dessus. Écarts : - « Semgrep : 0 ERROR » : vrai pour la branche L7 seule, faux à cette tête (1 `ERROR` en production et 9 dans les tests venus de L4-bis). - `panic = "abort"` du serveur, point à valider du compte rendu : traité par `a7e2e07`, avec la réserve du majeur `Cargo.toml:54`. - Builds Docker et commande ARM64 non rejoués par Vigie (analyse statique et tests Rust seulement). ### Tests Le durcissement des composes est prouvé par des essais manuels décrits dans le compte rendu (démarrages à vide et sur volume existant, `CapEff`, `NoNewPrivs`), sans test automatisé rejouable. Aucun test ne couvre une panique de worker en profil release. ### Questions ouvertes - Serveur : `panic = "abort"` avec redémarrage par le compose, ou déroulement avec surveillance des tâches et arrêt explicite ? Les deux sont défendables, pas le déroulement sans surveillance. - Retirer `use-rustls` de `rumqttc` maintenant, quitte à réactiver une fonctionnalité TLS explicite avec L6 ? - Branche linéaire ou fusion admise pour cette PR ? ### Outil Vigie lit désormais `.cargo/audit.toml` (en plus de l'ancien `audit.toml` racine) et analyse avec gitleaks le contenu des fusions contre leur premier parent, `git log -p` ne montrant pas le diff d'une fusion. ### Autres constats (non publies en ligne) | Gravite | Emplacement | Source | Constat | |---|---|---|---| | mineur | `.dockerignore:9` | revue | Les motifs de `.dockerignore` sont ancrés à la racine du contexte : `.env`, `.env.*`, `*.md` et `*.sh` n'excluent que les fichiers de la racine. Un `aquaserveur/.env` ou `aquaboitier-embedded/.env.local` (fichiers locaux courants) serait envoyé au démon puis copié dans l'étage de build par `COPY aquaserveur ./aquaserveur`, donc présent dans le cache de build et les couches intermédiaires. Aucun fichier de ce type n'existe aujourd'hui. Piste : `**/.env`, `**/.env.*`, `!**/.env.example` (même chose pour `**/*.log`). | | mineur | `aquaserveur/entrypoint.sh:34` | revue | `(exec 3<>/dev/tcp/host/port)` n'a pas de délai : si l'hôte de la base ne répond pas (paquets ignorés, pas de RST), chaque essai attend le délai de connexion TCP du noyau, de l'ordre de deux minutes, et les 30 essais peuvent retarder le démarrage de plus d'une heure. `nc -z` avait le même défaut. Piste : `timeout 2 bash -c 'exec 3<>/dev/tcp/...'` (`timeout` est dans coreutils, présent dans `debian:bookworm-slim`). | | mineur | `aquaserveur/tests/e2e_t3.rs:98` | revue | La correction des 19 `sqlx-no-format-in-query` consiste à sortir le `format!` dans une variable : la règle ne voit plus le motif, mais la requête est construite de la même façon. C'est acceptable ici (noms de tables dérivés d'entiers ou de constantes), mais ce n'est pas une correction : la règle reste contournable et l'écart n'est documenté nulle part. De plus, à la tête de cette PR, le code de L4-bis fusionné garde le motif d'origine : 1 résultat `ERROR` en production (`provisioning/handler.rs:456`) et 9 dans les tests ajoutés par L4-bis (`data_alert_rule.rs`, `sensor_typing.rs`, ... ; compte de la revue de #6), ce qui contredit « Semgrep : 0 ERROR » du compte rendu. Piste : une fonction `boitier_table(id: BoitierId, suffixe)` qui rend un identifiant validé, utilisée partout, et une exception semgrep documentée sur ce seul point. | | mineur | `docker-compose.base.yml:8` | revue | Passage de MariaDB 10.6 à 10.11.19 sur le volume `mariadb_data` existant : à vérifier, l'image officielle ne lance `mariadb-upgrade` au démarrage que si `MARIADB_AUTO_UPGRADE` est renseigné (variable absente ici) ; sinon les tables système restent au format 10.6 avec un avertissement. Piste : ajouter `MARIADB_AUTO_UPGRADE: "1"` ou documenter la commande de mise à niveau dans le README. | ### Limites Revue publiee en `COMMENT` : l'auteur des PR et le relecteur sont le meme compte, Forgejo refuse APPROVE et REQUEST_CHANGES. Le blocage s'exprime par le statut de commit `aquaprocess/revue-statique`. Analyse statique et tests automatises seulement, sans fusion ni deploiement. <!-- vigie:review -->
@ -0,0 +18,4 @@
# la version corrigee est >= 0.103.10. Le code n'ouvre aucune connexion TLS vers MQTT
# (transport TCP, aucun Transport::tls dans le depot) : la verification de certificats n'est jamais appelee.
# A retirer des que rumqttc depend de rustls-webpki >= 0.103.10, ou avant d'activer TLS sur MQTT.
"RUSTSEC-2026-0049",
Author
Member

[majeur] Cinq des six exceptions sont évitables plutôt qu'à justifier : rustls-webpki 0.102.8 et rustls-pemfile n'arrivent que par la fonctionnalité par défaut use-rustls de rumqttc (Cargo.toml de rumqttc 0.25.1 : default = ["use-rustls"]), inutile puisque le code n'ouvre aucune connexion TLS vers MQTT. Vérifié par Vigie sur un extrait de a7e2e07 avec rumqttc = { version = "0.25", default-features = false } : cargo tree -i rustls-webpki@0.102.8 et -i rustls-pemfile ne trouvent plus le paquet, il reste rustls-webpki 0.103.15 (via sqlx, non vulnérable), et cargo check -p aquaserveur -p aquaboitier-embedded passe. Retirer la fonctionnalité supprime RUSTSEC-2026-0049, 0098, 0099, 0104 et RUSTSEC-2025-0134 du verrou ; seule l'exception rsa (RUSTSEC-2023-0071) resterait. Activer TLS plus tard réimposera un choix explicite de fonctionnalité, ce qui est le bon moment pour revoir l'avis.

Source : revue.

**[majeur]** Cinq des six exceptions sont évitables plutôt qu'à justifier : `rustls-webpki` 0.102.8 et `rustls-pemfile` n'arrivent que par la fonctionnalité par défaut `use-rustls` de `rumqttc` (Cargo.toml de rumqttc 0.25.1 : `default = ["use-rustls"]`), inutile puisque le code n'ouvre aucune connexion TLS vers MQTT. Vérifié par Vigie sur un extrait de `a7e2e07` avec `rumqttc = { version = "0.25", default-features = false }` : `cargo tree -i rustls-webpki@0.102.8` et `-i rustls-pemfile` ne trouvent plus le paquet, il reste `rustls-webpki` 0.103.15 (via sqlx, non vulnérable), et `cargo check -p aquaserveur -p aquaboitier-embedded` passe. Retirer la fonctionnalité supprime RUSTSEC-2026-0049, 0098, 0099, 0104 et RUSTSEC-2025-0134 du verrou ; seule l'exception `rsa` (RUSTSEC-2023-0071) resterait. Activer TLS plus tard réimposera un choix explicite de fonctionnalité, ce qui est le bon moment pour revoir l'avis. _Source : revue._
@ -53,0 +51,4 @@
# Profil du boitier embarque : arret immediat sur panic, binaire plus petit.
# Le serveur reste sur [profile.release] avec deroulement de pile : une tache
# en panique ne doit pas arreter tout le processus pendant une ecriture.
Author
Member

[majeur] Avec le déroulement de pile, une panique dans un worker, le séquenceur d'accusés ou la tâche de livraison ne termine que cette tâche, et rien ne le voit en marche normale : workers_done n'est interrogé que pendant l'arrêt (server.rs, if drain_deadline.is_some()), et --health-check répond OK sans rien vérifier (main.rs:28). Conséquence : le récepteur du worker tombé est fermé, chaque message de ses boîtiers est refusé (Admission::Refuse), garde sa place inflight, et après 20 refus le broker cesse d'envoyer tout QoS 1 ; une panique du séquenceur arrête tous les accusés. Le serveur reste « healthy ». Avec panic = "abort", le processus redémarrait (restart: always) et le broker renvoyait les messages non accusés, ce que la conception L5 supporte (accusé après écriture, empreinte, test de coupure brutale). Garder le déroulement suppose de surveiller les JoinSet dans la boucle (join_next hors arrêt : journal error puis arrêt non nul) ; sinon revenir à abort pour le serveur.

Source : revue.

**[majeur]** Avec le déroulement de pile, une panique dans un worker, le séquenceur d'accusés ou la tâche de livraison ne termine que cette tâche, et rien ne le voit en marche normale : `workers_done` n'est interrogé que pendant l'arrêt (server.rs, `if drain_deadline.is_some()`), et `--health-check` répond OK sans rien vérifier (main.rs:28). Conséquence : le récepteur du worker tombé est fermé, chaque message de ses boîtiers est refusé (`Admission::Refuse`), garde sa place inflight, et après 20 refus le broker cesse d'envoyer tout QoS 1 ; une panique du séquenceur arrête tous les accusés. Le serveur reste « healthy ». Avec `panic = "abort"`, le processus redémarrait (`restart: always`) et le broker renvoyait les messages non accusés, ce que la conception L5 supporte (accusé après écriture, empreinte, test de coupure brutale). Garder le déroulement suppose de surveiller les `JoinSet` dans la boucle (`join_next` hors arrêt : journal `error` puis arrêt non nul) ; sinon revenir à `abort` pour le serveur. _Source : revue._
All checks were successful
aquaprocess/revue-statique succes : 9 verifications, 0 bloquant
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin pr/dette-l7:pr/dette-l7
git switch pr/dette-l7

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch pr/dette-l4bis
git merge --no-ff pr/dette-l7
git switch pr/dette-l7
git rebase pr/dette-l4bis
git switch pr/dette-l4bis
git merge --ff-only pr/dette-l7
git switch pr/dette-l7
git rebase pr/dette-l4bis
git switch pr/dette-l4bis
git merge --no-ff pr/dette-l7
git switch pr/dette-l4bis
git merge --squash pr/dette-l7
git switch pr/dette-l4bis
git merge --ff-only pr/dette-l7
git switch pr/dette-l4bis
git merge pr/dette-l7
git push origin pr/dette-l4bis
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
AcadeNice/Acquarefactoring-Thomas!7
No description provided.