Set-OPS-Public/docs/audit/reference-avant-reconstruction-2026-08-08.md

136 lines
6.5 KiB
Markdown
Raw Normal View History

# État de référence — avant reconstruction from-zero
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 :** qui **compare après une reconstruction** — le témoin contre lequel se lit
> toute divergence. Historique : il ne décrit pas l'état courant.
> Figé le 2026-08-08, écosystème **chezlepro** (index 17, 14 VM), avant destruction et
> rejeu complet du plan. **Ce document existe pour que « identique » soit prouvable
> plutôt que ressenti.** Toute divergence après reconstruction se lit contre lui.
## Les cinq devis, avant
```
identite CONFORME : le réel correspond au déclaré (1 compte(s) dans l'annuaire).
certificats CONFORME : aucun certificat servi n'est en fin de vie.
expositions CONFORME : toute exposition déclarée répond, des deux points de vue.
postgresql CONFORME : chiffrement imposé, et aucun réseau hors du supernet dérivé.
courriel CONFORME : la chaîne tient, de la résolution LDAP à la boîte.
```
## Ce que la flotte porte
```
backup-01 10.27.18.21 1 service(s) metier
collab-01 10.27.21.21 3 service(s) metier
data-sql-01 10.27.18.11 2 service(s) metier
edge-mta-01 10.27.16.21 2 service(s) metier
forge-01 10.27.21.11 1 service(s) metier
idm-01 10.27.17.11 3 service(s) metier
infra-dns-01 10.27.19.11 1 service(s) metier
infra-edge-01 10.27.16.11 2 service(s) metier
infra-mail-01 10.27.19.31 2 service(s) metier
infra-pki-01 10.27.19.21 2 service(s) metier
mon-01 10.27.20.21 6 service(s) metier
obs-01 10.27.20.11 2 service(s) metier
web-dorsal-01 10.27.21.41 2 service(s) metier
web-frontal-01 10.27.21.31 2 service(s) metier
```
## Ce qui sera perdu et devra être refait à la main
| Objet | Conséquence | Geste de reprise |
|---|---|---|
| **Clé racine de l'AC** (`/etc/step-ca/secrets/root_ca_key`) | la racine installée dans le navigateur devient inutile ; tous les certificats changent | `make ca-racine` + réinstaller, en comparant l'empreinte (§6.3) |
| **Mot de passe du compte `sysadmin`** | l'annuaire est recréé vide, puis amorcé | relever le jeton d'amorçage en voûte (§6.1) et le changer |
| **Contenu applicatif** (dépôts Forgejo, fichiers Nextcloud, courriels, tableaux Grafana) | non sauvegardé, non reconstructible par le code | aucun — assumé : c'est un POC |
> Ce qui **survit** : les deux voûtes et `~/.config/setops-vault-pass` vivent hors dépôt
> et hors cluster ; le code est sur `eregion.chezlepro.ca` (192.168.12.201), machine
> distincte du tenant. Rien de ce qui est nécessaire au rejeu n'est dans les 14 VM.
---
## Après reconstruction — 2026-08-09
Écosystème détruit (14 VM, disques compris) puis **rejoué depuis le plan seul**. Aucune
sauvegarde restaurée : ni LDAP, ni base, ni certificat, ni fichier.
```
identite CONFORME
certificats CONFORME
expositions CONFORME
postgresql CONFORME
courriel CONFORME
```
**Cinq sur cinq, identiques à la référence.** C'est la première fois que le dépôt peut dire
que le système reconstruit est celui que le plan décrit — au lieu de dire que le dépôt est
cohérent avec lui-même.
### Les six défauts que seul un rejeu depuis zéro pouvait montrer
| # | Défaut | Pourquoi il dormait |
|---|---|---|
| 1 | `amorcage_acces_courriel` jamais déclaré par le tenant | le compte existait déjà, le garde-fou n'avait jamais eu à se déclencher |
| 2 | rôles de realm créés **après** leur attachement aux groupes | un passage précédent les avait créés |
| 3 | claim de groupes posé **avant** les clients OIDC | les clients existaient déjà |
| 4 | écriture Keycloak annulée par le collecteur de transactions | la synchro LDAP complète ne se rejoue pas en exploitation |
| 5 | `grafana-cli` lancé avant que Grafana ait fini de démarrer | le service tournait depuis toujours |
| 6 | port d'Alloy (12345) en collision avec le SASL de Dovecot | Alloy tenait le port ; c'est **Dovecot** qui échouait, en silence |
Trois défauts d'ordre, deux courses de premier démarrage, un conflit de port. **Aucun
n'était visible à un redéploiement**, et aucun aux 31 preuves statiques — elles lisent le
dépôt, pas la machine.
Le sixième mérite d'être relu : le conflit existait depuis toujours, mais dans l'autre
sens. Alloy tenait `12345` et l'écoute SASL de Dovecot échouait sans que personne ne le
voie. La reconstruction a inversé l'ordre de démarrage et rendu visible un défaut qui
était là depuis le début.
### Ce qui a dû être refait à la main
- **La base de Grafana** : le premier démarrage, interrompu par le défaut nº 5, avait
laissé un schéma à moitié migré (`no such column: with_credentials`). Base mise de côté,
recréée par Grafana — 95 s pour lier son port, ce qui explique la course.
- **Rien d'autre.** Ni certificat, ni compte, ni zone DNS, ni base de données.
---
## Idempotence de la flotte — référence du 2026-08-09
Mesure faite après la seconde reconstruction, en rejouant `make deployer-tout` sur une
flotte intacte. Le dépôt n'avait jamais fait ce test à l'échelle de la flotte.
```
plays taches ok changed sautees
rejeu depuis zero 61 2152 924 443
2e passage 30 2249 17 ...
3e passage (apres) 30 2266 0 227
```
**Zéro tâche `changed`, zéro échec.**
Et ce zéro n'est pas du silence : **plus de tâches se sont exécutées au passage à vide
qu'au rejeu** (2266 contre 2152). Elles ont toutes tourné, et toutes trouvé le système
conforme. Un zéro obtenu avec moins de tâches aurait signifié l'inverse — des rôles qui ne
font plus leur travail.
### Ce que les 17 du deuxième passage cachaient
| Tâche | Ce que c'était vraiment |
|---|---|
| 13× `Activer et demarrer node_exporter` | **le service était mort** — tué par `SIGHUP` à chaque renouvellement de certificat |
| 2× Prometheus | liste de cibles non ordonnée (`intersect` rend un ensemble) |
| 2× Forgejo | **le dépôt faisait tourner le secret JWT du service** à chaque déploiement |
Deux des trois n'étaient pas des défauts d'idempotence : c'étaient des **pannes**, qu'un
compte de `changed` a rendues visibles. La collecte de métriques s'arrêtait toutes les
24 heures sur les quatorze hôtes, et les jetons OAuth2 de la forge étaient invalidés à
chaque passage. Ni l'une ni l'autre ne se signalait autrement.
### À quoi sert ce chiffre
Il devient la **ligne de base**. Un déploiement futur qui rapporte `changed` sur une flotte
non modifiée signale quelque chose : soit une dérive du système, soit un rôle qui a cessé
d'être idempotent. Tant que le fond était à 17, ce signal était noyé.