2026-08-08 07:20:56 -04:00
|
|
|
# Les devis de service : ce qui tourne correspond-il à ce qui est déclaré ?
|
|
|
|
|
|
preuve : P34 — chaque document declare son lecteur (D-74)
La refonte de ce matin posait une convention. Une convention qu'on n'outille
pas tient tant que quelqu'un y pense : c'est le raisonnement de D-70, applique
au corpus documentaire.
Etat de depart mesure : 2 documents sur 34 declaraient leur lecteur. Les 32
autres disaient leur SUJET — ce qui avait enfoui le runbook de reprise le plus
utile du depot au §6 de autorisation.md.
Les 38 le declarent desormais, lecteur determine document par document et non
colle au gabarit : l'exploitant (devis, migration de tenant, cycle de vie,
gabarit d'or), le mainteneur (conceptions, registres, carte), le lecteur
externe (ecosysteme-chezlepro), l'agent IA (MISE-A-JOUR-CODEX-CLAUDE).
Deux exemptions DERIVEES, pas listees — un chemin en dur aurait vieilli a la
premiere page ajoutee : un document qui s'annonce genere, et un fragment sans
titre. Les 13 exemptes verifies un par un ; aucun document ecrit a la main
n'est exempte par accident. La preuve ne lit que l'EN-TETE, ce qui empeche
frontiere-opnsense.md et plan-et-generation.md — qui parlent de generation
dans leur corps — d'etre exemptes a tort.
Eprouvee dans les deux sens. Elle a echoue seule des sa premiere execution en
nommant deux documents que mon inventaire avait manques (docs/audit/). Puis
test negatif delibere : declaration retiree de meta-classe.md -> ECHEC la
nommant ; restauree -> OK.
Ce qu'elle ne teste pas : que le lecteur declare soit le BON. Ca se juge en
revue ; elle garantit qu'on a du y penser.
P01–P34. Comptes perimes corriges au passage (AGENTS.md et devis-services.md
annoncaient encore 30 preuves).
Verifie : prouver.py 0 (34 OK), plan-recette inchange.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 07:53:04 -04:00
|
|
|
> **Pour qui :** l'**exploitant** qui veut savoir si ce qui tourne correspond à ce qui est déclaré.
|
|
|
|
|
|
2026-08-08 07:20:56 -04:00
|
|
|
> **Instrument ajouté le 2026-08-08**, après une série de défauts qu'aucun test n'avait
|
|
|
|
|
> signalés. Lecture seule — il ne modifie rien.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
make identite-plan # realm, fédération, mappeurs, politique, comptes
|
|
|
|
|
make certificats-plan # certificats sur disque contre certificats réellement servis
|
2026-08-08 07:33:49 -04:00
|
|
|
make expositions-plan # chaque `expose:` du plan répond-il, depuis l'edge et depuis le poste
|
devis PostgreSQL et courriel : la serie des devis de service est complete
PostgreSQL — rend visibles deux defauts deja vecus ici : un reseau ECRIT dans
pg_hba au lieu d'etre derive, et une ligne host en clair la ou il faut hostssl
(verrou qui saute sans bruit, les clients verify-full continuant de marcher).
Interroge pg_settings, jamais le fichier : un grep de postgresql.conf annonce
ssl_cert_file=snakeoil alors que le serveur sert le certificat de l'AC — la
valeur vient d'un conf.d que le grep ne voyait pas. L'instrument etait
incomplet, pas la configuration.
Courriel — chaque maillon interroge la ou il dit la verite : postmap -q pour
la resolution LDAP de Postfix, doveadm user pour celle de Dovecot (le maillon
exact ou la livraison avait bloque), vraies conversations SMTP/IMAP. Verifie
aussi qu'une adresse INEXISTANTE ne resout pas — sinon la boite est un
fourre-tout et le devis ne mesure plus rien.
Trouve immediatement une divergence reelle : Dovecot connait la boite de
sysadmin@chezlepro.internal, Postfix ne sait pas y router (query_filter
(mail=%s), et l'attribut mail porte sysadmin@chezlepro.ca). Collision de
roles : une identite ne porte qu'une adresse mail et on lui en demande deux —
notification joignable hors du systeme, et cle de routage local. Arbitrage a
rendre avant correction.
Tests negatifs : PostgreSQL 3 ecarts nommes, code 1. Une variable morte
trouvee dans une branche que le cas nominal n'emprunte jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:53:49 -04:00
|
|
|
make postgresql-plan # chiffrement imposé, et à quels réseaux
|
|
|
|
|
make courriel-plan # Postfix → LDAP → LMTP → Dovecot → IMAP
|
devis : sixieme instrument — « ce qui n'est pas declare est-il refuse ? »
devis_expositions pose la question positive (une exposition declaree
repond-elle). Celle-ci manquait, et ce n'est pas la meme : un pare-feu peut
servir tout ce qu'on lui demande ET laisser passer tout le reste.
Rien n'y est saisi. Les cibles sont les ports REELLEMENT en ecoute (sonder un
port ferme ne prouverait rien du pare-feu) ; la politique attendue est lue
dans devis_opnsense.py, la source qui configure la frontiere.
scripts/sonde_tcp.py refuse de conclure : un CONTROLE (adresse ou personne
n'ecoute) avant tout verdict — s'il repond, le releve est declare NUL. Et
etablir n'est pas livrer : banniere, sinon requete HTTP, sinon poignee TLS ;
sans reponse le verdict est AMBIGU, jamais « ouvert ».
Deux pieges rencontres en le construisant, corriges et commentes sur place :
la sonde « tenant » s'executait sur le poste (dans un play connection: local,
delegate_to herite de cette connexion) ; et 2 s de lecture faisaient ressortir
un Postfix sain en AMBIGU (postscreen retarde sa banniere expres) — porte a
8 s, compense par du parallelisme.
Premier verdict, frontiere en l'etat : 38 ecarts, dont collab-01:9980 qui
livre vraiment un HTTP 200 depuis le poste. Releve sortant declare NUL.
Verifie : ansible-lint Passed, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:03:28 -04:00
|
|
|
make frontiere-mesurer # ce qui n'est pas déclaré à la frontière est-il refusé ?
|
mtu : le 1450 de la zone n'atteignait pas les invites
Question de l'exploitant : « je ne vois nulle part un MTU a 1450 ». Fondee.
Le 1450 existait bien — MTU_OVERLAY_DEFAUT dans underlay.py, dont P23 derive
sa garde (transport >= overlay + 50), pose par le devis SDN sur chaque zone,
et verifie sur le cluster. Mais il ne se propage pas a la carte de l'invite :
les 14 VM tournaient a 1500, et cloner_vm_debian.yml n'avait aucune occurrence
de `mtu`. La VM emettait donc des trames que son propre chemin ne pouvait pas
encapsuler — la panne que le registre des flux appelle « la plus couteuse a
diagnostiquer ».
Invisible parce que les 14 VM vivent sur asgard : deux VM du meme hyperviseur
communiquent par le pont local, sans encapsulation. Le defaut serait apparu au
premier eclatement de la flotte — c'est-a-dire quand il faudra heberger les
deux tenants ensemble.
Corrige en DEUX endroits, avec `mtu=1` (« herite du pont ») plutot que 1450 en
dur : juste en SDN comme hors SDN, et encore juste le jour des trames jumbo.
Le GABARIT le porte — proposition de l'exploitant, meilleure que la mienne :
elle couvre les clonages qui ne passent pas par le playbook. Et le playbook le
repose a chaque clone, parce qu'un gabarit se RECAPTURE et que ce qui n'est pas
versionne se perd en silence.
make mtu-mesurer (7e devis) rattache chaque hote a sa zone par son pont derive
et lit le MTU attendu dans devis_sdn.py. Ecart trouve sur 14 hotes du premier
coup, puis confirme corrige.
ET J'AI CASSE LA FLOTTE EN CORRIGEANT : applique aux cartes de VM EN MARCHE,
le changement detache et rebranche la carte sans que l'invite reconfigure son
interface. 0/14 joignables pendant trois minutes. L'agent qemu, independant du
reseau invite, a permis de constater et de redemarrer par l'API. Dans le
playbook la meme tache s'execute AVANT le demarrage du clone : aucun risque.
Sur une VM en service : poser la config, puis redemarrer — une seule d'abord.
Verifie : mtu-mesurer CONFORME 14/14 a 1450, prouver.py 35 OK, ansible-lint
production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:54:03 -04:00
|
|
|
make mtu-mesurer # l'invité porte-t-il le MTU de sa zone SDN ?
|
devis : make versions-mesurer — l'ecart avec l'amont devient lisible
Question de l'exploitant : « ne devrait-on pas prendre les versions les plus
recentes ? » Non — resoudre « la derniere » au deploiement detruirait la
reproductibilite qu'on vient de prouver en rasant et remontant deux
ecosystemes, et ferait de chaque reconstruction une loterie. Six majeures de
Forgejo ne s'avalent pas en effet de bord d'un `make`.
Mais il avait raison de tirer le fil : L'ECART ETAIT INVISIBLE. Il a fallu
quatre requetes a la main pour decouvrir Forgejo 10.0.0 contre v16.0.2 publie,
Keycloak 26.0.7 contre 26.7.1, Nextcloud 34.0.1 contre 34.0.2, oauth2-proxy a
jour.
Le devis ne juge pas, il RENSEIGNE : un retard n'est pas une faute, il sort
donc en 0. Monter de version reste un acte explicite.
Ce qui le distingue d'une liste ecrite a la main : les versions epinglees sont
DERIVEES du depot. La source amont, elle, ne peut pas se deriver — elle depend
de l'editeur — et vit dans une table. TOUTE VERSION EPINGLEE SANS ENTREE DANS
CETTE TABLE FAIT SORTIR EN ERREUR : sans cette garde, un epinglage ajoute
demain vieillirait sans que personne ne le voie. Une exemption reste possible,
mais ecrite et motivee (deux le sont : la serie PHP de Debian, la version
PostgreSQL vide).
Eprouve dans les deux sens : un serveur_redis_version ajoute temporairement
fait sortir en 1 en le nommant ; retire, retour a 0.
Et il rappelle ce qu'on n'epingle PAS. Grafana n'a aucune version dans le
depot — le 13.1.1 vu dans l'historique apt etait ce que son depot servait ce
jour-la. Debian, Grafana, smallstep et Icinga prennent ce qu'on leur donne.
Deux regimes coexistent dans la meme flotte, et les taire donnerait l'illusion
que tout est maitrise.
Verifie : versions-mesurer code 0, garde eprouvee, prouver.py 35 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 23:02:16 -04:00
|
|
|
make versions-mesurer # de combien nos épinglages ont-ils vieilli ?
|
2026-08-08 07:20:56 -04:00
|
|
|
```
|
|
|
|
|
|
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
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
2026-09-06 16:18:23 -04:00
|
|
|
**Le patron a été porté sous les services, au monde physique** — mêmes pièces (un playbook
|
|
|
|
|
qui relève, un script qui compare), même refus d'écrire. Ce document ne traite que la
|
|
|
|
|
moitié haute ; ces cinq-là existent aussi :
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
make frontiere-plan # les règles de la frontière OPNsense contre leur devis
|
|
|
|
|
make proxmox-fw-plan # le pare-feu est-ouest de l'hyperviseur contre le registre des flux
|
|
|
|
|
make sdn-plan # la zone EVPN, ses VNets, et la sortie des VRF
|
|
|
|
|
make underlay-plan # l'underlay déclaré contre ce que le cluster porte vraiment
|
|
|
|
|
make placement-plan # chaque VM est-elle là où le plan la met
|
|
|
|
|
```
|
|
|
|
|
|
2026-08-08 07:20:56 -04:00
|
|
|
## Le trou qu'il comble
|
|
|
|
|
|
2026-10-05 00:14:32 -04:00
|
|
|
`scripts/prouver.py` porte 88 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
2026-08-08 07:20:56 -04:00
|
|
|
dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est
|
|
|
|
|
cohérent **avec lui-même** — que les handlers existent, que les intrants ont un
|
|
|
|
|
propriétaire, que rien n'est codé en dur.
|
|
|
|
|
|
|
|
|
|
Aucune ne demande au système déployé s'il ressemble à ce que le dépôt annonce.
|
|
|
|
|
|
|
|
|
|
Les quatre défauts du 2026-08-08 vivaient tous dans cet angle :
|
|
|
|
|
|
|
|
|
|
| Défaut | Le dépôt disait | La machine portait |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| politique de mot de passe | `length(12)…` | *aucune* — `abcd` accepté |
|
|
|
|
|
| entrée `ppolicy` figée | `pwdMustChange: TRUE` | `FALSE` |
|
|
|
|
|
| fédération en écriture | comptes écrits dans LDAP | `syncRegistrations` absent |
|
|
|
|
|
| prénom | `givenName` | `cn` (le nom complet) |
|
|
|
|
|
|
|
|
|
|
Chacun a été trouvé en relisant après avoir écrit. Aucun n'a été signalé.
|
|
|
|
|
|
|
|
|
|
## Ce qu'il vérifie
|
|
|
|
|
|
|
|
|
|
Le playbook `playbooks/maintenance/devis-identite.yml` **relève** deux dictionnaires — le
|
|
|
|
|
déclaré et le réel — et les dépose en JSON. `scripts/devis_identite.py` **compare**. La
|
|
|
|
|
séparation est délibérée : la comparaison en Jinja est illisible, et le dépôt a déjà cette
|
|
|
|
|
forme pour les devis réseau (Python raisonne, Ansible relève).
|
|
|
|
|
|
|
|
|
|
- **realm** — politique de mot de passe, anti-force-brute, « mot de passe oublié », relais SMTP
|
|
|
|
|
- **fédération** — mode d'écriture et les six clés de durcissement
|
|
|
|
|
- **mappeurs** — chaque attribut LDAP projeté
|
|
|
|
|
- **annuaire** — l'entrée `pwdPolicy`, attribut par attribut
|
|
|
|
|
- **comptes** — ceux qui n'ont pas d'adresse, pour qui « mot de passe oublié » ne mène nulle part
|
|
|
|
|
|
|
|
|
|
Le déclaré n'est jamais recopié ici : le playbook charge les défauts du rôle et appelle les
|
|
|
|
|
résolveurs. **Un devis qui redéclare ce qu'il vérifie ne vérifie rien.**
|
|
|
|
|
|
|
|
|
|
## Le devis des certificats
|
|
|
|
|
|
|
|
|
|
Il traque un défaut connu et récurrent : un certificat renouvelé **sur disque** mais
|
|
|
|
|
toujours servi **périmé** depuis la mémoire du service, tant que nginx / postfix / dovecot
|
|
|
|
|
/ slapd / step-ca n'ont pas été rechargés.
|
|
|
|
|
|
|
|
|
|
**Ce qu'il a fallu désapprendre pour l'écrire.** Les certificats step-ca vivent 24 h et le
|
|
|
|
|
minuteur les renouvelle toutes les ~14 minutes. Une empreinte servie *différente* de celle
|
|
|
|
|
sur disque est donc l'état **normal** : comparer les empreintes aurait produit un
|
|
|
|
|
vérificateur qui crie en permanence, et qu'on aurait appris à ignorer. Le signal utile est
|
|
|
|
|
l'**échéance de ce qui est réellement servi**, plus l'absence de `client_pki_reload_services`
|
|
|
|
|
— car alors rien ne rafraîchira jamais la copie en mémoire.
|
|
|
|
|
|
|
|
|
|
Il a trouvé quelque chose à sa première exécution : sur `infra-pki-01`, l'autorité
|
|
|
|
|
elle-même, le certificat était **expiré depuis plus de huit heures** et le renouvellement
|
|
|
|
|
échouait toutes les quatorze minutes sur `'step ca renew' requires the '--ca-url' flag`.
|
|
|
|
|
Rien ne le signalait.
|
|
|
|
|
|
devis : make versions-mesurer — l'ecart avec l'amont devient lisible
Question de l'exploitant : « ne devrait-on pas prendre les versions les plus
recentes ? » Non — resoudre « la derniere » au deploiement detruirait la
reproductibilite qu'on vient de prouver en rasant et remontant deux
ecosystemes, et ferait de chaque reconstruction une loterie. Six majeures de
Forgejo ne s'avalent pas en effet de bord d'un `make`.
Mais il avait raison de tirer le fil : L'ECART ETAIT INVISIBLE. Il a fallu
quatre requetes a la main pour decouvrir Forgejo 10.0.0 contre v16.0.2 publie,
Keycloak 26.0.7 contre 26.7.1, Nextcloud 34.0.1 contre 34.0.2, oauth2-proxy a
jour.
Le devis ne juge pas, il RENSEIGNE : un retard n'est pas une faute, il sort
donc en 0. Monter de version reste un acte explicite.
Ce qui le distingue d'une liste ecrite a la main : les versions epinglees sont
DERIVEES du depot. La source amont, elle, ne peut pas se deriver — elle depend
de l'editeur — et vit dans une table. TOUTE VERSION EPINGLEE SANS ENTREE DANS
CETTE TABLE FAIT SORTIR EN ERREUR : sans cette garde, un epinglage ajoute
demain vieillirait sans que personne ne le voie. Une exemption reste possible,
mais ecrite et motivee (deux le sont : la serie PHP de Debian, la version
PostgreSQL vide).
Eprouve dans les deux sens : un serveur_redis_version ajoute temporairement
fait sortir en 1 en le nommant ; retire, retour a 0.
Et il rappelle ce qu'on n'epingle PAS. Grafana n'a aucune version dans le
depot — le 13.1.1 vu dans l'historique apt etait ce que son depot servait ce
jour-la. Debian, Grafana, smallstep et Icinga prennent ce qu'on leur donne.
Deux regimes coexistent dans la meme flotte, et les taire donnerait l'illusion
que tout est maitrise.
Verifie : versions-mesurer code 0, garde eprouvee, prouver.py 35 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 23:02:16 -04:00
|
|
|
## Le devis des versions
|
|
|
|
|
|
|
|
|
|
Il répond à une question posée le 2026-08-10 : *« ne devrait-on pas prendre les versions les
|
|
|
|
|
plus récentes ? »* Non — résoudre « la dernière » au moment du déploiement détruirait la
|
|
|
|
|
reproductibilité et ferait de chaque reconstruction une loterie. Mais **l'écart, lui, était
|
|
|
|
|
invisible** : il a fallu quatre requêtes à la main pour découvrir que Forgejo avait six
|
|
|
|
|
versions majeures de retard.
|
|
|
|
|
|
|
|
|
|
Ce devis ne juge pas, il **renseigne**. Un retard n'est pas une faute ; monter de version
|
|
|
|
|
reste un acte explicite — on édite, on relit, on reconstruit, on repasse les devis.
|
|
|
|
|
|
|
|
|
|
La liste des versions épinglées est **dérivée** du dépôt (`roles/*/defaults/*_version`). La
|
|
|
|
|
source amont de chacune, elle, ne peut pas se dériver — elle dépend de l'éditeur — et vit
|
|
|
|
|
donc dans une table. **Toute version épinglée sans entrée dans cette table fait sortir le
|
|
|
|
|
devis en erreur** : sans cette garde, un épinglage ajouté demain vieillirait sans que
|
|
|
|
|
personne ne le voie, et on croirait tout surveillé. Une exemption reste possible, mais
|
|
|
|
|
**écrite et motivée**.
|
|
|
|
|
|
|
|
|
|
Il rappelle enfin ce que l'on n'épingle pas : les paquets Debian, Grafana, smallstep et
|
|
|
|
|
Icinga prennent ce que leur dépôt sert au moment du déploiement. Deux régimes coexistent, et
|
|
|
|
|
les taire donnerait l'illusion que tout est maîtrisé.
|
|
|
|
|
|
devis : sixieme instrument — « ce qui n'est pas declare est-il refuse ? »
devis_expositions pose la question positive (une exposition declaree
repond-elle). Celle-ci manquait, et ce n'est pas la meme : un pare-feu peut
servir tout ce qu'on lui demande ET laisser passer tout le reste.
Rien n'y est saisi. Les cibles sont les ports REELLEMENT en ecoute (sonder un
port ferme ne prouverait rien du pare-feu) ; la politique attendue est lue
dans devis_opnsense.py, la source qui configure la frontiere.
scripts/sonde_tcp.py refuse de conclure : un CONTROLE (adresse ou personne
n'ecoute) avant tout verdict — s'il repond, le releve est declare NUL. Et
etablir n'est pas livrer : banniere, sinon requete HTTP, sinon poignee TLS ;
sans reponse le verdict est AMBIGU, jamais « ouvert ».
Deux pieges rencontres en le construisant, corriges et commentes sur place :
la sonde « tenant » s'executait sur le poste (dans un play connection: local,
delegate_to herite de cette connexion) ; et 2 s de lecture faisaient ressortir
un Postfix sain en AMBIGU (postscreen retarde sa banniere expres) — porte a
8 s, compense par du parallelisme.
Premier verdict, frontiere en l'etat : 38 ecarts, dont collab-01:9980 qui
livre vraiment un HTTP 200 depuis le poste. Releve sortant declare NUL.
Verifie : ansible-lint Passed, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:03:28 -04:00
|
|
|
## Le devis d'étanchéité de la frontière
|
|
|
|
|
|
|
|
|
|
Le devis des expositions pose la question **positive** — chaque exposition déclarée
|
|
|
|
|
répond-elle. Celui-ci pose la **négative** : la frontière laisse-t-elle passer ce que la
|
|
|
|
|
politique n'autorise pas ? Les deux sont nécessaires. Un pare-feu peut très bien servir
|
|
|
|
|
tout ce qu'on lui demande *et* laisser passer tout le reste.
|
|
|
|
|
|
|
|
|
|
Rien n'y est saisi. Les **cibles** sont les ports réellement en écoute dans la flotte —
|
|
|
|
|
sonder un port fermé ne prouverait rien du pare-feu, le refus viendrait de la machine. La
|
|
|
|
|
**politique attendue** est lue dans `devis_opnsense.py`, la source même qui configure la
|
|
|
|
|
frontière.
|
|
|
|
|
|
|
|
|
|
`scripts/sonde_tcp.py` porte deux principes, tirés de trois faux diagnostics :
|
|
|
|
|
|
frontiere : etanche — CONFORME sur 56 lignes, dans les deux sens
L'exploitant a retire la derniere regle heritee. `make frontiere-mesurer`
rend CONFORME (code 0) : tout ce qui est declare est livre, tout le reste est
refuse — y compris collab-01:9980, le seul qui livrait vraiment un HTTP 200
depuis le poste.
Et mon instrument avait tort, pas la frontiere. Il comptait 38 ecarts en
concluant depuis le client : « connexion etablie => la bordure a relaye ».
Faux, verifie A LA DESTINATION : pendant que le poste tenait une connexion
« etablie » vers idm-01:389, idm-01 n'en voyait aucune ; collab-01 n'en
voyait aucune sur 9980. La frontiere repond a la poignee TCP sans relayer.
Le devis raisonne desormais sur la LIVRAISON seule, et le controle ne rend le
releve NUL que s'il LIVRE des donnees — qu'il ressorte AMBIGU est attendu ici
et le rapport le dit a chaque execution. Cette relaxation rend aussi le sens
sortant mesurable : il etait declare NUL en permanence.
Nouveau mot-cle `poste: false` dans meta/flux.yml : un service publie n'est
pas forcement fait pour un poste de travail. Le 25 entrant de Postfix est un
flux serveur a serveur ; la frontiere l'etendait au VLAN d'administration ou
le nftables de l'hote le refusait. Deux regles retirees. Le mot-cle vit avec
le role qui sait ce que son port veut dire ; le generateur ne connait
toujours aucun numero de port.
Verifie : frontiere-plan sans ecart (41 regles, 12 routes), flotte 14/14,
frontiere-mesurer CONFORME, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:33:04 -04:00
|
|
|
- **Un contrôle avant tout verdict** — une adresse où personne n'écoute. S'il **livre** des
|
|
|
|
|
données, le relevé est déclaré **NUL**. Mieux vaut pas de mesure qu'une mesure fausse.
|
devis : sixieme instrument — « ce qui n'est pas declare est-il refuse ? »
devis_expositions pose la question positive (une exposition declaree
repond-elle). Celle-ci manquait, et ce n'est pas la meme : un pare-feu peut
servir tout ce qu'on lui demande ET laisser passer tout le reste.
Rien n'y est saisi. Les cibles sont les ports REELLEMENT en ecoute (sonder un
port ferme ne prouverait rien du pare-feu) ; la politique attendue est lue
dans devis_opnsense.py, la source qui configure la frontiere.
scripts/sonde_tcp.py refuse de conclure : un CONTROLE (adresse ou personne
n'ecoute) avant tout verdict — s'il repond, le releve est declare NUL. Et
etablir n'est pas livrer : banniere, sinon requete HTTP, sinon poignee TLS ;
sans reponse le verdict est AMBIGU, jamais « ouvert ».
Deux pieges rencontres en le construisant, corriges et commentes sur place :
la sonde « tenant » s'executait sur le poste (dans un play connection: local,
delegate_to herite de cette connexion) ; et 2 s de lecture faisaient ressortir
un Postfix sain en AMBIGU (postscreen retarde sa banniere expres) — porte a
8 s, compense par du parallelisme.
Premier verdict, frontiere en l'etat : 38 ecarts, dont collab-01:9980 qui
livre vraiment un HTTP 200 depuis le poste. Releve sortant declare NUL.
Verifie : ansible-lint Passed, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:03:28 -04:00
|
|
|
- **Établir n'est pas livrer.** La sonde fait parler le service : bannière, sinon requête
|
|
|
|
|
HTTP minimale, sinon poignée TLS. Sans réponse, le verdict est **AMBIGU**, jamais
|
|
|
|
|
« ouvert » — LDAP et PostgreSQL attendent un message qu'on ne fabrique pas ici, et un
|
|
|
|
|
synproxy se comporte exactement pareil.
|
|
|
|
|
|
frontiere : etanche — CONFORME sur 56 lignes, dans les deux sens
L'exploitant a retire la derniere regle heritee. `make frontiere-mesurer`
rend CONFORME (code 0) : tout ce qui est declare est livre, tout le reste est
refuse — y compris collab-01:9980, le seul qui livrait vraiment un HTTP 200
depuis le poste.
Et mon instrument avait tort, pas la frontiere. Il comptait 38 ecarts en
concluant depuis le client : « connexion etablie => la bordure a relaye ».
Faux, verifie A LA DESTINATION : pendant que le poste tenait une connexion
« etablie » vers idm-01:389, idm-01 n'en voyait aucune ; collab-01 n'en
voyait aucune sur 9980. La frontiere repond a la poignee TCP sans relayer.
Le devis raisonne desormais sur la LIVRAISON seule, et le controle ne rend le
releve NUL que s'il LIVRE des donnees — qu'il ressorte AMBIGU est attendu ici
et le rapport le dit a chaque execution. Cette relaxation rend aussi le sens
sortant mesurable : il etait declare NUL en permanence.
Nouveau mot-cle `poste: false` dans meta/flux.yml : un service publie n'est
pas forcement fait pour un poste de travail. Le 25 entrant de Postfix est un
flux serveur a serveur ; la frontiere l'etendait au VLAN d'administration ou
le nftables de l'hote le refusait. Deux regles retirees. Le mot-cle vit avec
le role qui sait ce que son port veut dire ; le generateur ne connait
toujours aucun numero de port.
Verifie : frontiere-plan sans ecart (41 regles, 12 routes), flotte 14/14,
frontiere-mesurer CONFORME, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:33:04 -04:00
|
|
|
**Seule la livraison compte, et c'est l'enseignement de la semaine.** Sur ce réseau, la
|
|
|
|
|
frontière répond elle-même à la poignée TCP pour toute destination qu'elle route, sans
|
|
|
|
|
jamais relayer : le contrôle ressort AMBIGU à chaque exécution, et le rapport le dit. Vérifié
|
|
|
|
|
**à la destination** le 2026-08-09 — pendant que le poste tenait une connexion « établie »
|
|
|
|
|
vers `idm-01:389`, `idm-01` n'en voyait aucune. Un port est donc conforme s'il livre quand il
|
|
|
|
|
doit livrer et ne livre rien quand il ne doit pas ; ce que fait la poignée ne regarde
|
|
|
|
|
personne.
|
|
|
|
|
|
|
|
|
|
Reste un écart de nature différente, et le devis le nomme comme tel : *livré attendu, rien
|
|
|
|
|
livré* = la bordure autorise et l'hôte refuse. Rien ne fuit, mais les deux couches ne
|
|
|
|
|
déclarent pas la même politique — et une politique qu'on ne peut plus lire est une politique
|
|
|
|
|
qu'on ne tient plus. C'est ce qui a fait naître le mot-clé `poste: false` dans
|
|
|
|
|
`meta/flux.yml` (voir [`frontiere-opnsense.md`](frontiere-opnsense.md)).
|
devis : sixieme instrument — « ce qui n'est pas declare est-il refuse ? »
devis_expositions pose la question positive (une exposition declaree
repond-elle). Celle-ci manquait, et ce n'est pas la meme : un pare-feu peut
servir tout ce qu'on lui demande ET laisser passer tout le reste.
Rien n'y est saisi. Les cibles sont les ports REELLEMENT en ecoute (sonder un
port ferme ne prouverait rien du pare-feu) ; la politique attendue est lue
dans devis_opnsense.py, la source qui configure la frontiere.
scripts/sonde_tcp.py refuse de conclure : un CONTROLE (adresse ou personne
n'ecoute) avant tout verdict — s'il repond, le releve est declare NUL. Et
etablir n'est pas livrer : banniere, sinon requete HTTP, sinon poignee TLS ;
sans reponse le verdict est AMBIGU, jamais « ouvert ».
Deux pieges rencontres en le construisant, corriges et commentes sur place :
la sonde « tenant » s'executait sur le poste (dans un play connection: local,
delegate_to herite de cette connexion) ; et 2 s de lecture faisaient ressortir
un Postfix sain en AMBIGU (postscreen retarde sa banniere expres) — porte a
8 s, compense par du parallelisme.
Premier verdict, frontiere en l'etat : 38 ecarts, dont collab-01:9980 qui
livre vraiment un HTTP 200 depuis le poste. Releve sortant declare NUL.
Verifie : ansible-lint Passed, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:03:28 -04:00
|
|
|
|
2026-08-08 07:33:49 -04:00
|
|
|
## Le devis des expositions
|
|
|
|
|
|
|
|
|
|
Il pose la question que se pose vraiment un utilisateur : **est-ce que mes services
|
|
|
|
|
répondent ?** Et quand la réponse est non, il dit *où* ça casse.
|
|
|
|
|
|
|
|
|
|
**Une vraie requête, jamais un `connect()`.** À travers l'OPNsense (anti-spoofing), toute
|
|
|
|
|
connexion TCP réussit — y compris vers une adresse où aucune machine n'existe. Et « lire
|
|
|
|
|
des données après connexion » ne vaut rien en TLS, où c'est le *client* qui parle en
|
|
|
|
|
premier : le port 443 d'un edge sain se comporte exactement comme un port mort. Seul le
|
|
|
|
|
code de retour d'une requête HTTPS complète tranche.
|
|
|
|
|
|
|
|
|
|
**Deux points de vue, et c'est leur différence qui diagnostique :**
|
|
|
|
|
|
|
|
|
|
| Point de vue | Ce qu'il traverse |
|
|
|
|
|
|---|---|
|
|
|
|
|
| depuis l'edge | edge + dorsal |
|
|
|
|
|
| depuis le poste | DNS + frontière + edge + dorsal — le chemin d'un vrai utilisateur |
|
|
|
|
|
|
|
|
|
|
L'un répond et pas l'autre : ce n'est pas le service, c'est le chemin.
|
|
|
|
|
|
|
|
|
|
**Un code n'est pas un verdict.** Un 502 ou un 504 *est* une réponse, et pourtant le dorsal
|
|
|
|
|
est mort derrière. Le premier jet ne testait que la présence d'un code et laissait donc
|
|
|
|
|
passer un dorsal tombé — trouvé par le test négatif, pas par la relecture. Un 302 ou un 401,
|
|
|
|
|
en revanche, est un service parfaitement vivant qui redirige vers l'IdP ou exige une
|
|
|
|
|
authentification.
|
|
|
|
|
|
devis PostgreSQL et courriel : la serie des devis de service est complete
PostgreSQL — rend visibles deux defauts deja vecus ici : un reseau ECRIT dans
pg_hba au lieu d'etre derive, et une ligne host en clair la ou il faut hostssl
(verrou qui saute sans bruit, les clients verify-full continuant de marcher).
Interroge pg_settings, jamais le fichier : un grep de postgresql.conf annonce
ssl_cert_file=snakeoil alors que le serveur sert le certificat de l'AC — la
valeur vient d'un conf.d que le grep ne voyait pas. L'instrument etait
incomplet, pas la configuration.
Courriel — chaque maillon interroge la ou il dit la verite : postmap -q pour
la resolution LDAP de Postfix, doveadm user pour celle de Dovecot (le maillon
exact ou la livraison avait bloque), vraies conversations SMTP/IMAP. Verifie
aussi qu'une adresse INEXISTANTE ne resout pas — sinon la boite est un
fourre-tout et le devis ne mesure plus rien.
Trouve immediatement une divergence reelle : Dovecot connait la boite de
sysadmin@chezlepro.internal, Postfix ne sait pas y router (query_filter
(mail=%s), et l'attribut mail porte sysadmin@chezlepro.ca). Collision de
roles : une identite ne porte qu'une adresse mail et on lui en demande deux —
notification joignable hors du systeme, et cle de routage local. Arbitrage a
rendre avant correction.
Tests negatifs : PostgreSQL 3 ecarts nommes, code 1. Une variable morte
trouvee dans une branche que le cas nominal n'emprunte jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:53:49 -04:00
|
|
|
## Le devis PostgreSQL
|
|
|
|
|
|
|
|
|
|
Deux défauts déjà vécus dans ce dépôt, et qu'il rend visibles : un réseau **écrit** dans
|
|
|
|
|
`pg_hba.conf` au lieu d'être dérivé (`10.11.0.0/16` sous un commentaire « AJUSTER » que
|
|
|
|
|
personne n'a suivi), et une ligne `host` en clair là où il faut `hostssl` — le verrou saute
|
|
|
|
|
sans bruit, puisque les clients en `verify-full` continuent de fonctionner.
|
|
|
|
|
|
|
|
|
|
**Interroger l'état effectif, jamais le fichier.** Un `grep` de `postgresql.conf` annonce
|
|
|
|
|
`ssl_cert_file = snakeoil` sur `data-sql-01`, alors que le serveur sert bien le certificat
|
|
|
|
|
de l'AC : la valeur vient d'un `conf.d/99-setops.conf` que le grep ne voyait pas. C'était
|
|
|
|
|
l'instrument qui était incomplet, pas la configuration. Le devis lit `pg_settings`.
|
|
|
|
|
|
|
|
|
|
## Le devis du courriel
|
|
|
|
|
|
|
|
|
|
La chaîne est longue — Postfix → LDAP → LMTP → Dovecot → IMAP — et chaque maillon peut
|
|
|
|
|
casser sans que le suivant s'en aperçoive. Chacun est donc interrogé là où il dit la
|
|
|
|
|
vérité : `postmap -q` pour la résolution LDAP de Postfix, `doveadm user` pour celle de
|
|
|
|
|
Dovecot — précisément le maillon où la livraison avait bloqué — et une vraie conversation
|
|
|
|
|
SMTP/IMAP pour ce que les serveurs annoncent.
|
|
|
|
|
|
2026-08-08 09:42:45 -04:00
|
|
|
Il vérifie aussi qu'une adresse **inexistante** ne résout pas. Ce n'est pas un luxe :
|
|
|
|
|
Dovecot a `allow_all_users = yes` et accepte donc n'importe quelle partie locale — le
|
|
|
|
|
garde-fou est entièrement chez Postfix.
|
|
|
|
|
|
|
|
|
|
**Et il regarde la file d'attente.** C'est la leçon la plus chère de la série : sa première
|
|
|
|
|
version disait `CONFORME` pendant que *toute* livraison interne était différée. Résolution
|
|
|
|
|
correcte, dialectes corrects, et rien qui arrive — Postfix exigeait TLS
|
|
|
|
|
(`lmtp_tls_security_level = verify`) vers un port Dovecot qui écoutait en clair. Les deux
|
|
|
|
|
côtés d'un même flux avaient été durcis séparément. **Un devis qui ne regarde que les
|
|
|
|
|
réglages ne dit pas si le service rend son service** ; la file est le seul endroit qui
|
|
|
|
|
révèle que le courrier ne bouge pas.
|
|
|
|
|
|
|
|
|
|
Le routage local se fait par **identifiant** (`uid`), pas par l'attribut `mail` — voir le
|
|
|
|
|
gabarit `ldap-mailboxes.cf.j2` pour la raison et le coût assumé.
|
devis PostgreSQL et courriel : la serie des devis de service est complete
PostgreSQL — rend visibles deux defauts deja vecus ici : un reseau ECRIT dans
pg_hba au lieu d'etre derive, et une ligne host en clair la ou il faut hostssl
(verrou qui saute sans bruit, les clients verify-full continuant de marcher).
Interroge pg_settings, jamais le fichier : un grep de postgresql.conf annonce
ssl_cert_file=snakeoil alors que le serveur sert le certificat de l'AC — la
valeur vient d'un conf.d que le grep ne voyait pas. L'instrument etait
incomplet, pas la configuration.
Courriel — chaque maillon interroge la ou il dit la verite : postmap -q pour
la resolution LDAP de Postfix, doveadm user pour celle de Dovecot (le maillon
exact ou la livraison avait bloque), vraies conversations SMTP/IMAP. Verifie
aussi qu'une adresse INEXISTANTE ne resout pas — sinon la boite est un
fourre-tout et le devis ne mesure plus rien.
Trouve immediatement une divergence reelle : Dovecot connait la boite de
sysadmin@chezlepro.internal, Postfix ne sait pas y router (query_filter
(mail=%s), et l'attribut mail porte sysadmin@chezlepro.ca). Collision de
roles : une identite ne porte qu'une adresse mail et on lui en demande deux —
notification joignable hors du systeme, et cle de routage local. Arbitrage a
rendre avant correction.
Tests negatifs : PostgreSQL 3 ecarts nommes, code 1. Une variable morte
trouvee dans une branche que le cas nominal n'emprunte jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:53:49 -04:00
|
|
|
|
doctrine : D-68 et D-69 — relire ce qu'on ecrit, pas « toujours l'API »
Question de l'exploitant apres deux pannes causees par kcadm. La reponse est
non, et elle se fonde sur la mesure : sur six familles de defauts du jour,
deux seulement viennent d'un CLI ; un module Ansible (ldap_entry) a commis la
meme faute, et trois autres viennent d'un grep de fichier, de la precedence
Ansible et de mon propre comparateur.
D-68 — ecrire, puis relire et comparer, quelle que soit l'interface ; choisir
celle dont le chemin de lecture parle le meme langage que celui d'ecriture.
Une API est souvent preferable parce qu'elle rend la ressource ENTIERE, ce qui
permet le patron de chaque devis. Mais la plupart de la flotte n'a pas d'API,
et postconf -h / -e sont parfaitement symetriques.
D-69 — sur Keycloak : l'API pour toute map ou collection, kcadm ailleurs
(vocabulaire de la doc du produit, donc lisible sans IA).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 11:45:31 -04:00
|
|
|
## La règle dont ces devis découlent (D-68)
|
|
|
|
|
|
|
|
|
|
> **Écrire, puis relire et comparer** — quelle que soit l'interface. Choisir celle dont le
|
|
|
|
|
> chemin de *lecture* parle le même langage que le chemin d'*écriture*.
|
|
|
|
|
|
|
|
|
|
Ce n'est pas « toujours préférer l'API ». Cette règle-là n'aurait prédit aucune des pannes
|
|
|
|
|
du 2026-08-08. Sur six familles de défauts ce jour-là, **deux** venaient d'un CLI :
|
|
|
|
|
|
|
|
|
|
| Ce qui a menti | Interface |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `kcadm -s` sur une map : succès, rien d'écrit | CLI |
|
|
|
|
|
| `grafana-cli` : « password changed ✔ » dans une base fantôme | CLI |
|
|
|
|
|
| `ldap_entry` : crée, ne modifie jamais | module Ansible |
|
|
|
|
|
| `grep postgresql.conf` : valeur fausse (surcharge `conf.d`) | lecture de fichier |
|
|
|
|
|
| `include_vars` : le devis rapportait le défaut, pas le réel | précédence Ansible |
|
|
|
|
|
| le comparateur d'expositions : un 502 compté comme vivant | mon propre code |
|
|
|
|
|
|
|
|
|
|
Le facteur commun n'est pas l'interface, c'est d'avoir **écrit sans relire**. Et la plupart
|
|
|
|
|
de la flotte n'a pas d'API du tout — Postfix, Dovecot, nginx, slapd, nftables. `postconf -h`
|
|
|
|
|
et `postconf -e` sont parfaitement symétriques : lecture et écriture dans le même
|
|
|
|
|
vocabulaire, c'est tout ce qu'on demande.
|
|
|
|
|
|
|
|
|
|
Une API est souvent préférable, mais pour une raison précise : elle rend la ressource
|
|
|
|
|
**entière**, ce qui permet le patron qu'on retrouve dans chaque devis — *fusionner
|
|
|
|
|
l'attendu dans le réel ; si rien ne change, c'est conforme*. Une interface qui n'accepte
|
|
|
|
|
que des écritures ne peut pas le soutenir.
|
|
|
|
|
|
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
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
2026-09-06 16:18:23 -04:00
|
|
|
**Ces devis sont cette relecture**, faite après coup et par une autre main que celle
|
doctrine : D-68 et D-69 — relire ce qu'on ecrit, pas « toujours l'API »
Question de l'exploitant apres deux pannes causees par kcadm. La reponse est
non, et elle se fonde sur la mesure : sur six familles de defauts du jour,
deux seulement viennent d'un CLI ; un module Ansible (ldap_entry) a commis la
meme faute, et trois autres viennent d'un grep de fichier, de la precedence
Ansible et de mon propre comparateur.
D-68 — ecrire, puis relire et comparer, quelle que soit l'interface ; choisir
celle dont le chemin de lecture parle le meme langage que celui d'ecriture.
Une API est souvent preferable parce qu'elle rend la ressource ENTIERE, ce qui
permet le patron de chaque devis. Mais la plupart de la flotte n'a pas d'API,
et postconf -h / -e sont parfaitement symetriques.
D-69 — sur Keycloak : l'API pour toute map ou collection, kcadm ailleurs
(vocabulaire de la doc du produit, donc lisible sans IA).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 11:45:31 -04:00
|
|
|
qui a écrit. C'est ce qui les distingue d'un déploiement : `make deployer` réconcilie, les
|
|
|
|
|
devis constatent.
|
|
|
|
|
|
2026-08-08 07:20:56 -04:00
|
|
|
## Un piège de construction, à connaître avant d'écrire le prochain
|
|
|
|
|
|
|
|
|
|
`include_vars` au niveau du *play* **prime sur les `group_vars`**. Charger les défauts d'un
|
|
|
|
|
rôle tels quels fait donc mentir le devis : il compare au défaut du dépôt au lieu de la
|
|
|
|
|
valeur réellement déclarée par l'inventaire. Le premier jet rapportait
|
|
|
|
|
`client_pki_reload_services: []` sur les quatorze hôtes alors que quatre groupes le
|
|
|
|
|
déclarent — **le devis portait exactement le défaut qu'il est censé traquer.**
|
|
|
|
|
|
|
|
|
|
La forme correcte, reprise dans les deux devis :
|
|
|
|
|
|
|
|
|
|
1. retenir ce que l'inventaire déclare, **avant** tout chargement de défauts ;
|
|
|
|
|
2. charger les défauts du rôle (`include_vars`, sans `name:` — les défauts se citent entre
|
|
|
|
|
eux, et ces références ne se résolvent plus une fois enfermées dans un dictionnaire) ;
|
|
|
|
|
3. réimposer les valeurs de l'inventaire **clé par clé**, en boucle. `set_fact` n'accepte
|
|
|
|
|
pas un dictionnaire entier passé en argument libre : il le prend sans erreur et n'en
|
|
|
|
|
fait rien — c'est ainsi que le premier correctif a paru fonctionner tout en laissant le
|
|
|
|
|
devis mentir.
|
|
|
|
|
|
|
|
|
|
## Ce qu'ils ne font pas
|
|
|
|
|
|
|
|
|
|
Ils ne corrigent pas — c'est `make deployer` qui réconcilie. Ils répondent à l'autre
|
|
|
|
|
question, et sortent en code 1 s'il y a un écart.
|
|
|
|
|
|
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
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
2026-09-06 16:18:23 -04:00
|
|
|
**Ce paragraphe disait, en dernière ligne du document, que le courriel, la base de données
|
|
|
|
|
et les expositions web « attendent le même traitement ».** Ils l'ont reçu — ce document
|
|
|
|
|
décrit leurs trois devis quelques écrans plus haut, et le patron a même été porté sous les
|
|
|
|
|
services, au monde physique (frontière, pare-feu de l'hyperviseur, SDN, underlay, placement).
|
|
|
|
|
|
|
|
|
|
Ce qui reste vraiment hors de leur portée, et qu'aucun ne mesure : la **tenue sous charge**
|
|
|
|
|
et le **comportement dans la durée**. Un devis dit que le service rend son service à
|
|
|
|
|
l'instant où on le lui demande, pas qu'il le rendra encore à mille utilisateurs, ni dans six
|
|
|
|
|
mois.
|