Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée #8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "pr/dette-l6"
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?
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.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 écritdata-%u,firstco-%u,maj_boitier_ACK-%uet litmaj_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) etscripts/mqtt/verify-auth.sh(12 vérifications de refus et d'isolation sur la dev).MQTT_USERNAMEetMQTT_PASSWORD(aquashared::mqtt::MqttCredentials, mot de passe masqué dans Debug et dans les erreurs).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 comptesupervision.docker-compose.e2e.ymlete2e/stack.sh up|down|status, aucun port publié, scripts e2e authentifiés et corrigés.920460d,294148a,3bb7989, plus le commit de fusion7a121aaqui intègre L7. Conflit résolu dansdocker-compose.dev.yml: volumeboitier_conf_devconservé, volumeboitier_stateajouté.Choix à valider par l'humain
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 dansscripts/mqtt/README.md.+: Mosquitto refusemaj_serv-+dans un fichier ACL (« Invalid ACL topic »). Conséquence : topics à un seul niveau uniquement.--idsou--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.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 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 conteneurspr/dette-l6: Dette passe 1, lot L6 : authentification Mosquitto, ACL par compte, healthchecks réels, pile e2e isolée (cette PR)pr/dette-l8: Dette passe 1, lot L8 : tests non destructifs, fixtures synthétiques, script de tests sur base jetableCommits du lot
920460dfeat: authenticate MQTT clients and report real health294148afeat: require MQTT accounts and restrict topics per account3bb7989feat: isolated e2e stack with authenticated scripts7a121aachore: merge L7 build hardening into dette-pass-1e6ec8e9fix: let the root healthcheck read the supervision passwordRevue statique Vigie : PR #8
Tete
7a121aa04e, basepr/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
Analyse qualitative
Analyse qualitative (revue statique, option B)
Verdict : aucun constat bloquant dans le code. Le statut est
failurepour 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 dansrotate.Points vérifiés et conformes
scripts/mqtt/mqtt-accounts.sh: mots de passe aléatoires (modulesecrets), fichiers de comptes en 600, empreintes générées sans mot de passe en argument (init,add,sync),envetrotatepassent par fichier. Seulrotatepasse 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-Pen argument (constat mineur).configs/mosquitto/mosquitto.conf:allow_anonymous false,password_fileetacl_filehors dépôt,use_username_as_clientidplacé aprèslistener.configs/mosquitto/acl: droits boîtier par%u, serveur sur+(limite Mosquitto documentée), supervision limitée à$SYS/#.firstcofermé aux boîtiers : choix à valider, signalé dans la description de la PR.aquaserveur/src/health.rsetaquaboitier-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 semgrepno-unwrap-in-productionsont dans des modules#[cfg(test)](faux positifs, l'extraction ne distingue pas les tests en ligne).read_only: true(L7) avec les écritures de L6 :/tmpest en tmpfs sur le serveur et le simulateur (fichiers d'activité et de santé),/mosquitto/dataet/mosquitto/logsont des volumes,/mosquitto/authest monté en lecture seule et lu par l'utilisateur 1883,boitier_state(/data) etboitier_conf_dev(/app/conf) sont des volumes. Aucune incompatibilité trouvée. Le conflit de fusion dedocker-compose.dev.ymlest correctement résolu (les deux volumes présents).docker-compose.e2e.ymlete2e/stack.sh: projetacqua-e2e, aucun port publié, secrets générés hors dépôt en 600 (e2e.env,db.env),downlimité 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) :
firstcofermé, serveur sur+, retain non filtrable, comptes sur liste explicite, MQTT en clair (pas de TLS), variables MQTT absentes de l'anciendeploy.yml.Autres remarques non publiées en ligne :
has_accountconstruit une expression régulière avec le nom de compte reçu parrotatesans 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) ettest-mqtt-e2e.sh(lignes 23 et 24). Danstest-mqtt-e2e.sh, ce sont des lignes préexistantes modifiées pour le compteur (PASS=$((PASS + 1))), les marqueurs étaient déjà là ; dansENV_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)
ENV_SETUP.md:115294148a, decalee dans la vue de la PR)ENV_SETUP.md:116294148a, decalee dans la vue de la PR)ENV_SETUP.md:117294148a, decalee dans la vue de la PR)ENV_SETUP.md:118294148a, decalee dans la vue de la PR)ENV_SETUP.md:120294148a, decalee dans la vue de la PR)scripts/mqtt/mqtt-accounts.sh:256rotatepasse le nouveau mot de passe en argument depython3("$(random_password)"). Pendant l'exécution, il est lisible danspset/proc/<pid>/cmdlinede l'hôte pour tout utilisateur local, y compris pour le compteaquaserveuren 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 blocsecrets.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 commit294148a, decalee dans la vue de la PR)aquaboitier-embedded/src/health.rs:61tokio-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.aquaboitier-embedded/src/health.rs:61tokio-spawn-fire-and-forget: leJoinHandlerenvoyé parspawn_heartbeatest ignoré dansmain.rs. Sans conséquence ici (tâche à durée de vie du processus), à acquitter explicitement.aquaboitier-embedded/src/health.rs:94no-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.aquaboitier-embedded/src/settings.rs:281no-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.aquaserveur/src/health.rs:116last_writereste vide et chaque paquet reçu du broker journalise un avertissement : inondation de journaux possible si/tmpn'est pas inscriptible (conteneur en lecture seule sans tmpfs). Mémoriser l'instant de la tentative, pas seulement celui du succès.aquaserveur/src/health.rs:246no-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.aquaserveur/src/mqtt/settings.rs:334no-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.aquashared/src/mqtt.rs:139no-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.docker-compose.e2e.yml:25read_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.docker-compose.e2e.yml:52supervisionen argument demosquitto_sub(-P "$$(cat ...)") : visible dansdocker topetpsde l'hôte toutes les 10 s. Même motif dans les composes base, dev et prod, ainsi quescripts/mqtt/verify-auth.sh:89,e2e/common.sh:137ete2e/boitier/common-boitier.sh:170. Le client Mosquitto 2.0 n'a pas d'option fichier ou environnement pour-P; comptesupervisionlimité à la lecture de$SYS, risque réduit, à documenter.scripts/mqtt/mqtt-accounts.sh:196broker/avant lechown 1883(ligne 199). Si Docker échoue entre les deux (set -e),passwdetaclrestent en 600 au nom de l'utilisateur courant et Mosquitto ne pourra plus les lire au redémarrage. Faire lechownsur le dossier temporaire avant lemv.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.@ -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)); }[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)); }[majeur] Caractere emoji U+274C dans une ligne ajoutee (regle IA-23).
Source : emoji.
Complément de revue statique sur
e6ec8e9(fix: let the root healthcheck read the supervision password). Le commit ne modifie quescripts/mqtt/mqtt-accounts.sh,scripts/mqtt/README.mdetENV_SETUP.md. Lecture :chown 1883limité àpasswdetacl,supervision.pwrendu à root, cohérent aveccap_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 dansrotate) 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é sure6ec8e9.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.