Set-OPS-Public/docs/audit/preuve-2026-08-28.md

89 lines
9.6 KiB
Markdown
Raw Normal View History

# Preuve de conformite — Set-OPS — 2026-08-28
> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer
> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ;
> aucune validation n'est reimplementee ici. Voir le mode d'emploi :
> [`docs/audit/README.md`](README.md), et le registre trace :
> [`docs/audit/affirmations.md`](affirmations.md).
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
2026-08-28 13:45:44 -04:00
- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
- **Verdict** : ✅ CONFORME (52 OK · 0 echec · 0 saute)
## Preuves
| # | Preuve | Affirmations | Statut | Detail |
|---|---|---|---|---|
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK |  |
| P02 | Tests unitaires (inventory_host) | — | ✅ OK | >>> le verrou tient : aucune VM n'aurait ete touchee |
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0 : plan et inventaire applique coincident. |
| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | |
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | |
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). |
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 37 groupes classes, aucun cycle, aucune arete en arriere. |
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 add94f2 (08-25), remplace par `bande_basse_de`. Invisible parce que le geste quotidien est `make prouver`, qui ne joue pas les tests. Remis sur le contrat actuel, avec sa contrepartie : sans `bande_basse_de`, aucun chevauchement n'est tolere. 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
2026-08-28 13:09:28 -04:00
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 35 rôles, 94 flux, schéma + matrice OK. |
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml |
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. |
| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. |
| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. |
| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 15 hotes, 36 groupes (inventaire dechiffre et parse). |
| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. |
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
2026-08-28 14:23:33 -04:00
| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 23 secret(s) exige(s), tous presents. (Voute reelle non lisible ici : verification sautee.) |
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. |
| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. |
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). |
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 11 reseau(x), aucune collision avec la plage tenant. |
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | note : serveur_powerdns declare un port `derive` que le plan du site ne resout pas — aucune regle emise. |
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 add94f2 (08-25), remplace par `bande_basse_de`. Invisible parce que le geste quotidien est `make prouver`, qui ne joue pas les tests. Remis sur le contrat actuel, avec sa contrepartie : sans `bande_basse_de`, aucun chevauchement n'est tolere. 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
2026-08-28 13:09:28 -04:00
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 51 groupe(s), 81 regle(s). |
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 15 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 35 VM placee(s), aucun nom ni VMID en collision. |
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 29 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 18, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
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
2026-08-28 14:23:33 -04:00
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 52 scripts expliques et atteignables, 104 cibles make documentees, 61 roles avec README. |
runner : armer un runner est un acte declare, jamais un defaut La separation faite, la distribution devient sure. `serveur_ops_site` et `serveur_ops_tenant` deposent desormais LA CLE de la voute qu'ils deposaient deja chiffree. Le runner du site l'a recue : une seule cle chez lui, en 0600, et il ouvre sa voute tout seul. La fabric, pas un tenant. `false` PAR DEFAUT, ET CE N'EST PAS DECORATIF. Armer est le moment ou un humain remet la cle — le seul geste que la reproduction exige de lui. Un defaut a `true` armerait des machines sans que personne ne l'ait decide. Les deux roles exigent une declaration au plan et refusent d'armer sans qu'on dise AVEC QUELLE cle : poser un fichier vide laisserait un runner se croire arme. ECRIRE PUIS RELIRE, contre la faute SYMETRIQUE de celle de la voute : la on craignait un chiffre devenu clair, ici on craint une cle qui serait une voute, ou vide — Ansible accepte un mot de passe vide et n'ouvre rien. Existence, taille et mode mesures apres ecriture. CE QUE CA CHANGE : le mot de passe se tapait a chaque deploiement. Un geste repete vingt fois par semaine ne prouve rien, et il rendait tout deploiement non interactif impossible sans blocage. Il se remet une fois, et c'est un evenement. P32 A TROUVE LA MOITIE MANQUANTE : le role du tenant exigeait `serveur_ops_tenant_cle_source`, absente du plan de Chezlepro. Dit avant tout deploiement, dans les bons termes. make verifier : vert. make prouver : CONFORME, 52 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 14:46:29 -04:00
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 41 exigence(s) de role, toutes satisfaites (128 cle(s) declaree(s) par l'instance). |
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
2026-08-28 13:45:44 -04:00
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 33 revendication(s) de port, aucune collision entre roles co-localises (38 groupes). |
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (26 genere(s) exempte(s)). |
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). |
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 9 hote(s) detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
| P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (SITE-Chezlepro) : noeud, stockage, pont — tous offerts. |
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 36 role(s) serveur/client tous nommes, 37 groupe(s) cite(s) en table existent tous. |
| P39 | Glossaire : tout mot employe est enseigne | — | ✅ OK | Glossaire complet : 81 terme(s) du jargon expliques, 15 lien(s) valides, 27 page(s) de wiki toutes atteignables. |
| P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. |
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
2026-08-28 14:23:33 -04:00
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 48 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 4 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal, OPS-Patient0/product |
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 add94f2 (08-25), remplace par `bande_basse_de`. Invisible parce que le geste quotidien est `make prouver`, qui ne joue pas les tests. Remis sur le contrat actuel, avec sa contrepartie : sans `bande_basse_de`, aucun chevauchement n'est tolere. 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
2026-08-28 13:09:28 -04:00
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 5 machine(s) du plan retrouvees, 57 regle(s) du site. |
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 4 integration(s) appliquent leur serveur avant leurs clients. |
| P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). |
| P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos |
| P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). |
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 84 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
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 add94f2 (08-25), remplace par `bande_basse_de`. Invisible parce que le geste quotidien est `make prouver`, qui ne joue pas les tests. Remis sur le contrat actuel, avec sa contrepartie : sans `bande_basse_de`, aucun chevauchement n'est tolere. 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
2026-08-28 13:09:28 -04:00
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (112 lignes). |
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 122 regles `pass`), tous non consignes et tous motives. |
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
| P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. |
## Couverture des affirmations ✅ du registre
Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus.
Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005
`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite
d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ;
elles restent hors du harnais recurrent (rien d'executable a rejouer).
## Declarations d'intention (⚪ invérifiables localement — assumees)
Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees**
comme declarations d'intention, non comme preuves :
- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement
contre une flotte vivante.
- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles.
- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee.
- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume.
_Rapport genere le 2026-08-28._