Set-OPS-Public/docs/audit
Daniel Allaire 6077b179af
Some checks are pending
verifier / verifier (push) Waiting to run
plan : l ecriture des registres devient atomique — tout, ou rien
`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
2026-09-08 14:57:57 -04:00
..
affirmations.md preuve : P34 — chaque document declare son lecteur (D-74) 2026-08-10 07:53:04 -04:00
plan-de-recette.md preuves : trois gardes pour ce que ma lecture ne tiendra pas 2026-09-08 12:47:18 -04:00
preuve-2026-07-20.md Preuve complète (16/16) + détail P16 lisible 2026-07-20 20:47:36 -04:00
preuve-2026-07-22.md Trois preuves qui regardent au-dela d'une seule instance + champ liens/websocket au GUI 2026-07-22 21:32:42 -04:00
preuve-2026-07-23.md Créer une instance depuis un modèle (CLI + GUI) 2026-07-23 14:13:36 -04:00
preuve-2026-08-01.md client_pki : l'empreinte du root CA n'est pas un secret de voûte 2026-08-01 22:37:27 -04:00
preuve-2026-08-02.md modèles : l'underlay devient modélisable, et P17 le valide 2026-08-02 17:11:18 -04:00
preuve-2026-08-03.md SDN : make devis-sdn — zone, VNets et sous-réseaux dérivés du seed (D-43/44, P30) 2026-08-04 11:06:42 -04:00
preuve-2026-08-04.md réseau : le VNet d'une VM se dérive, un hyperviseur a plusieurs pattes (D-55 à D-59) 2026-08-04 16:20:56 -04:00
preuve-2026-08-06.md dns : client_unbound universel, et un resolveur d'amorcage 2026-08-06 21:41:58 -04:00
preuve-2026-08-08.md D-71 : PKI et DNS debout avant tout, et la zone inverse manquait 2026-08-08 15:29:57 -04:00
preuve-2026-08-09.md frontiere : etanche — CONFORME sur 56 lignes, dans les deux sens 2026-08-09 21:33:04 -04:00
preuve-2026-08-10.md preuve : P34 — chaque document declare son lecteur (D-74) 2026-08-10 07:53:04 -04:00
preuve-2026-08-11.md supervision : c'est le DEPOT qui dit ou en sont les sauvegardes 2026-08-11 21:39:05 -04:00
preuve-2026-08-12.md index : Technolibre 11->23, lab 1->13 — sortir des plages occupees par le materiel 2026-08-12 18:13:23 -04:00
preuve-2026-08-13.md frontiere : poster en formulaire encode — independant de la version du boitier 2026-08-13 23:57:10 -04:00
preuve-2026-08-14.md frontiere : une frontiere ne police que les tenants de SON site 2026-08-14 00:55:39 -04:00
preuve-2026-08-18.md P03 : la preuve comparait chaque instance a l'inventaire d'UNE SEULE 2026-08-18 14:35:01 -04:00
preuve-2026-08-19.md catalogue : la carte des services avait quatre mois de retard sur le moteur 2026-08-19 11:07:59 -04:00
preuve-2026-08-20.md CI : le harnais ne se declenchait que par memoire 2026-08-20 19:29:46 -04:00
preuve-2026-08-23.md artefacts : la forge sert le code, il manquait qui sert les binaires 2026-08-23 14:41:56 -04:00
preuve-2026-08-24.md site : le chemin qui materialise les machines de l'hebergeur 2026-08-24 23:11:32 -04:00
preuve-2026-08-25.md dns : quatre zones inverses pour le site, et rien de plus 2026-08-25 20:58:50 -04:00
preuve-2026-08-26.md flux : P49 — le registre des flux avait derive sans bruit 2026-08-26 16:58:17 -04:00
preuve-2026-08-27.md creer-vm : prouver la materialisation sans entrer chez le tenant 2026-08-27 22:48:49 -04:00
preuve-2026-08-28.md inseminer : le geste sort de mes mains et entre dans le depot 2026-08-28 17:14:37 -04:00
preuve-2026-08-30.md resolveur du site : le troisieme service prete pendant la jeunesse d un tenant 2026-08-30 12:37:33 -04:00
preuve-2026-08-31.md audit + timers : deux services en echec sur les quinze machines, muets depuis toujours 2026-08-31 16:16:13 -04:00
preuve-2026-09-01.md sauvegarde : l etat d un locataire quitte enfin sa propre flotte 2026-09-01 22:16:20 -04:00
preuve-2026-09-02.md supervision du site, et la panne qui dormait dans un mot 2026-09-02 17:34:01 -04:00
preuve-2026-09-03.md paquets tiers : passer par le cache du controleur, plus par Internet 2026-09-05 08:22:14 -04:00
preuve-2026-09-05.md documentation : la tournee des 74 documents, parce qu un balayage ne lit pas 2026-09-06 16:18:23 -04:00
preuve-2026-09-06.md documentation : la tournee des 74 documents, parce qu un balayage ne lit pas 2026-09-06 16:18:23 -04:00
preuve-2026-09-08.md plan : l ecriture des registres devient atomique — tout, ou rien 2026-09-08 14:57:57 -04:00
protocole-operateur-independant.md documentation : la tournee des 74 documents, parce qu un balayage ne lit pas 2026-09-06 16:18:23 -04:00
README.md documentation : la tournee des 74 documents, parce qu un balayage ne lit pas 2026-09-06 16:18:23 -04:00
reference-avant-reconstruction-2026-08-08.md preuve : P34 — chaque document declare son lecteur (D-74) 2026-08-10 07:53:04 -04:00
wiki-publie.yml wiki-publier : un wiki vide se clone tres bien, et le garde-fou ne le voyait pas 2026-09-08 12:56:21 -04:00

Audit de conformité — mode d'emploi

Pour qui : le mainteneur — comment le dispositif de preuve fonctionne, et comment l'étendre.

Ce dossier contient le dispositif qui garde Set-OPS honnête : il ne doit jamais affirmer plus que ce qu'il prouve.

Les pièces

Fichier Rôle
affirmations.md Le registre : chaque affirmation publique du dépôt (README, AGENTS, QUICKSTART, docs, wiki, aide make, GUI) tracée vers une commande de preuve et un statut (✅/🟡/❌/⚪), plus le journal des traitements (ce qui a été corrigé, quand, comment).
scripts/prouver.py Le harnais : un orchestrateur mince qui rejoue les preuves automatisables du registre en appelant l'outillage existant (les mêmes scripts que make verifier). Il ne réimplémente aucune validation.
preuve-AAAA-MM-JJ.md La pièce justificative : le rapport horodaté produit par make prouver. Rejouable et présentable (audit, certification, revue).
protocole-operateur-independant.md L'épreuve humaine : le protocole qui met AFF-002 (« exploitable sans IA ») à l'épreuve d'un sysadmin qui n'est pas l'auteur. Aucune commande locale ne peut prouver cette affirmation ; produit un rapport operateur-independant-AAAA-MM-JJ.md.

Produire une preuve

make prouver

Cela exécute chaque preuve et écrit docs/audit/preuve-<date>.md. La commande sort en erreur (rc≠0) si une preuve automatisable échoue — utilisable en garde-fou (CI locale, pré-commit). Une preuve SAUTÉE (⚪) n'est pas un échec.

Prérequis Vault

La preuve P16 (inventaire Ansible complet, ansible-inventory --list) déchiffre le group_vars de l'instance. Sans la clé de la voûte, elle est automatiquement sautée (⚪) avec la mention du prérequis — le reste du harnais reste vert, car les validateurs Python lisent le plan et l'inventaire directement, sans secret.

Pour l'inclure, il suffit que la clé de l'instance soit en place ; il n'y a rien à exporter (une voûte, une clé — scripts/voutes.py la trouve par convention de nommage) :

python3 scripts/voutes.py etat   # la clé de cette instance est-elle là ?
make prouver

(Ce paragraphe désignait P15 et un ANSIBLE_VAULT_PASSWORD_FILE unique jusqu'au 2026-09-06 : ni l'un ni l'autre n'était juste.)

Ce que couvre make prouver

Ce tableau est un EXTRAIT, pas l'inventaire. Il s'arrête à P23 et le dépôt porte 57 preuves (P01–P57, sans trou). La liste complète et à jour est produite par le harnais lui-même, jamais recopiée :

make prouver                      # écrit docs/audit/preuve-<date>.md : chaque preuve, son verdict
grep -oE '"id": "P[0-9]+", "titre": "[^"]+"' scripts/prouver.py   # la source

On garde l'extrait parce qu'il explique les premières preuves, celles qui fondent le reste. On ne le complète pas : un tableau de 57 lignes recopié à la main aurait dérivé avant d'être fini — c'est exactement ce qui est arrivé à celui-ci.

# Preuve Ce qu'elle établit
P01 Lint (ansible-lint) 0 violation, profil production.
P02 Tests unitaires inventory_host — cas nominal + refus.
P03 Diff-vide du plan l'inventaire est généré depuis le plan (diff vide).
P04 Groupes ↔ playbooks chaque groupe opérationnel a son playbook homonyme.
P05 Dépendances de groupes graphe cohérent, aucune entrée orpheline.
P06 Validateurs de registres serveurs / applications / bases / domaines valides.
P07 GUI node --check du JS du GUI.
P08 Orchestration couches + graphe : aucun cycle, aucune arête en arrière.
P09 Flux réseau schéma + matrice d'audit cohérents.
P10 Handlers ↔ notify tout notify pointe vers un handler du même rôle.
P11 Syntaxe --syntax-check de tous les playbooks (via make syntaxe).
P12 Runbooks cités les fichiers docs/ référencés existent.
P13 Invariants structurels LICENSE, socle en forme dossier, pas de couches parallèles, SSH clé-only, nftables désactivé par défaut.
P14 Chemins d'inventaire aucun instance/inventories/lab/group_vars codé en dur.
P15 Modèle public socle ses registres (domaines/serveurs/applications/bases) valident.
P16 Inventaire Ansible (voûte) ansible-inventory --list — sauté sans mot de passe Vault.
P17 Tous les modèles d'instance chaque modèle découvert valide (pas seulement socle). SETOPS_MODELES=../Set-OPS-Modeles inclut les modèles assemblés privés.
P18 Gabarit de voûte complet vault.yml.example couvre exactement les secrets que le plan exige (rôles actifs + bases + group_vars).
P19 GUI couvre le plan tout champ présent dans un plan réel est éditable par le GUI (nomenclature tolérée : le seed index est désormais un intrant ; l'adressage est dérivé, donc rien à éditer).
P20 Adressage dérivé du seed aucune nomenclature ne stocke d'adressage (supernet, sous-réseau, passerelle, VLAN) : tout se dérive du seul index. Attrape toute rechute vers l'écriture manuelle.
P21 Fédération sans collision aucune paire d'instances fédérées ne partage un index (mêmes VLAN/VMID sur le trunk). Le garde-fou du multi-instances ; vue avec make instances.
P22 Plan de recette à jour docs/audit/plan-de-recette.md (les 78 gestes manuels, générés des exercices du wiki) est à jour — il ne peut pas dériver du wiki. Régénérer : make plan-recette.
P23 Underlay sans collision la fabric physique (underlay.yml : mgmt/iSCSI/Ceph, cluster-global) n'empiète pas sur la plage tenant — VLAN < 1000 et sous-réseaux hors des supernets 10.(10+index).0.0/16. Sautée si underlay.yml absent. Vue : make underlay.

P17, P18, P19 ferment les angles morts du harnais : il ne vérifiait qu'une instance et le seul modèle socle. P17 aurait attrapé l'hôte fantôme d'integral ; P18, les neuf secrets absents du gabarit Chezlepro ; P19, les champs liens / websocket que le GUI ne savait pas écrire. Chacune a une CLI dédiée (scripts/modeles.py, voute.py, couverture_gui.py) utilisable seule.

Le rapport relie chaque preuve aux affirmations qu'elle couvre (colonne « Affirmations »), et liste à part les déclarations d'intention (⚪ invérifiables localement : AFF-036, 091, 096, 007) — assumées comme intentions, jamais présentées comme prouvées.

Ajouter une preuve

  1. Ajouter (ou corriger) l'affirmation dans affirmations.md avec sa commande de preuve.
  2. Ajouter une entrée à la liste PREUVES de scripts/prouver.py : soit une ou plusieurs commandes (cmds, toutes doivent renvoyer 0), soit une fonction native func renvoyant (ok, détail) pour un invariant simple. Renseigner refs avec les ID d'affirmations.
  3. Ne pas réimplémenter de logique de validation : appeler l'outillage existant (scripts/*.py, ansible-lint, cibles make). Le harnais orchestre, il ne valide pas.

Rapport avec make verifier

make verifier inclut désormais les preuves : il enchaîne ses vérifications au fil de l'eau (lint, tests, cohérence, syntaxe — arrêt au premier échec) puis termine par python3 scripts/prouver.py --verifier — les preuves du registre sans écrire de rapport (pour ne pas écraser la pièce justificative committée). Ainsi, make verifier échoue si une preuve échoue.

make prouver (sans --verifier) reste le mode pièce justificative : il exécute tout, horodate et écrit docs/audit/preuve-<date>.md. Les deux réutilisent le même outillage. (Quelques vérifications se recouvrent entre les deux étapes — coût assumé : la sortie détaillée de verifier est conservée, et prouver ajoute les preuves manquantes.)