fix(deployer) : le dépôt rend sa racine au service qui écrit chez lui #112
No reviewers
Labels
No labels
montee-majeure
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
foXaCe/backend!112
Loading…
Reference in a new issue
No description provided.
Delete branch "passe-20261018-091602-racineresident"
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?
La PR #111 a donné au déploiement un garde qui constate qu'un service ne peut pas graver
chez lui. Celle-ci enlève la cause, et la cause était une mécanique, pas un accident.
Le défaut, nommé
Le dépôt laissait la racine de la destination au propriétaire inscrit dans l'archive.
tar -xzftourne en root dans le conteneur, et l'archive contient./: tar restaure doncl'uid du poste qui a fabriqué le tar —
1000,depotmajdans le conteneur.ordre_pose_droitschownait tout sauf
./, sur la foi d'un commentaire («./— la destination elle-même — estécarté : elle existe avant le dépôt et n'en fait pas partie ») faux sur le mécanisme : l'archive
la dépose, elle.
⇒ Chaque déploiement re-cassait la racine de
tvexchange. Le service tourne ennuvio-tvx(uid 997), grave ses sessions de la cabine dans ce dossier, et le dossier ne lui appartenait plus.
Trois
CABINE-SESSIONS-NON-GRAVEES PermissionErrordans son journal, etcabine_sessions.jsonn'existait pas du tout au lieu d'exister vide : toute session tombe à la première relance.
La mesure, sur la machine réelle
Chaîne entière rejouée dans le LXC 125 : archive fabriquée sur le poste (uid 1000), extraite
en root dans le conteneur, puis le fragment que
ordre_pose_droitsproduit vraiment — jouédans ses deux natures. Le témoin vit dans
/tmp:/opt/nuvio-tvexchangen'a pas été ouvert.C'est la même archive, le même tar, le même root, le même
runuser: la seule différence est lanature, et elle change l'écriture du service de
REFUSEEàPOSSIBLE.Le correctif
resident— la seule qui a un processus qui écrit chez lui — la racine reçoitle propriétaire du service, le DOSSIER SEUL, jamais
-R;oubli d'argument (
naturevide) n'élargit pas le périmètre ;RACINE-AU-SERVICE:<propriétaire>, et le dépôt le LIT :une pose manquée est un ÉCHEC, jamais l'absence d'erreur ;
-Rest le gestequi a mis la cabine hors service onze heures le 2026-08-18, et c'est celui qu'un opérateur pressé
tapera.
Le périmètre ne s'élargit pas. Une cible
statique(la cabine, servie en lecture par nginx)et une cible
lot(timers sous root) ne voient pas leur racine touchée. Ce qui a tué la cabine n'estpas « avoir touché un dossier », c'est
-R: il avait retiré à.htpasswdle groupe dont nginxavait besoin.
La pose ne remplace pas la mesure. Poser la racine prouve qu'une commande a rendu 0, pas que
le service écrit : un ACL, un bit immuable, une monture en lecture seule, un dossier parent non
traversable, et le
chownréussit sur un dossier où le noyau refusera l'écriture.preuve_resident(#111) continue de demander au noyau, en tant que le service — les deux se surveillent.
Les preuves
Quatre tests, éprouvés par réinjection :
-R.htpasswdetconfig.jsvivent dans ce dossier : ils doivent rester intactsstatiquen'est pas touchéestatique: aucun chemin racine, aucun motLe mot de passage est lu côté dépôt (
test_le_depot_exige_la_racine_au_service_avant_de_rendre_vert),et la réparation y est nommée avec son piège.
Porte
./run_tests.sh -q: garde d'octetsVERT, parité conforme, 2131 passés, 14 sautés, 0 échec —référence
main2127, mesurée sur le même banc avant d'écrire cette ligne (+4).Ce que cette PR ne porte pas
JOURNAL.md/CLAUDE.md: la référence de porte se remesure sur lebanc en vigueur (
RG78), ce n'est pas à cette PR de choisir le banc. À la demande.saine, et cette branche ne touche aucun fichier du service (
tvlogin/inchangé). Aprèsfusion, le prochain
--cible tvexchangeposera la racine et l'écrira. À ta demande, je le joueavant : le contenu déposé serait identique octet pour octet, seul le déployeur change.
Le dépôt laissait la racine de la destination au propriétaire INSCRIT DANS L'ARCHIVE. `tar -xzf` tourne EN ROOT dans le conteneur, et l'archive contient `./` : tar restaure donc l'uid du poste qui a fabriqué le tar — `1000`, `depotmaj` dans le conteneur. `ordre_pose_droits` chownait tout SAUF `./`, sur la foi d'un commentaire (« elle existe avant le dépôt et n'en fait pas partie ») faux sur le mécanisme : l'archive la dépose, elle. MESURÉ le 2026-10-18 sur `/opt/nuvio-tvexchange` : racine à `10001`, un uid SANS NOM, puis reposée à `1000` par un déploiement. Le service tourne en `nuvio-tvx` et grave ses sessions de la cabine dans ce dossier : trois `CABINE-SESSIONS-NON-GRAVEES PermissionError` dans son journal, et le fichier n'existait pas du tout au lieu d'exister vide. Chaque déploiement re-cassait la racine, et rien ne le disait. Ce que le dépôt fait maintenant : · pour une cible `resident` — la seule qui a un processus qui ÉCRIT chez lui — la racine reçoit le propriétaire du service, le DOSSIER SEUL, jamais `-R` ; · la nature se lit dans la table des cibles (4e colonne) : la fonction ne la suppose pas ; · un second mot de passage positif, `RACINE-AU-SERVICE:<propriétaire>`, et le dépôt le LIT : une pose manquée est un ÉCHEC, jamais l'absence d'erreur ; · le refus nomme la réparation ET son piège — `-R` est le geste qui a mis la cabine hors service onze heures le 2026-08-18. Le périmètre ne s'élargit pas : une cible `statique` (la cabine, servie en LECTURE par nginx) et une cible `lot` (timers sous root) ne voient pas leur racine touchée. La pose ne remplace pas la mesure : elle prouve qu'une COMMANDE a rendu 0, pas que le service écrit. `preuve_resident` demande toujours au noyau, EN TANT QUE le service — un ACL, un bit immuable ou une monture en lecture seule feraient réussir le chown sur un dossier où l'écriture est refusée. Tests : 4 de plus, éprouvés par réinjection — le fragment réel AMPUTÉ de l'étape de la racine doit faire rougir le test de la racine, et le cas `statique` mesure l'absence. Porte : 2131 passés, 14 sautés, 0 échec (référence `main` : 2127, mesurée avant d'écrire cette ligne).