diff --git a/AGENTS.md b/AGENTS.md index 675f120..cdcb9a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -644,6 +644,15 @@ comptes, clés SSH, mots de passe et réseau. Sur une machine que le plan possè produisait un va-et-vient à chaque déploiement. **Le gabarit, lui, le garde** — sans lui un clone n'a ni adresse ni nom. **P63** garde les trois moitiés. +**Ce que ce retrait ne ferme pas.** Il n'ôte **aucun pouvoir à l'hébergeur** : +`qemu-guest-agent` est au gabarit (il doit y être — P56), et l'API Proxmox expose sur son +dos `exec`, `file-write`, `set-user-password`, `shutdown` — strictement plus que le lecteur +cloud-init. Ce qui est fermé est étroit et réel : une réapplication *automatique, à chaque +démarrage*, depuis un support que le plan ne possède pas, et un interpréteur Python +complet exécuté en root au boot. La mainmise de l'hyperviseur sur ses invités est une +propriété de la virtualisation, pas de cloud-init — elle appelle sa propre décision, non +prise. + --- ## Nettoyage avant template diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dab2a2..4f85eca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,45 @@ existe pour qui veut, en connaissance de cause. `/etc/cloud/cloud-init.disabled` est lu par cloud-init lui-meme au demarrage et l'arrete avant qu'il ne lise la moindre source de donnees. +### Ce que cette decision NE ferme PAS + +Ajoute le meme jour, apres la question « cloud-init est-il vraiment une valeur ajoutee +ici ? ». La premiere redaction de D-85 se lisait comme une emancipation. Elle n'en est +pas une, et il valait mieux le dire que de laisser un lecteur presse en conclure trop. + +**Retirer cloud-init n'ote AUCUN pouvoir a l'hebergeur.** `qemu-guest-agent` est au +gabarit — il doit y etre (P56 : c'est par lui que `creer-vm` confirme la materialisation +sans entrer chez le tenant). Releve du 2026-09-09 sur `edge-mta-01`, avec le jeton d'API +du site, les sous-chemins que l'API expose sur son dos : + + exec · exec-status · file-read · file-write · set-user-password · shutdown · ... + +C'est un pouvoir STRICTEMENT PLUS GRAND que le lecteur cloud-init, et il est deja la, sur +chaque VM vivante de la flotte. + +Ce que D-85 ferme est donc precis et etroit : + + 1. une reapplication AUTOMATIQUE, a chaque demarrage, depuis un support que le plan ne + possede pas et qu'aucune preuve ne lit ; + 2. le code de cloud-init lui-meme — un interpreteur Python complet execute en root au + demarrage, et ses ~29 dependances. + +La mainmise d'un hyperviseur sur ses invites est une propriete de la VIRTUALISATION, pas +de cloud-init. Elle appelle sa propre decision, qui n'est pas prise. + +### Le seuil ou le remplacer deviendrait juste + +L'alternative existe et sa piece est deja au gabarit : ecrire l'adresse et la cle par +`agent/file-write` + `agent/exec` depuis l'hyperviseur, sans reseau. Aujourd'hui ce serait +REIMPLEMENTER UN STANDARD — ce que `positionnement.md` interdit — et echanger un mecanisme +eprouve contre du code maison au moment le plus fragile, dont le mode de panne est le +pire : une VM injoignable. + +Le jour ou une premiere seconde ne pourra plus etre amorcee par Proxmox — autre +hyperviseur, metal nu, hebergeur sans API — ce chemin cesse d'etre une reimplementation et +devient LE CHEMIN PORTABLE. C'est l'axe emancipation que le depot suit deja. Le seuil est +nomme ici pour ne pas etre franchi sans le voir. + ### Non deploye Le code est ecrit, valide et prouve ; **il n'a pas ete applique a la flotte**. Retirer un diff --git a/docs/decisions-architecture.md b/docs/decisions-architecture.md index cace5de..b7cbec9 100644 --- a/docs/decisions-architecture.md +++ b/docs/decisions-architecture.md @@ -58,7 +58,7 @@ sont les seules vérifiables. | **D-82** | **Patient 0 n'est le parent de personne.** Il est la **mise en œuvre de référence** du modèle `origine` — le plus petit écosystème complet — et un pair de la famille du génome, pas sa racine | trois faits l'ont retiré un par un : D-81 a donné l'autorité du génome à la forge du SITE (son dernier lecteur corrigé le 2026-08-26, Technolibre le 08-31) ; le dénominateur commun vit dans les modèles depuis le 08-24 ; et **l'ancêtre était locataire de son enfant** — index 29 sur la fabric de `SITE-Chezlepro`, qui descend de lui. Ce qu'il devait éliminer — le SPOF `eregion`, hors flotte — n'a PAS été éliminé mais **promu** : le poste y pousse, la forge du site en tire. Cette dette appartient désormais au SITE, et la nommer est le minimum : *un objectif qu'on abandonne sans le dire devient un objectif qu'on croit atteint* | `OPS-Patient0/README.md`, `docs/filiation-emancipation.md` | — | | **D-83** | **Patient 0 a été retiré** — ses machines n'existent plus (constaté le 2026-09-06) | D-82 lui avait laissé une raison d'être : la mise en œuvre de référence du modèle `origine`, et un **témoin** de plus du génome. Le retrait solde la première et **abaisse la seconde de trois copies vivantes à deux** (`eregion`, la forge du site). Ce qu'il devait éliminer — le SPOF `eregion` — reste entier, et sans lui il n'y a plus de miroir indépendant pour l'absorber. **Son plan reste sur disque et la fédération lui réserve toujours l'index 29** : tant que ce n'est pas tranché, le site ouvre SSH, apt, DNS et HTTPS à `10.29.0.0/16` — un périmètre vide | `OPS-Patient0/`, `SITE-Chezlepro/flux-genere/` | **P21** (index), **P23** | | **D-84** | **Le plan de contrôle reste gelé — c'est la CARTE DES SEUILS qui était fausse** | La question « et si on retirait le gel ? » a mis à l'épreuve les cinq seuils de `positionnement.md`, et deux ne tenaient pas. **RBAC** : couvert depuis que trois classes d'acteurs aux pouvoirs disjoints existent — poste, runner de site, runners de tenant — séparés **cryptographiquement** (une voûte, une clé, 2026-08-28) et non par une table de permissions qu'une faille applicative contournerait ; adopter AWX pour ce besoin serait **régresser**. **IPAM** : sans objet par construction — rien ne s'alloue, tout dérive du seed, et P20/P21/P23/P28/P33 tiennent déjà ce qu'un IPAM vérifierait *a posteriori*. Les deux lignes sont retirées du tableau : les garder aurait fait adopter un outil pour un besoin déjà rempli. **Et un seuil manquait** — l'**émancipation** : le GUI est mono-utilisateur (`127.0.0.1` + jeton), or la trajectoire mène à plusieurs humains aux portées disjointes, sur des machines qui ne sont pas les nôtres. Ce seuil n'appelle pas AWX, il appelle une décision non prise. *Un seuil qu'on ne nomme pas est un seuil qu'on franchit sans le voir.* Corollaire consigné : le gel porte sur les **fonctions**, jamais sur les **vues** — montrer à l'écran ce que le moteur sait déjà ne franchit aucun seuil | `positionnement.md` §3, §4, §5 | — | -| **D-85** | **cloud-init naît avec la VM et ne lui survit pas** | cloud-init n'est pas un logiciel d'installation : c'est une **source de vérité externe**. À chaque démarrage il relit le lecteur attaché par l'hyperviseur, qui peut redéfinir comptes, clés SSH autorisées, mots de passe et réseau. Sur une machine que le plan possède, c'est un **second maître** — que le plan ne décrit pas, que `make valider` ne mesure pas, et qui gagne parce qu'il parle en premier. Sa tâche est pourtant finie à la première seconde : c'est parce qu'il a **réussi** à poser l'adresse et les clés qu'Ansible a pu entrer. **La décision a trois moitiés, et elles se défont séparément** : le **gabarit** le garde (sans lui un clone ne naît pas — P56) ; le **socle** ne l'installe plus (le garder produisait un va-et-vient à chaque déploiement : le socle installe, le durcissement retire, deux `changed` par passage) ; le **durcissement** le retire (`cloud_init_retrait`). **Ce qui rend le retrait sûr est mesuré, pas supposé** (2026-09-09, `obs-01`) : `/etc/network/interfaces.d/50-cloud-init` n'appartient à aucun paquet — `dpkg -S` ne le trouve pas — et le `postrm` ne le nomme jamais, même en `purge`. L'adresse survit. Le rôle le **vérifie** malgré tout, avant et après : une VM qui perd ce fichier ne se plaint pas, elle repart sans adresse et plus personne ne peut entrer pour le constater | `roles/cloud_init_retrait/`, `serveur_durci.yml` | **P63** | +| **D-85** | **cloud-init naît avec la VM et ne lui survit pas** | cloud-init n'est pas un logiciel d'installation : c'est une **source de vérité externe**, qui se réveille à *chaque* démarrage et relit le lecteur attaché par l'hyperviseur — lequel peut redéfinir comptes, clés SSH autorisées, mots de passe et réseau. Sur une machine que le plan possède, c'est un **second maître** : le plan ne le décrit pas, `make valider` ne le mesure pas, et il parle en premier. Sa tâche est pourtant finie à la première seconde — c'est parce qu'il a **réussi** à poser l'adresse et les clés qu'Ansible a pu entrer. **Trois moitiés, qui se défont séparément** : le **gabarit** le garde (sans lui un clone ne naît pas — P56) ; le **socle** ne l'installe plus (le garder produisait un va-et-vient à chaque déploiement : le socle installe, le durcissement retire, deux `changed` par passage) ; le **durcissement** le retire (`cloud_init_retrait`, en dernier). **Ce qui rend le retrait sûr est mesuré, pas supposé** (2026-09-09, `obs-01`) : `/etc/network/interfaces.d/50-cloud-init` n'appartient à aucun paquet — `dpkg -S` ne le trouve pas — et le `postrm` ne le nomme jamais, même en `purge`. L'adresse survit. Le rôle le **vérifie** malgré tout, avant et après : une VM qui perd ce fichier ne se plaint pas, elle repart sans adresse et plus personne ne peut entrer pour le constater. **⚠ CE QUE CETTE DÉCISION NE FERME PAS — et il faut le dire, sinon elle se lit comme une émancipation qu'elle n'est pas.** Retirer cloud-init **n'ôte aucun pouvoir à l'hébergeur**. `qemu-guest-agent` est au gabarit (P56 : il doit y être — c'est par lui que `creer-vm` confirme la matérialisation sans entrer chez le tenant), et l'API Proxmox expose sur son dos, sur toute VM vivante de la flotte, un pouvoir **strictement plus grand** que le lecteur cloud-init : `exec`, `file-write`, `file-read`, `set-user-password`, `shutdown` (relevé le 2026-09-09 sur `edge-mta-01`, jeton d'API du site). Ce que D-85 ferme est donc **précis et étroit** : (a) une réapplication **automatique, à chaque démarrage**, depuis un support que le plan ne possède pas et qu'aucune preuve ne lit ; (b) le code de cloud-init lui-même — un interpréteur Python complet, exécuté en root au démarrage, et ses ~29 dépendances. Elle ne ferme **pas** la mainmise de l'hyperviseur sur ses invités : celle-là est une propriété de la virtualisation, pas de cloud-init, et elle appelle sa propre décision — non prise. **Le seuil où le remplacer deviendrait juste** : le jour où une première seconde ne peut plus être amorcée par Proxmox (autre hyperviseur, métal nu, hébergeur sans API), le chemin par l'agent invite cesse d'être une réimplémentation d'un standard — que `positionnement.md` interdit — et devient **le chemin portable**. Tant que ce seuil n'est pas atteint, écrire soi-même l'amorçage serait échanger un standard éprouvé contre du code maison au moment le plus fragile, dont le mode de panne est le pire : une VM injoignable | `roles/cloud_init_retrait/`, `serveur_durci.yml`, `positionnement.md` | **P63** | | **D-81** | **La forge du SITE fait autorité pour le génome.** Toute autre copie — y compris celle d'où le moteur a été poussé jusqu'ici — est un **miroir**. Le poste de l'exploitant ne route pas jusqu'à elle : c'est le **runner du site** qui publie, par `make genome-pousser` | un écosystème se reproduit depuis la forge de son site : c'est de là qu'il clone son moteur, ses plans, ses modèles. Si l'autorité est ailleurs, cette forge devient un cache qu'on croit à jour — et le 2026-08-26 elle était **quatre commits en arrière** sans que rien ne le signale, dont le correctif qui désarme le pare-feu Proxmox. **Un écosystème qui se reproduit depuis une forge en retard reproduit ses défauts.** Le poste n'a de patte que sur l'administration, et on ne perce pas de chemin pour lui : le runner existe pour ce travail | `playbooks/maintenance/genome_pousser.yml`, `scripts/genome_colis.py`, `Makefile` §genome-pousser | — | | **D-13** | Un **hébergeur** sert plusieurs **tenants** et a son tenant par défaut | Chezlepro est les deux à la fois, ce qui masquait la distinction | `frontiere-opnsense.md` §2 | — | | **D-14** | `underlay.yml` appartient à l'**hébergeur**, monté par symlink | ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas | `sdn-evpn.md`, `underlay.yml.example` | — | diff --git a/roles/cloud_init_retrait/README.md b/roles/cloud_init_retrait/README.md index bbf584c..9e93c6c 100644 --- a/roles/cloud_init_retrait/README.md +++ b/roles/cloud_init_retrait/README.md @@ -16,6 +16,24 @@ d'aller plus loin si le fichier a disparu. **Ce qui n'est pas retiré.** `cloud-guest-utils` (`growpart`) : aucun service, aucune source de données, aucun pouvoir. Le retirer ne fermerait rien. +## Ce que ce rôle ne ferme PAS + +Il **n'ôte aucun pouvoir à l'hébergeur**, et le croire serait la mauvaise leçon. +`qemu-guest-agent` est au gabarit — il doit y être (P56) — et l'API Proxmox expose sur son +dos, sur toute VM vivante, un pouvoir **strictement plus grand** que le lecteur +cloud-init : `exec`, `file-write`, `file-read`, `set-user-password`, `shutdown` (relevé le +2026-09-09 sur `edge-mta-01`). + +Ce que ce rôle ferme est précis et étroit : + +1. une réapplication **automatique, à chaque démarrage**, depuis un support que le plan ne + possède pas et qu'aucune preuve ne lit ; +2. le code de cloud-init lui-même — un interpréteur Python complet exécuté en root au + démarrage, et ses ~29 dépendances. + +La mainmise de l'hyperviseur sur ses invités est une propriété de la virtualisation, pas de +cloud-init. Elle appelle sa propre décision, qui n'est pas prise. Cf. **D-85**. + **Le gabarit garde cloud-init** — il est le seul chemin vers la première seconde d'un clone (P56). Le retrait n'intervient qu'après, sur la machine déployée. C'est pourquoi `serveur_debian` ne l'applique plus : sinon le socle l'installerait et le durcissement le diff --git a/roles/cloud_init_retrait/tasks/main.yml b/roles/cloud_init_retrait/tasks/main.yml index cd58b81..7679806 100644 --- a/roles/cloud_init_retrait/tasks/main.yml +++ b/roles/cloud_init_retrait/tasks/main.yml @@ -9,6 +9,18 @@ # Sa tache est finie : il a donne a la VM son adresse, son nom et ses cles d'hote a la # premiere seconde. C'est precisement parce qu'il a REUSSI qu'Ansible a pu entrer. # +# CE QUE CE ROLE NE FERME PAS, et le croire serait la mauvaise lecon. Il n'ote AUCUN +# pouvoir a l'hebergeur. `qemu-guest-agent` est au gabarit — il doit y etre (P56) — et +# l'API Proxmox expose sur son dos, sur toute VM vivante, un pouvoir STRICTEMENT PLUS +# GRAND que le lecteur cloud-init : `exec`, `file-write`, `file-read`, +# `set-user-password`, `shutdown` (releve le 2026-09-09 sur `edge-mta-01`). +# +# Ce qui est ferme ici est etroit et reel : (a) une reapplication AUTOMATIQUE, a chaque +# demarrage, depuis un support que le plan ne possede pas et qu'aucune preuve ne lit ; +# (b) le code de cloud-init lui-meme, un interpreteur Python complet execute en root au +# demarrage. La mainmise de l'hyperviseur sur ses invites est une propriete de la +# virtualisation, pas de cloud-init : elle appelle sa propre decision (D-85). +# # POURQUOI LE RETIRER NE COUPE PAS LE RESEAU (mesure du 2026-09-09 sur `obs-01`) : # # /etc/network/interfaces.d/50-cloud-init -> dpkg -S : aucun paquet ne le possede diff --git a/wiki/Virtualisation-et-clonage.md b/wiki/Virtualisation-et-clonage.md index 6e27762..853088a 100644 --- a/wiki/Virtualisation-et-clonage.md +++ b/wiki/Virtualisation-et-clonage.md @@ -49,6 +49,15 @@ Le durcissement (`serveur_durci`) le **retire** donc, une fois qu'il a réussi parce qu'il a réussi qu'Ansible a pu entrer. Le gabarit, lui, le garde : sans lui, un clone n'a ni adresse ni nom d'hôte. +*Ce que ça ne fait pas* : ça **ne reprend pas la main à l'hébergeur**. `qemu-guest-agent` +reste sur chaque VM — il doit y être, c'est par lui que la création confirme qu'une machine +existe — et l'API de l'hyperviseur expose sur son dos `exec`, `file-write`, +`set-user-password`, `shutdown` : strictement plus que le lecteur cloud-init. Ce qui est +fermé est plus étroit, et bien réel : une réapplication *automatique, à chaque démarrage*, +depuis un support que le plan ne possède pas, et un interpréteur Python complet lancé en +root au démarrage. Qu'un hyperviseur puisse tout faire chez ses invités est une propriété +de la virtualisation, pas de cloud-init. + *Pourquoi ça ne coupe pas le réseau* : l'adresse posée à la naissance vit dans `/etc/network/interfaces.d/50-cloud-init`, un fichier qui **n'appartient à aucun paquet** — `dpkg -S` ne le trouve pas, et le `postrm` ne le nomme jamais, même en `purge` (mesuré le