Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée #8

Open
Thomas wants to merge 5 commits from pr/dette-l6 into pr/dette-l7
Member

Contenu

Lot L6 de la passe de dette : authentification Mosquitto et ACL par compte, healthchecks réels, pile e2e isolée. Rapport complet : _byan-output/dette-pass-1/L6.md.

  • Broker : allow_anonymous false, fichiers de mots de passe et d'ACL dans un dossier hors dépôt monté en lecture seule. Chaque boîtier écrit data-%u, firstco-%u, maj_boitier_ACK-%u et lit maj_serv-%u, firstco_ACK-%u. Le serveur lit et écrit +, la supervision lit $SYS/#.
  • scripts/mqtt/mqtt-accounts.sh (init, add, rotate, list, env, sync) et scripts/mqtt/verify-auth.sh (12 vérifications de refus et d'isolation sur la dev).
  • Clients Rust : MQTT_USERNAME et MQTT_PASSWORD (aquashared::mqtt::MqttCredentials, mot de passe masqué dans Debug et dans les erreurs).
  • Healthchecks réels : aquaserveur --health-check (base et activité MQTT de moins de 90 s), simulateur sain seulement tant que la session MQTT est établie, healthcheck Mosquitto avec le compte supervision.
  • Pile e2e isolée : docker-compose.e2e.yml et e2e/stack.sh up|down|status, aucun port publié, scripts e2e authentifiés et corrigés.
  • Commits : 920460d, 294148a, 3bb7989, plus le commit de fusion 7a121aa qui intègre L7. Conflit résolu dans docker-compose.dev.yml : volume boitier_conf_dev conservé, volume boitier_state ajouté.

Choix à valider par l'humain

  1. Topic historique firstco (sans id) fermé aux comptes boîtier : le contrôle L3 ne s'applique pas sans id dans le topic. Le firmware ESP32 n'est pas vérifiable ici ; si les boîtiers l'utilisent encore, une ouverture par compte est décrite dans scripts/mqtt/README.md.
  2. Droits du serveur exprimés sur + : Mosquitto refuse maj_serv-+ dans un fichier ACL (« Invalid ACL topic »). Conséquence : topics à un seul niveau uniquement.
  3. Retain non filtrable par ACL (ACL 2.0) : un boîtier peut retenir ses seuls topics, effet borné.
  4. Comptes MQTT créés sur liste explicite (--ids ou --ids-file) : les environ 450 boîtiers de la base sont legacy et n'utilisent pas MQTT, aucune colonne ne sépare les V4 de façon fiable.
  5. MQTT en TCP clair : mots de passe en clair sur le réseau si le broker est joint par Internet. TLS n'est pas traité dans cette passe.
  6. Variables MQTT de prod non récupérées par l'ancien deploy.yml : CI/CD traitée dans une autre session.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18


Pile de PR

Base de cette PR : pr/dette-l7. 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
  7. #8 pr/dette-l6 : Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée (cette PR)
  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

  • 920460d feat: authenticate MQTT clients and report real health
  • 294148a feat: require MQTT accounts and restrict topics per account
  • 3bb7989 feat: isolated e2e stack with authenticated scripts
  • 7a121aa chore: merge L7 build hardening into dette-pass-1
  • e6ec8e9 fix: let the root healthcheck read the supervision password
## Contenu Lot L6 de la passe de dette : authentification Mosquitto et ACL par compte, healthchecks réels, pile e2e isolée. Rapport complet : `_byan-output/dette-pass-1/L6.md`. - Broker : `allow_anonymous false`, fichiers de mots de passe et d'ACL dans un dossier hors dépôt monté en lecture seule. Chaque boîtier écrit `data-%u`, `firstco-%u`, `maj_boitier_ACK-%u` et lit `maj_serv-%u`, `firstco_ACK-%u`. Le serveur lit et écrit `+`, la supervision lit `$SYS/#`. - `scripts/mqtt/mqtt-accounts.sh` (init, add, rotate, list, env, sync) et `scripts/mqtt/verify-auth.sh` (12 vérifications de refus et d'isolation sur la dev). - Clients Rust : `MQTT_USERNAME` et `MQTT_PASSWORD` (`aquashared::mqtt::MqttCredentials`, mot de passe masqué dans Debug et dans les erreurs). - Healthchecks réels : `aquaserveur --health-check` (base et activité MQTT de moins de 90 s), simulateur sain seulement tant que la session MQTT est établie, healthcheck Mosquitto avec le compte `supervision`. - Pile e2e isolée : `docker-compose.e2e.yml` et `e2e/stack.sh up|down|status`, aucun port publié, scripts e2e authentifiés et corrigés. - Commits : `920460d`, `294148a`, `3bb7989`, plus le commit de fusion `7a121aa` qui intègre L7. Conflit résolu dans `docker-compose.dev.yml` : volume `boitier_conf_dev` conservé, volume `boitier_state` ajouté. ## Choix à valider par l'humain 1. Topic historique `firstco` (sans id) fermé aux comptes boîtier : le contrôle L3 ne s'applique pas sans id dans le topic. Le firmware ESP32 n'est pas vérifiable ici ; si les boîtiers l'utilisent encore, une ouverture par compte est décrite dans `scripts/mqtt/README.md`. 2. Droits du serveur exprimés sur `+` : Mosquitto refuse `maj_serv-+` dans un fichier ACL (« Invalid ACL topic »). Conséquence : topics à un seul niveau uniquement. 3. Retain non filtrable par ACL (ACL 2.0) : un boîtier peut retenir ses seuls topics, effet borné. 4. Comptes MQTT créés sur liste explicite (`--ids` ou `--ids-file`) : les environ 450 boîtiers de la base sont legacy et n'utilisent pas MQTT, aucune colonne ne sépare les V4 de façon fiable. 5. MQTT en TCP clair : mots de passe en clair sur le réseau si le broker est joint par Internet. TLS n'est pas traité dans cette passe. 6. Variables MQTT de prod non récupérées par l'ancien `deploy.yml` : CI/CD traitée dans une autre session. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18 --- ### Pile de PR Base de cette PR : `pr/dette-l7`. 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 7. #8 `pr/dette-l6` : Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée (cette PR) 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 - `920460d` feat: authenticate MQTT clients and report real health - `294148a` feat: require MQTT accounts and restrict topics per account - `3bb7989` feat: isolated e2e stack with authenticated scripts - `7a121aa` chore: merge L7 build hardening into dette-pass-1 - `e6ec8e9` fix: let the root healthcheck read the supervision password <!-- vigie:stack -->
The server and the boitier simulator pass set_credentials from MQTT_USERNAME
and MQTT_PASSWORD (a boitier defaults its username to its id, a username
without password is refused instead of going anonymous, the password never
appears in Debug or errors).

aquaserveur --health-check now checks the database (own connection, SELECT 1)
and the age of the last packet received from the broker, kept in an activity
file. The simulator only refreshes /tmp/health while its MQTT session is
established and removes it on disconnection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
Mosquitto refuses anonymous connections and reads a hashed password file and
an ACL generated outside the repository by scripts/mqtt/mqtt-accounts.sh (one
account per boitier named after its id, aquaserveur, supervision). The ACL
lets a boitier write data-%u, firstco-%u and maj_boitier_ACK-%u and read
maj_serv-%u and firstco_ACK-%u. The legacy firstco topic stays closed to
boitier accounts. use_username_as_clientid stops a client from evicting the
server by announcing its client id.

The composes pass the server and simulator credentials, mount the auth
directory, and authenticate the broker healthcheck with the supervision
account. The simulator keeps its identity in a volume and inherits the
Dockerfile healthcheck. verify-auth.sh runs the negative tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
docker-compose.e2e.yml runs its own MariaDB, Mosquitto, server and simulator
under a dedicated project (e2e/stack.sh up/down), so the e2e scripts no longer
write into the shared dev database. The scripts authenticate to Mosquitto with
the account of the boitier they simulate and a pool of e2e-tool accounts for
observation and server-side actions.

Frames use the single sw420 flag, test-mqtt-e2e.sh loses its duplicated second
half and its stale assertions (ack payload field, sensor ordering, thresholds),
the pH of boitier 9993 follows the ext section, and mqtt_sub_bg no longer
hides the subscriber from wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
chore: merge L7 build hardening into dette-pass-1
Some checks failed
aquaprocess/revue-statique echec : emoji dans le code ajoute
7a121aa04e
Integrates fix/dette-pass-1-int (pinned toolchain, locked builds, hardened
composes, release-embedded profile) on top of the L6 MQTT authentication.

Conflict in docker-compose.dev.yml (boitier volumes): keep the existing
boitier_conf_dev volume, which holds the dev simulator identity, and add
the boitier_state volume required by the read-only filesystem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH3JcaVWsmFLC6kyEsVL18
fix: let the root healthcheck read the supervision password
Some checks failed
aquaprocess/revue-statique echec : emoji dans le code ajoute
e6ec8e9116
The hardened composes start Mosquitto with cap_drop ALL. Without
CAP_DAC_OVERRIDE, the healthcheck (root in the container) cannot read
supervision.pw owned by 1883 in mode 600: the broker stays unhealthy and
the server and simulator never start. The broker itself never reads this
file, so mqtt-accounts.sh now gives it to root; passwd and acl stay 1883.

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 #8

Tete 7a121aa04e, base pr/dette-l7 (a7e2e073fd), 4 commit(s), 48 fichier(s) ajoutes ou modifies.

Statut aquaprocess/revue-statique : failure (emoji dans le code ajoute)

Constats : 0 bloquant, 8 majeur, 11 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 375 passes, 0 echec(s), 76 ignores
tests ignores (base jetable) success 76 passes, 0 echec(s) ; brokers MQTT : mosquitto, mosquitto-nolimit ; dump charge en 28 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 11 fichier(s) ; lignes ajoutees : 28 resultat(s) hors tests ; 21 sur lignes inchangees
gitleaks (plage de commits) success 3 commit(s) analyses, plus 1 fusion(s) contre leur premier parent, aucune fuite
format des commits success 4 commit(s) conformes dont 1 fusion(s) exemptee(s)
emoji dans le code ajoute failure 7 ligne(s) ajoutee(s) avec emoji

Analyse qualitative

Analyse qualitative (revue statique, option B)

Verdict : aucun constat bloquant dans le code. Le statut est failure pour deux causes de porte, détaillées dans le tableau : l'emoji dans des lignes ajoutées (règle IA-23) et, selon le journal, les tests ignorés. Un constat majeur reste ouvert : mot de passe en argument de commande dans rotate.

Points vérifiés et conformes

  • scripts/mqtt/mqtt-accounts.sh : mots de passe aléatoires (module secrets), fichiers de comptes en 600, empreintes générées sans mot de passe en argument (init, add, sync), env et rotate passent par fichier. Seul rotate passe un secret en argument (constat majeur). Aucun mot de passe n'est affiché.
  • scripts/mqtt/verify-auth.sh : sur la ligne de commande de l'hôte, les mots de passe passent par -e MQTT_P (variable d'environnement du conteneur) ; le mot de passe de test 2 est un faux littéral. Réserve : à l'intérieur du conteneur, mosquitto_* reçoit -P en argument (constat mineur).
  • configs/mosquitto/mosquitto.conf : allow_anonymous false, password_file et acl_file hors dépôt, use_username_as_clientid placé après listener. configs/mosquitto/acl : droits boîtier par %u, serveur sur + (limite Mosquitto documentée), supervision limitée à $SYS/#. firstco fermé aux boîtiers : choix à valider, signalé dans la description de la PR.
  • aquaserveur/src/health.rs et aquaboitier-embedded/src/health.rs : fichier d'activité supprimé au démarrage, âge borné à 90 s côté serveur, simulateur sain seulement session établie, messages d'erreur sans URL ni mot de passe. Les 5 résultats semgrep no-unwrap-in-production sont dans des modules #[cfg(test)] (faux positifs, l'extraction ne distingue pas les tests en ligne).
  • Compatibilité read_only: true (L7) avec les écritures de L6 : /tmp est en tmpfs sur le serveur et le simulateur (fichiers d'activité et de santé), /mosquitto/data et /mosquitto/log sont des volumes, /mosquitto/auth est monté en lecture seule et lu par l'utilisateur 1883, boitier_state (/data) et boitier_conf_dev (/app/conf) sont des volumes. Aucune incompatibilité trouvée. Le conflit de fusion de docker-compose.dev.yml est correctement résolu (les deux volumes présents).
  • docker-compose.e2e.yml et e2e/stack.sh : projet acqua-e2e, aucun port publié, secrets générés hors dépôt en 600 (e2e.env, db.env), down limité au projet. Le durcissement de L7 n'y est pas repris (constat mineur).

Réserves de fond (décisions humaines, déjà listées dans la description) : firstco fermé, serveur sur +, retain non filtrable, comptes sur liste explicite, MQTT en clair (pas de TLS), variables MQTT absentes de l'ancien deploy.yml.

Autres remarques non publiées en ligne : has_account construit une expression régulière avec le nom de compte reçu par rotate sans le valider (les points correspondent à n'importe quel caractère) ; les empreintes PBKDF2 utilisent 101 itérations, valeur par défaut de Mosquitto, compensée ici par des mots de passe de 32 caractères aléatoires.

Emoji (IA-23) : ENV_SETUP.md (lignes 115 à 118 et 120) et test-mqtt-e2e.sh (lignes 23 et 24). Dans test-mqtt-e2e.sh, ce sont des lignes préexistantes modifiées pour le compteur (PASS=$((PASS + 1))), les marqueurs étaient déjà là ; dans ENV_SETUP.md, les coches suivent la convention du tableau existant (17 occurrences sur la base) et le cadenas de la ligne 120 est nouveau. À trancher par l'humain : retirer, ou accepter pour ces deux fichiers.

Autres constats (non publies en ligne)

Gravite Emplacement Source Constat
majeur ENV_SETUP.md:115 emoji Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 116 du commit 294148a, decalee dans la vue de la PR)
majeur ENV_SETUP.md:116 emoji Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 117 du commit 294148a, decalee dans la vue de la PR)
majeur ENV_SETUP.md:117 emoji Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 118 du commit 294148a, decalee dans la vue de la PR)
majeur ENV_SETUP.md:118 emoji Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 119 du commit 294148a, decalee dans la vue de la PR)
majeur ENV_SETUP.md:120 emoji Caractere emoji U+1F510 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 121 du commit 294148a, decalee dans la vue de la PR)
majeur scripts/mqtt/mqtt-accounts.sh:256 revue rotate passe le nouveau mot de passe en argument de python3 ("$(random_password)"). Pendant l'exécution, il est lisible dans ps et /proc/<pid>/cmdline de l'hôte pour tout utilisateur local, y compris pour le compte aquaserveur en prod. L'en-tête du script (ligne 53 et 54) affirme le contraire. Remède : générer le mot de passe dans le script Python lui-même (même bloc secrets.choice), ou le lui transmettre par l'environnement ou l'entrée standard. (non publie en ligne : Forgejo l'ancrerait a la ligne 264 du commit 294148a, decalee dans la vue de la PR)
mineur aquaboitier-embedded/src/health.rs:61 semgrep semgrep tokio-spawn-fire-and-forget (WARNING) : tokio::spawn() sans stocker le JoinHandle (fire-and-forget). 1 occurrence(s) sur lignes ajoutees (61), y compris d'eventuels modules de test internes.
mineur aquaboitier-embedded/src/health.rs:61 revue Semgrep tokio-spawn-fire-and-forget : le JoinHandle renvoyé par spawn_heartbeat est ignoré dans main.rs. Sans conséquence ici (tâche à durée de vie du processus), à acquitter explicitement.
mineur aquaboitier-embedded/src/health.rs:94 semgrep semgrep no-unwrap-in-production (WARNING) : .unwrap() ou .expect() peut paniquer en production. 6 occurrence(s) sur lignes ajoutees (94, 96, 98, 99, 101, 124), y compris d'eventuels modules de test internes.
mineur aquaboitier-embedded/src/settings.rs:281 semgrep semgrep no-unwrap-in-production (WARNING) : .unwrap() ou .expect() peut paniquer en production. 5 occurrence(s) sur lignes ajoutees (281, 281, 291, 310, 311), y compris d'eventuels modules de test internes.
mineur aquaserveur/src/health.rs:116 revue En cas d'échec d'écriture, last_write reste vide et chaque paquet reçu du broker journalise un avertissement : inondation de journaux possible si /tmp n'est pas inscriptible (conteneur en lecture seule sans tmpfs). Mémoriser l'instant de la tentative, pas seulement celui du succès.
mineur aquaserveur/src/health.rs:246 semgrep semgrep no-unwrap-in-production (WARNING) : .unwrap() ou .expect() peut paniquer en production. 4 occurrence(s) sur lignes ajoutees (246, 248, 250, 259), y compris d'eventuels modules de test internes.
mineur aquaserveur/src/mqtt/settings.rs:334 semgrep semgrep no-unwrap-in-production (WARNING) : .unwrap() ou .expect() peut paniquer en production. 4 occurrence(s) sur lignes ajoutees (334, 336, 336, 346), y compris d'eventuels modules de test internes.
mineur aquashared/src/mqtt.rs:139 semgrep semgrep no-unwrap-in-production (WARNING) : .unwrap() ou .expect() peut paniquer en production. 8 occurrence(s) sur lignes ajoutees (139, 139, 151, 151, 155, 155, 197, 197), y compris d'eventuels modules de test internes.
mineur docker-compose.e2e.yml:25 revue Aucun service de la pile e2e n'applique le durcissement de L7 (read_only, tmpfs: /tmp, cap_drop: ALL, no-new-privileges). Sans incidence fonctionnelle (le serveur écrit /tmp/aquaserveur-mqtt-activity, le simulateur /tmp/health), mais la pile e2e ne vérifie donc pas le comportement en lecture seule de l'image déployée.
mineur docker-compose.e2e.yml:52 revue Le healthcheck met le mot de passe du compte supervision en argument de mosquitto_sub (-P "$$(cat ...)") : visible dans docker top et ps de l'hôte toutes les 10 s. Même motif dans les composes base, dev et prod, ainsi que scripts/mqtt/verify-auth.sh:89, e2e/common.sh:137 et e2e/boitier/common-boitier.sh:170. Le client Mosquitto 2.0 n'a pas d'option fichier ou environnement pour -P ; compte supervision limité à la lecture de $SYS, risque réduit, à documenter.
mineur scripts/mqtt/mqtt-accounts.sh:196 revue Les fichiers sont déplacés dans broker/ avant le chown 1883 (ligne 199). Si Docker échoue entre les deux (set -e), passwd et acl restent en 600 au nom de l'utilisateur courant et Mosquitto ne pourra plus les lire au redémarrage. Faire le chown sur le dossier temporaire avant le mv.

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 #8 Tete `7a121aa04e`, base `pr/dette-l7` (`a7e2e073fd`), 4 commit(s), 48 fichier(s) ajoutes ou modifies. **Statut `aquaprocess/revue-statique` : failure** (emoji dans le code ajoute) Constats : 0 bloquant, 8 majeur, 11 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 | 375 passes, 0 echec(s), 76 ignores | | tests ignores (base jetable) | success | 76 passes, 0 echec(s) ; brokers MQTT : mosquitto, mosquitto-nolimit ; dump charge en 28 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 | 11 fichier(s) ; lignes ajoutees : 28 resultat(s) hors tests ; 21 sur lignes inchangees | | gitleaks (plage de commits) | success | 3 commit(s) analyses, plus 1 fusion(s) contre leur premier parent, aucune fuite | | format des commits | success | 4 commit(s) conformes dont 1 fusion(s) exemptee(s) | | emoji dans le code ajoute | failure | 7 ligne(s) ajoutee(s) avec emoji | ### Analyse qualitative ### Analyse qualitative (revue statique, option B) **Verdict** : aucun constat bloquant dans le code. Le statut est `failure` pour deux causes de porte, détaillées dans le tableau : l'emoji dans des lignes ajoutées (règle IA-23) et, selon le journal, les tests ignorés. Un constat majeur reste ouvert : mot de passe en argument de commande dans `rotate`. **Points vérifiés et conformes** - `scripts/mqtt/mqtt-accounts.sh` : mots de passe aléatoires (module `secrets`), fichiers de comptes en 600, empreintes générées sans mot de passe en argument (`init`, `add`, `sync`), `env` et `rotate` passent par fichier. Seul `rotate` passe un secret en argument (constat majeur). Aucun mot de passe n'est affiché. - `scripts/mqtt/verify-auth.sh` : sur la ligne de commande de l'hôte, les mots de passe passent par `-e MQTT_P` (variable d'environnement du conteneur) ; le mot de passe de test 2 est un faux littéral. Réserve : à l'intérieur du conteneur, `mosquitto_*` reçoit `-P` en argument (constat mineur). - `configs/mosquitto/mosquitto.conf` : `allow_anonymous false`, `password_file` et `acl_file` hors dépôt, `use_username_as_clientid` placé après `listener`. `configs/mosquitto/acl` : droits boîtier par `%u`, serveur sur `+` (limite Mosquitto documentée), supervision limitée à `$SYS/#`. `firstco` fermé aux boîtiers : choix à valider, signalé dans la description de la PR. - `aquaserveur/src/health.rs` et `aquaboitier-embedded/src/health.rs` : fichier d'activité supprimé au démarrage, âge borné à 90 s côté serveur, simulateur sain seulement session établie, messages d'erreur sans URL ni mot de passe. Les 5 résultats semgrep `no-unwrap-in-production` sont dans des modules `#[cfg(test)]` (faux positifs, l'extraction ne distingue pas les tests en ligne). - Compatibilité `read_only: true` (L7) avec les écritures de L6 : `/tmp` est en tmpfs sur le serveur et le simulateur (fichiers d'activité et de santé), `/mosquitto/data` et `/mosquitto/log` sont des volumes, `/mosquitto/auth` est monté en lecture seule et lu par l'utilisateur 1883, `boitier_state` (`/data`) et `boitier_conf_dev` (`/app/conf`) sont des volumes. Aucune incompatibilité trouvée. Le conflit de fusion de `docker-compose.dev.yml` est correctement résolu (les deux volumes présents). - `docker-compose.e2e.yml` et `e2e/stack.sh` : projet `acqua-e2e`, aucun port publié, secrets générés hors dépôt en 600 (`e2e.env`, `db.env`), `down` limité au projet. Le durcissement de L7 n'y est pas repris (constat mineur). **Réserves de fond (décisions humaines, déjà listées dans la description)** : `firstco` fermé, serveur sur `+`, retain non filtrable, comptes sur liste explicite, MQTT en clair (pas de TLS), variables MQTT absentes de l'ancien `deploy.yml`. **Autres remarques non publiées en ligne** : `has_account` construit une expression régulière avec le nom de compte reçu par `rotate` sans le valider (les points correspondent à n'importe quel caractère) ; les empreintes PBKDF2 utilisent 101 itérations, valeur par défaut de Mosquitto, compensée ici par des mots de passe de 32 caractères aléatoires. **Emoji (IA-23)** : `ENV_SETUP.md` (lignes 115 à 118 et 120) et `test-mqtt-e2e.sh` (lignes 23 et 24). Dans `test-mqtt-e2e.sh`, ce sont des lignes préexistantes modifiées pour le compteur (`PASS=$((PASS + 1))`), les marqueurs étaient déjà là ; dans `ENV_SETUP.md`, les coches suivent la convention du tableau existant (17 occurrences sur la base) et le cadenas de la ligne 120 est nouveau. À trancher par l'humain : retirer, ou accepter pour ces deux fichiers. ### Autres constats (non publies en ligne) | Gravite | Emplacement | Source | Constat | |---|---|---|---| | majeur | `ENV_SETUP.md:115` | emoji | Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 116 du commit 294148a, decalee dans la vue de la PR) | | majeur | `ENV_SETUP.md:116` | emoji | Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 117 du commit 294148a, decalee dans la vue de la PR) | | majeur | `ENV_SETUP.md:117` | emoji | Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 118 du commit 294148a, decalee dans la vue de la PR) | | majeur | `ENV_SETUP.md:118` | emoji | Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 119 du commit 294148a, decalee dans la vue de la PR) | | majeur | `ENV_SETUP.md:120` | emoji | Caractere emoji U+1F510 dans une ligne ajoutee (regle IA-23). (non publie en ligne : Forgejo l'ancrerait a la ligne 121 du commit 294148a, decalee dans la vue de la PR) | | majeur | `scripts/mqtt/mqtt-accounts.sh:256` | revue | `rotate` passe le nouveau mot de passe en argument de `python3` (`"$(random_password)"`). Pendant l'exécution, il est lisible dans `ps` et `/proc/<pid>/cmdline` de l'hôte pour tout utilisateur local, y compris pour le compte `aquaserveur` en prod. L'en-tête du script (ligne 53 et 54) affirme le contraire. Remède : générer le mot de passe dans le script Python lui-même (même bloc `secrets.choice`), ou le lui transmettre par l'environnement ou l'entrée standard. (non publie en ligne : Forgejo l'ancrerait a la ligne 264 du commit 294148a, decalee dans la vue de la PR) | | mineur | `aquaboitier-embedded/src/health.rs:61` | semgrep | semgrep `tokio-spawn-fire-and-forget` (WARNING) : `tokio::spawn()` sans stocker le JoinHandle (fire-and-forget). 1 occurrence(s) sur lignes ajoutees (61), y compris d'eventuels modules de test internes. | | mineur | `aquaboitier-embedded/src/health.rs:61` | revue | Semgrep `tokio-spawn-fire-and-forget` : le `JoinHandle` renvoyé par `spawn_heartbeat` est ignoré dans `main.rs`. Sans conséquence ici (tâche à durée de vie du processus), à acquitter explicitement. | | mineur | `aquaboitier-embedded/src/health.rs:94` | semgrep | semgrep `no-unwrap-in-production` (WARNING) : `.unwrap()` ou `.expect()` peut paniquer en production. 6 occurrence(s) sur lignes ajoutees (94, 96, 98, 99, 101, 124), y compris d'eventuels modules de test internes. | | mineur | `aquaboitier-embedded/src/settings.rs:281` | semgrep | semgrep `no-unwrap-in-production` (WARNING) : `.unwrap()` ou `.expect()` peut paniquer en production. 5 occurrence(s) sur lignes ajoutees (281, 281, 291, 310, 311), y compris d'eventuels modules de test internes. | | mineur | `aquaserveur/src/health.rs:116` | revue | En cas d'échec d'écriture, `last_write` reste vide et chaque paquet reçu du broker journalise un avertissement : inondation de journaux possible si `/tmp` n'est pas inscriptible (conteneur en lecture seule sans tmpfs). Mémoriser l'instant de la tentative, pas seulement celui du succès. | | mineur | `aquaserveur/src/health.rs:246` | semgrep | semgrep `no-unwrap-in-production` (WARNING) : `.unwrap()` ou `.expect()` peut paniquer en production. 4 occurrence(s) sur lignes ajoutees (246, 248, 250, 259), y compris d'eventuels modules de test internes. | | mineur | `aquaserveur/src/mqtt/settings.rs:334` | semgrep | semgrep `no-unwrap-in-production` (WARNING) : `.unwrap()` ou `.expect()` peut paniquer en production. 4 occurrence(s) sur lignes ajoutees (334, 336, 336, 346), y compris d'eventuels modules de test internes. | | mineur | `aquashared/src/mqtt.rs:139` | semgrep | semgrep `no-unwrap-in-production` (WARNING) : `.unwrap()` ou `.expect()` peut paniquer en production. 8 occurrence(s) sur lignes ajoutees (139, 139, 151, 151, 155, 155, 197, 197), y compris d'eventuels modules de test internes. | | mineur | `docker-compose.e2e.yml:25` | revue | Aucun service de la pile e2e n'applique le durcissement de L7 (`read_only`, `tmpfs: /tmp`, `cap_drop: ALL`, `no-new-privileges`). Sans incidence fonctionnelle (le serveur écrit `/tmp/aquaserveur-mqtt-activity`, le simulateur `/tmp/health`), mais la pile e2e ne vérifie donc pas le comportement en lecture seule de l'image déployée. | | mineur | `docker-compose.e2e.yml:52` | revue | Le healthcheck met le mot de passe du compte `supervision` en argument de `mosquitto_sub` (`-P "$$(cat ...)"`) : visible dans `docker top` et `ps` de l'hôte toutes les 10 s. Même motif dans les composes base, dev et prod, ainsi que `scripts/mqtt/verify-auth.sh:89`, `e2e/common.sh:137` et `e2e/boitier/common-boitier.sh:170`. Le client Mosquitto 2.0 n'a pas d'option fichier ou environnement pour `-P` ; compte `supervision` limité à la lecture de `$SYS`, risque réduit, à documenter. | | mineur | `scripts/mqtt/mqtt-accounts.sh:196` | revue | Les fichiers sont déplacés dans `broker/` avant le `chown 1883` (ligne 199). Si Docker échoue entre les deux (`set -e`), `passwd` et `acl` restent en 600 au nom de l'utilisateur courant et Mosquitto ne pourra plus les lire au redémarrage. Faire le `chown` sur le dossier temporaire avant le `mv`. | ### 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 -->
@ -22,3 +23,1 @@
pass() { echo -e "${GREEN} ✅ PASS${NC} — $1"; ((PASS++)); }
fail() { echo -e "${RED} ❌ FAIL${NC} — $1"; ((FAIL++)); }
pass() { echo -e "${GREEN} ✅ PASS${NC} — $1"; PASS=$((PASS + 1)); }
Author
Member

[majeur] Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23).

Source : emoji.

**[majeur]** Caractere emoji U+2705 dans une ligne ajoutee (regle IA-23). _Source : emoji._
@ -23,2 +23,2 @@
pass() { echo -e "${GREEN} ✅ PASS${NC} — $1"; ((PASS++)); }
fail() { echo -e "${RED} ❌ FAIL${NC} — $1"; ((FAIL++)); }
pass() { echo -e "${GREEN} ✅ PASS${NC} — $1"; PASS=$((PASS + 1)); }
fail() { echo -e "${RED} ❌ FAIL${NC} — $1"; FAIL=$((FAIL + 1)); }
Author
Member

[majeur] Caractere emoji U+274C dans une ligne ajoutee (regle IA-23).

Source : emoji.

**[majeur]** Caractere emoji U+274C dans une ligne ajoutee (regle IA-23). _Source : emoji._
Author
Member

Complément de revue statique sur e6ec8e9 (fix: let the root healthcheck read the supervision password). Le commit ne modifie que scripts/mqtt/mqtt-accounts.sh, scripts/mqtt/README.md et ENV_SETUP.md. Lecture : chown 1883 limité à passwd et acl, supervision.pw rendu à root, cohérent avec cap_drop: ALL (root sans CAP_DAC_OVERRIDE ne lit un fichier 600 que s'il en est propriétaire) ; le broker ne lit pas ce fichier. Aucun nouveau constat. Le constat majeur de la revue #12 (mot de passe en argument dans rotate) reste ouvert. Verdict inchangé : fmt, clippy, tests (375), tests ignorés (76), audit, semgrep, gitleaks, commits OK ; échec sur l'emoji (règle IA-23) dans des lignes ajoutées. Statut republié sur e6ec8e9.

Complément de revue statique sur `e6ec8e9` (fix: let the root healthcheck read the supervision password). Le commit ne modifie que `scripts/mqtt/mqtt-accounts.sh`, `scripts/mqtt/README.md` et `ENV_SETUP.md`. Lecture : `chown 1883` limité à `passwd` et `acl`, `supervision.pw` rendu à root, cohérent avec `cap_drop: ALL` (root sans CAP_DAC_OVERRIDE ne lit un fichier 600 que s'il en est propriétaire) ; le broker ne lit pas ce fichier. Aucun nouveau constat. Le constat majeur de la revue #12 (mot de passe en argument dans `rotate`) reste ouvert. Verdict inchangé : fmt, clippy, tests (375), tests ignorés (76), audit, semgrep, gitleaks, commits OK ; échec sur l'emoji (règle IA-23) dans des lignes ajoutées. Statut republié sur `e6ec8e9`.
Some checks failed
aquaprocess/revue-statique echec : emoji dans le code ajoute
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-l6:pr/dette-l6
git switch pr/dette-l6

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-l7
git merge --no-ff pr/dette-l6
git switch pr/dette-l6
git rebase pr/dette-l7
git switch pr/dette-l7
git merge --ff-only pr/dette-l6
git switch pr/dette-l6
git rebase pr/dette-l7
git switch pr/dette-l7
git merge --no-ff pr/dette-l6
git switch pr/dette-l7
git merge --squash pr/dette-l6
git switch pr/dette-l7
git merge --ff-only pr/dette-l6
git switch pr/dette-l7
git merge pr/dette-l6
git push origin pr/dette-l7
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!8
No description provided.