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>
97 lines
6.9 KiB
Markdown
97 lines
6.9 KiB
Markdown
# 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`](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`](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
|
|
|
|
```bash
|
|
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 `P15` (inventaire Ansible complet, `ansible-inventory --list`) déchiffre le
|
|
`group_vars` de l'instance. Sans `ANSIBLE_VAULT_PASSWORD_FILE`, 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 :
|
|
|
|
```bash
|
|
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
|
make prouver
|
|
```
|
|
|
|
## Ce que couvre `make prouver`
|
|
|
|
| # | 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.)
|