emancipation : l instrument de la quatrieme ligne — couper, pas sonder
Some checks are pending
verifier / verifier (push) Waiting to run
Some checks are pending
verifier / verifier (push) Waiting to run
docs/filiation-emancipation.md decrit quatre temps et n en outillait que trois. Le
quatrieme est celui qu on oublie : « une emancipation non prouvee est une
emancipation non faite ».
SONDER NE PROUVE RIEN. Verifier que le service local repond ne dit pas si l amont
sert encore — le depot le disait deja du cache : « tant qu internet repond, un apt
update qui reussit ne dit pas d ou vient l octet ». L instrument COUPE donc l amont
et refait marcher la chose.
LE MEME ESSAI REND LES DEUX VERDICTS, et c est ce qui le rend honnete :
coupe, la fonction marche -> EMANCIPE, et c est prouve
coupe, la fonction casse -> PAS EMANCIPE, dependance prouvee REELLE
Le second n est pas un echec de l outil, c est son CONTROLE NEGATIF rendu par la
meme commande. Une preuve d emancipation incapable de montrer la dependance qu elle
mesure ne prouverait rien le jour ou elle passerait au vert.
UN TEMOIN PRECEDE LA COUPURE : la fonction marchait-elle seulement avant ? Sans lui,
une panne preexistante se lirait comme une dependance.
LA COUPURE EST GARANTIE REVERSIBLE : une TABLE nftables dediee, jamais une regle
glissee dans une table existante — elle se retire d un geste et ne peut pas laisser
d etat partiel. Le bloc `always` la retire meme si la mesure echoue ou si le play
est interrompu, et une tache verifie ensuite qu elle a bien disparu.
MESURE LE JOUR DE SA NAISSANCE, les deux verdicts sur du vrai materiel :
obs-01 / resolveur PAS EMANCIPE — plus aucune resolution des la coupure
forge-01 / artefacts EMANCIPE — apt installe, cache du site coupe
Ce second verdict a ete DOUTE puis verifie : apt aurait pu reussir en rejouant des
listes fraiches. Refait avec un dossier de listes NEUF, amont coupe : reussit
quand meme. Le cache sert vraiment son contenu.
make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
This commit is contained in:
parent
89ca92b132
commit
0b653170fa
6 changed files with 354 additions and 3 deletions
43
CHANGELOG.md
43
CHANGELOG.md
|
|
@ -1,5 +1,48 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-01 — `make emancipation-prouver` : couper, pas sonder
|
||||
|
||||
**55 preuves.** `docs/filiation-emancipation.md` decrivait quatre temps et n'en outillait
|
||||
que trois. Le quatrieme est celui qu'on oublie — *« une emancipation non prouvee est une
|
||||
emancipation non faite »*.
|
||||
|
||||
**Sonder ne prouve rien.** Verifier que le service local repond ne dit pas si l'amont sert
|
||||
encore ; le depot le disait deja du cache : *« tant qu'internet repond, un `apt update` qui
|
||||
reussit ne dit pas d'ou vient l'octet »*. L'instrument **coupe** donc l'amont, et refait
|
||||
marcher la chose.
|
||||
|
||||
### Le meme essai rend les deux verdicts
|
||||
|
||||
```
|
||||
coupe, la fonction marche -> ÉMANCIPÉ, et c'est prouvé
|
||||
coupe, la fonction casse -> PAS ÉMANCIPÉ, dépendance prouvée RÉELLE
|
||||
```
|
||||
|
||||
Le second n'est pas un echec de l'outil : c'est son **controle negatif**, rendu par la meme
|
||||
commande. *Une preuve d'emancipation incapable de montrer la dependance qu'elle mesure ne
|
||||
prouverait rien le jour ou elle passerait au vert.*
|
||||
|
||||
Un **temoin** precede la coupure — la fonction marchait-elle seulement avant ? Sans lui,
|
||||
une panne preexistante se lirait comme une dependance.
|
||||
|
||||
### La coupure est garantie reversible
|
||||
|
||||
Une **table** `nftables` dediee, jamais une regle glissee dans une table existante : elle se
|
||||
retire d'un geste et ne peut pas laisser d'etat partiel. Le bloc `always` la retire meme si
|
||||
la mesure echoue ou si le play est interrompu, et une tache verifie ensuite qu'elle a bien
|
||||
disparu.
|
||||
|
||||
### Mesure le jour de sa naissance
|
||||
|
||||
```
|
||||
obs-01 / resolveur PAS ÉMANCIPÉ — plus aucune resolution des la coupure
|
||||
forge-01 / artefacts ÉMANCIPÉ — apt installe, cache du site coupe
|
||||
```
|
||||
|
||||
**Ce second verdict a ete doute avant d'etre cru** : `apt` aurait pu reussir en rejouant des
|
||||
listes deja fraiches. Refait avec un dossier de listes **neuf**, amont coupe — il reussit
|
||||
quand meme. Le cache sert vraiment son contenu.
|
||||
|
||||
## 2026-08-31 — D-82 : patient 0 n'est le parent de personne
|
||||
|
||||
**55 preuves.** Le dilemme ouvert le 2026-08-28 est referme, et ce sont les faits qui l'ont
|
||||
|
|
|
|||
29
Makefile
29
Makefile
|
|
@ -338,6 +338,35 @@ inseminer: ansible-runtime ## Le SITE amorce le runner d'un tenant — TENANT=<d
|
|||
printf '%s\n' "L armer est le geste d un humain, depuis son poste :"; \
|
||||
printf '%s\n' " make appliquer GROUPE=serveur_ops_tenant (voir docs/filiation-emancipation.md)"
|
||||
|
||||
# PROUVER QU'UN LIEN DE FILIATION EST COUPE — la quatrieme ligne de
|
||||
# `docs/filiation-emancipation.md`, celle qu'on oublie : « une emancipation non prouvee
|
||||
# est une emancipation non faite ».
|
||||
#
|
||||
# GESTE DESTRUCTIF ET REVERSIBLE : il COUPE l'amont pendant quelques secondes pour voir
|
||||
# si la fonction tient sans lui. Sonder ne prouverait rien — tant que l'amont repond, une
|
||||
# fonction qui marche ne dit pas d'ou vient l'octet.
|
||||
#
|
||||
# LE MEME ESSAI REND LES DEUX VERDICTS, et c'est voulu : « emancipe, prouve » quand la
|
||||
# fonction tient, « pas emancipe, dependance REELLE » quand elle casse. Le second est le
|
||||
# controle negatif du premier.
|
||||
.PHONY: emancipation-prouver
|
||||
emancipation-prouver: ansible-runtime _instance-requise ## Prouve qu un lien est coupe — SERVICE=<artefacts|genome|resolveur> [HOTE=] CONFIRMER=true
|
||||
@set -e; \
|
||||
if [[ -z "$(SERVICE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec SERVICE=<artefacts|genome|resolveur>.'; \
|
||||
exit 2; \
|
||||
fi; \
|
||||
if [[ "$(CONFIRMER)" != "true" ]]; then \
|
||||
printf '%s\n' 'Refus: cette preuve COUPE l amont quelques secondes pour mesurer.'; \
|
||||
printf '%s\n' 'La coupure est retiree quoi qu il arrive (bloc `always`), mais'; \
|
||||
printf '%s\n' 'pendant ce temps la fonction eprouvee peut echouer sur cet hote.'; \
|
||||
printf '%s\n' 'Relancer avec CONFIRMER=true.'; \
|
||||
exit 2; \
|
||||
fi; \
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/maintenance/emancipation-prouver.yml \
|
||||
-e emancipation_service=$(SERVICE) \
|
||||
$(if $(HOTE),-e emancipation_hotes=$(HOTE)) $(ARGS)
|
||||
|
||||
.PHONY: sonder
|
||||
sonder: ## Sonde une cible et DIT ce qui distingue — CIBLE=<hote|ip> [PORTS="22 443"]
|
||||
@if [[ -z "$(CIBLE)" ]]; then \
|
||||
|
|
|
|||
91
docs/audit/preuve-2026-09-01.md
Normal file
91
docs/audit/preuve-2026-09-01.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-01
|
||||
|
||||
> 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).
|
||||
|
||||
- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (55 OK · 0 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| 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 : 39 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 37 rôles, 97 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 | serveur_resolveur_site |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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 | 31 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 20, 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. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 53 scripts expliques et atteignables, 107 cibles make documentees, 63 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 41 exigence(s) de role, toutes satisfaites (130 cle(s) declaree(s) par l'instance). |
|
||||
| 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 (29 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 : 38 role(s) serveur/client tous nommes, 39 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. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 49 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 |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 5 machine(s) du plan retrouvees, 66 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. |
|
||||
| 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 (115 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 131 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. |
|
||||
| P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 15 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b |
|
||||
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 10 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 15 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 15. |
|
||||
|
||||
## 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-09-01._
|
||||
|
|
@ -26,7 +26,7 @@ README de rôles). Cette page comble ces deux trous.
|
|||
| rôles | 63 | `roles/*/` |
|
||||
| README de rôles | 63 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| documents | 38 | `docs/*.md` |
|
||||
| pièces d'audit | 31 | `docs/audit/*` |
|
||||
| pièces d'audit | 32 | `docs/audit/*` |
|
||||
| unités de wiki | 27 | `wiki/*.md` |
|
||||
| décisions en vigueur | 79 | lignes `\| **D-nn** \|` de `decisions-architecture.md` |
|
||||
| décisions renversées | 3 | lignes `\| **D-nn** —` du même document |
|
||||
|
|
|
|||
|
|
@ -138,8 +138,36 @@ servi. Sans elle, on croirait s'être émancipé en restant dépendant sans le s
|
|||
exactement le défaut que ce dépôt traque partout ailleurs : un vert sur un périmètre vide.
|
||||
**Une émancipation non prouvée est une émancipation non faite.**
|
||||
|
||||
*L'instrument de la quatrième ligne n'existe pas encore. C'est la pièce manquante nommée
|
||||
au bas de ce document.*
|
||||
### L'instrument de la quatrième ligne — `make emancipation-prouver`
|
||||
|
||||
Il existe depuis le 2026-09-01, et il **coupe** au lieu de sonder.
|
||||
|
||||
Vérifier que le service local répond ne prouve rien : *tant que l'amont répond, une
|
||||
fonction qui marche ne dit pas d'où vient l'octet.* L'instrument bloque donc l'amont —
|
||||
table `nftables` dédiée, retirée par un bloc `always` quoi qu'il arrive — puis refait
|
||||
marcher la chose.
|
||||
|
||||
**Le même essai rend les deux verdicts, et c'est ce qui le rend honnête :**
|
||||
|
||||
```
|
||||
coupé, la fonction marche -> ÉMANCIPÉ, et c'est prouvé
|
||||
coupé, la fonction casse -> PAS ÉMANCIPÉ, et la dépendance est prouvée RÉELLE
|
||||
```
|
||||
|
||||
Le second n'est pas un échec de l'outil : c'est son **contrôle négatif**, rendu par la
|
||||
même commande. Une preuve d'émancipation incapable de montrer la dépendance qu'elle mesure
|
||||
ne prouverait rien le jour où elle passerait au vert.
|
||||
|
||||
Un témoin précède la coupure — *la fonction marchait-elle seulement avant ?* Sans lui, une
|
||||
panne préexistante se lirait comme une dépendance.
|
||||
|
||||
```
|
||||
make emancipation-prouver SERVICE=artefacts HOTE=forge-01 CONFIRMER=true
|
||||
```
|
||||
|
||||
*Mesuré le jour de sa naissance : `obs-01` ne résout plus rien dès que le résolveur du
|
||||
site est coupé — dépendance réelle ; `forge-01` installe des paquets le cache du site
|
||||
coupé, listes vidées — émancipation prouvée sur ce service.*
|
||||
|
||||
## Forme attendue d'une déclaration
|
||||
|
||||
|
|
|
|||
160
playbooks/maintenance/emancipation-prouver.yml
Normal file
160
playbooks/maintenance/emancipation-prouver.yml
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
# PROUVER QU'UN LIEN EST COUPÉ — l'instrument de la quatrième ligne.
|
||||
#
|
||||
# `docs/filiation-emancipation.md` décrit quatre temps, et n'en outillait que trois :
|
||||
#
|
||||
# le lien de filiation déclaré au plan le moteur
|
||||
# le constat d'aptitude « tu pourrais » la machine INSTRUIT
|
||||
# l'acte d'émancipation retirer le lien l'HUMAIN, CONFIRMER=true
|
||||
# la preuve que c'est coupé ← CE PLAYBOOK la machine, APRÈS l'acte
|
||||
#
|
||||
# « Une émancipation non prouvée est une émancipation non faite. »
|
||||
#
|
||||
# ─── POURQUOI SONDER NE SUFFIT PAS ───────────────────────────────────────────
|
||||
#
|
||||
# Vérifier que le service local répond ne prouve RIEN. Le dépôt le dit déjà, à propos du
|
||||
# cache : « tant qu'internet répond, un `apt update` qui réussit ne dit pas d'où vient
|
||||
# l'octet ». Un écosystème peut avoir monté son propre service ET continuer à tirer de
|
||||
# l'amont sans que personne ne le voie.
|
||||
#
|
||||
# On COUPE donc, et on refait marcher la chose. C'est le seul geste qui distingue
|
||||
# « j'ai un service local » de « je n'ai plus besoin du tien ».
|
||||
#
|
||||
# ─── LE MÊME ESSAI REND LES DEUX VERDICTS ────────────────────────────────────
|
||||
#
|
||||
# Le verdict vient de la MESURE, jamais d'une déclaration :
|
||||
#
|
||||
# coupé, la fonction marche -> ÉMANCIPÉ, et c'est prouvé
|
||||
# coupé, la fonction casse -> PAS ÉMANCIPÉ — et la dépendance est prouvée REELLE
|
||||
#
|
||||
# Le second n'est pas un échec de l'outil : c'est son contrôle négatif, rendu par la même
|
||||
# commande. Une preuve d'émancipation qui ne saurait pas montrer la dépendance qu'elle
|
||||
# mesure ne prouverait rien le jour où elle passerait au vert.
|
||||
#
|
||||
# ─── LA COUPURE EST GARANTIE RÉVERSIBLE ──────────────────────────────────────
|
||||
#
|
||||
# Une TABLE nftables dédiée, jamais une règle glissée dans une table existante : elle se
|
||||
# retire d'un seul geste, et ne peut pas laisser d'état partiel si l'essai s'interrompt.
|
||||
# Le `always` la retire quoi qu'il arrive — y compris si la mesure échoue.
|
||||
#
|
||||
# Usage :
|
||||
# make emancipation-prouver SERVICE=artefacts CONFIRMER=true
|
||||
# make emancipation-prouver SERVICE=genome CONFIRMER=true
|
||||
# make emancipation-prouver SERVICE=resolveur CONFIRMER=true
|
||||
|
||||
- name: Prouver qu'un lien de filiation est coupé
|
||||
hosts: "{{ emancipation_hotes | default('hotes_actifs') }}"
|
||||
gather_facts: false
|
||||
become: true
|
||||
vars:
|
||||
# CE QU'ON COUPE, ET CE QU'ON REFAIT MARCHER. Chaque service nomme son amont (lu au
|
||||
# plan, jamais écrit ici) et l'épreuve qui dit s'il tient sans lui.
|
||||
emancipation_services:
|
||||
artefacts:
|
||||
amont: "{{ serveur_artefacts_amont | default(artefacts_amorcage | default('')) }}"
|
||||
epreuve: "apt-get -qq update"
|
||||
dit: "installer des paquets"
|
||||
genome:
|
||||
amont: "{{ serveur_ops_forge_amont | default('') }}"
|
||||
epreuve: "git ls-remote {{ serveur_ops_forge_amont | default('') }}/genome/set-ops-public.git HEAD"
|
||||
dit: "cloner le génome"
|
||||
resolveur:
|
||||
amont: "{{ dns_amorcage | default('') }}"
|
||||
epreuve: "getent hosts deb.debian.org"
|
||||
dit: "résoudre un nom public"
|
||||
emancipation_ce_service: "{{ emancipation_services[emancipation_service] }}"
|
||||
# L'ADRESSE SEULE, sans schéma ni port : c'est elle qu'on bloque.
|
||||
emancipation_adresse: >-
|
||||
{{ (emancipation_ce_service.amont | regex_replace('^\w+://', '')
|
||||
| regex_replace('[:/].*$', '')) }}
|
||||
|
||||
tasks:
|
||||
- name: Exiger un service connu et un amont déclaré
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- emancipation_service is defined
|
||||
- emancipation_service in emancipation_services
|
||||
- emancipation_adresse | length > 0
|
||||
fail_msg: >-
|
||||
SERVICE doit valoir l'un de {{ emancipation_services.keys() | join(', ') }},
|
||||
et cet écosystème doit DÉCLARER l'amont correspondant. Un emprunt qu'aucune
|
||||
variable ne nomme ne peut pas être prouvé coupé — c'est la forme normée de
|
||||
`docs/filiation-emancipation.md` qui rend cette preuve possible.
|
||||
# PAS de `run_once` : l'amont se lit dans les variables de CHAQUE hote. Un
|
||||
# ecosysteme peut tres bien emprunter sur une machine et pas sur une autre — c'est
|
||||
# meme le cas ici, ou seul le porteur du cache chaine vers celui du site. Verifier
|
||||
# une fois pour toutes lirait le premier hote et parlerait au nom des autres.
|
||||
|
||||
- name: Prouver
|
||||
block:
|
||||
# ── L'ÉTAT AVANT : la fonction marche-t-elle seulement ? ────────────
|
||||
#
|
||||
# Sans ce témoin, une fonction déjà cassée AVANT la coupure se lirait comme une
|
||||
# dépendance prouvée. On mesure donc le point de départ.
|
||||
- name: Témoin — la fonction marche-t-elle AVANT toute coupure ?
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ emancipation_ce_service.epreuve }}"
|
||||
register: emancipation_temoin
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Refuser de mesurer une fonction déjà cassée
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- emancipation_temoin.rc == 0
|
||||
fail_msg: >-
|
||||
« {{ emancipation_ce_service.dit }} » ne marche DÉJÀ PAS sur cet hôte, avant
|
||||
qu'on ait rien coupé. Une coupure ne prouverait rien ici : on lirait une
|
||||
panne préexistante comme une dépendance. Réparer d'abord.
|
||||
|
||||
# ── LA COUPURE ─────────────────────────────────────────────────────
|
||||
- name: Couper le lien vers l'amont
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- nft
|
||||
- -f
|
||||
- "-"
|
||||
stdin: |
|
||||
table inet setops_emancipation {
|
||||
chain output {
|
||||
type filter hook output priority -10; policy accept;
|
||||
ip daddr {{ emancipation_adresse }} reject with icmpx type admin-prohibited
|
||||
}
|
||||
}
|
||||
changed_when: true
|
||||
|
||||
# ── L'ÉPREUVE ──────────────────────────────────────────────────────
|
||||
- name: L'épreuve, l'amont coupé
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ emancipation_ce_service.epreuve }}"
|
||||
register: emancipation_epreuve
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
always:
|
||||
# RETIRÉE QUOI QU'IL ARRIVE — y compris si l'épreuve échoue, si le play est
|
||||
# interrompu, ou si la mesure lève. Une coupure qui survit à son essai serait une
|
||||
# panne qu'on aurait fabriquée soi-même.
|
||||
- name: Rétablir le lien
|
||||
ansible.builtin.command:
|
||||
argv: ["nft", "delete", "table", "inet", "setops_emancipation"]
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
|
||||
- name: Vérifier que le lien est bien rétabli
|
||||
ansible.builtin.command:
|
||||
argv: ["nft", "list", "table", "inet", "setops_emancipation"]
|
||||
register: emancipation_reste
|
||||
failed_when: emancipation_reste.rc == 0
|
||||
changed_when: false
|
||||
|
||||
- name: "✔ Émancipation — {{ emancipation_service }}"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ (emancipation_epreuve.rc == 0) | ternary(
|
||||
'ÉMANCIPÉ (prouvé) — « ' ~ emancipation_ce_service.dit ~ ' » fonctionne
|
||||
l amont ' ~ emancipation_adresse ~ ' COUPÉ. Le lien peut être retiré du
|
||||
plan : il ne sert plus.',
|
||||
'PAS ÉMANCIPÉ — « ' ~ emancipation_ce_service.dit ~ ' » casse dès que
|
||||
l amont ' ~ emancipation_adresse ~ ' est coupé. La dépendance est REELLE,
|
||||
et cette mesure est le contrôle négatif de la preuve future.') }}
|
||||
Loading…
Reference in a new issue