257 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 53d7b4c4c2 |
durcissement : cloud-init nait avec la VM et ne lui survit pas
cloud-init n est pas un logiciel d installation : c est une SOURCE DE VERITE EXTERNE. Il se reveille a chaque demarrage et relit le lecteur attache par l hyperviseur, qui peut redefinir comptes, cles SSH, mots de passe et reseau. Sur une machine que le plan possede, c est un second maitre — que le plan ne decrit pas, que make valider ne mesure pas, et qui parle en premier. Sa tache est finie a la premiere seconde : c est parce qu il a REUSSI a poser l adresse et les cles qu Ansible a pu entrer. TROIS MOITIES, ET ELLES SE DEFONT SEPAREMENT. - le GABARIT le garde : sans lui un clone n a ni adresse ni nom ; - le SOCLE ne l installe plus : le garder produisait un va-et-vient a chaque deploiement, deux changed par passage, idempotence perdue ; - le DURCISSEMENT le retire (roles/cloud_init_retrait, en dernier). P63 garde les trois, plus le CONTENU du role : une coquille vide passerait les trois premiers controles sans rien fermer. Quatre controles negatifs rejoues. CE QUI REND LE RETRAIT SUR EST MESURE, PAS SUPPOSE (obs-01, 2026-09-09) : /etc/network/interfaces.d/50-cloud-init n appartient a aucun paquet — dpkg -S ne le trouve pas — et le postrm ne le nomme jamais, meme en purge. L adresse survit. Le role le verifie quand meme, avant et apres, et n accuse que si le retrait l a emporte : une VM qui perd ce fichier ne se plaint pas, elle repart sans adresse et plus personne ne peut entrer. DEUX CHOIX DITS FRANCHEMENT. cloud-guest-utils reste (growpart : ni service, ni port, ni source de donnees). Les ~29 paquets orphelins ne sont pas retires par defaut : autoremove deciderait a partir des drapeaux dpkg, et un durcissement ne doit pas pouvoir surprendre. NON DEPLOYE : le code est ecrit, valide et prouve ; il n a pas ete applique a la flotte. Essai a blanc sur obs-01 : cloud-init a retirer, configuration reseau intacte. make prouver : CONFORME, 62 OK, 0 echec, 1 saute (63 preuves). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| 2887b57f0c |
GUI : les six registres ont un formulaire genere, et la sauvegarde aussi
CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus gros, sont passes au generateur — chargement, rendu et sauvegarde. L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25 applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe un formulaire genere d un formulaire qui en a l air — un champ visible a l ecran et perdu en silence a l enregistrement serait le pire des deux mondes. test_rendu_gui.py le mesure a chaque make prouver. TROIS DEFAUTS TROUVES EN CHEMIN. Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il n existe aucun defaut fixe — deriver_ressources calcule depuis les roles portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere faux fait croire qu on connait la valeur. Le schema nomme le champ derive, et l ecran montre la valeur reelle de cet hote. L option vide d un select dit desormais ce qu elle produira : « (defaut : asgard) ». Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs : elle lisait encore data.nomenclature. Elle n avait jamais leve parce que la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne voit que les chemins vivants : verifier_gui.py fait donc aussi une verification STATIQUE, qui voit ce qui dort. La validation client s accrochait a data-v, pose a la main sur trois champs. Le formulaire genere l aurait perdu et la validation serait passee au vert sur ZERO champ. Le generateur marque chaque controle, et la sauvegarde refuse si elle n en inspecte aucun. DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la matrice des integrations montre les universelles et les exemptions, et l editeur de liens contraint le role a meta/liens.yml. Le generateur s efface plutot que de remplacer un editeur qui en sait plus que lui. LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| 935a64a1bf |
plan : sauvegarder n emportait plus quarante lignes de commentaire
En voulant generer deux formulaires de plus, j ai trouve pire que ce que
je cherchais.
CE QUI ETAIT DEJA LA. Les quatre ecrivains de registre ecrasaient le
fichier au safe_dump. Mesure sur les fichiers reels : domaines.yml 6->3,
applications.yml 27->5, serveurs.yml 18->3. Quarante lignes, detruites
par n importe quel clic sur Sauvegarder dans les vues Serveurs,
Applications ou Domaines. Parmi elles, celle qui explique pourquoi
backup-01 a ete retire, et celle qui dit dans quel ordre les deux roles
du runner s appliquent. C etait l incident du 2026-08-18, jamais corrige
pour les registres du plan. Les quatre passent par _ecrire_registre :
aller-retour a vide identique a l octet, sur les quatre fichiers.
TROIS ECARTS DE SCHEMA, trouves en confrontant le schema aux VALIDATEURS
et non aux seuls plans :
- edge designe un GROUPE, pas un hote. Le schema disait serveurs : un
formulaire genere aurait offert une valeur qu aucun hote ne reconnait,
donc aucun SAN, donc la panne du 2026-08-25 reintroduite ;
- exposition, entierement valide par le moteur, manquait au schema ;
- liens etait items: {type: object} — une liste d objets sans forme.
Et mail, offert par la vue Domaines depuis sa creation, decrit ici comme
un booleen, saisi la-bas comme du texte, lu par rien : retire.
P62 garde tout ca. Elle separe l entite du reste mecaniquement : un
validateur lit son entite par des variables LOCALES, les autres registres
par ses PARAMETRES. Controle negatif rejoue.
LES FORMULAIRES. Serveurs de BD et Domaines sont generes, chargement et
sauvegarde compris. Quatre registres sur six. Le generateur a appris la
liste d objets.
LIMITE : restent serveurs et applications, les deux plus gros ; et je n ai
toujours pas ouvert ces pages dans un navigateur.
make prouver : CONFORME, 61 OK, 0 echec, 1 saute (62 preuves).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
|
|||
| 88033f37b8 |
GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas
Some checks are pending
verifier / verifier (push) Waiting to run
LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre sur six. couverture_gui verifier passe : les 28 champs des plans reels sont editables. Elle n est pas un registre comme les autres : elle decrit la REGLE dont VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce qu elle derive et les VM qui la portent ; l index est montre mais pas editable, parce qu il est alloue par le site ; valider_nomenclature refuse de retirer une fonction encore portee, ou de designer une zone non declaree. DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS. Le formulaire des bases, livre la veille, etait casse dans un navigateur. Il lisait data.schema, or il n existe aucun data global : c est une const locale de charger(). ReferenceError a l ouverture, et zone morte dans sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui charge le JS entier dans un DOM simule et dessine les douze vues, avec son controle negatif. Le schema decrivait reservations comme une table de zones ; le fichier reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait rien. Elle compare desormais aussi la FORME. ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le bloc entier des qu une valeur change : quinze entrees compactes devenaient 42 lignes, et le commentaire du poste d exploitation se retrouvait en tete du bloc, ou il affirmait que collab etait le poste d exploitation. Un commentaire deplace n est pas laid, il est faux. _fusion_table edite les tables ligne a ligne ; le diff fait trois lignes. Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran (reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait encore par write_text, oubliee au passage des ecritures atomiques. LIMITE : deux registres sur six sont generes, et je n ai toujours pas ouvert cette page dans un navigateur. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| 34b7ec3886 |
schema : le marqueur du champ requis, et l entree de CHANGELOG qui manquait
Some checks are pending
verifier / verifier (push) Waiting to run
Deux dettes des commits |
|||
| 03c628d5e1 |
GUI : le formulaire des bases est GENERE depuis le schema
Etape 3, sur un seul registre — `bases_donnees`, le plus simple et le seul ou observe et editable coincidaient deja. Les cinq autres gardent leurs formulaires ecrits a la main : on ne bascule pas six vues d un coup. CE QUI DISPARAIT DU JAVASCRIPT Huit `<label>` en dur, trois constructions de `<option>`, et la regle qui choisissait la source du consommateur selon la portee. Cette derniere ne vivait que dans le JS ; elle est desormais DECLAREE au schema (`x-source-selon`), donc lisible et gardee. Le formulaire rend exactement les memes huit champs qu avant — verifie en EXECUTANT le moteur sous node avec le schema et des donnees reelles, pas seulement en passant `node --check`. LA BOUCLE EST FERMEE DES DEUX COTES Le chemin de SAUVEGARDE enumerait lui aussi les sept champs en dur. Un champ ajoute au registre serait apparu au formulaire genere et aurait disparu SILENCIEUSEMENT a l enregistrement — le pire des deux mondes. Il derive maintenant du schema, valeurs par defaut comprises (`default`). LA SEPARATION FORME / COHERENCE, MONTREE portee=groupe + consommateur APPLICATION -> REFUSE par valider_bases portee=application + consommateur app -> ACCEPTE secret absent -> REFUSE Le schema a rempli la FORME (les defauts `groupe` et `principale` se sont poses), le validateur a attrape l INCOHERENCE. Aucune de ces trois regles ne s exprime en JSON Schema, et vouloir l y mettre creerait la seconde source de verite que ce depot refuse. CHAMPS_ECRITS_PAR_GUI COMMENCE A DISPARAITRE Renomme CHAMPS_ECRITS_A_LA_MAIN, et `bases_donnees` en est SORTIE : sa couverture se derive du schema. P19 lit desormais `champs_ecrits_par_gui()`, qui reunit les deux. Le jour ou la table sera vide, elle gardera un mecanisme au lieu d une liste. UNE GARDE A CORRIGER AU PASSAGE `declaration_derive()` verifiait que chaque champ declare apparait dans le SOURCE du GUI. Pour un registre genere il n y apparait plus — c est le but. Elle aurait crie sur precisement le progres qu elle devait constater. Les registres generes en sont exemptes : c est P61 qui tient la promesse pour eux. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. P07 (node --check), P19 (couverture), P61 (schema) : verts. Reste : les cinq autres vues, et le trou de la nomenclature — que le passage au generateur fermera par construction, puisque le schema decrit deja `categorie` et `service`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| 0eaceb1048 |
schema du plan : la forme des registres devient derivee, et gardee
Etape 2 du chantier « l UI reflete fidelement la structure ». Le GUI porte CHAMPS_ECRITS_PAR_GUI, une liste tenue A LA MAIN de ce qu il sait ecrire, que P19 confronte au reel. C est une copie — gardee, donc honnete, mais une copie : quelqu un doit penser a l allonger. `make schema` produit docs/audit/schema-plan.json : six registres, 42 champs, leurs types, leurs enumerations et ce qui est requis. CE QUE LE SCHEMA EST, ET CE QU IL N EST PAS schema -> la FORME -> generera les champs du formulaire validateurs -> la COHERENCE -> refusent une saisie incoherente Un JSON Schema ne sait pas dire qu un `consommateur` designe une application inexistante, ni qu une integration universelle recopiee au plan est un defaut. Vouloir l y mettre creerait la seconde source de verite que tout ce depot refuse. Les valider_* restent l autorite. LES ENUMERATIONS SONT IMPORTEES, JAMAIS RECOPIEES ETATS_SERVEUR, PORTEES_BD et AUTORITES_DNS viennent des constantes que les validateurs appliquent. Une enumeration recopiee diverge — c est la lecon des neuf resolutions d instance que P41 garde depuis. CE QUE L ETAPE 1 AVAIT TROUVE, ET QUE P61 A CONFIRME Le recensement montrait `categorie` et `service` presents dans TOUS les plans et absents de CHAMPS_ECRITS_PAR_GUI, dont la ligne `nomenclature` est vide : le GUI ne sait pas les editer, l operateur doit ouvrir le YAML. P61 a refuse le premier schema pour cette raison exacte. Les trois tables imbriquees de la nomenclature sont donc DECRITES et non resumees en « object ». 31 champs observes dans l instance courante, 42 decrits par le schema. La difference n est pas du bavardage : observer une instance n est pas un schema. `noeud`, `stockage` et `coeurs` sont legitimes et simplement inutilises ici — un schema derive de l observation les INTERDIRAIT. P61, EPROUVEE DANS LES DEUX SENS fichier genere perime -> REFUSE champ du plan absent du schema -> REFUSE champ decrit mais inutilise au plan -> COMPTE, pas refuse Le troisieme point est delibere : refuser obligerait a retirer du schema un champ valide des que plus personne ne s en sert. Meme mesure que les lacunes nommees de P29. UN DEFAUT DE MON INSTRUMENT, PAYE EN ROUTE P61 comparait des noms a plat quand couverture_gui aplatit les tables imbriquees : elle criait sur un schema correct. L instrument mesurait autre chose que la cible. On aplatit desormais des deux cotes. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. Prochaine etape : generer les formulaires depuis ce schema, et retirer CHAMPS_ECRITS_PAR_GUI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| 6077b179af |
plan : l ecriture des registres devient atomique — tout, ou rien
Some checks are pending
verifier / verifier (push) Waiting to run
`path.open("w")` TRONQUE avant d ecrire : entre les deux, le fichier est vide.
Une exception dans yaml.safe_dump, un disque plein, un Ctrl-C, et
instance/plan/serveurs.yml reste mutile.
L asymetrie fait la gravite : hosts.yml se regenere d un
make instancier-appliquer, le PLAN ne se regenere de rien. C est la source
unique de verite. Git est le filet, mais encore faut-il savoir qu on est tombe.
TREIZE SITES, UNE SEULE FONCTION
Le defaut n etait pas dans le GUI seul : douze sites dans sept fichiers, dont
les miroirs CLI des MEMES registres. Corriger le GUI seul aurait recree la
divergence que P41 garde depuis les neuf resolutions d instance. La fonction
vit donc dans inventory_rules.py, que les sept importaient deja. Une source,
pas douze.
TROIS DETAILS QUI FONT LA DIFFERENCE ENTRE « CA MARCHE » ET « CA TIENT »
temporaire dans le MEME dossier os.replace n est atomique qu au sein d un
meme systeme de fichiers ; un /tmp sur une
autre partition casserait la garantie sans
rien dire
fsync AVANT le rename sinon le renommage peut atteindre le disque
avant le contenu : au retour d une coupure
brutale, un fichier neuf et VIDE — le defaut
qu on ferme, deplace d un cran
report des droits mkstemp cree en 0600, le plan est en 0664 et
doit rester lisible par le groupe sur les
runners
LE TEST PORTE SON PROPRE CONTROLE NEGATIF
scripts/tests/test_ecriture_atomique.py rejoue D ABORD l ancienne forme et
verifie qu elle DETRUIT. Sans ce controle, « le fichier est intact » ne
prouverait rien — il pourrait l etre parce que rien n a ete ecrit du tout. Une
garantie qu on n a jamais vue echouer n est pas une garantie, c est une
habitude.
Branche sur P02, dont le titre annoncait « inventory_host » alors qu il lance
maintenant trois tests. Corrige au passage.
LA VOUTE DU GUI : VERIFIEE, PAS DE DEFAUT
Le soupcon etait qu executer_flux pose ANSIBLE_VAULT_PASSWORD_FILE (un seul mot
de passe) alors que creer une VM ouvre DEUX voutes depuis « une voute, une cle ».
Eprouve contre deux voutes JETABLES a mots de passe distincts — jamais les
vraies. Les deux variables se CUMULENT : Ansible essaie tous les secrets, et un
PASSWORD_FILE errone n empeche rien. Confirme en sondant l environnement qu une
recette make recoit reellement : le mot de passe saisi ET les cinq cles
calculees par voutes.py.
Ce qui sauve ce chemin n est donc pas le mot de passe saisi, c est
l IDENTITY_LIST que make pose par-dessus. Chacun couvre ce que l autre ne
couvre pas — le PASSWORD_FILE sert le runner qui n a que sa cle, l IDENTITY_LIST
le poste qui les a toutes. Ecrit au-dessus du code, pour que personne ne
« simplifie » en retirant l un des deux.
make prouver : CONFORME, 59 OK, 0 echec, 1 saute.
make instancier : DIFF VIDE, quatre registres relus, droits 664 preserves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
|
|||
| c9d31e9a60 |
preuves : trois gardes pour ce que ma lecture ne tiendra pas
Une revision de documentation vieillit comme le reste. Ce qui tient, c est ce
qu une machine verifie — et trois lacunes etaient nommees sans etre gardees.
P58 HABILITATIONS. autorisation.md posait la regle (un service nomme un
GROUPE, jamais une personne, D-66) et meta/acces.yml la portait ; rien ne
la verifiait. P29 gardait les POSITIONS d authentification, personne ne
gardait les DROITS.
Le controle qui porte la preuve est un croisement : une entree
porte_par: role-realm affirme que l habilitation voyage par un role de
realm projete depuis un groupe LDAP. P58 le confronte a serveur_keycloak.
Sans ca, un service annonce une habilitation que rien ne transporte, et l
ecran reste vide sans que personne sache pourquoi.
CE QU ELLE N EXIGE PAS, et c est le point le plus important : que les
groupes nommes existent dans l annuaire. Ce serait contredire le regime du
paragraphe 2 — le depot AMORCE un acces et se retire, les appartenances
appartiennent a une personne. dev et personnel n existent dans aucun code,
et ce n est pas un defaut.
P59 ENUMERATIONS ANNONCEES. Les deux ecarts trouves a la main pendant la
tournee — cinq portes annoncees devant une table de six, huit lignes
renvoyees vers une fiche qui en compte dix — etaient d une forme que P57
ne voit pas.
Ma premiere version a signale CINQ ecarts, et les cinq etaient du bruit :
dans « reprise dans les deux devis : », le nombre qualifie autre chose que
la liste. Cent pour cent de faux positifs — la preuve qui crie sur un cas
sain et qu on apprend a ignorer. Resserree aux deux formes ou le nombre ne
peut compter rien d autre. Etroite et vraie plutot que large et devineuse.
P60 WIKI PUBLIE. Le wiki est publie DEPUIS le depot ; rien ne mesurait l ecart,
et il s est creuse de VINGT-SEPT JOURS en silence. Deux unites jamais
publiees, vingt et une differentes : pour qui lit la forge plutot que le
depot, toute la revision n existait pas.
Le harnais est STATIQUE, zero appel reseau — cloner la forge romprait la
seule propriete qui fasse qu une preuve vaille hors de ce poste. La mesure
passe donc par un TEMOIN que make wiki-publier depose. Amorce avec la
valeur MESUREE : le wiki d eregion porte b6167f2, dont le message dit
source:
|
|||
| 5bc3bceac1 |
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
Some checks failed
verifier / verifier (push) Has been cancelled
La revision a commence par un balayage par motifs — chemins morts, cibles make absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque tout le reste : un motif ne voit que ce qui s exprime en motif. make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus haut. Il fallait lire pour la voir. 74 documents lus un par un. 66 corriges, 8 exacts. CE QUI ETAIT FRANCHEMENT FAUX AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre des VM reelles. Elle a ete rasee et remontee depuis zero trois fois. ecosysteme-chezlepro.md, le document montre a un client, portait la meme phrase : il se sous-vendait gravement. courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n est construit alors qu il rapporte des mesures datees du role en fonctionnement. hebergeur-exploitation.md disait rien n est fait d un depot qui existe. filiation-emancipation.md se contredisait a deux ecrans de distance. DES MODELES DECRITS D APRES UN MONDE ANTERIEUR Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le donnaient en exemple d integration FACULTATIVE — il est universel depuis le 2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki qui avait raison. CE QUI CASSE AU PREMIER ESSAI Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le FABRIQUE et le critere R2 de l epreuve d operateur independant. preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait detruite : raser derive du plan, il ne la detruira jamais — le risque est l inverse. Un mot de passe d essai en clair dans un depot public. DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux declarations reelles : 12 annonces, 21 reels. Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une erreur ajoute l assurance a l erreur. CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il nomme existe. P29 tient les positions d authentification, personne ne tient les habilitations. make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q |
|||
| dcbb57db69 |
cles : la cle USB se suffit a elle-meme, et la restauration est prouvee
Some checks are pending
verifier / verifier (push) Waiting to run
Le jour ou l on s en sert, le poste est mort — et cloner le depot demande la cle SSH qui est dans l archive qu on essaie d ouvrir. Une procedure rangee dans le depot serait inaccessible exactement quand elle sert. L export depose donc restaurer_cles.py et un LISEZ-MOI a cote de l archive. La cle ne demande plus que gpg, python3 et la phrase de passe. Le script remet chaque fichier a sa place selon son NOM (machine neuve, parfois autre compte), repose les droits a 0600 — ssh refuse une cle privee lisible par d autres, et son message ne dit pas qu il s agit d un droit — et refuse d ecraser une cle presente, en regardant AVANT d ecrire. Le LISEZ-MOI porte les trois commandes manuelles. Un outil peut avoir un defaut ; gpg et tar seront la. EPROUVE : export vers une cle, poste neuf vide, restauration depuis la cle SEULE, empreintes comparees — identiques 4 sur 4, droits 700/600, et le refus d ecraser tire. Le filet manuel passe au meme test separement, identiques 4 sur 4. make cles-restaurer refusera puisque les cles sont en place — et ce refus est la preuve que l archive s ouvre et que la phrase de passe est la bonne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 3eff217bd5 |
cles : un chmod refuse ne doit pas annuler une verification reussie
Some checks are pending
verifier / verifier (push) Waiting to run
Une cle USB est le plus souvent en FAT32 ou exFAT — pas de permissions Unix. Le chmod de la derniere ligne y echoue, et le script serait mort APRES avoir ecrit et verifie l archive : une trace d erreur sur un travail termine, au pire moment pour semer un doute. On le dit plutot que de le taire : sur un support sans droits, l archive est lisible par qui branche la cle. C est le chiffrement qui protege, pas le support. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| bebdb84212 |
cles : sortir du poste ce qui n existe qu au poste
Some checks are pending
verifier / verifier (push) Waiting to run
Le code est replique trois fois (eregion, forge du site, patient 0) et les voutes chiffrees y sont aussi — le coffre est solide. Les CLES qui l ouvrent vivaient dans neuf fichiers, 1644 octets, sans copie ailleurs. Poste seul : les mots de passe restic restent lisibles sur les machines vivantes, donc recuperable mais douloureux. Poste + une machine : l etat de cette machine devient illisible. Poste + site : terminal. make cles-recenser montre ce qui sortirait sans rien ecrire — nom, taille, empreinte, JAMAIS le contenu. make cles-exporter chiffre en AES256 puis REDECHIFFRE ce qu il vient d ecrire et compare les empreintes une a une : une sauvegarde de cles qu on n a pas rouverte n est pas une sauvegarde. A lancer par l exploitant lui-meme : gpg demande une phrase de passe, elle ne doit passer ni par un journal ni par le contexte d un assistant. Trois refus, eprouves en les faisant echouer : destination dans l infrastructure (un coffre dont la cle est dedans), archive existante (elle est peut-etre la seule), archive illisible (supprimee). Le premier essai du premier refus etait faux — le shell developpait HOME avant que je le remplace, l instrument mesurait ailleurs que la cible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 42becd0c02 |
paquets tiers : passer par le cache du controleur, plus par Internet
Some checks are pending
verifier / verifier (push) Waiting to run
Trois depots tiers etaient en HTTPS, et client_artefacts pose Acquire::https::Proxy DIRECT — sans quoi le cache du site refuse les tunnels et aucun depot tiers n est joignable. La ligne est juste ; sa consequence ne l avait pas ete vue : ces trois depots CONTOURNENT le cache, et chaque VM neuve allait les chercher sur Internet a sa naissance. step-cli et alloy sont poses par des integrations UNIVERSELLES. Sans lien, une machine neuve n obtenait ni son client d autorite ni ses metriques — elle n entrait dans aucun flux chiffre. Dernier obstacle a une reconstruction hors ligne, et il tenait dans un mot. make cacher-paquets tire les 21 deb aux versions epinglees, empreinte SHA256 verifiee depuis l index du depot. Le role partage paquets_tiers les depose et les installe EN UN SEUL appel a apt — il sait resoudre un ensemble de fichiers locaux qui se dependent, la ou paquet par paquet echouerait sur l ordre (Icinga en apporte dix-sept). Six roles branches ; chacun retombe sur le depot distant pour ce que le cache n a PAS fourni, et rien d autre. Eprouve pour de vrai : packages.smallstep.com renvoye vers 127.0.0.1, step-cli desinstalle, cache local efface. Le role rejoue installe 0.30.6-1 depuis le cache et la machine obtient ses certificats. Zero echec. Deux defauts de mon propre outil, trouves en le construisant : Smallstep sert son index NON COMPRESSE (404 sur .gz) donc step-cli n etait jamais mis en cache ; et un depot injoignable faisait continue AVANT d incrementer le total, si bien que le script rapportait 20 sur 20 alors qu il en manquait un. Une garde qui ne peut pas echouer ne garde rien — troisieme fois cette semaine. Corrige puis eprouve en le faisant echouer. Reste hors ligne : NTP externe, et l expedition des alertes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| cccb4e5b43 |
supervision : declarer a qui elle parle
Some checks are pending
verifier / verifier (push) Waiting to run
Icinga livre son exemple avec root@localhost — une adresse que personne ne lit. Une supervision qui voit tout et n en parle a personne a le meme effet qu aucune supervision, en plus couteux : elle rassure. serveur_icinga_destinataire cree un utilisateur dans le groupe auquel les notifications sont deja assignees (Icinga refuse deux objets de meme nom, on ne peut pas redefinir l exemple ; on en ajoute un autre, l exemple reste muet). Vide, aucun destinataire n est pose ET LE DEPLOIEMENT LE DIT — plutot que de laisser croire qu une alerte partira. Eprouve : message expedie par le relais du site, status=sent (250 2.0.0 Ok), livre directement a mx.chezlepro.ca sans intermediaire ni dependance envers un locataire. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 4e12c3a802 |
supervision du site, et la panne qui dormait dans un mot
Some checks are pending
verifier / verifier (push) Waiting to run
Le site a son temoin : site-mon-01 (VLAN 36) porte PostgreSQL, Icinga et un relais. Le depot lui rapporte, et son premier verdict fut un vrai defaut — site-mon-01 n avait jamais depose son propre etat. Les trois sont au vert. serveur_backup_verification_locale repasse a true sur le depot : le drapeau ne dit plus on renonce mais verifie ce que tu peux ouvrir . La liste des noeuds attendus derive de l inventaire ou tourne le role, donc du site seul. serveur_postfix gagne un mode relais : un site n heberge aucune boite, il expedie. Directement par sa frontiere — emprunter le MTA d un locataire ferait dependre l hebergeur d un ecosysteme qu il peut outvivre. LA PANNE DE FOND : le modele de routes OPNsense lit enabled ; on lui envoyait disabled=0, un champ ignore. enabled restait a son defaut, ETEINT. Chaque route creee par Set-OPS depuis l origine l etait desactivee — invisible, parce qu une route eteinte EXISTE dans le modele et compte posee . Le rechargement lance pour activer la nouvelle patte a fait reprendre au noyau sa table depuis le modele : 14 routes sur 15 disparues, 11 machines de Chezlepro injoignables le lendemain de sa reconstruction, et le devis toujours vert. Trois corrections empilees : la cause (enabled), la cecite (le plan lit la table du NOYAU et signale absent ou eteint), l inaction (la reconfiguration ne se declenchait que sur un changement du modele). Aussi : un role du site peut enfin en appeler un autre a travers la frontiere — le devis traitait tout pair nomme comme l exterieur . Et nftables_admin_ssh se DERIVE de la carte : recopie a la main, il retardait d une zone, et m a verrouille dehors de la machine que je venais de creer. Reste ouvert : le destinataire des alertes est encore root@localhost. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| cf9abe74b6 |
sauvegarde : le site protege enfin son propre etat
Some checks are pending
verifier / verifier (push) Waiting to run
L hebergeur protegeait l etat de tous ses locataires et pas le sien. Deux choses qu il detient et que personne ne peut regenerer : la racine de son AC, et la forge du genome. Le reste est reconstructible par le code. Preuve faite, pas annoncee : sauvegarde reelle puis restic check et restitution. 13 fichiers pour l AC (root_ca_key et intermediate_ca_key compris), 835 pour la forge. Le site depose avec SON identite, sur le compte restic que serveur_backup lui cree, separe des comptes des locataires par les memes permissions. La cible est derivee du expose de l application qui porte serveur_backup_site : le nom du service, pas une adresse — client_backup_repo la grave dans le chemin de chaque instantane. P36 ne lisait que le plan de l instance montee, donc jamais celui du site — qui detient pourtant le plus. Elle lit desormais les deux. Verifiee en la faisant echouer : integration retiree, la preuve tire. Reste ouvert : personne ne verifie les sauvegardes du site. Le depot tourne avec verification_locale a false (pose pour les locataires, dont il ne peut pas ouvrir les depots) et le site n a pas de supervision. La sauvegarde existe et se restaure ; c est son SILENCE qui n alerte pas encore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 53c5f41a1a |
durcissement : le site recoit enfin serveur_durci
Some checks are pending
verifier / verifier (push) Waiting to run
Les six machines du SITE — racine de l AC, forge, cache, resolveur, depot de sauvegarde, runner — ne recevaient que serveur_debian. Ni auditd, ni fail2ban, ni apparmor, ni sysctl, ni pare-feu. Le commentaire au-dessus de GROUPE_SOCLE affirmait pourtant le contraire, ce qui rendait l ecart invisible. Le pare-feu du site n avait aucune des trois pieces qui le rendent applicable : - aucune regle generee (resoudre_flux lit un hosts.yml ; le site a un inventaire dynamique) -> commande nftables-site, branchee sur make flux - le chemin du jeu de regles sortait du depot (inventory_dir vaut scripts/ pour un inventaire dynamique) -> host_var explicite - aucun nftables_admin_ssh : l exploitant administre PAR REBOND, la connexion arrive avec l adresse de la patte de frontiere DANS LA ZONE VISEE. Le generateur refuse desormais de produire des regles sans cet intrant. Defaut attrape en LISANT le jeu de regles avant de l appliquer : la regle DNS de site-dns-01 omettait 10.17.0.0/16. _supernets_voisins retire l instance montee — juste chez un tenant, faux du cote du site, ou ca retire le locataire qu on pilote. L appliquer aurait prive de DNS les quinze machines reconstruites la veille. MaxStartups du depot passe en host_var : dans le meta du role, il ne portait que dans son play, et le passage de serveur_durci le remettait au defaut sans rien dire. Un reglage qui depend de l ordre des plays revient en arriere. Verifie depuis un locataire a travers le nouveau pare-feu : cache, forge, depot (2 snapshots) et resolveur repondent ; l AC du site refuse, et c est correct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 0854b2a93c |
reconstruction : quatre defauts que seule une flotte rasee pouvait montrer
Some checks are pending
verifier / verifier (push) Waiting to run
Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution compris. Aucun defaut ne venait de la flotte ni du gabarit. 1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM clonees a 100 pourcent. Le message parlait d etat stopped — celui de la TACHE, pas de la VM. 2. client_artefacts se contredisait : son commentaire disait de degrader, son code arretait. L autorite monte en premier, donc avant le cache du locataire : aucun ordre ne pouvait satisfaire la garde. 3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD internal a toute la zone du site, sans jamais interroger l autoritatif. Declencheur : toute question sur un nom absent sous internal, y compris la zone d un autre locataire. Le cache contenait la bonne reponse ET un message negatif ; c est le negatif qui etait servi. aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait le cache, pas le reglage. 4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi il ne peut plus nommer son depot de sauvegarde. La derivation prenait dns_amorcage pour le resolveur du site : faux chez Technolibre, dont l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement. Au passage : instancier tentait encore le mot de passe unique d avant la separation des voutes ; comparer echouait en exit 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 3ea0c95492 |
sauvegarde : l etat d un locataire quitte enfin sa propre flotte
Some checks are pending
verifier / verifier (push) Waiting to run
Chezlepro rangeait ses instantanes sur une VM DE SA PROPRE FLOTTE. Raser l ecosysteme pour le reconstruire, c etait raser le filet avec. Le site a son depot ; les neuf detenteurs d etat y deposent ; une restitution est sortie (annuaire LDAP lisible, hors flotte). Isolation par compte Unix, pas par convention : home 0700, cle exclusive, racine partagee a root en 0711 (traversable, non listable). Les deux refus constates. Le site heberge du chiffre : il ne peut ni lire ni ouvrir, d ou la verification deplacee chez le locataire qui detient la cle. Quatre defauts reveles par ce deuxieme usage : - la racine des depots ne peut etre le home de personne (StrictModes rendait Permission denied publickey pour un refus de CHEMIN) - la racine nie le TLD internal, et harden-below-nxdomain etendait ce non a toute la zone sans jamais interroger l autoritatif : aucun locataire ne pouvait nommer un service du site - le gabarit transporte des fichiers de durcissement perimes, et les machines du site ne recoivent jamais ssh_hardening - MaxStartups compte les connexions non authentifiees : un depot de site en voit la somme de ses locataires Constat non corrige : les machines du site ne sont pas durcies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 4abf875e5f |
gabarit : q35 n est pas un reglage, c est la raison de la procedure manuelle
Some checks are pending
verifier / verifier (push) Waiting to run
CONSTAT DE L EXPLOITANT, PAYE EN ANOMALIES : convertir une machine deja installee d `i440fx` a `q35` produit une serie de pannes dont chacune ressemble a autre chose qu a sa cause. Ce n est pas une correction, c est une transplantation. LE MECANISME, ECRIT POUR QU ON NE LE REDECOUVRE PAS : `i440fx` est un chipset PCI, `q35` est PCIe. La topologie des bus change, donc les NOMS D INTERFACES PREDICTIBLES changent avec le chemin PCI (enp0s3 -> enp1s0) et la machine perd le reseau ; les chemins de disques bougent ; l ordre d enumeration suit. C EST AUSSI POURQUOI SET-OPS N UTILISE PAS L IMAGE CLOUD OFFICIELLE DE DEBIAN : `genericcloud` est livree configuree pour `i440fx`. Une machine nait `q35`, ou elle ne le sera jamais proprement — et c est ce que l installation depuis l ISO garantit. Ces deux lignes de la procedure n etaient qu une ligne de tableau. Elles portent maintenant leur pourquoi, et le SITE les declare comme DONNEES (cle `gabarit`), plus seulement comme prose. `make gabarit-etat` compare le gabarit reel a ce que le site declare de lui. ON VERIFIE LA SOURCE, PAS CHAQUE COPIE. Ma premiere version gardait le CLONAGE : la propriete s herite, donc verifier chaque clone coute a chaque creation sans rien dire de plus que verifier le gabarit une fois. Retiree. TROIS FOIS J AI DEVINE LA FORME DE LA REPONSE AU LIEU DE LA REGARDER — regex_search a groupe qui rend None, proxmox_vm_info sans `config: current` qui ne rend que l etat. La garde a declare « ? » sur une VM parfaitement conforme : une garde qui crie toujours est pire qu aucune, on apprend a l ignorer. A la demande et non dans `make prouver` : ce controle exige le cluster, que le harnais ne suppose pas joignable. Meme nature que genome-etat et underlay-plan. Controle negatif verifie : declarer i440fx fait echouer, rc=1. make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 9aef3614ab |
gabarit : refabrique, minimal, et puise aux ressources du SITE
Some checks are pending
verifier / verifier (push) Waiting to run
REFABRIQUE (VMID 9006, modeleSetOPS-minimal). Quatre roles au lieu de dix-sept :
qemu_guest_agent, cloud_init, sudo_ansible, ssh_baseline — des conditions
d existence, pas des choix d efficacite. Tout le reste vient du socle, et P56 refuse
qu un role retire ne soit repris par personne.
FABRIQUE CHEZ LE SITE, ET C EST LA DECISION QUI COMPTE. « Les ressources du SITE
font autorite pour tous ses artefacts ; elles servent les tenants jusqu a ce qu ils
s emancipent. »
Il se fabriquait DEHORS : `-i "<ip>,"` ne porte aucun group_vars, donc ni mandataire
ni resolveur. L ancien gabarit allait chercher ses paquets chez Debian et resolvait
chez l ancien LAN — 192.168.10.10, lu sur la VM 99998. Le site avait son cache et
son resolveur, et son propre artefact les ignorait.
Desormais la fabrication DERIVE ses ressources du plan du site (underlay --adresses)
et tourne sur le reseau du genome. PROUVE : dix requetes de 10.0.33.31 servies par
site-cache-01, resolveur pose a 10.0.34.11.
L IDENTITE DU GABARIT VIENT DU SITE, PLUS DU TENANT. `proxmox_clone_vmid_modele`
vivait dans les group_vars de l ecosysteme : deux tenants pouvaient cloner deux
gabarits differents sans que rien ne le dise, et un tenant decidait d un objet dont
descend chaque VM de chaque ecosysteme. Meme mouvement que l INDEX (2026-08-25) : le
site ALLOUE, le tenant RECOIT. Cle `gabarit` du plan du site, lue par
underlay --gabarit.
PIEGE FERME EN CHEMIN : creer-vm passait VMID_MODELE="$SETOPS_VMID_MODELE" a
cloner-vm, or inventory_host.py n emet PAS cette variable. Elle valait donc le VIDE,
et ce vide ECRASAIT la valeur derivee — le gabarit du tenant reprenait la main sans
bruit.
ET UN PIEGE DEJA DOCUMENTE, PAYE UNE TROISIEME FOIS : le chemin du controleur
contient une espace, et `lookup('pipe', ...)` le decoupait. Guillemets.
EPROUVE DE BOUT EN BOUT : VM clonee du gabarit minimal — nom, adresse, machine-id
neuf, cle d hote regeneree, agent invite actif ; puis socle applique dessus,
changed=5, 0 echec, auditd compris. VM d essai retiree.
make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 2f2323bd3e |
gabarit : minimal — il etait un cache du socle, et il perimait sans le dire
Some checks are pending
verifier / verifier (push) Waiting to run
Il portait DIX-SEPT roles : exactement ceux du socle et du durcissement, que le
deploiement rejoue a l identique. C etait donc un CACHE — et comme tout cache, il
perimait sans le dire.
MESURE : derniere recapture le 2026-08-09, et SIX de ses roles avaient change depuis
— common_packages, cloud_init, ssh_baseline, ssh_hardening, auditd,
nftables_baseline. Rien ne le signalait : le deploiement masquait la derive en
reappliquant tout, donc personne ne pouvait la voir. Aucune preuve du harnais ne
regardait sa fraicheur.
IL NE GARDE QUE CE QUI DOIT EXISTER AVANT QU ANSIBLE PUISSE AGIR :
qemu_guest_agent l agent repond AVANT SSH — P52 s en sert
cloud_init le seul chemin vers la premiere seconde
sudo_ansible la porte par ou tout entre
ssh_baseline le serveur SSH
Ce ne sont pas des choix d efficacite, ce sont des conditions d existence.
CE QUE CA COUTE, ET QUI EST COUVERT : une VM neuve n est plus durcie a la naissance.
Elle nait cependant DERRIERE LE PARE-FEU DE L HYPERVISEUR, policy_in=REJECT arme au
clonage — verifie sur obs-01. La fenetre d exposition est fermee par la fabric, pas
par le gabarit. Mon objection initiale tombait devant la mesure.
P56 GARDE LES DEUX MOITIES. Qu il ne REGROSSISSE pas : un role ajoute recree le
cache, donc la peremption invisible. Et que rien de retire ne soit PERDU : un role
absent du gabarit ET du socle disparaitrait de toutes les machines neuves, sans
erreur ni trace, et la panne arriverait des mois plus tard sur une machine qu on
croyait durcie. Verifie : 14 retires, 14 repris, zero orphelin. Deux controles
negatifs.
make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| fde0903ed8 |
flux + postfix : un port symbolique dans le pare-feu, un rechargement qui n applique rien
Le deploiement depuis ops-01 passe de 3 plays a 21 : douze machines sur quinze
deployees completement. Deux defauts restaient.
UN PORT SYMBOLIQUE ECRIT TEL QUEL.
/etc/nftables.conf:36: Could not resolve service: Servname not supported
ip saddr { ... } udp dport derive accept # serveur_powerdns
`port: derive` dit que le port depend du deploiement. Le devis de la frontiere le
resout depuis le 2026-08-25 ; le generateur nftables ecrivait le mot, et nft
refusait TOUT le fichier. Il resout desormais par le plan et n emet rien quand il
ne peut pas, en le DISANT — un flux tu en silence est une porte qu on croit
ouverte. Verifie que ca ne ferme rien : infra-dns-01 garde son 53 par
serveur_resolveur, et l omission de PowerDNS est juste puisqu il ecoute en loopback
derriere lui.
LA VALIDATION NE POSE PAS LA MEME QUESTION QUE L EMISSION. Ma premiere garde
refusait tout port non numerique et a fait echouer P09, qui valide les roles HORS
instance — la ou derive est legitime. PORTS_SYMBOLIQUES nomme le vocabulaire : le
mot passe a la validation, jamais dans un fichier, et un mot inconnu reste refuse
des deux cotes.
RECHARGER N APPLIQUE PAS UN CHANGEMENT D ECOUTE, deuxieme fois.
warning: to change inet_protocols, stop and start Postfix
fatal: :::submission: Address family for hostname not supported
main.cf porte inet_protocols, que Postfix refuse de changer a chaud. Le master garde
all, tente d ouvrir :::submission en IPv6 et meurt — APRES avoir accepte une
configuration valide. postfix check ne dit rien parce que la configuration EST
valide : c est la transition qui ne l est pas. Meme famille que nginx. main.cf
notifie desormais le redemarrage.
Diagnostic corrige en chemin : j ai d abord accuse postfix@-.service, dont
l assertion echouait — c est moi qui l avais declenche par un demarrage manuel,
postfix.service le declare en Conflicts.
make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| a5c9a88f00 |
resolveur du site : le troisieme service prete pendant la jeunesse d un tenant
Apres les paquets (serveur_cache_site) et le genome (serveur_forge_site), les NOMS.
Meme patron : un installateur, un marqueur.
CE QUE CA CORRIGE, mesure sur infra-pki-01, premiere machine a porter l autorite
de Chezlepro :
DNS BLOQUE un tenant resout chez lui, sa requete ne sort pas
443 sortant OK par adresse
apt via le cache OK le mandataire resout a sa place
apt s en sortait ; TOUT LE RESTE ETAIT AVEUGLE AUX NOMS. Versionner la cle de
signature Smallstep a fait passer une tache et l echec s est deplace d un cran : le
depot lui-meme devait etre joint par son nom.
POURQUOI PAS L UNBOUND DE LA FRONTIERE : il tourne sur le boitier sans etre un
service gere, et le plan du site l avait deja ecrit une fois — un processus n est
pas un service. site-dns-01 est deploye par le moteur et verifie par le harnais.
OU VIT LA DERIVATION : dans site_inventaire.py, qui connait le plan du site ET le
registre des tenants. Le role tourne sur une machine qui n a aucune raison de lire
la carte de l hebergeur — la derivation appartient a qui detient les deux sources.
PIEGE DE FILTRE, ATTRAPE EN LE BRANCHANT : _supernets_voisins() EXCLUT l instance
montee (personne n est son propre voisin). Le site sert TOUS ceux qu il heberge — y
compris celui qu on deploie. Reutilise tel quel, il privait de resolution le seul
tenant en cours de montage. On reutilise donc la DECOUVERTE partagee sans son
filtre : deux recensements divergent, deux filtres non.
Le marqueur verifie deux choses : que le resolveur ECOUTE, et que sa liste
d autorisation ADMET reellement les tenants. Sans la seconde, la panne chez le
locataire ressemblerait a un DNS mort alors que c est une ACL.
make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| bb63865a37 |
cles : le terrain etait inoccupable — la cle du tenant nait avec ses machines
Le deploiement lance depuis ops-01 s est arrete au premier geste, sur les quinze
machines a la fois : Permission denied (publickey). Les VM neuves n acceptaient que
la cle de l exploitant. Celle du runner EST declaree au plan, mais c est le SOCLE
qui la depose — et le socle doit etre applique par quelqu un qui peut deja entrer.
Boucle fermee : le tenant recevait un terrain qu il ne pouvait pas occuper.
DEUX CLES, DEUX PORTEES :
la cle du SITE -> sur le SEUL runner du tenant l insemination
la cle du TENANT -> sur TOUTES ses machines il va les configurer
POSER UNE CLE AU CLONAGE N EST PAS ENTRER CHEZ LE TENANT. C est un parametre de
creation, au meme titre que l adresse ou le disque : le site ecrit les conditions
de NAISSANCE, il n ouvre aucune session. Le site n obtient aucun acces sur ces
machines ; seul le runner du tenant en obtient un.
Forme tranchee par l exploitant : le site renseigne le seul runner, qui se charge
de toute sa flotte. Le plancher /etc/hosts suit le meme chemin — ops-01 a le sien
depuis son insemination et resout ses quinze voisines par leur nom.
LA REVOCATION EST HONOREE A LA NAISSANCE : une entree a etat absent n est pas
reposee. Sans cette lecture, une cle retiree de la flotte serait ressuscitee sur
chaque VM creee ensuite — panne lente, silencieuse, invisible au plan.
P55 garde les deux moities : la cle du site ne nait que sur un porteur de
serveur_ops_tenant, et aucune machine ne reste sans celle de son tenant. Une
frontiere tenue a une seule couche n est pas tenue. Deux controles negatifs.
make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 5c59f19530 |
voutes : une cle absente n est pas une faute — elle est souvent la bonne reponse
Sur le runner d un tenant, la cle du SITE DOIT manquer : il porte la carte de la
fabric et ne doit jamais pouvoir l ouvrir. Ma premiere version sortait en erreur
des qu une cle manquait — elle presentait une SEPARATION REUSSIE comme un defaut,
et a fait echouer une chaine parfaitement saine.
Seule l absence de la cle de l INSTANCE MONTEE empeche la machine de travailler.
C est elle, et elle seule, qui decide du code de sortie ; le reste s affiche.
Constate sur ops-01 le jour de son armement :
instance OPS-Chezlepro cle presente
hebergeur SITE-Chezlepro sans cle
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 732aff9c70 |
sdn : le puits avalait le plan d administration — une route, et le chemin existe
CE QUE LA MESURE A ETABLI, saut par saut. Le poste de l exploitant atteint la
frontiere, qui LAISSE PASSER (rule 31/0 match, pass out vlan040). Asgard RECOIT le
paquet sur vlan40. La VM ne le voit jamais. Et le noyau dit pourquoi :
ip route get 10.17.19.41 from 10.0.31.11 iif vlan40 -> dev vrf_t17
ip route get 10.17.19.41 from 10.17.0.17 iif vlan40 -> Invalid cross-device link
LA SOURCE EST LE DISCRIMINANT, pas l interface. Le chemin de RETOUR, vu du VRF :
vers 10.0.31.11 -> via 10.0.4.1 dev vlan40
vers 10.17.0.17 -> Invalid argument <- le puits
LE PUITS A SES RAISONS et on n y touche pas : sans lui, une adresse non attribuee
du supernet sort par le defaut, revient par la frontiere dans la table PRINCIPALE
et repart vers le reseau de gestion (mesure du 2026-08-09). Le defaut n est pas
qu il existe, c est qu il est TROP LARGE : il couvre la bande basse ou D-77 place
justement l underlay d un site. Deux regles justes separement, contradictoires
ensemble.
LA CORRECTION EST DERIVEE, PAS ECRITE : pour chaque tenant, les reseaux
d administration qu il DECLARE (nftables_admin_ssh) et qui tombent dans son propre
supernet recoivent une route vers la frontiere, plus specifique que le puits. Ceux
qui vivent dehors n en ont pas besoin — la route par defaut les joint deja.
vrf_t17 ip route 10.17.0.0/24 ... l admin de Chezlepro
vrf_t23 (rien) le sien est hors de son supernet
vrf_t29 ip route 10.29.19.41/32 ... l admin de patient 0 : son ops-01
CE QUE CA REPARE AU-DELA DE L ACCES : les regles administration -> tenant de la
frontiere etaient VRAIES et INAPPLICABLES a la fois. Elles correspondaient, elles
laissaient passer, et le paquet mourait un saut plus loin. Un devis vert sur un
chemin qui ne pouvait pas aboutir.
make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 90ea474745 |
inseminer : le geste sort de mes mains et entre dans le depot
Some checks failed
verifier / verifier (push) Has been cancelled
L insemination avait ete conduite A LA MAIN depuis le runner du site — hors du
depot, donc sans preuve. Elle a maintenant sa cible :
make inseminer TENANT=OPS-Chezlepro
TENANT= PLUTOT QUE LE SYMLINK instance : le runner du SITE amorce PLUSIEURS
locataires ; pointer un lien global sur l un d eux le ferait se prendre pour ce
tenant. Il en NOMME un par commande. Ca borne aussi le couplage que creer-vm
imposait en silence — rien ne disait sur quels tenants ce lien pouvait pointer.
L HOTE SE DERIVE : celui qui porte serveur_ops_tenant. Meme critere que le flux
d insemination et que la cle SSH du runner — le meme mot borne les trois pouvoirs.
P54 GARDE LA LIGNE DE PARTAGE la ou elle glisserait sans bruit. Les deux couches
retenues sont les seules qui ne reclament aucun secret. Le jour ou l on en
ajouterait une, le deploiement echouerait chez le tenant sur une valeur vide, et ce
message ne dirait pas qu un POUVOIR a ete franchi. Controle negatif : ajouter
client_pki, la couche suivante, fait echouer la preuve.
UN GARDE-FOU EXISTANT A INTERCEPTE UNE INSEMINATION MAL DIRIGEE. Le make parent
exporte SETOPS_INVENTAIRE ; ma resolution en heritait et visait l inventaire d un
AUTRE ecosysteme. Le refus vient d inventory_rules, pas de la cible — exactement
l erreur qu un runner servant plusieurs locataires commettrait en silence.
make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 0595bf03c1 |
sonder : rapporter ce qui distingue, au lieu d un echec
Some checks are pending
verifier / verifier (push) Waiting to run
Trois faux diagnostics en une journee, tous dus a l instrument et aucun au composant. curl et bash /dev/tcp ecrasent quatre causes incompatibles dans le meme mot : ouvert, une POLITIQUE qui refuse, une machine ABSENTE, et la frontiere muette. LE MEME CODE DIT DEUX CHOSES SELON LE DELAI, et c est la distinction qui a coute le plus cher : EHOSTUNREACH immediat = pas de route ; le meme apres trois secondes = il n y a pas de machine, c est l ARP qui renonce. Les confondre a fait appliquer un pare-feu pour reparer un vide. est pure, donc gardee par un test qui exige que les deux ne se lisent jamais pareil. LA GARDE ECHOUAIT DU COTE SILENCIEUX. L outil dit par quelle SOURCE le paquet part et refuse de conclure sur une passerelle anycast — le piege qui m a fait declarer muet un REJECT qui emettait bien ses RST. Ma premiere version rendait « pas anycast » quand inventory_rules manquait, c est-a-dire sur un hyperviseur ou l on a copie le seul fichier : precisement la ou le piege se produit. Une garde qui echoue doit crier, pas se taire. ET « NON CONCLUANT » EST UN RESULTAT : sur une source anycast et un silence, l outil ne tranche pas, il dit quoi faire — compter les paquets du cote qui refuse, ou sonder depuis une VM. TCP seulement, et c est dit : UDP n a pas de poignee. Valide contre le reel depuis ops-01 : trois cas sur quatre, le quatrieme attendant deux machines vivantes dans un meme tenant. make verifier : vert. make prouver : CONFORME, 53 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| 4beb7e0724 |
flux : l interne refuse a voix haute, la bordure se tait (P53)
Some checks are pending
verifier / verifier (push) Waiting to run
Decision de l exploitant : block vers l Internet, reject a l interieur, parce que
c est prudent. Ce n est pas le refus qui informe, c est CE QU IL FAIT AU SILENCE :
sous drop partout, un timeout voulait dire aucune machine, aucune route, ou une
politique. Quand la politique parle, il n en reste qu une.
nftables par hote policy drop + reject with icmpx type admin-prohibited
pare-feu Proxmox policy_in = REJECT (POLITIQUE_VM, source unique)
frontiere OPNsense block — INCHANGE, et c est la condition
admin-prohibited ET NON tcp reset : un RST est indiscernable d un port ferme sans
service. La chaine forward reste muette : elle porte le trafic qui TRAVERSE l hote,
et y repondre ferait parler cette machine au nom d une destination qui n est pas
elle.
POURQUOI C EST PRUDENT : l obscurite etait deja nulle a l interieur (chaque machine
porte un /etc/hosts qui liste ses voisines), et la bordure protege le reject —
rien d indeclare ne franchit le perimetre, donc il ne repond jamais a l Internet.
982 000 entrees par jour a la frontiere, dont 82 % un balayage VNC.
LA MESURE A CORRIGE LA MESURE, DEUX FOIS.
Ma preuve interdisait le litteral DROP et a fait echouer un code JUSTE : la
detection d une politique posee AU DATACENTER, qui est un garde-fou. Une preuve qui
interdit un mot au lieu de mesurer une propriete finit par accuser ce qu elle
devrait proteger.
Et l absence parlait deja : EHOSTUNREACH en 3,05 s pour une machine inexistante,
timeout a 6 s pour un refus de la frontiere. Mes deux erreurs de diagnostic ne
venaient pas du drop mais de ma SONDE — curl et bash /dev/tcp ecrasent les deux
dans un meme echec.
Applique : 6 VM en REJECT, 0 creee, 0 retiree. Rien ne se ferme.
Trois controles negatifs verifies.
make verifier : vert. make prouver : CONFORME, 53 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| 377462b141 |
voutes : une voute, une cle — separer avant de distribuer
Decision de l'exploitant : chaque runner est maitre de sa voute et en detient la cle. C'est ce qui rend un runner autonome, donc ce qui rend l'emancipation atteignable. Elle exigeait un prealable, mesure ce matin : UN SEUL mot de passe ouvrait les SIX voutes de la flotte, celle de la fabric comprise. DISTRIBUER AVANT DE SEPARER AURAIT ETE PIRE QUE LE STATU QUO : poser « la » cle sur chaque runner rendait chaque runner capable d'ouvrir les autres. Compromettre le plus petit locataire donnait les secrets de l'hebergeur. Cinq cles, une par ecosysteme, sous ~/.config/setops-vault-<depot>. Verification croisee apres rechiffrement : la diagonale, et rien qu'elle. L'ancienne cle maitresse n'ouvre plus aucune des six. UN SEUL MOT DE PASSE NE POUVAIT PLUS SUFFIRE, et pas pour la raison qu'on croit : `cloner_vm_debian.yml` charge la voute du TENANT puis celle de l'UNDERLAY dans la meme execution. `ANSIBLE_VAULT_IDENTITY_LIST` en porte plusieurs et les essaie toutes — un seul export suffit pour les 28 appels a ansible-playbook, sans en toucher un seul. La liste se derive dans scripts/voutes.py. CE QUI BORNE LE POUVOIR N'EST PAS LA LISTE MAIS LA PRESENCE DES FICHIERS. Sur le poste, toutes les cles sont la — c'est l'humain qui les detient toutes, et P03 lit les inventaires de tous les freres. Sur un runner, une seule existe. Le code est identique, le pouvoir ne l'est pas. DEUX PREUVES ONT DIT CE QUI MANQUAIT. P03 est tombee des la separation : elle lit les inventaires voisins, donc il lui faut leurs cles — c'est elle qui a etabli que la liste devait couvrir le voisinage. P16 s'est mise a SAUTER : sa garde ne connaissait que ANSIBLE_VAULT_PASSWORD_FILE. Une preuve sautee se lit trop facilement comme une preuve passee. COUT ASSUME : le chiffre et sa cle cohabiteront sur la meme machine des que les runners recevront la leur. Le pari tient parce que le perimetre est borne. Les six voutes sauvegardees avant rechiffrement. L'ancienne cle maitresse reste sur le poste : la retirer est une decision, pas un nettoyage. make verifier : vert. make prouver : CONFORME, 52 OK, 0 echec, 0 saute — sans ANSIBLE_VAULT_PASSWORD_FILE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on |
|||
| ef832d11d6 |
insemination : la cle d'amorcage, bornee au meme groupe que le flux
Le flux etait declare et applique ; il manquait l'IDENTITE. Le runner du SITE
atteignait la porte de ops-01 sans avoir de cle.
LE PIEGE COMPTE PLUS QUE LE CORRECTIF. La cle s'injecte au CLONAGE, et `creer-vm`
cree TOUTES les machines d'un tenant. L'injecter a chaque clonage aurait donne a
l'hebergeur un acces SSH a la flotte entiere de chaque locataire, en silence — ca
aurait defait a la couche IDENTITE ce que le pare-feu venait de borner a la couche
RESEAU. Le meme critere gouverne donc les deux : porter `serveur_ops_tenant`.
`SETOPS_CLES_AMORCAGE` est vide partout ailleurs. Elle S'AJOUTE a celle de
l'exploitant, elle ne la remplace pas : c'est l'humain qui arme.
La cle vient du PLAN DU SITE, pas du disque local : materialiser depuis le poste
et depuis le runner doit produire la meme VM.
DEUX COUCHES MANGEAIENT LES ESPACES. Proxmox rendait `SSH public key validation
error` — message muet sur la cause. Isole par un CONTROLE (rejouer sans la cle :
la tache passe), puis par la mesure de ce qui arrivait au module :
"sshkeys": "ssh-ed25519" <- le premier mot, rien d'autre
J'ai accuse `make` d'abord ; c'etait `ansible-playbook -e cle=valeur`, qui decoupe
AU SHLEX. D'ou l'environnement pour le transport et `-e '{...}'` en JSON pour
l'entree. (Un scalaire YAML plie ne produit pas non plus de saut de ligne.)
TROIS TESTS QUI NE GARDAIENT RIEN. `test_inventory_host` inscrit ses tests dans une
liste explicite ; mes deux nouveaux n'y etaient pas — definis, jamais joues. La
garde d'exhaustivite ajoutee en a trouve un TROISIEME le jour meme,
`test_etiquette_vlan_repli_et_vide_explicite`, jamais inscrit depuis sa creation :
inscrit, il levait un KeyError sur une fixture qu'il lisait mal. Un test non
inscrit est pire qu'un test absent : on croit l'avoir.
PREUVE, AVEC SON CONTROLE NEGATIF :
runner du SITE -> ops-01 ops-01 10.17.19.41/24 entre
runner du SITE -> 10.17.19.21 Connection timed out refuse
make verifier : vert. make prouver : CONFORME, 52 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
|
|||
| d3ba520500 |
insemination : declarer le lien, l'emettre d'un seul cote, et un test rouge
L'insemination avait un nom depuis ce matin ; elle n'avait pas de flux. Deux
declarations, aux deux bouts, et rien d'autre :
serveur_ops_site egress 22/tcp -> serveur_ops_tenant
serveur_ops_tenant ingress 22/tcp <- runner_site partage: true
ETROIT PAR CONSTRUCTION : il vise le GROUPE `serveur_ops_tenant`, qu'un ecosysteme
ne pose que sur une machine. Au socle, il aurait ouvert le SSH du site vers toute
la flotte du tenant.
L'en-tete disait « ce role n'entre JAMAIS chez un tenant ». Frontiere intenable :
`creer-vm` exige `_instance-requise`, et le runner du site avait deja du basculer
son symlink `instance` sur OPS-Chezlepro pour materialiser ses VM. Declarer ne cree
pas ce pouvoir — ca rend limitable un pouvoir qui s'exercait sans borne. Ce qui
reste interdit n'est pas une regle mais un FAIT : il n'a pas la voute du tenant.
LA REGLE EST EMISE D'UN SEUL COTE, et pas celui qu'on croit. Le paquet penetre le
pare-feu par la patte du SITE, pas par le transit : la regle appartient au cote
site du devis. L'emettre aussi depuis l'`ingress` du tenant aurait produit une
seconde regle sur la mauvaise interface — jamais evaluee, indiscernable d'une regle
utile. La declaration du tenant pose sa regle nftables, et elle seule :
ip saddr { 10.0.31.11 } tcp dport 22 accept
L'adresse DERIVE du plan du site. Ecrite a la main, elle aurait survecu au prochain
deplacement du runner sans bruit — le site a deja deplace ses machines le 08-25.
Plan de la frontiere : 2 objets a creer, 0 a retirer, 126 inchanges. RIEN D'APPLIQUE.
P41 APPLIQUEE AU PLAN DU SITE : `resoudre_flux` en avait besoin a son tour ; les
trois lecteurs demenagent dans `underlay` et `devis_opnsense` delegue.
DEUX GARDES ONT TRAVAILLE : P33 a refuse `ingress 22` sur un hote portant deja le
sshd du socle (reponse : `partage: true`, comme `serveur_backup`), et le devis a
refuse d'emettre vers un alias vide.
UN TEST ROUGE DEPUIS TROIS JOURS. `test_adressage_derive` construisait un site avec
un `index` — or un SITE n'en a pas depuis
|
|||
| 6fe62f74e1 |
creer-vm : prouver la materialisation sans entrer chez le tenant
`creer-vm` confirmait son succes en attendant une reponse SSH. Le runner du SITE
materialise le terrain de TOUS les tenants, mais la frontiere lui refuse d'entrer
chez eux — c'est le sens meme de leur isolation. La premiere VM qu'il a creee a
donc ete declaree en echec apres 600 secondes alors qu'elle tournait, avec
l'adresse exacte que le plan lui destinait :
Attente de SSH sur ops-01 ............ ECHEC: injoignable apres 600s.
LA TENTATION ETAIT D'OUVRIR LE SSH du runner vers tous les tenants. Ca aurait
repare la mesure en detruisant ce qu'elle protege : une machine capable d'entrer
chez chaque locataire est precisement ce que cette architecture refuse d'avoir.
L'agent invite repond sans rien ouvrir — l'API des hyperviseurs est deja le flux
par lequel la VM vient d'etre creee, donc qui peut la creer peut la voir naitre —
et il PROUVE DAVANTAGE. « Quelque chose ecoute sur le port 22 » ne dit ni quel
systeme a demarre, ni si cloud-init a pose la bonne adresse. Sur ops-01 :
10.17.19.41 portee sur asgard — Debian GNU/Linux 13 (trixie) 6.12.101
Ses echecs distinguent deux causes tres differentes : « la machine demarre mais
rapporte une AUTRE adresse — cloud-init, ou le pont sur lequel elle est posee »,
et « introuvable sur la fabric ».
ATTENDRE LA DISPONIBILITE A CHANGE DE MAIN. Guetter cloud-init et la liberation de
dpkg appartient a qui va CONFIGURER : `deployer` le fait desormais, la ou il se
contentait d'un `ping` unique. Il y gagne ce qu'il n'avait pas — attendre le verrou
APT, faute de quoi la premiere couche echouait dessus. Contrepartie assumee : un
nom d'hote errone patiente au lieu d'echouer vite ; echouer vite interdirait de
chainer creation et deploiement, le geste central d'une reconstruction.
P52 garde le couplage ferme. Deux controles negatifs verifies.
make prouver : CONFORME, 52 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 5d0f82792a |
portabilite : declarer ce dont le moteur depend — quatre defauts reveles par le runner
Premiere materialisation d'une VM de tenant depuis le runner du SITE. Elle a
echoue quatre fois d'affilee, sur quatre dependances que le moteur ne declarait
nulle part. Chacune fonctionnait chez le mainteneur pour une raison DIFFERENTE,
et aucune de ces raisons n'existe sur une autre machine.
1. VERSIONS DES COLLECTIONS. `requirements.yml` les nommait sans les epingler. Le
runner a recu `community.general` 13.3.0 quand le poste porte la 10.3.0 — et la
11 a retire les modules Proxmox de cette collection.
ERROR! couldn't resolve module/action 'community.general.proxmox_pool'
2. INSTALLATION EN AVANT. `ansible-galaxy` ne retrograde pas : declarer la 10.3.0
ne suffisait pas a defaire une 13.3.0 deja posee. `--force`.
3. EMPLACEMENT. Le `Makefile` pose `ANSIBLE_HOME ?= $(CURDIR)/.ansible` : sous
`make`, Ansible ne lit QUE `<moteur>/.ansible/collections`. Le role deposait
dans `<racine>/.ansible/collections` — a cote, jamais lu. Invisible chez le
mainteneur, ou les collections viennent du paquet systeme, toujours dans le
chemin quel que soit ANSIBLE_HOME. Et le garde-fou d'installation suivait le
DEPOT des archives : changer la destination ne le declenchait pas. Il mesure
desormais la destination — meme piege qu'en 2026-08-24, deplace d'un cran.
4. BIBLIOTHEQUES PYTHON. Le venv recevait `ansible-core` et `pyyaml`, ecrits en
dur. Les modules Proxmox tournent `delegate_to: localhost` et exigent
`proxmoxer` SUR LE CONTROLEUR.
La bibliotheque Python proxmoxer est absente
Ne d'ici : `requirements-python.txt`, avec sa frontiere ecrite — il ne declare
QUE ce qui tourne sur le controleur. `python-ldap` et `psycopg2` s'executent
sur leurs cibles ; le poste du mainteneur ne les a pas et la flotte se deploie,
ce qui prouve que la ligne est au bon endroit.
P51 garde les trois faiblesses de cette famille : une dependance utilisee sans
etre declaree (le defaut du 2026-08-24, `ansible.posix`), une dependance declaree
sans version, et `proxmoxer` absent alors que des playbooks Proxmox existent.
Quatre controles negatifs verifies.
RESULTAT : ops-01 materialisee par le runner du site. L'agent invite rapporte
`eth0 10.17.19.41/24`, Debian 13 trixie — l'adressage derive du plan, applique.
Un seul executant masque ces ecarts indefiniment ; un second les revele tous en
une soiree. Meme mecanique que le second tenant en aout.
make prouver : CONFORME, 51 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 3fa6e4c3e1 |
collections : epingler les versions — le runner echouait la ou le poste reussissait
Premiere materialisation de VM depuis le runner du SITE :
ERROR! couldn't resolve module/action 'community.general.proxmox_pool'
Meme depot, meme playbook, meme plan que chez le mainteneur. La difference tenait
a une seule chose que le moteur ne disait pas : la VERSION de ses collections.
Le poste porte `community.general` 10.3.0. Le runner, monte un jour plus tard, a
recu la 13.3.0 — et la version 11 a RETIRE les modules Proxmox de cette collection
(ils vivent desormais dans `community.proxmox`). `requirements.yml` nommait ses
collections sans dire lesquelles : chaque machine installait donc ce qui etait
courant le jour de son montage. Une dependance non epinglee n'est pas une
dependance, c'est un pari sur l'etat d'Internet a la date du deploiement.
TROIS CORRECTIONS.
`requirements.yml` epingle les trois collections aux versions eprouvees.
`serveur_ops` installe avec `--force`. Sans lui, ansible-galaxy laisse en place une
version SUPERIEURE a celle demandee : il ne retrograde pas. Un poste peut etre en
avance, pas seulement en retard, et le depot doit faire autorite dans les deux sens.
P51 garde les deux faiblesses de ce fichier — celle du 2026-08-24, une collection
utilisee sans etre declaree (`ansible.posix`), et celle d'aujourd'hui, declaree sans
version. La preuve ne lit que les fichiers de TACHES : un `defaults/main.yml` porte
`net.ipv4.ip_forward`, qu'un motif trop large prend pour un module.
MIGRATION CONNUE, PAS FAITE : passer les quatre modules Proxmox a
`community.proxmox` permettra de suivre `community.general` au-dela de la 11.
make prouver : CONFORME, 51 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 3b65384b0f |
frontiere : le devis sait desormais refuser SANS consigner
L'outil ne savait qu'AUTORISER — `"action": "pass"` etait en dur dans l'emetteur. Une regle de silence ne pouvait donc pas naitre du depot, et j'en avais pose deux a la main sur le boitier : exactement ce que ce projet refuse. CE QUI L'A MOTIVE. Le journal de la frontiere ecrivait 982 000 entrees par jour, dont 82 % un balayage Internet contre le port VNC et le reste du bavardage de decouverte du reseau local. Sa fenetre utile etait tombee a QUARANTE-QUATRE SECONDES. J'y ai cherche la trace d'un flux du site vers les hyperviseurs, je n'ai rien trouve, et j'en ai conclu a tort qu'aucune regle ne bloquait. Un journal noye ment aussi surement qu'un journal mort. Mesure apres declaration : ~20 700/jour. Une regle de silence ne change AUCUN comportement : ce qu'elle vise etait deja refuse par le defaut. Elle ne supprime qu'une trace que personne ne lira. TROIS PIECES. `cle_regle` accepte une action sans changer d'un octet la cle des regles `pass` deja posees. L'ajout naif d'un champ les aurait toutes detruites pour les recreer a l'identique, sur la frontiere, en production. Le plan l'a confirme : 7 a creer, 0 a retirer, 121 inchangees. `_corps_regle` lit l'action, la consignation et la SEQUENCE depuis le devis. La sequence est ce qui rend un `block` sur : OPNsense evalue en `quick`, donc un blocage large emis avant les `pass` fermerait courrier, web et acces distant. `devis_opnsense` lit `opnsense_silences` et en fabrique regles et alias, motif compris — une regle `block` muette sans raison ecrite est indiscernable d'un oubli. P50 garde ces deux dangers. Controles negatifs verifies : un silence en sequence 1 echoue, un silence sans motif echoue. Carte : 28 pieces d'audit (P48 l'avait vu juste). make prouver : CONFORME, 50 OK, 0 echec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 561c034eb4 |
flux : P49 — le registre des flux avait derive sans bruit
Some checks failed
verifier / verifier (push) Has been cancelled
`docs/registre-flux.md` est GENERE depuis les `roles/*/meta/flux.yml`, et c'est le document qu'un humain lit pour savoir ce que le pare-feu laisse passer. Son EXISTENCE etait verifiee depuis longtemps ; sa FRAICHEUR ne l'etait pas. Il avait derive : la garde d'administration y portait encore `10.0.0.0/24` alors que le reseau d'administration vaut `10.17.0.0/24`, deux flux `client_resolveur` ajoutes depuis n'y figuraient pas, et un hote manquait des listes de sources. Un lecteur y aurait lu un pare-feu qui n'existe plus. L'inventaire avait deja sa garde — P03, le diff-vide du plan. Le registre des flux est le meme genre d'artefact : genere, versionne, lu par un humain. Il lui manquait la meme. `generer_registre` etant une fonction PURE, P49 la rejoue en memoire et compare — une preuve qui repare ce qu'elle mesure ne mesure plus rien. Controle negatif ideal, et il ne s'invente pas : la version commitee elle-meme. Restauree, la preuve echoue ; regeneree, elle passe. CE QUE CETTE DECOUVERTE CORRIGE AUSSI DANS MA TETE. J'avais decrit le symptome comme « le runner salit ses propres clones » — une contradiction structurelle entre un depot-clone et un repertoire de travail. C'etait faux, et la question de l'exploitant l'a mis au jour. Regenerer un artefact DOIT produire un diff quand les sources ont change ; ce qui manquait n'etait pas une architecture, c'etait une garde. Un symptome observe depuis un seul endroit ressemble toujours a une propriete de cet endroit. Ce commit emporte aussi la regeneration elle-meme : le registre du moteur, et les quatorze fichiers nftables de Chezlepro, remis en accord avec leurs sources. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 9e8679215d |
carte : P48 — l'index du mainteneur ne peut plus mentir
Some checks are pending
verifier / verifier (push) Waiting to run
`docs/carte-set-ops.md` est l'index du MAINTENEUR : l'ordre de lecture du corpus, et surtout le catalogue des MECANISMES TRANSVERSES avec, pour chacun, OU IL VIT DANS LE CODE. Son but est ecrit en toutes lettres : « ne plus re-deterrer ce qui existe ». Elle n'avait aucune garde, alors que `catalogue-services.md` a la sienne depuis P38. Ses sept chiffres etaient faux — 54 roles annonces contre 60, 34 documents contre 38, 15 pieces d'audit contre 27, 70 decisions contre 78. Le defaut couteux n'est pourtant pas la. C'est le POINTEUR MORT : la carte dit ou vit un mecanisme, quelqu'un ne l'y trouve pas, et le reimplemente a cote — exactement la panne qu'elle existe pour prevenir. Aucun de ces nombres ne fait travailler personne ; mais un document dont les faits verifiables sont faux cesse d'etre consulte, et c'est alors ses pointeurs qu'on perd. P48 verifie les deux : 84 chemins cites existent, et 7 chiffres correspondent a la mesure. Controle negatif verifie sur les DEUX moities — un chiffre fausse, un pointeur casse, la preuve echoue dans les deux cas. QUATRE DISTINCTIONS ont du etre ecrites pour qu'elle ne soit pas fausse dans l'autre sens : un gabarit de nom (`preuve-<date>.md`) decrit une forme, pas un fichier ; un chemin hors depot (`~/.config/setops-vault-pass`) vit sur le poste de l'exploitant, et c'est tout l'interet de la doctrine des voutes ; un fragment (`meta/acces.yml`) vaut comme SUFFIXE, parce qu'un index se lit ainsi ; et un artefact GENERE (`hosts.yml`) n'a pas a exister dans le moteur. Les quatre sont nommees dans le code plutot que sautees en silence. Le tableau « Le depot en chiffres » remplace les comptes en prose : ce qu'on n'entretient pas, on ne l'affirme pas — ou bien on le fait recompter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 87716cefc7 |
genome : la forge du SITE fait autorite (D-81), et make genome-etat le verifie
Some checks are pending
verifier / verifier (push) Waiting to run
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un MIROIR. Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu Proxmox. UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE. `make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas encore faits sont une autre forme de retard. Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite. Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps d'inactivite. Les deux cibles reessaient. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 6a5a49f924 |
site : le runner travaille, et le genome remonte chez lui
Some checks are pending
verifier / verifier (push) Waiting to run
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur, les plans des trois tenants, les collections hors ligne, et la voute de l'underlay deposee CHIFFREE. Le site a son runner. DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE. `serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` — la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le lien `instance`, detient sa voute. Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais que le runner pilote QUELQUE CHOSE, sans prescrire quoi. Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait `instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le suit croit avoir trouve une instance. LE CERTIFICAT COUVRE LES NOMS DU SERVICE. `client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le clonage du genome a bute dessus : « certificate subject name (site-forge-01.genese.internal) does not match target hostname 'forge.genese.internal' ». Le nom declare `expose:` etait publie partout — plancher, zone DNS — et couvert nulle part. Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs serait une usurpation, pas une commodite. `make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS. La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT, puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement. Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un seul. Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours `main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le dossier des depots freres contient un espace, d'ou `argv` et non `cmd`. Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots verifies. Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses defauts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| e5841ea684 |
dns : quatre zones inverses pour le site, et rien de plus
Some checks are pending
verifier / verifier (push) Waiting to run
Le site ne servait aucun PTR. `serveur_powerdns_zone_inverse` derivait d'un supernet /16 — la forme d'un TENANT, qui tire tout de son index. Un site ne derive pas : il declare plusieurs /24 et n'a pas de supernet unique, si bien que la derivation rendait une chaine vide et qu'aucune zone n'etait generee. Le site revendique desormais exactement ce qu'il occupe : 31.0.10.in-addr.arpa 32.0.10.in-addr.arpa 33.0.10.in-addr.arpa 34.0.10.in-addr.arpa Revendiquer `0.10.in-addr.arpa` d'un seul geste aurait ete plus simple et faux : cette zone couvre aussi la frontiere, le transit et les hyperviseurs, qui ne sont pas a lui. Une autorite qu'on s'attribue sans l'exercer est une panne differee — le resolveur repondrait NXDOMAIN pour des adresses qu'un autre sait nommer. LA DERIVATION VIT DANS UN FILTRE (`zones_inverses`) parce qu'elle a DEUX appelants : `serveur_powerdns` ecrit ces zones, `serveur_resolveur` les delegue a l'autoritatif. Deux calculs separes finiraient par diverger, et la divergence ne se verrait qu'au premier PTR interroge. Le nom d'une zone dit sa profondeur — trois etiquettes numeriques valent un /24, deux valent un /16 — et le modele en deduit seul la forme du PTR. DEUX CHEMINS MORTS TROUVES EN ROUTE. Les zones etaient servies, et personne ne les demandait. `serveur_resolveur` deleguait la zone directe par une `stub-zone` mais pas les inverses : `dig -x` rendait vide depuis les cinq machines alors que la meme requete posee directement a l'autoritatif repondait juste. Un service correct derriere un chemin que rien n'emprunte. Puis, les stubs poses, Unbound repondait toujours NXDOMAIN avec le drapeau `aa` — une reponse AUTORITAIRE, sans jamais consulter le stub. Il embarque des `local-zone` pour tout l'espace RFC1918 inverse. `nodefault` n'y change rien : ce mode n'agit que si le nom correspond EXACTEMENT a une zone par defaut, et la sienne est `10.in-addr.arpa`, le /8 entier. C'est `transparent` qui laisse la requete suivre son cours — pour nos quatre zones seulement, la ou `unblock-lan-zones` aurait ouvert tout l'espace prive. Rien ne distinguait ce blocage d'une absence : le meme NXDOMAIN qu'un nom qui n'existe pas. AUSSI : les zones inverses sont desormais validees par `named-checkzone` comme la directe (un fichier mal forme etait refuse en silence par PowerDNS), et une zone qu'un ecosysteme cesse de revendiquer est retiree du repertoire. VERIFICATION. Les cinq PTR resolvent depuis les cinq hotes par le resolveur, les huit noms directs par le plancher ET par le DNS, et les deux roles sont idempotents (changed=0). P47 evalue la derivation sur cinq cas — un site a quatre zones, un tenant a une, deux machines d'un meme /24 qui n'en font qu'une, aucune adresse, une adresse illisible. Controle negatif verifie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| a74e35bf21 |
site : le plancher survit au redemarrage, et la zone dit les vraies adresses
Some checks are pending
verifier / verifier (push) Waiting to run
Le decoupage du site en quatre zones a deplace cinq machines. Ni le plancher
/etc/hosts ni la zone DNS n'avaient suivi. Quatre defauts, tous dans le moteur.
LE PLANCHER ETAIT EFFACE A CHAQUE DEMARRAGE — ET LE PREMIER CORRECTIF N'EN
ETAIT PAS UN.
`hosts_statiques` posait `99-setops-hosts.cfg` avec `manage_etc_hosts: false`,
pendant que `cloud_init` posait `99_setops.cfg` avec `true`. Dans `cloud.cfg.d`
l'ordre est LEXICAL et le dernier gagne : `-` vaut 0x2D, `_` vaut 0x5F. On a
donc retire la cle de `cloud_init` — le role qui POSSEDE le fichier decide —
puis renomme notre fragment `zz-` pour passer apres le `99_chezlepro.cfg` du
gabarit dore.
Et ca ne suffisait toujours pas. Redemarrage d'epreuve : plancher encore efface.
La cause reelle est ailleurs — Proxmox inscrit `manage_etc_hosts: true` dans la
USER-DATA de son lecteur cloud-init, et la user-data prime sur `cloud.cfg.d`
tout entier. Aucun fragment ne pouvait gagner ; renommer pour parler en dernier
ne servait a rien, le dernier mot n'appartenant pas a ce repertoire.
Ce que cloud-init regenere, il le regenere depuis `hosts.debian.tmpl` — le
gabarit le documente lui-meme. `hosts_statiques` le pose desormais avec le MEME
contenu que /etc/hosts, et une garde compare les deux a chaque passage.
Redemarrage d'epreuve : les neuf entrees sont la.
La garde precedente affirmait « conforme » en mesurant l'ordre lexical — vrai,
et sans rapport avec ce qui se passait. Une garde qui mesure la mauvaise chose
est pire qu'aucune.
LA ZONE DNS NE PUBLIAIT PAS LES NOMS DE SERVICE.
`forge.genese.internal` et `pki.genese.internal` — des noms que les certificats
portent et que les clients appellent — n'avaient aucun enregistrement. Seul le
plancher savait les resoudre. Trois causes empilees :
- le plan du site coupait `serveur_powerdns_publier_expositions`, au motif que
« le site n'a pas d'edge » : ca confondait PUBLIC et EXPOSE ;
- `expositions_des_applications` rendait `domaine: None` faute de
`domaines.yml`, et le modele de zone ecarte les expositions dont le domaine
n'est pas la zone. Repli ajoute, symetrique de celui deja ecrit pour `edge` :
sans domaine public declare, le domaine est celui que porte le FQDN ;
- `serveur_powerdns` exigeait les deux registres et echouait si `domaines.yml`
manquait — le meme tout-ou-rien que `hosts_statiques` a corrige le meme jour.
Puis `named-checkzone` a refuse la zone : `dns.genese.internal` heritait d'un
CNAME par defaut du role ET d'un A par exposition. La garde a bien joue son role
— elle a arrete une zone cassee avant qu'elle soit servie. Le plan l'emporte
desormais sur le defaut du role.
Un service ne doit pas dependre d'un plancher pour etre joignable : le plancher
est un filet, pas le sol.
VERIFICATION. Les huit noms — cinq machines, trois services — resolvent vers les
bonnes adresses depuis les cinq hotes, par le plancher ET par le DNS, et le
plancher survit au redemarrage.
P46 refuse desormais deux choses : plus d'un role ecrivant `manage_etc_hosts`,
et l'absence du gabarit maitre. Controle negatif verifie.
RESTE NOMME, PAS CORRIGE : la zone INVERSE. `serveur_powerdns_zone_inverse`
derive d'un supernet /16 — la forme d'un tenant. Un site declare plusieurs /24
et n'a pas de supernet unique : aucune zone inverse n'est generee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| ac48b7650b |
proxmox : le pare-feu ne s'arme que dans le SDN — et 45e preuve
Some checks are pending
verifier / verifier (push) Waiting to run
Le decoupage du site en quatre zones a revele un defaut qui dormait dans le moteur. `firewall=1` sur l'interface d'une VM fait passer tout le trafic ponte par conntrack. Sur un VNet SDN c'est sans consequence : en EVPN le routage inter-VNet se fait dans le VRF, SUR LE NOEUD, et le flux ne quitte jamais l'hyperviseur. Sur un pont classique route par une frontiere externe, deux VM du MEME noeud dans deux VLAN differents ne se parlent qu'en EPINGLE : la trame sort par le lien physique, la frontiere la route, elle revient sur le meme pont. La meme table conntrack voit alors les deux moities de la connexion, classe le retour INVALID, et PVEFW-FORWARD le jette. La mesure a tranche : ops(asgard) -> pki(asgard) 0/8 dns(gandalf) -> cache(gandalf) 0/8 ops(asgard) -> forge(vishnu) 6/8 dns(gandalf) -> pki(asgard) 6/8 ops(asgard) -> cache(gandalf) 8/8 dns(gandalf) -> forge(vishnu) 8/8 Toutes les paires intra-noeud echouent, toutes les paires inter-noeuds passent. Douze tentatives faisaient monter le compteur `ctstate INVALID` de +112 sur asgard et +116 sur gandalf ; `firewall=0` pose, il ne bouge plus — 0 sur les deux, pour le meme trafic. Le defaut se deguise en panne reseau : la poignee TCP ABOUTIT, et ce sont les paquets de DONNEES qui disparaissent. L'AC le disait dans son propre journal (`TLS handshake error ... i/o timeout` : elle accepte et attend un ClientHello qui n'arrive jamais). Ecartes un par un, par la mesure : regles identiques champ par champ, alias corrects, assignation des interfaces confirmee, ARP et routes saines, IPS desactive, shaper vide, NAT source limite a `wan`, MTU a 1500 de bout en bout — et la taille sans effet, 100 octets se perdant comme 1460. L'INTENTION DECLAREE NE SUFFIT PAS : un tenant declare `proxmox_clone_parefeu_interface: true` avec `proxmox_clone_pont: vmbr1` comme valeur PAR DEFAUT, chaque hote la remplacant par son VNet derive. L'hote qui retombe sur `vmbr1` naitrait arme sur un pont classique. `cloner_vm_debian.yml` croise donc l'intention avec le pont REELLEMENT utilise, et le dit quand il desarme — un desarmement muet serait le meme piege, en silence. P45 EVALUE l'expression du playbook sur quatre cas plutot que d'en lire le texte, dont un qui DOIT rendre vrai : sans lui, une expression constamment fausse passerait la preuve sans rien garantir. Controle negatif verifie — le drapeau remis a plat fait echouer la preuve. Ce commit emporte aussi le desarmement d'`unattended-upgrades` dans `common_packages`, jusqu'ici applique sur les machines mais pas versionne : le verrou dpkg tenu 48 minutes par une vague de maj automatiques a coute deux deploiements. Le site a revele ce defaut parce qu'il a ete le premier a porter plusieurs zones. Ce n'est pas une particularite du site : un tenant derive les siennes du meme principe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 1a7c042e5b |
site : quatre zones d'autorite, et l'ordre inscrit dans les integrations
Le site n'est plus un /24 plat. Une zone par nature d'autorite — pilotage, autorite,
genome, service — chacune son VLAN et sa patte sur la frontiere. L'inversion corrigee,
mesuree : les cinq VM du site n'avaient AUCUN filtrage est-ouest, contre policy_in=DROP
sur une machine de tenant. La plus autoritaire etait la moins protegee.
Filtrage nord-sud par choix de l'exploitant : un seul point de police, un seul devis.
90 -> 117 regles. Chaque flux `flotte` produit une regle par zone SOURCE, destination
nommee — ce qui etait gratuit devient police.
L'ORDRE FAIT PARTIE DE L'INTEGRATION. client_pki tournait en parallele sur tous les hotes ;
sur celui qui porte l'autorite il recharge step-ca, et les quatre autres echouaient dans
cette fenetre sur `TLS handshake timeout` — un message qui accuse le reseau. Chaque
integration declare desormais sa dependance dans meta/integration.yml, les playbooks en
sont le miroir genere, et P44 refuse l'ecart (4 controles negatifs). `serveur: ~` est une
reponse valable : client_metrique pose un exportateur qu'on vient LIRE.
Autres defauts du meme soir :
- le plancher /etc/hosts venait APRES le premier apt, qui vise le cache par son NOM :
boucle fermee des que les adresses changent. Il ne s'installe pas, il rend installable.
- dns_amorcage ecrit en dur a eu tort deux fois ; il se derive de serveur_resolveur.
- l'ACL du resolveur derivait d'un seul sous-reseau : trois zones refusees sur quatre.
ET UNE ERREUR A MOI : j'ai diagnostique un trou noir de MTU et declare 1450. Faux — pas de
VXLAN sur ce chemin, tout est a 1500 de bout en bout. Ma mesure etait reelle, mon
interpretation non : je venais de debrancher la carte a chaud, et chaque `ip link set mtu`
reconfigurait l'interface. C'est la reconfiguration qui debloquait, pas la valeur.
44 preuves vertes, ansible-lint profil production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 158c3b314a |
preuve : P43 — la frontiere voit-elle les machines du site ?
Couvre ce qui a failli coûter 36 objets ce soir : le devis de la frontiere avait cesse de voir le site et proposait de retirer tous ses alias et toutes ses regles. Harnais vert, lint vert — seule la lecture manuelle du plan avant application l'a attrape. La premiere version etait inutile, et c'est instructif : elle lisait le plan par `devis_opnsense._machines_du_plan_site()`, la fonction meme dont la panne etait a detecter. Eprouvee sur la regression reelle, elle SE TAISAIT — les deux voyaient le vide, et elle concluait « rien a prouver ». Une preuve qui partage la source de ce qu'elle verifie ne verifie rien. La version retenue lit plan/serveurs.yml et plan/applications.yml DIRECTEMENT et confronte au devis produit. Eprouvee sur la regression reelle : elle refuse et nomme la cause. 43 preuves vertes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| ea67b75a15 |
dns : le site resout chez lui, et le socle cesse de le defaire
Mise au point de l'exploitant : la frontiere n'a pas de service DNS actif et gere. Un
Unbound y tourne — il repondait, ce qui m'a induit en erreur — mais un processus n'est pas
un service. La delegation de zone que j'y avais posee est retiree.
Et le site a son propre DNS : `dns_amorcage` pointait encore sur la frontiere, valeur d'un
moment ou site-dns-01 n'existait pas. Elle vaut desormais 10.0.3.51.
Deux defauts revelés au passage :
- devis_opnsense lisait encore underlay.machines(), vide depuis le deplacement du plan.
Il proposait de RETIRER 36 objets — tous les alias et regles du site. Aucune preuve ne
couvre ce devis : c'est le plan avant application qui l'a attrape.
- le socle defaisait la bascule de client_resolveur a chaque passage. Sa garde ne
protegeait que l'hote du resolveur (127.0.0.1). Invisible chez un tenant, ou
dns_amorcage vaut l'adresse du resolveur : la coincidence masquait le defaut. Ca ne
s'est vu qu'en retirant la regle de pare-feu — les cinq machines ont perdu la
resolution d'un coup.
L'amorcage ne s'applique plus que si rien de sense n'est en place. Verifie : socle
`changed=0`, les cinq machines restent sur 10.0.3.51.
42 preuves vertes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 43666cff6e |
site : un SITE a bien un plan — et huit defauts que lui seul pouvait reveler
J'ai repete toute la journee qu'un SITE n'est pas un plan. C'etait faux : j'en
reconstruisais un morceau par morceau dans underlay.yml sans le nommer. Ce qui est vrai,
c'est qu'un site ne DERIVE pas — il declare ses adresses parce qu'il EST le terrain — et
ne passe donc pas par `instancier`. Mais ne pas deriver n'est pas ne pas avoir de plan.
SITE-Chezlepro/plan/{10-intrants,serveurs,applications}.yml ; underlay.yml ne garde que
la fabric. Ce qui est MESURE d'un cote, ce qui est VOULU de l'autre. Les groupes viennent
du registre des applications, les gardes ont suivi le plan (4 controles negatifs).
Huit defauts, tous invisibles chez un tenant parce qu'il a toujours tout :
- client_pki codait `infra-pki-01` EN DUR — vrai par coincidence de nomenclature ;
- aucun moyen pour un service non-root de lire la cle (Forgejo tourne en `git`) ;
- le certificat, PUBLIC par nature, restait en 0600 ;
- l'unite Forgejo n'avait pas d'ExecReload : un cert renouvele aurait ete servi perime ;
- le role ne savait pas servir TLS lui-meme (il y avait toujours un edge) ;
- le cert ne couvrait pas le nom de SERVICE, faute d'`expose:` ;
- les registres se chargeaient en tout-ou-rien : sans domaines.yml, plancher vide ;
- le flux declarait 3000 en dur.
Le message accusait presque toujours autre chose : le DNS quand c'etait un nom faux, une
permission de fichier quand c'etait un port privilegie, rien du tout quand le plancher
s'ecrivait vide.
ROOT_URL est gravee dans les URL de clonage : la forge sert desormais sur 443, avec
CAP_NET_BIND_SERVICE, et son URL n'a plus de port.
Verifie depuis le reseau : Verify return code 0, https://forge.genese.internal/ -> 200.
42 preuves vertes, flux coherents (34 roles, 92 flux).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| add94f2cbd |
underlay : un SITE n'a pas d'index — le vestige est retire
`index: 17` ne disait pas « voici mon adressage » : un site ne derive rien, ses machines vivent sur un reseau de fabric hors de toute derivation. Il disait UNE seule chose — quel supernet de tenant est le mien — pour autoriser un reseau du site a en occuper la bande basse (D-77). Un reste de la coincidence hebergeur<->tenant chez Chezlepro, qui est les deux a la fois ; ailleurs il aurait fallu inventer un index a un hebergeur qui n'heberge pas son propre ecosysteme. L'exception se declare desormais sur le RESEAU qui en a besoin, et elle NOMME le tenant dont elle occupe la bande basse (`bande_basse_de: OPS-Chezlepro`). Plus vrai : un seul reseau est concerne. Plus verifiable : le nom se resout dans le registre `tenants:`, donc une faute de frappe est refusee au lieu de passer. Quatre controles negatifs, tous refuses — tenant inconnu, exception absente, debordement sur la bande des zones, et le mauvais tenant nomme. 42 preuves vertes, les quatre devis du panneau OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |