Dette passe 1, lot L7 : builds reproductibles, dépendances, durcissement des conteneurs #7
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "pr/dette-l7"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Objet
Lot L7 de la passe 1 de dette : builds reproductibles, dépendances à jour et auditées, durcissement des conteneurs.
Contenu de la branche
fix/dette-pass-1-l7, partie de74df6f6(tête de L5). Ses cinq commits (d2436fd,112bfc5,2e044ee,e9a63e1,f257e0a) sont fusionnés au-dessus de L4-bis par le commit de fusion1795d14.a7e2e07du 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 :[profile.release]sanspanic) : une tâche en panique est absorbée partokio::spawnau lieu d'arrêter tout le processus pendant une écriture ;release-embedded(inherits = "release",panic = "abort"), comme son crate le prévoyait ;aquaboitier-embedded/Dockerfileconstruit et copie depuistarget/release-embedded/.Dettes traitées et preuves
Cargo.lockcopié et--lockeddans les deux Dockerfiles,cargo generate-lockfilesupprimé ; images épinglées par version et empreinte@sha256:(builder serveurrust:1.99.0-slim-bookworm, runtimedebian:bookworm-20261005-slim, builder boîtierrust:1.99.0-alpine3.24, runtimealpine:3.24.2, composesmariadb:10.11.19,eclipse-mosquitto:2.0.22,webdb/app:2025.06.10) ;rust-toolchain.tomlen 1.99.0aquaserveur/Dockerfile,aquaboitier-embedded/Dockerfile, composes,rust-toolchain.tomltesterdu Dockerfile boîtier supprimée ;Dockerfile.prodsupprimé (aucune référence hors README, mis à jour)aquaboitier-embedded/,README.mdRUN_MIGRATIONSet modemigrateretirés ;libpq-dev,libssl-dev,pkg-config,netcat-openbsdretirés (attente de la base par/dev/tcpde bash)aquaserveur/Dockerfile,entrypoint.sh,ENV_SETUP.md.dockerignoreracine.dockerignore,.gitignoredocker-compose.base.ymlsur127.0.0.1;no-new-privilegesetcap_drop: [ALL]sur tous les services saufrust-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: trueavec tmpfs pour mariadb, mosquitto, backend, boîtier ; volumesboitier_stateetboitier_conf; MariaDB de base 10.6 vers 10.11.19docker-compose.base.yml,docker-compose.dev.yml,docker-compose.prod.ymlaudit.tomlracine supprimé,.cargo/audit.tomlseul fichier, lu parcargo audit.cargo/audit.tomlcargo updatecompatible,reqwestretiré : 336 paquets avant, 263 après ; résolus : h2, rustls, rustls-webpki 0.103, anyhow, event-listener, rand, spinCargo.lock,Cargo.tomlsqlx-no-format-in-query(ERROR) des tests corrigés : SQL construit avant l'appel ; 2unwrapde production deconfig_sync.rsremplacés par un repli journaliséaquaserveur/tests/*,aquaserveur/src/config_sync.rs.env.gitcheck.env.prod, n'affiche pas les valeurs).env.gitcheckExceptions
cargo auditjustifiéesSix 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 derumqttcet desqlx, et au plus tard le 2027-01-07.rsa0.9.10 (viasqlx-mysql)rustls-webpki0.102.8 (imposé parrumqttc0.25.1, dernière version)Transport::tls), donc pas de vérification de certificats par ce cheminrumqttcdépend derustls-webpki>= 0.103.10, ou avant d'activer TLS sur MQTTrustls-pemfile2.2.0 (non maintenu, avertissement)rumqttcRésultat attendu :
cargo auditsans vulnérabilité hors de cette liste.Portes (compte rendu du lot)
cargo fmt --all -- --check,cargo clippy ... -D warningscargo test --workspace.semgrep/rulesaquaserveur/Dockerfileaquaboitier-embedded/Dockerfilea7e2e07)Écarts et points à valider
docker-compose.base.yml: 10.6 vers 10.11.19.rust-test(dev) :no-new-privilegesseulement, sanscap_drop(root sur les sources montées).BOITIER_DEV_MODE=1attend alors une identité injectée.docker buildx build --platform linux/arm64 -f aquaboitier-embedded/Dockerfile) non exécutée.webdb/app: image tierce, seul tag daté disponible.aquaboitier-embedded/.dockerignorenon suivi du dépôt principal : à supprimer à la main.Hors périmètre
N21 hors code (règle
no-unwrap-in-productionsans exclusion des tests, configuration Semgrep et CI), N25 à N28, authentification et ACL Mosquitto et healthchecks réels (L6).Retour arrière
Revenir
a7e2e07puis la fusion1795d14(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 retargetrebase la PR suivante surmain.pr/dette-l2: Dette passe 1, lot L2 : contrats partagés dans aquashared et client MQTT du boîtierpr/dette-l3: Dette passe 1, lot L3 : provisioning non destructif et identifiant validé à l'entrée MQTTpr/dette-l4: Dette passe 1, lot L4 : mesures, alertes et déduplicationpr/dette-l5: Dette passe 1, lot L5 : boucle MQTT du serveur, livraison de la configuration et journauxpr/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 MQTTpr/dette-l7: Dette passe 1, lot L7 : builds reproductibles, dépendances, durcissement des conteneurs (cette PR)pr/dette-l6: Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isoléepr/dette-l8: Dette passe 1, lot L8 : tests non destructifs, fixtures synthétiques, script de tests sur base jetableCommits du lot
d2436fdchore: pin rust 1.99.0, update dependencies, drop reqwest, single audit config112bfc5build: reproducible pinned images, locked builds, root dockerignore2e044eebuild: harden compose services and bind base ports to loopbacke9a63e1fix: build test SQL outside sqlx calls and drop panics from config payloadsf257e0achore: fix gitcheck paths for the current repository layout1795d14Merge branch 'fix/dette-pass-1-l7' into fix/dette-pass-1-inta7e2e07build: keep unwinding on the server, abort only in the boitier profileRevue statique Vigie : PR #7
Tete
a7e2e073fd, basepr/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
1795d14: sujet hors formattype: description, ligneCo-Authored-By:absente, ligneClaude-Session:absenteAnalyse qualitative
Verdict : le lot L7 tient ses promesses principales (images épinglées par empreinte, builds
--locked, composes durcis, dépendances à jour, audit propre). Statutfailurepour une raison de forme seulement : le commit de fusion1795d14porte le message par défaut de git (formattype: descriptionet lignes de signature absents). 2 majeurs : la politique de panique du serveur aprèsa7e2e07, et cinq exceptions d'audit évitables. Le reste en mineurs.Plage revue :
dfd85f7..a7e2e07(les 5 commits de L7, la fusion1795d14, le commita7e2e07), sans L4-bis ni les lots précédents.cargo audit: vérifié, aucune vulnérabilité non justifiée.cargo/audit.toml, lue par Vigie) : 0 vulnérabilité, 0 avertissement, 6 avis ignorés.Cargo.lockdea7e2e07(263 paquets) : 5 vulnérabilités et 1 avertissement, exactement les 6 avis listés : RUSTSEC-2023-0071 (rsa0.9.10), RUSTSEC-2026-0049, 0098, 0099, 0104 (rustls-webpki0.102.8), RUSTSEC-2025-0134 (rustls-pemfile2.2.0, non maintenu). Aucun autre avis.h2) et RUSTSEC-2026-0285 (rustls), disparaissent avec la mise à jour..cargo/audit.toml:21) : sans la fonctionnalité par défautuse-rustlsderumqttc, cinq des six exceptions deviennent inutiles ; vérifié parcargo treeetcargo check.Ce que cette PR corrige parmi les constats des PR #2 à #5
Dockerfile.prod:73:/dataet/app/confprésentés comme persistants sans volume, identité perdue à la recréationDockerfile.prodsupprimé, volumesboitier_stateetboitier_confdans le compose de devcargo auditenattentionsur #3 à #6 (h2,rustls,rustls-webpki)h2etrustls;rustls-webpkijustifié (voir plus haut)Déjà relevé dans la pile, toujours présent (non repris en constat ici)
worker.rs:183(désormais:179) : fragment de payload d'une trame refusée recopié dans les journaux. Non touché par L7.provisioning/handler.rs:456: identique à cette tête, la fusion ne reprend pas la méthode de L7 pour le code de L4-bis (voiraquaserveur/tests/e2e_t3.rs:98).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 (voirCargo.toml:54).mosquitto.conf:27(max_queued_messagessansmax_queued_bytesni expiration) : renvoyé à L6, non touché.main.rs:52(émojis dans les messages) :entrypoint.shen 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 dansCargo.lock(ajout deflume, retrait dereqwest), correcte. Deux voies, au choix de l'auteur : reconstruire une branche linéaire (L7 rebasé sur L4-bis, puisa7e2e07) 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é
FROMet celles des composes sont épinglées par version et empreinte ;Cargo.lockcopié et--lockeddans les deux Dockerfiles ;rust-toolchain.tomlaligné sur l'image. Plus degenerate-lockfile, plus d'OpenSSL ni denetcatdans l'image serveur.a7e2e07) : la séparationrelease(serveur) etrelease-embedded(boîtier,panic = "abort") est propre, et le Dockerfile du boîtier construit et copie bien depuistarget/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).docker-compose.base.ymlsur127.0.0.1,no-new-privilegespartout,cap_drop: ALLpartout saufrust-test(justifié), capacités rendues minimales et cohérentes avec les entrypoints (gosu,chown),read_onlyavec 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:expectremplacé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@sha256dans les deux Dockerfiles et les trois composes,--locked,reqwestretiré (263 paquets dans le verrou),RUN_MIGRATIONSretiré de l'entrypoint, du Dockerfile, deENV_SETUP.mdet du README,.dockerignoreversionné, ports en127.0.0.1, capacités telles que décrites, MariaDB 10.11.19. Résultats reproduits par Vigie :cargo test350 passés, 76 ignorés ; 76 tests ignorés passés sur base et brokers jetables ;cargo auditcomme ci-dessus.Écarts :
ERRORen production et 9 dans les tests venus de L4-bis).panic = "abort"du serveur, point à valider du compte rendu : traité para7e2e07, avec la réserve du majeurCargo.toml:54.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
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.use-rustlsderumqttcmaintenant, quitte à réactiver une fonctionnalité TLS explicite avec L6 ?Outil
Vigie lit désormais
.cargo/audit.toml(en plus de l'ancienaudit.tomlracine) et analyse avec gitleaks le contenu des fusions contre leur premier parent,git log -pne montrant pas le diff d'une fusion.Autres constats (non publies en ligne)
.dockerignore:9.dockerignoresont ancrés à la racine du contexte :.env,.env.*,*.mdet*.shn'excluent que les fichiers de la racine. Unaquaserveur/.envouaquaboitier-embedded/.env.local(fichiers locaux courants) serait envoyé au démon puis copié dans l'étage de build parCOPY 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).aquaserveur/entrypoint.sh:34(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 -zavait le même défaut. Piste :timeout 2 bash -c 'exec 3<>/dev/tcp/...'(timeoutest dans coreutils, présent dansdebian:bookworm-slim).aquaserveur/tests/e2e_t3.rs:98sqlx-no-format-in-queryconsiste à sortir leformat!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ésultatERRORen 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 fonctionboitier_table(id: BoitierId, suffixe)qui rend un identifiant validé, utilisée partout, et une exception semgrep documentée sur ce seul point.docker-compose.base.yml:8mariadb_dataexistant : à vérifier, l'image officielle ne lancemariadb-upgradeau démarrage que siMARIADB_AUTO_UPGRADEest renseigné (variable absente ici) ; sinon les tables système restent au format 10.6 avec un avertissement. Piste : ajouterMARIADB_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 commitaquaprocess/revue-statique. Analyse statique et tests automatises seulement, sans fusion ni deploiement.@ -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",[majeur] Cinq des six exceptions sont évitables plutôt qu'à justifier :
rustls-webpki0.102.8 etrustls-pemfilen'arrivent que par la fonctionnalité par défautuse-rustlsderumqttc(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 dea7e2e07avecrumqttc = { version = "0.25", default-features = false }:cargo tree -i rustls-webpki@0.102.8et-i rustls-pemfilene trouvent plus le paquet, il resterustls-webpki0.103.15 (via sqlx, non vulnérable), etcargo check -p aquaserveur -p aquaboitier-embeddedpasse. Retirer la fonctionnalité supprime RUSTSEC-2026-0049, 0098, 0099, 0104 et RUSTSEC-2025-0134 du verrou ; seule l'exceptionrsa(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.[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_donen'est interrogé que pendant l'arrêt (server.rs,if drain_deadline.is_some()), et--health-checkré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 ». Avecpanic = "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 lesJoinSetdans la boucle (join_nexthors arrêt : journalerrorpuis arrêt non nul) ; sinon revenir àabortpour le serveur.Source : revue.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.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.