Compare commits
No commits in common. "main" and "v2026.08.21" have entirely different histories.
main
...
v2026.08.2
701 changed files with 2153 additions and 76232 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,9 +9,6 @@ facts_cache/
|
|||
**/vault.yml
|
||||
# Underlay = fabric physique de l'operateur (cluster-global) ; gabarit public seul.
|
||||
/underlay.yml
|
||||
# Contexte actif de CETTE machine (`site:<depot>` ou `locataire:<depot>`) : propre a chaque
|
||||
# poste et a chaque runner, jamais versionne. Voir scripts/contexte.py.
|
||||
/contexte
|
||||
*.secret
|
||||
*.pem
|
||||
*.key
|
||||
|
|
|
|||
135
AGENTS.md
135
AGENTS.md
|
|
@ -20,7 +20,7 @@ Piliers de l’écosystème :
|
|||
- confiance — autorité de certification interne (ACME) ;
|
||||
- nommage et adressage — DNS interne et nomenclature dérivable ;
|
||||
- données — bases relationnelles et cache, avec registre des connexions ;
|
||||
- communication — service de courriel souverain (boîtes LDAP, SMTP, IMAP, antispam, DKIM) ; le relais des notifications système en est distinct (`client_smtp`) ;
|
||||
- communication — relais courriel interne ;
|
||||
- observabilité et supervision — métriques, journaux, tableaux de bord, supervision active ;
|
||||
- applicatif — services internes (forge, etc.) et couche web.
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ Propriétés visées, avec leurs nuances honnêtes :
|
|||
- **Souverain / auto-suffisant** : c’est l’objectif. Aucune dépendance à un service externe pour la confiance, l’identité, le nom ou la communication. Cela justifie le choix de construire plutôt qu’assembler des SaaS.
|
||||
- **Déclaratif et convergent** : l’état voulu est décrit dans des registres machine-lisibles (`instance/plan/nomenclature.yml`, `docs/dependances-groupes.yml`, `instance/plan/bases-donnees.yml`) et appliqué par les groupes Ansible. Ces registres sont la **source unique de vérité**.
|
||||
- **Pas (encore) auto-réparé** : la convergence est pilotée par l’opérateur (GUI / CLI / `make`), pas une boucle fermée d’auto-remédiation.
|
||||
- **Éprouvé sur VM réelles — mais la nuance tient toujours.** Cette ligne a dit, jusqu’au 2026-09-06, que « une grande partie est planifiée et validée mais pas encore exécutée contre des VM réelles ». C’est faux depuis longtemps : la flotte a été **rasée et remontée depuis zéro** le 2026-08-13, puis **deux fois le 2026-09-02** (15/15 puis 14/14 hôtes, 0 échec, `make valider` à 0 échec sur 13 hôtes). Ce qui reste vrai, et qu’il faut garder : **un `--syntax-check` vert ne prouve rien de l’exécution**, et le tableau de maturité de `docs/catalogue-services.md` — pas ce fichier — dit ce qui est éprouvé et ce qui ne l’est pas. Ne jamais présenter comme « en production » un rôle que ce tableau ne donne pas pour tel.
|
||||
- **Définition avant déploiement** : une grande partie est planifiée et validée (`--syntax-check`, `ansible-lint`) mais pas encore exécutée contre des VM réelles. Ne jamais présenter un rôle non déployé comme « en production ».
|
||||
|
||||
Conséquence pour le travail : préserver la discipline qui tient l’ensemble — registres comme source unique, dépendances explicites, validation de chaque pièce, secrets hors dépôt. C’est ce qui empêche l’écosystème de devenir un objet ingérable. Le template Debian 13 reste la fondation (le moule des VM), pas la finalité.
|
||||
|
||||
|
|
@ -40,9 +40,7 @@ Conséquence pour le travail : préserver la discipline qui tient l’ensemble
|
|||
`Set-OPS` se pilote par un **plan**, pas par l’édition directe de l’inventaire.
|
||||
L’inventaire Ansible est **généré** depuis le plan.
|
||||
|
||||
**RÈGLE D’OR : `instance/inventories/<inventaire>/hosts.yml` est un artefact GÉNÉRÉ. Ne jamais l’éditer à la main.** On édite le *plan*, puis on régénère.
|
||||
|
||||
`<inventaire>` est une **place, pas un nom** : le moteur le résout (`principal`, sinon `production` — `scripts/inventory_rules.py`, `ORDRE_INVENTAIRE`). La flotte dit `principal`, le modèle public dit `production`. Ne coder ni l’un ni l’autre en dur dans un document.
|
||||
**RÈGLE D’OR : `instance/inventories/production/hosts.yml` est un artefact GÉNÉRÉ. Ne jamais l’éditer à la main.** On édite le *plan*, puis on régénère.
|
||||
|
||||
- L’**application** est l’entité pivot ; le **groupe** Ansible n’est qu’une capacité (le rôle appliqué), plus une cible de liaison.
|
||||
- Le plan vit dans des registres machine-lisibles : `instance/plan/serveurs.yml` (les VM), `instance/plan/applications.yml` (les services et leurs liens `requiert`/`utilise`/`expose`), `instance/plan/bases-donnees.yml`, `instance/plan/domaines.yml`, `instance/plan/nomenclature.yml`.
|
||||
|
|
@ -53,11 +51,7 @@ L’inventaire Ansible est **généré** depuis le plan.
|
|||
|
||||
Référence complète : **`docs/plan-et-generation.md`**.
|
||||
|
||||
**Plan de contrôle gelé en périmètre** : le GUI, le générateur et la modélisation sont volontairement *maison* et **souverains**, mais leur périmètre est gelé. Ne pas y ajouter de fonctionnalités de type NetBox/AWX — **historique d'audit applicatif, API riche, source de vérité partagée entre organisations** : le besoin réel d'une de ces fonctions est le **signal d'adopter l'outil mûr correspondant** (NetBox pour la source de vérité, AWX pour l'exécution), pas de le réimplémenter.
|
||||
|
||||
**Avant d'invoquer un seuil, vérifier qu'il n'est pas déjà couvert autrement (D-84).** Deux exemples cités ici jusqu'au 2026-09-08 ne tenaient plus : le **RBAC** est assuré par la séparation *cryptographique* des voûtes et des runners — l'adopter d'AWX serait régresser ; la **détection de conflits IPAM** est sans objet, rien ne s'alloue et cinq preuves (P20, P21, P23, P28, P33) tiennent déjà ce qu'un IPAM vérifierait. Une carte des seuils fausse ne fait pas perdre du temps : elle fait **franchir un seuil qui ne l'est pas**.
|
||||
|
||||
**Le gel porte sur les FONCTIONS, jamais sur les VUES.** Montrer à l'écran ce que le moteur sait déjà — l'écart d'un devis, l'état du diff, le périmètre sur lequel un ✅ a porté — ne franchit aucun seuil. Décision et seuils : **`docs/positionnement.md`**.
|
||||
**Plan de contrôle gelé en périmètre** : le GUI, le générateur, l'IPAM et la modélisation sont volontairement *maison* et **souverains**, mais leur périmètre est gelé. Ne pas y ajouter de fonctionnalités de type NetBox/AWX (RBAC, historique d'audit, détection de conflits IPAM, API riche) : le besoin réel d'une de ces fonctions est le **signal d'adopter l'outil mûr correspondant** (NetBox pour la source de vérité, AWX pour l'exécution), pas de le réimplémenter. Décision et seuils : **`docs/positionnement.md`**.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -166,11 +160,9 @@ Avant de proposer un changement comme terminé, vérifier au minimum la syntaxe
|
|||
Exemple pour le template Debian 13 Proxmox :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i "$SETOPS_INVENTAIRE" playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
|
||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
|
||||
```
|
||||
|
||||
`SETOPS_INVENTAIRE` est **exporté par le `Makefile`**, qui résout le nom de l’inventaire au lieu de le coder en dur (cf. la règle d’or ci-dessus) — la variable est donc déjà là dans toute recette `make`. Pour couvrir tous les playbooks d’un coup : `make syntaxe`. Depuis un shell nu, la variable n’existe pas : passer le chemin réel de l’inventaire de l’instance montée.
|
||||
|
||||
Pour un autre playbook, remplacer le chemin par le playbook concerné.
|
||||
|
||||
Ne jamais déclarer un playbook prêt si `--syntax-check` échoue.
|
||||
|
|
@ -188,7 +180,7 @@ Si `ansible-lint` n’est pas disponible, le signaler clairement. Ne pas invente
|
|||
## Écrire, puis relire (D-68)
|
||||
|
||||
`--syntax-check` et `ansible-lint` prouvent que le dépôt est cohérent **avec lui-même**.
|
||||
C'est aussi ce que font les 94 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||||
C'est aussi ce que font les 35 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||||
moindre appel réseau. **Aucune ne demande au système déployé s'il ressemble à ce que le
|
||||
dépôt annonce.**
|
||||
|
||||
|
|
@ -215,9 +207,6 @@ make identite-plan make certificats-plan make expositions-plan
|
|||
make postgresql-plan make courriel-plan
|
||||
```
|
||||
|
||||
Le même patron existe **sous** les services, pour le monde physique — `make frontiere-plan`,
|
||||
`proxmox-fw-plan`, `sdn-plan`, `underlay-plan`, `placement-plan`.
|
||||
|
||||
Ils ne modifient rien et sortent en code 1 s'il y a un écart. Après un changement qui
|
||||
touche l'identité, les certificats, une exposition, la base ou le courriel, **lancer le
|
||||
devis correspondant** : une tâche verte ne prouve pas que le service rend son service.
|
||||
|
|
@ -287,32 +276,36 @@ Lorsqu’une commande shell est nécessaire, elle doit être encadrée avec les
|
|||
|
||||
## Structure générale du dépôt
|
||||
|
||||
Ce que la racine porte réellement (mesuré le 2026-09-06) :
|
||||
Le dépôt peut contenir progressivement :
|
||||
|
||||
```text
|
||||
roles/ les rôles Ansible
|
||||
playbooks/ les playbooks, classés par domaine
|
||||
scripts/ le moteur de plan, la GUI, les devis, les preuves
|
||||
filter_plugins/ les filtres Jinja du dépôt
|
||||
docs/ wiki/ la documentation
|
||||
exemples/ les modèles d'instance prêts à copier
|
||||
instance/ SYMLINK vers le dépôt de l'instance active — jamais un vrai dossier ici
|
||||
inventories/
|
||||
playbooks/
|
||||
roles/
|
||||
templates/
|
||||
files/
|
||||
scripts/
|
||||
docs/
|
||||
```
|
||||
|
||||
**Il n'y a ni `inventories/`, ni `templates/`, ni `files/` à la racine**, et il ne doit pas y en avoir : les inventaires appartiennent à l'instance (derrière le symlink), les gabarits et fichiers appartiennent à leur rôle.
|
||||
|
||||
Les playbooks sont classés par domaine :
|
||||
Les playbooks peuvent être classés par domaine :
|
||||
|
||||
```text
|
||||
playbooks/
|
||||
├── groupes/ un playbook par groupe opérationnel — la conformité passe par là
|
||||
├── maintenance/ les devis et les manœuvres ponctuelles
|
||||
├── modeles_vm/ la fabrication du gabarit doré
|
||||
├── proxmox/ le clonage et le cycle de vie des VM
|
||||
└── applications/ backup/ database/ monitoring/ web/ — vides, un README d'espace réservé
|
||||
├── groupes/
|
||||
├── maintenance/
|
||||
├── monitoring/
|
||||
├── networking/
|
||||
├── proxmox/
|
||||
├── modeles_vm/
|
||||
├── web/
|
||||
├── database/
|
||||
├── identity/
|
||||
├── backup/
|
||||
└── applications/
|
||||
```
|
||||
|
||||
Seuls les quatre premiers sont peuplés. Les cinq autres ne contiennent qu'un README : ce sont des **espaces réservés**, et leur existence contredit à demi la règle « ne pas créer de structure inutile » ci-dessous — les laisser vides est un choix assumé, en créer d'autres ne l'est pas.
|
||||
À ce jour, seuls `groupes/`, `maintenance/`, `modeles_vm/` et `proxmox/` sont réellement peuplés. Les autres domaines de cette liste sont **prospectifs** : ils n'apparaissent que lorsqu'un besoin réel les justifie (cf. la règle « ne pas créer de structure inutile » ci-dessous).
|
||||
|
||||
Les rôles peuvent être ajoutés progressivement selon les besoins.
|
||||
|
||||
|
|
@ -345,12 +338,11 @@ Ne pas multiplier les cibles `make` secondaires si elles ne correspondent pas à
|
|||
Les cibles d'exploitation des VM doivent privilégier les groupes :
|
||||
|
||||
```text
|
||||
make deployer HOTE=web-frontal-01
|
||||
make deployer HOTE=web-01
|
||||
make deployer-groupe GROUPE=serveur_debian
|
||||
make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveur_debian serveur_durci serveur_prometheus"
|
||||
```
|
||||
|
||||
**`make hote-planifier`, `hote-ajouter` et `hote-groupes` sont DÉPRÉCIÉES** — elles refusent et sortent en 2. Elles éditaient l'inventaire à la main, ce que la RÈGLE D'OR interdit. Pour ajouter un hôte : le déclarer dans `instance/plan/serveurs.yml` (ou la vue **Serveurs** du GUI), puis `make instancier-appliquer`. Le VMID n'est plus saisi : il est **dérivé** (neuf chiffres, miroir de l'IP — `117602101`, et non le format à cinq chiffres d'avant).
|
||||
|
||||
Éviter les cibles parallèles qui réappliquent les mêmes rôles par couche, par exemple `make socle`, `make durcissement`, `make converger` ou `make deployer-vm`.
|
||||
|
||||
Toute cible `make` qui lance une action destructive ou risquée doit exiger une confirmation explicite.
|
||||
|
|
@ -445,20 +437,15 @@ intégration cliente : raccorde les VM au service central
|
|||
Exemples :
|
||||
|
||||
```text
|
||||
PowerDNS serveur → hosts_statiques (plancher) + client_resolveur (opt-in)
|
||||
step-ca serveur → client_pki (certificat + renouvellement + rechargement du service)
|
||||
LDAP serveur → resoudre_annuaire, et les applications qui s'y lient (Postfix, Dovecot, Keycloak)
|
||||
Keycloak serveur → intégrations OIDC applicatives, ou serveur_oauth2_proxy si l'app n'a pas d'OIDC
|
||||
Prometheus → client_metrique (node_exporter) sur les VM
|
||||
Icinga2 → SANS AGENT : contrôles actifs depuis le cœur, résultats passifs poussés par l'API
|
||||
PowerDNS serveur → clients DNS / resolver / enregistrements
|
||||
step-ca serveur → confiance CA / ACME client
|
||||
LDAP serveur → SSSD / NSS / PAM client
|
||||
Keycloak serveur → intégrations OIDC applicatives
|
||||
Prometheus → node_exporter sur les VM
|
||||
Icinga2 → agent ou checks distants
|
||||
Grafana → datasources et dashboards côté plateforme
|
||||
```
|
||||
|
||||
Deux précisions qui ont manqué ici longtemps, et qui changent la conception d'un nouveau service :
|
||||
|
||||
- **Pas de login LDAP au niveau du système.** `SSSD` / `NSS` / `PAM` ne sont pas la couche cliente de l'annuaire : le rôle `client_ldap` a été **retiré (2026-07-04), hors conception**. L'annuaire sert les *applications*, pas l'ouverture de session Unix.
|
||||
- **Pas d'agent de supervision.** Icinga ne pose rien sur les hôtes. Un nœud qui doit rapporter le fait **lui-même**, en poussant son résultat à l'API — c'est ainsi que `client_backup` rapporte l'état de son propre dépôt. Ne pas prévoir de rôle « agent ».
|
||||
|
||||
Quand un nouveau service central est ajouté, prévoir aussi le ou les rôles clients nécessaires pour intégrer les VM existantes et futures.
|
||||
|
||||
Les dépendances entre groupes doivent être déclarées dans :
|
||||
|
|
@ -488,13 +475,11 @@ Les variables de rôle doivent utiliser un préfixe correspondant au rôle.
|
|||
Exemples :
|
||||
|
||||
```yaml
|
||||
ssh_hardening_port: 22 # rôle ssh_hardening
|
||||
nftables_baseline_enabled: false # rôle nftables_baseline
|
||||
fail2ban_ssh_enabled: true # rôle fail2ban_ssh
|
||||
ssh_durcissement_port: 22
|
||||
nftables_socle_enabled: false
|
||||
fail2ban_ssh_enabled: true
|
||||
```
|
||||
|
||||
Le préfixe est **le nom du rôle, tel qu'il est**, pas sa traduction : ces trois lignes sont copiées des `defaults/main.yml` réels. (Elles disaient `ssh_durcissement_port` et `nftables_socle_enabled` jusqu'au 2026-09-06 — deux préfixes qui ne correspondaient à aucun rôle, dans l'exemple censé illustrer la règle.)
|
||||
|
||||
Séparer clairement :
|
||||
|
||||
```text
|
||||
|
|
@ -547,8 +532,8 @@ Il peut contenir :
|
|||
- compte technique `ansible` ;
|
||||
- sudo NOPASSWD pour `ansible` lorsque requis ;
|
||||
- `qemu-guest-agent` ;
|
||||
- `cloud-init` — **au gabarit seulement** : retiré par `serveur_durci` une fois la VM née (D-85) ;
|
||||
- `cloud-guest-utils` (`growpart`) — conservé : ni service, ni source de données ;
|
||||
- `cloud-init` ;
|
||||
- `cloud-guest-utils` ;
|
||||
- chrony ;
|
||||
- outils de diagnostic de base ;
|
||||
- durcissement raisonnable ;
|
||||
|
|
@ -611,7 +596,7 @@ Ne pas activer un pare-feu générique dans un template sans confirmation explic
|
|||
|
||||
Pour le template Debian 13, `nftables` peut être installé et préparé, mais rester désactivé par défaut.
|
||||
|
||||
L’activation du pare-feu doit être faite sur un clone ou sur un serveur final. **Ses règles ne s’écrivent pas à la main** : elles sont *dérivées* du registre des flux — chaque rôle déclare ce qu’il reçoit dans son `meta/flux.yml`, et `scripts/resoudre_flux.py` en produit le jeu de règles. Le même registre alimente le pare-feu de l’hyperviseur et la frontière OPNsense, ce qui leur interdit de se contredire. Voir `docs/flux-conception.md` et `docs/registre-flux.md`.
|
||||
L’activation du pare-feu doit être faite sur un clone ou sur un serveur final, avec des règles adaptées à son rôle.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -636,23 +621,6 @@ Proxmox + cloud-init : identité initiale de la VM
|
|||
Set-OPS + Ansible : configuration réelle du serveur
|
||||
```
|
||||
|
||||
**Et cloud-init ne survit pas à cette première seconde** (D-85, 2026-09-09). Il se réveille
|
||||
à *chaque* démarrage et relit le lecteur attaché par l'hyperviseur — lequel peut redéfinir
|
||||
comptes, clés SSH, mots de passe et réseau. Sur une machine que le plan possède, c'est un
|
||||
**second maître**, que le plan ne décrit pas. Le groupe `serveur_durci` le retire donc
|
||||
(`cloud_init_retrait`), et le socle ne l'installe plus : le garder aux deux endroits
|
||||
produisait un va-et-vient à chaque déploiement. **Le gabarit, lui, le garde** — sans lui un
|
||||
clone n'a ni adresse ni nom. **P63** garde les trois moitiés.
|
||||
|
||||
**Ce que ce retrait ne ferme pas.** Il n'ôte **aucun pouvoir à l'hébergeur** :
|
||||
`qemu-guest-agent` est au gabarit (il doit y être — P56), et l'API Proxmox expose sur son
|
||||
dos `exec`, `file-write`, `set-user-password`, `shutdown` — strictement plus que le lecteur
|
||||
cloud-init. Ce qui est fermé est étroit et réel : une réapplication *automatique, à chaque
|
||||
démarrage*, depuis un support que le plan ne possède pas, et un interpréteur Python
|
||||
complet exécuté en root au boot. La mainmise de l'hyperviseur sur ses invités est une
|
||||
propriété de la virtualisation, pas de cloud-init — elle appelle sa propre décision, non
|
||||
prise.
|
||||
|
||||
---
|
||||
|
||||
## Nettoyage avant template
|
||||
|
|
@ -662,7 +630,7 @@ Le nettoyage final avant conversion en template doit être protégé par une con
|
|||
Exemple :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i "$SETOPS_INVENTAIRE" playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
||||
```
|
||||
|
||||
Le nettoyage peut inclure :
|
||||
|
|
@ -682,22 +650,21 @@ Ne jamais lancer ce nettoyage sur un serveur de production sans confirmation exp
|
|||
|
||||
Chaque modification significative doit être inscrite dans `CHANGELOG.md`.
|
||||
|
||||
**Le format a changé, et ce fichier disait encore l'ancien.** Les entrées d'avant le
|
||||
2026-08-03 suivaient trois sections fixes (`### Ajouté` / `### Modifié` / `### Corrigé`).
|
||||
Depuis, chaque entrée est un **récit** : ce qui était cassé, pourquoi personne ne le voyait,
|
||||
ce que ça coûte de le réapprendre. Suivre la pratique en vigueur :
|
||||
Format recommandé :
|
||||
|
||||
```markdown
|
||||
## AAAA-MM-JJ — un titre qui dit CE QUI A ÉTÉ APPRIS, pas ce qui a été touché
|
||||
## YYYY-MM-DD
|
||||
|
||||
**N preuves.** Une ou deux phrases : l'état du harnais, et l'enjeu.
|
||||
### Ajouté
|
||||
- ...
|
||||
|
||||
### Un sous-titre par piège payé
|
||||
Ce que le dépôt croyait, ce que la machine faisait, et la mesure qui a tranché.
|
||||
### Modifié
|
||||
- ...
|
||||
|
||||
### Corrigé
|
||||
- ...
|
||||
```
|
||||
|
||||
Deux entrées le même jour se distinguent par un rang : `## 2026-09-02 (6) — …`.
|
||||
|
||||
Les corrections de rôles, handlers, playbooks et templates doivent être consignées.
|
||||
|
||||
---
|
||||
|
|
|
|||
14227
CHANGELOG.md
14227
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
|
@ -22,10 +22,8 @@ git clone <url-de-Set-OPS> Set-OPS && cd Set-OPS
|
|||
```
|
||||
|
||||
## 2. Choisir un modèle et créer ton instance
|
||||
Le dépôt public fournit **un modèle générique : `socle`** — quatre VM, le minimum
|
||||
souverain : PKI (`step-ca`), DNS interne (PowerDNS), edge TLS (nginx) et **magasin
|
||||
courriel** (Dovecot). C'est un magasin de boîtes, pas un relais : le MTA (`serveur_postfix`)
|
||||
s'ajoute ensuite, comme les autres modules. Les
|
||||
Le dépôt public fournit **un modèle générique : `socle`** — le socle souverain minimal
|
||||
(DNS interne, AC/PKI, edge TLS, relais courriel) sur lequel on ajoute des modules. Les
|
||||
modèles **assemblés** par offre (`identite`, `observabilite`, `forge`, `collaboration`,
|
||||
`presence-web`, `integral`) sont un actif à part, dans le dépôt privé `Set-OPS-modeles`
|
||||
(cf. [`exemples/modeles/README.md`](exemples/modeles/README.md)).
|
||||
|
|
@ -37,11 +35,6 @@ ln -s ../mon-instance instance # le moteur la trouve via ce lien
|
|||
*(Alternative au symlink : `export SETOPS_INSTANCE=../mon-instance`.)*
|
||||
|
||||
## 3. Renseigner ton instance (« tes couleurs »)
|
||||
|
||||
> Les chemins ci-dessous disent `production/` parce que **c'est le nom que porte le modèle
|
||||
> que tu viens de copier**. Ce n'est pas un nom imposé : le moteur cherche `principal`
|
||||
> d'abord, `production` ensuite. Si tu lis ailleurs `<inventaire>`, c'est cette place-là.
|
||||
|
||||
- `instance/inventories/production/group_vars/all/10-intrants.yml` → **`domaine_interne`** (ex. `monorg.internal`) ;
|
||||
- `instance/plan/nomenclature.yml` → ton **supernet** (ex. `10.20.0.0/16`) ;
|
||||
- `instance/plan/domaines.yml` → ton **domaine public** ;
|
||||
|
|
@ -54,26 +47,14 @@ Tu peux aussi le faire dans le GUI plus tard (`make inventaire-ui`).
|
|||
make config # renseigne API host/user/port, nœud, stockage, VMID du template...
|
||||
```
|
||||
Chaque paramètre demandé est expliqué dans [`docs/config-proxmox.md`](docs/config-proxmox.md).
|
||||
Tous tes secrets (token API + `vault_*`) vont dans **une voûte unique par instance** :
|
||||
Tous tes secrets (token API + `vault_*`) vont dans **une voûte unique** :
|
||||
`instance/inventories/production/group_vars/all/vault.yml`, à partir du
|
||||
gabarit [`exemples/vault.exemple.yml`](exemples/vault.exemple.yml), chiffrée avec
|
||||
`ansible-vault`.
|
||||
|
||||
**Une voûte, une clé** (depuis le 2026-08-28). Le mot de passe de ta voûte se dépose dans un
|
||||
fichier dont le nom est **dérivé du dossier de ton instance**, en minuscules :
|
||||
|
||||
`ansible-vault`. Exporte ton mot de passe Vault, ex. :
|
||||
```bash
|
||||
# instance dans ../mon-instance -> clé dans ~/.config/setops-vault-mon-instance
|
||||
install -m 600 /dev/null ~/.config/setops-vault-mon-instance
|
||||
$EDITOR ~/.config/setops-vault-mon-instance # ta phrase de passe, une seule ligne
|
||||
python3 scripts/voutes.py etat # vérifie que le moteur la trouve
|
||||
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
||||
```
|
||||
|
||||
Il n'y a **rien à exporter** : le `Makefile` construit `ANSIBLE_VAULT_IDENTITY_LIST` en
|
||||
appelant `scripts/voutes.py`. Un seul `ANSIBLE_VAULT_PASSWORD_FILE` ne suffirait plus de
|
||||
toute façon — créer une VM ouvre **deux** voûtes dans la même exécution : la tienne, et
|
||||
celle de l'hébergeur qui détient le jeton Proxmox.
|
||||
|
||||
## 5. Construire le golden template Debian 13 (UNE seule fois)
|
||||
Une grappe vierge n'a aucun template. Crée une VM **Debian 13 vanille**, rends-la
|
||||
joignable par Ansible, puis :
|
||||
|
|
@ -82,10 +63,8 @@ make preparer-modele # socle + durcissement + cloud-init + qemu-guest-age
|
|||
make verifier-modele
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
Convertis ensuite la VM en **template Proxmox**. Son nom doit être celui que `make config`
|
||||
a enregistré comme *nom logique du modèle* — **`modeleSetOPS`** par défaut
|
||||
(`proxmox_clone_source_nom`). Un nom qui ne correspond pas se solde par un clonage qui ne
|
||||
trouve pas sa source. Détails et procédure : `docs/vm-lifecycle.md` et
|
||||
Convertis ensuite la VM en **template Proxmox** nommé `modele-debian13` (le nom de
|
||||
clone source par défaut). Détails et procédure : `docs/vm-lifecycle.md` et
|
||||
`docs/procedure-template-debian13-proxmox.md`.
|
||||
|
||||
## 6. Générer ton inventaire depuis le plan
|
||||
|
|
@ -123,8 +102,8 @@ Les déploiements de groupe ne ciblent **que** les hôtes actifs.
|
|||
make inventaire-verifier # registres + inventaire + garde-fous
|
||||
make verifier # + ansible-lint + --syntax-check
|
||||
```
|
||||
> Ces cibles chargent l'inventaire complet : pose d'abord la clé de ta voûte
|
||||
> (étape 4), sinon Ansible s'arrête sur
|
||||
> Ces cibles chargent l'inventaire complet : exporte d'abord ton mot de passe Vault
|
||||
> (étape 4, `ANSIBLE_VAULT_PASSWORD_FILE`), sinon Ansible s'arrête sur
|
||||
> « Attempting to decrypt but no vault secrets found ».
|
||||
|
||||
---
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -6,7 +6,7 @@ Le dépôt est le **moteur** (générique, partageable). Chaque déploiement ré
|
|||
|
||||
## Par où entrer — selon ce que tu viens faire
|
||||
|
||||
On n'arrive pas avec un *sujet*, on arrive avec une **situation**. Il y en a six :
|
||||
On n'arrive pas avec un *sujet*, on arrive avec une **situation**. Il y en a cinq :
|
||||
|
||||
| Ta situation | Ta porte |
|
||||
|---|---|
|
||||
|
|
@ -36,18 +36,18 @@ Piliers de l'écosystème :
|
|||
- **confiance** — autorité de certification interne (ACME) ;
|
||||
- **nommage et adressage** — DNS interne et nomenclature dérivable ;
|
||||
- **données** — bases relationnelles et cache, avec registre des connexions ;
|
||||
- **communication** — service de courriel souverain (boîtes LDAP, SMTP, IMAP, antispam, DKIM), et le relais des notifications système ;
|
||||
- **communication** — relais courriel interne ;
|
||||
- **observabilité et supervision** — métriques, journaux, tableaux de bord, supervision active ;
|
||||
- **applicatif** — services internes (forge, etc.) et couche web.
|
||||
|
||||
L'état voulu est **déclaratif et convergent** (appliqué par les groupes Ansible), **souverain** par conception, mais **piloté par l'opérateur** (pas d'auto-remédiation : la boucle n'est pas fermée). Ce n'est pas resté sur le papier : la flotte a été **rasée et remontée depuis zéro** le 2026-08-13, puis deux fois le 2026-09-02, sans échec. Le degré de maturité service par service est dans `docs/catalogue-services.md`. Le template Debian 13 reste la fondation, pas la finalité.
|
||||
L'état voulu est **déclaratif et convergent** (appliqué par les groupes Ansible), **souverain** par conception, mais **piloté par l'opérateur** (pas d'auto-remédiation : la boucle n'est pas fermée). Une grande partie est aujourd'hui *définie et validée* avant d'être déployée sur des VM réelles ; le template Debian 13 reste la fondation, pas la finalité.
|
||||
|
||||
Cadre et règles d'autorité : voir `AGENTS.md` (section « Mission et identité »).
|
||||
|
||||
## Le plan : on édite, l'inventaire se génère
|
||||
|
||||
`Set-OPS` se pilote par un **plan**, pas par l'édition directe de l'inventaire.
|
||||
`instance/inventories/<inventaire>/hosts.yml` est **généré** depuis le plan — **ne pas l'éditer à la main**.
|
||||
`instance/inventories/production/hosts.yml` est **généré** depuis le plan — **ne pas l'éditer à la main**.
|
||||
|
||||
```
|
||||
éditer le PLAN → make instancier (revoir le diff) → make instancier-appliquer → make deployer
|
||||
|
|
@ -80,18 +80,16 @@ playbooks/modeles_vm/debian13_proxmox_nettoyer.yml
|
|||
|
||||
## Principe
|
||||
|
||||
Le template contient seulement le socle commun. Les services spécialisés sont installés
|
||||
ensuite sur les clones, par les playbooks de groupes : edge nginx, PostgreSQL et Redis,
|
||||
identité (OpenLDAP, Keycloak), courriel (Postfix, Dovecot, rspamd), observabilité
|
||||
(Prometheus, Loki, Grafana), supervision (Icinga), forge (Forgejo), collaboration
|
||||
(Nextcloud, Collabora), plateforme web. La liste qui fait foi est
|
||||
[`docs/catalogue-services.md`](docs/catalogue-services.md).
|
||||
Le template contient seulement le socle commun.
|
||||
|
||||
> **Ni MariaDB, ni Docker, ni Podman.** Cette section les a listés jusqu'au 2026-09-06,
|
||||
> par recopie de la liste de ce que le *template* ne doit pas contenir. Le dépôt n'a pas de
|
||||
> rôle MariaDB, et **plus aucun rôle n'a besoin de Docker** depuis la réécriture native de
|
||||
> `serveur_collabora` — qui en était la dernière exception. Le conteneur n'est pas un
|
||||
> détail d'implémentation ici : c'est un choix fondateur (voir `roles/serveur_collabora/README.md`).
|
||||
Les services spécialisés seront installés ensuite sur les clones :
|
||||
|
||||
- NGINX ;
|
||||
- PostgreSQL ;
|
||||
- MariaDB ;
|
||||
- Docker/Podman ;
|
||||
- monitoring complet ;
|
||||
- applications métier.
|
||||
|
||||
## Exploitation courante
|
||||
|
||||
|
|
@ -137,7 +135,7 @@ Planifier une VM passe désormais par le **plan**, pas par l'édition de l'inven
|
|||
|
||||
```bash
|
||||
make instancier # génère + diff sémantique (que va-t-il changer ?)
|
||||
make instancier-appliquer # régénère instance/inventories/<inventaire>/hosts.yml
|
||||
make instancier-appliquer # régénère instance/inventories/production/hosts.yml
|
||||
```
|
||||
|
||||
Les anciennes commandes `make hote-planifier` / `hote-ajouter` / `hote-groupes` éditaient l'inventaire **directement** ; elles sont **supplantées** par le plan (l'inventaire est généré, ne pas l'éditer à la main).
|
||||
|
|
|
|||
|
|
@ -46,19 +46,15 @@ après avoir vérifié l'accès SSH par clé et les privilèges sudo du compte `
|
|||
## Vérification
|
||||
|
||||
```bash
|
||||
make verifier-modele
|
||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_verifier.yml
|
||||
```
|
||||
|
||||
## Nettoyage final avant conversion
|
||||
|
||||
```bash
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
||||
```
|
||||
|
||||
*(Ces deux blocs appelaient `ansible-playbook -i instance/inventories/lab/hosts.yml …`
|
||||
jusqu'au 2026-09-06. Cet inventaire n'existe pas : le nom est résolu par le `Makefile`,
|
||||
c'est précisément pourquoi les cibles `make` supplantent les commandes brutes.)*
|
||||
|
||||
Ensuite :
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -2,28 +2,7 @@
|
|||
|
||||
> **Pour qui :** l'**agent IA** qui reprend le dépôt — et le mainteneur qui relit ce qu'on lui dit.
|
||||
|
||||
> ## ⚠️ Document HISTORIQUE — relu le 2026-09-06
|
||||
>
|
||||
> Cette note était une **passation datée de juillet 2026**, écrite quand le chantier en
|
||||
> cours était le gabarit Debian 13. Elle a continué d'être listée comme lecture de
|
||||
> gouvernance longtemps après avoir cessé d'être vraie, et elle envoyait l'agent réparer un
|
||||
> incident réglé, dans un rôle qui n'existe pas (`roles/ssh_durcissement/` — le rôle
|
||||
> s'appelle `ssh_hardening`).
|
||||
>
|
||||
> **Ce qui tient toujours** : les fichiers de gouvernance à lire (ci-dessous), la règle de
|
||||
> conduite finale (« lire l'existant, corriger petit, valider »), et la discipline des
|
||||
> handlers — qui est désormais **prouvée** par **P04** et le harnais, plus seulement
|
||||
> recommandée.
|
||||
>
|
||||
> **Ce qui ne tient plus** : « le chantier en cours est le template Debian 13 »,
|
||||
> l'« incident récent » et son correctif. Les deux `handlers/main.yml` existent
|
||||
> (`ssh_baseline`, `ssh_hardening`) depuis longtemps.
|
||||
>
|
||||
> **Où aller à la place** : `AGENTS.md` (autorité), `docs/carte-set-ops.md` (par où entrer
|
||||
> pour modifier le moteur), `docs/catalogue-services.md` (ce qui est éprouvé et ce qui ne
|
||||
> l'est pas).
|
||||
|
||||
## État du dépôt *(juillet 2026)*
|
||||
## État du dépôt
|
||||
|
||||
`Set-OPS` est le moteur Ansible global d’exploitation d’écosystèmes numériques souverains.
|
||||
|
||||
|
|
@ -88,12 +67,7 @@ Il ne doit pas contenir :
|
|||
|
||||
---
|
||||
|
||||
## Incident de juillet 2026 — RÉGLÉ, conservé pour la leçon
|
||||
|
||||
> Les deux `handlers/main.yml` existent. `roles/ssh_durcissement/` cité plus bas **n'a
|
||||
> jamais existé sous ce nom** : le rôle s'appelle `ssh_hardening`. Ce qui reste utile ici,
|
||||
> c'est la *classe* d'erreur — un `notify` sans handler local — et le fait qu'elle est
|
||||
> maintenant tenue par une preuve plutôt que par la vigilance.
|
||||
## Incident récent à corriger
|
||||
|
||||
Le playbook :
|
||||
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
# L'accès d'administration — un tunnel nominatif
|
||||
|
||||
> **Pour qui :** celui qui administre le site et ses écosystèmes, et celui qui se demande
|
||||
> par où un humain entre, avec quelle clé, et ce qu'il peut atteindre une fois entré.
|
||||
>
|
||||
> Décidé le 2026-09-17, en réponse à une question : *le runner ne devrait-il pas être le
|
||||
> rebond SSH des admins ?*
|
||||
|
||||
## Pourquoi pas le runner
|
||||
|
||||
Le runner du site détient **la clé de la voûte du site** et les clés SSH qui configurent
|
||||
toutes les machines. En faire la porte des humains réunirait deux pouvoirs que tout le reste
|
||||
du dépôt sépare :
|
||||
|
||||
- une session humaine compromise (un agent SSH transféré de trop) deviendrait le **plan de
|
||||
contrôle** — pas seulement un rebond ;
|
||||
- il **ne doit jamais entrer chez un locataire** (charte des responsabilités) ; en faire le
|
||||
passage obligé des admins créerait ce chemin en fait ;
|
||||
- il est **reconstructible par le code**, et c'est sa vertu. Un point d'entrée doit survivre à
|
||||
la reconstruction de ce qu'il sert ;
|
||||
- « qui est entré » et « qu'est-ce qui a été déployé » cesseraient de se raconter séparément.
|
||||
|
||||
## Ce qui a été construit
|
||||
|
||||
Un **second** tunnel WireGuard sur la frontière — l'instance `admins`, port 51821 — à côté du
|
||||
tunnel site-à-site vers le site pair (instance `chezlePro`, port 51820), jamais mêlé à lui.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| déclaré | `SITE-<nom>/plan/10-intrants.yml`, clé `acces_admin_vpn` |
|
||||
| réconcilié | `scripts/vpn_admin.py` (`make vpn-admin-plan`, `make vpn-admin-appliquer`) |
|
||||
| réseau | `10.37.29.0/24`, la frontière en `.1` |
|
||||
| un pair | **une personne ET un appareil** — révoquer l'appareil perdu ne coupe pas les autres |
|
||||
| clés | seule la clé **publique** est au plan ; la privée ne quitte jamais l'appareil |
|
||||
|
||||
**Le réseau du tunnel est un réseau d'administration, et tout en dérive** : les pare-feux des
|
||||
machines du site (`resoudre_flux`), le contrat vers les locataires (`site_intrants`, donc les
|
||||
pare-feux de leurs machines) et les règles de la frontière (`devis_opnsense`). Rien à recopier
|
||||
— une liste recopiée prend toujours du retard sur celle qu'elle suit.
|
||||
|
||||
## Ajouter un appareil
|
||||
|
||||
```bash
|
||||
python3 scripts/vpn_admin.py pair-nouveau --nom prenom-appareil # tire la paire de clés
|
||||
# coller le bloc `pairs:` rendu dans SITE-<nom>/plan/10-intrants.yml
|
||||
make vpn-admin-plan # lire ce qui changerait
|
||||
CONFIRMER=true make vpn-admin-appliquer # poser le pair
|
||||
python3 scripts/vpn_admin.py config --nom prenom-appareil # la config de l'appareil
|
||||
make flux && make deployer-groupe GROUPE=serveur_durci # les pare-feux des machines
|
||||
```
|
||||
|
||||
La clé privée ne s'affiche **qu'une fois**, au moment où elle est tirée : elle appartient à
|
||||
l'appareil. Perdue, on en tire une autre ; l'ancienne se révoque par `etat: absent`.
|
||||
|
||||
## Retirer un accès
|
||||
|
||||
`etat: absent` sur le pair, puis `CONFIRMER=true make vpn-admin-appliquer`. Le script retire
|
||||
aussi tout pair **attaché à notre instance que le plan ne déclare plus** : un accès qui
|
||||
survivrait à la décision de le retirer est exactement ce qu'on ne veut pas.
|
||||
|
||||
## Deux trous que seul l'usage a montrés (2026-09-17, une heure après la pose)
|
||||
|
||||
- **Le SSH des machines du site.** Le socle déclare son 22 en `pair: [flotte, externe]`, jamais
|
||||
`admin` : la source dérivée est le site lui-même. L'exploitant y arrivait **par rebond sur la
|
||||
frontière** — la connexion partait alors du boîtier, que pf laisse sortir sans règle. Par le
|
||||
tunnel, il arrive comme une source extérieure, et plus rien ne l'autorisait. Les locataires,
|
||||
eux, marchaient : leur règle SSH naît de `nftables_admin_ssh`, qui contient déjà le tunnel.
|
||||
- **La frontière elle-même.** Aucun flux ne la désignait comme destination d'administration :
|
||||
monter le tunnel faisait perdre sa console et son API — donc le moyen même de réparer la
|
||||
règle manquante. *La panne se referme sur celui qui la répare* : il a fallu remettre
|
||||
l'adresse d'avant pour poser le correctif.
|
||||
|
||||
Les deux règles sont désormais émises par `devis_opnsense` dès que `acces_admin_vpn` est
|
||||
déclaré (`ports_frontiere`, par défaut 22 et 443). Une règle **par port** : OPNsense refuse
|
||||
« 22,443 » dans un champ de port, et aucun flux jusque-là n'en portait deux.
|
||||
|
||||
## Un tunnel par locataire (2026-09-17)
|
||||
|
||||
Chaque écosystème a le sien, et **c'est lui qui décide qui entre chez lui**. Le site porte la
|
||||
route, jamais la liste des gens — même partage que les zones DNS publiques.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| déclaré par | le **locataire**, dans `plan/acces.yml` (registre `acces_admin_vpn`) |
|
||||
| réseau | `10.<index>.29.0/24` — **dérivé** de son index, hors de ses six zones (.16 à .21) |
|
||||
| port | `52000 + index` — dérivé, donc sans collision : P21 garde déjà l'unicité des index |
|
||||
| instance | `wg<index>` — un rang (2, 3, 4…) renommerait les interfaces le jour où un écosystème part |
|
||||
| ce qu'il atteint | **son supernet, et rien d'autre** : SSH et consoles web |
|
||||
|
||||
**L'isolement est gardé, pas promis.** `devis_opnsense.verifier` refuse toute règle dont la
|
||||
source est le tunnel d'un locataire et la destination autre chose que **son** supernet —
|
||||
sinon, un écosystème obtiendrait un accès chez un voisin en ajoutant un pair dans son propre
|
||||
plan. La garde est exécutée par P24, avant toute écriture. Mise en défaut vérifiée.
|
||||
|
||||
**Le pare-feu de ses machines suit tout seul** : `resoudre_flux` ajoute le réseau du tunnel aux
|
||||
sources d'administration **dérivées de son plan**, et seulement s'il déclare au moins un pair
|
||||
actif — déclarer un réseau que personne n'emprunte ouvrirait le SSH à un tunnel qui n'existe
|
||||
pas.
|
||||
|
||||
```bash
|
||||
python3 scripts/vpn_admin.py pair-nouveau --nom prenom-appareil --locataire OPS-<X>
|
||||
# coller le bloc rendu dans OPS-<X>/plan/acces.yml
|
||||
make vpn-admin-plan && CONFIRMER=true make vpn-admin-appliquer
|
||||
python3 scripts/vpn_admin.py config --nom prenom-appareil --locataire OPS-<X>
|
||||
```
|
||||
|
||||
## Ce que le tunnel donne, et ce qu'il ne donne pas
|
||||
|
||||
`AllowedIPs` est **dérivé** de la carte : zones du site, fabric, lien de transit et supernets
|
||||
des locataires. Ce que la frontière laisse ensuite passer reste décidé par les flux déclarés —
|
||||
SSH partout, et les consoles d'administration (Grafana, Icinga Web, la forge, le runner).
|
||||
|
||||
**Le seul port ouvert sur l'Internet par cette voie est le 51821/udp**, vers l'adresse publique
|
||||
de la frontière. Tout le reste voyage dans le tunnel.
|
||||
|
||||
**Ce qui n'est pas fait** : l'enregistrement des sessions (un rebond dédié le permettrait, pas
|
||||
un tunnel), et la double authentification — la possession de l'appareil fait foi.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
> **Pour qui :** le **mainteneur** — le survol du modèle. À lire avant `plan-et-generation.md`.
|
||||
|
||||
Set-OPS définit et construit l'écosystème numérique souverain. Il se
|
||||
pilote par un **plan** : l'inventaire Ansible (`instance/inventories/<inventaire>/hosts.yml`)
|
||||
pilote par un **plan** : l'inventaire Ansible (`instance/inventories/production/hosts.yml`)
|
||||
est **généré** depuis le plan, pas édité à la main.
|
||||
|
||||
- **Par où commencer + catalogue des mécanismes transverses : `docs/carte-set-ops.md`.**
|
||||
|
|
@ -36,7 +36,7 @@ Les valeurs communes aux rôles doivent rester dans les defaults des rôles quan
|
|||
Les groupes opérationnels de l'inventaire doivent correspondre à un playbook homonyme :
|
||||
|
||||
```text
|
||||
instance/inventories/<inventaire>/hosts.yml
|
||||
instance/inventories/production/hosts.yml
|
||||
serveur_debian
|
||||
|
||||
playbooks/groupes/serveur_debian.yml
|
||||
|
|
@ -60,11 +60,9 @@ Le cycle de vie des VM est documenté dans :
|
|||
docs/vm-lifecycle.md
|
||||
```
|
||||
|
||||
## Intégrations transversales
|
||||
## Intégrations futures
|
||||
|
||||
Elles ne sont plus « futures » : `client_pki`, `client_backup`, `client_metrique`,
|
||||
`client_journal`, `client_smtp`, `client_resolveur` et `client_artefacts` sont déployés sur
|
||||
la flotte. Elles sont documentées dans :
|
||||
Les intégrations transversales des VM sont documentées dans :
|
||||
|
||||
```text
|
||||
docs/integrations-vm.md
|
||||
|
|
|
|||
|
|
@ -26,37 +26,18 @@ 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) :
|
||||
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
|
||||
python3 scripts/voutes.py etat # la clé de cette instance est-elle là ?
|
||||
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
||||
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 :
|
||||
>
|
||||
> ```bash
|
||||
> 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`. |
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Ce plan est le **pendant manuel** de `make prouver` : là où le harnais prouve
|
|||
le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (cf.
|
||||
`protocole-operateur-independant.md`).
|
||||
|
||||
**90 gestes** sur **22 unités** · **20** en « casse-répare »
|
||||
**85 gestes** sur **21 unités** · **19** en « casse-répare »
|
||||
· **5** doublés d'un garde-fou machine (colonne *Preuve auto*).
|
||||
|
||||
> **Honnêteté de couverture.** La colonne *Preuve auto* n'est remplie que lorsqu'une
|
||||
|
|
@ -27,7 +27,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | Observe le claim | Dans Keycloak (console admin) → Clients → grafana → *Client scopes* → *Evaluate* pour testmail : le jeton contient "roles": ["grafana-editor"]. C'est ② en vrai. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Retire grafana-editor de testmail (ou renomme-le en grafana-viewer), reconnecte : Explore disparaît. Remets-le : il revient. Tu *sens* que c'est le rôle, pas l'identité, qui ouvre la porte. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Autorisation & RBAC](../../wiki/Autorisation-et-RBAC.md) · § À toi de jouer.*
|
||||
*Source : [Autorisation & RBAC](Autorisation-et-RBAC) · § À toi de jouer.*
|
||||
|
||||
## Bases de données
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | Suis une liaison | Ouvre plan/bases-donnees.yml : l'entrée keycloak (serveur, base, propriétaire, secret) est *exactement* ce que le rôle serveur_keycloak va lire pour se connecter. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Change le mot de passe d'un compte dans PostgreSQL (garde l'ancien !) sans mettre à jour la voûte : l'app ne se connecte plus. Restaure : ça repart. Tu *sens* que la connexion = *identité + secret cohérents des deux côtés*. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Bases de données](../../wiki/Bases-de-donn%C3%A9es.md) · § À toi de jouer.*
|
||||
*Source : [Bases de données](Bases-de-données) · § À toi de jouer.*
|
||||
|
||||
## Cache
|
||||
|
||||
|
|
@ -49,17 +49,17 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | Vois la borne | : redis-cli -a … CONFIG GET maxmemory et … maxmemory-policy (LRU). | 👁 observe | — |
|
||||
| 4 | Casse & répare | Interroge sans mot de passe : redis-cli GET essai:1 → NOAUTH (refusé). Puis vide le cache (FLUSHALL) : rien ne casse dans l'écosystème — la vraie donnée est en base. Tu *sens* qu'un cache est jetable. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Cache](../../wiki/Cache.md) · § À toi de jouer.*
|
||||
*Source : [Cache](Cache) · § À toi de jouer.*
|
||||
|
||||
## Courriel (SMTP / IMAP)
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Envoie via la soumission `:587` | (client authentifié) : « commande » *(MDP_ESSAI se saisit à la main — read -rs MDP_ESSAI. Un mot de passe écrit ici partirait dans l'historique du shell et dans le dépôt public : ce document en portait un en clair jusqu'au 2026-09-06.)*… | 👁 observe | — |
|
||||
| 1 | Envoie via la soumission `:587` | (client authentifié) : « commande » 235 Authentication successful puis 250 queued = ① en action. | 👁 observe | — |
|
||||
| 2 | Lis la boîte | (le MDA) : doveadm search -u testmail mailbox INBOX all \| wc -l sur infra-mail-01. | 👁 observe | — |
|
||||
| 3 | Casse & répare | Coupe l'annuaire (arrête OpenLDAP), renvoie un courriel : Postfix **rejette le destinataire** (il ne peut plus valider en LDAP). Rallume OpenLDAP : ça repart. Tu *sens* la dépendance requise MTA → annuaire. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Courriel (SMTP / IMAP)](../../wiki/Courriel.md) · § À toi de jouer.*
|
||||
*Source : [Courriel (SMTP / IMAP)](Courriel) · § À toi de jouer.*
|
||||
|
||||
## DNS & résolution de noms
|
||||
|
||||
|
|
@ -68,32 +68,20 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 1 | Le plancher, sans DNS | Sur un nœud : « commande » | 👁 observe | — |
|
||||
| 2 | Interroge l'autoritatif | Demande à PowerDNS directement : « commande » | 👁 observe | — |
|
||||
| 3 | Vois les couches | Compare getent hosts (plancher) et dig (DNS) : deux chemins, même IP. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Vide /etc/hosts de ses entrées chezlepro (garde une sauvegarde !) et pointe /etc/resolv.conf ailleurs : la résolution interne échoue. Restaure /etc/hosts seul : ça remarche sans DNS. Tu viens de *sentir* pourquoi le plancher est le filet… | 🔨 casse-répare | — |
|
||||
| 5 | Le piège du récursif | Demande un nom qui n'existe pas sous internal., puis redemande un nom qui existe. Si le résolveur répond NXDOMAIN aux deux, tu viens de reproduire la panne de deux jours du 2026-09-02 : la racine étant signée, elle *prouve* que internal.… | 👁 observe | — |
|
||||
| 4 | Casse & répare | Sur un nœud sans client_unbound, vide /etc/hosts de ses entrées chezlepro (garde une sauvegarde !) et coupe l'accès au DNS : la résolution interne échoue. Restaure /etc/hosts : ça remarche sans DNS. Tu viens de *sentir* pourquoi le planc… | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [DNS & résolution de noms](../../wiki/DNS-et-r%C3%A9solution.md) · § À toi de jouer.*
|
||||
|
||||
## Filiation, signatures et témoins
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | — | make genome — quatre dépôts ? Lequel n'a pas de *remote* ? Celui-là n'existe qu'ici. | 👁 observe | — |
|
||||
| 2 | — | git verify-tag v2026.08.21 — que se passe-t-il si tu retires ta ligne de .git-allowed-signers ? (remets-la ensuite) | 👁 observe | — |
|
||||
| 3 | — | make genome-inscrire, puis ouvre parente.yml. Dans un an, qu'est-ce que ce fichier te dira que ta mémoire ne dira plus ? | 👁 observe | — |
|
||||
| 4 | — | Demande-toi où sont les témoins aujourd'hui. Combien de copies vivantes du moteur existent, sur combien de machines distinctes ? C'est la vraie mesure de la résistance de la lignée — pas la longueur des clés. Voir aussi : Multi-instance… | 👁 observe | — |
|
||||
|
||||
*Source : [Filiation, signatures et témoins](../../wiki/Filiation-signatures-et-t%C3%A9moins.md) · § À toi de jouer.*
|
||||
*Source : [DNS & résolution de noms](DNS-et-résolution) · § À toi de jouer.*
|
||||
|
||||
## Identité & SSO
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Vis le SSO | Ouvre https://observatoire.chezlepro.internal → « *Se connecter avec Chezlepro* » → testmail. Puis ouvre Forgejo, puis Icinga : tu n'es reconnecté nulle part. | 👁 observe | — |
|
||||
| 1 | Vis le SSO | Ouvre https://grafana.lab.chezlepro.internal → « *Se connecter avec Chezlepro* » → testmail. Puis ouvre Forgejo, puis Icinga : tu n'es reconnecté nulle part. | 👁 observe | — |
|
||||
| 2 | Observe le flux | Rouvre Grafana en navigation privée, ouvre les outils dév (F12 → Réseau) : repère la redirection vers Keycloak, puis le retour avec un code=. C'est ① en action. | 👁 observe | — |
|
||||
| 3 | Interroge l'annuaire (la source de vérité) | Sur un nœud avec ldap-utils : « commande » Tu vois l'entrée que Keycloak fédère — il ne l'a pas recopiée. | 👁 observe | — |
|
||||
| 4 | Casse & répare (la fédération) | Dans la console admin Keycloak → *User Federation* → désactive le fournisseur LDAP. Reconnecte-toi : échec (l'IdP ne voit plus l'annuaire). Réactive : ça remarche. Tu viens de *sentir* la dépendance requise entre l'IdP et l'annuaire. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Identité & SSO](../../wiki/Identit%C3%A9-et-SSO.md) · § À toi de jouer.*
|
||||
*Source : [Identité & SSO](Identité-et-SSO) · § À toi de jouer.*
|
||||
|
||||
## Infra as Code & idempotence
|
||||
|
||||
|
|
@ -104,7 +92,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | La réversibilité | git diff / git checkout sur le plan : l'état est du code, donc annulable. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Modifie à la main un fichier géré par un rôle (ex. un .conf), puis redéploie : Ansible rétablit l'état voulu (le code gagne sur la dérive manuelle). Tu *sens* que la source de vérité, c'est le code. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Infra as Code & idempotence](../../wiki/Infra-as-Code-et-idempotence.md) · § À toi de jouer.*
|
||||
*Source : [Infra as Code & idempotence](Infra-as-Code-et-idempotence) · § À toi de jouer.*
|
||||
|
||||
## La preuve — prouver, pas affirmer
|
||||
|
||||
|
|
@ -116,7 +104,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 4 | Lis le registre | Ouvre docs/audit/affirmations.md : trouve une affirmation ⚪ (*non prouvable localement*) — vois comment elle est assumée comme intention, jamais présentée comme prouvée. | 👁 observe | — |
|
||||
| 5 | Comprends la valeur | Demande-toi : *quelle promesse est-ce que je fais sans preuve ?* C'est exactement ce que ce registre force à regarder en face. | 👁 observe | — |
|
||||
|
||||
*Source : [La preuve — prouver, pas affirmer](../../wiki/La-preuve.md) · § À toi de jouer.*
|
||||
*Source : [La preuve — prouver, pas affirmer](La-preuve) · § À toi de jouer.*
|
||||
|
||||
## Le GUI (console d'exploitation)
|
||||
|
||||
|
|
@ -129,7 +117,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 5 | Sens le garde-fou | Essaie de sauvegarder un plan incohérent (ex. une base dont le consommateur n'existe pas) : la console refuse avec une raison. L'invalide ne passe pas. | 👁 observe | — |
|
||||
| 6 | Casse & répare | Édite hosts.yml à la main, reviens dans la GUI, « Appliquer le plan » : ta modification est écrasée par le plan. La source de vérité, c'est le plan — pas l'inventaire. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Le GUI (console d'exploitation)](../../wiki/Le-GUI-console-d-exploitation.md) · § À toi de jouer.*
|
||||
*Source : [Le GUI (console d'exploitation)](Le-GUI-console-d-exploitation) · § À toi de jouer.*
|
||||
|
||||
## Le plan & l'adressage dérivé
|
||||
|
||||
|
|
@ -141,7 +129,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 4 | Casse & répare | Édite hosts.yml à la main (change une IP). Relance make instancier : il signale l'écart. Ré-applique : le plan écrase ta modification. Tu *sens* que hosts.yml n'est pas la vérité — le plan l'est. | 🔨 casse-répare | — |
|
||||
| 5 | Éprouve le garde-fou | Ajoute une ligne supernet: 10.99.0.0/16 dans une nomenclature, puis make prouver : P20 échoue (« adressage stocké »). Retire-la : vert. La règle se *prouve*. | 👁 observe | **P20** |
|
||||
|
||||
*Source : [Le plan & l'adressage dérivé](../../wiki/Le-plan-et-l-adressage-d%C3%A9riv%C3%A9.md) · § À toi de jouer.*
|
||||
*Source : [Le plan & l'adressage dérivé](Le-plan-et-l-adressage-dérivé) · § À toi de jouer.*
|
||||
|
||||
## Le réseau des tenants — du câble au VRF
|
||||
|
||||
|
|
@ -150,9 +138,9 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 1 | Lis ton réseau physique | : make underlay. Repère les VLAN sous 1000 (l'underlay) et les MTU. Pourquoi le transport doit-il être à 1500 quand l'overlay est à 1450 ? | 👁 observe | — |
|
||||
| 2 | Regarde sans écrire | : make sdn-plan. Si le cluster dit déjà ce que le plan dit, la sortie tient en une ligne. | 👁 observe | — |
|
||||
| 3 | Trouve la sortie d'un tenant | : dans make devis-sdn, repère la strophe FRR et l'adresse du prochain saut. À quel équipement appartient-elle ? | 👁 observe | — |
|
||||
| 4 | Change `index` dans un modèle | (jamais en production) et régénère : combien de valeurs ont bougé ? C'est la mesure exacte de ce que la dérivation t'épargne. Pour aller plus loin : docs/sdn-evpn.md dans le dépôt (référence technique), Le plan & l'adressage dérivé, Mult… | 👁 observe | — |
|
||||
| 4 | Change `index` dans un modèle | (jamais en production) et régénère : combien de valeurs ont bougé ? C'est la mesure exacte de ce que la dérivation t'épargne. Pour aller plus loin : docs/sdn-evpn.md (référence technique), Le plan & l'adressage dérivé, Multi-instance & f… | 👁 observe | — |
|
||||
|
||||
*Source : [Le réseau des tenants — du câble au VRF](../../wiki/Le-r%C3%A9seau-des-tenants.md) · § À toi de jouer.*
|
||||
*Source : [Le réseau des tenants — du câble au VRF](Le-réseau-des-tenants) · § À toi de jouer.*
|
||||
|
||||
## Liaisons (bindings)
|
||||
|
||||
|
|
@ -160,10 +148,10 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
|---|---|---|---|---|
|
||||
| 1 | Lis une liaison | Ouvre instance/plan/applications.yml : une app avec expose: (liaison *app→domaine*), et serveurs.yml : un nœud avec integrations: (liaison *nœud→service*). | 👁 observe | — |
|
||||
| 2 | Vois-la se résoudre | Après make instancier, regarde l'inventaire généré : la cible est devenue une valeur concrète (FQDN, groupe) — le moteur a câblé. | 👁 observe | — |
|
||||
| 3 | Requise, optionnelle, universelle | Compare les trois : retirer client_backup d'un nœud sans état → aucun problème (optionnelle). Déclarer une base sans serveur → make instancier échoue (requise). Essayer de recopier client_metrique dans serveurs.yml → le plan refuse (univ… | 👁 observe | — |
|
||||
| 3 | Requise vs optionnelle | Compare : retirer client_journal d'un nœud → aucun problème (optionnelle). Déclarer une base sans serveur → make instancier/la validation échoue (requise). Tu *sens* la différence de modalité. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Casse une liaison requise (ex. réfère une base à un serveur inexistant), relance l'instanciation : échec clair *avant* tout déploiement. Corrige : ça passe. Le moteur attrape le câblage manquant à ta place. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Liaisons (bindings)](../../wiki/Liaisons-bindings.md) · § À toi de jouer.*
|
||||
*Source : [Liaisons (bindings)](Liaisons-bindings) · § À toi de jouer.*
|
||||
|
||||
## Multi-instance & fédération
|
||||
|
||||
|
|
@ -176,7 +164,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 5 | Casse & répare | Donne à deux instances fédérées le même index (édite une nomenclature), make instances : la bannière de collision s'allume ; make prouver : P21 échoue. Corrige l'index : tout redevient vert. | 🔨 casse-répare | **P21** |
|
||||
| 6 | (Avancé) Promeus un produit | Une instance qui *tourne et se prouve* peut devenir un modèle vendable : make model-creer MODE=instance SOURCE=OPS-… NOM=… — elle est généralisée (identité → exemple.*, secrets retirés) et validée. | 👁 observe | — |
|
||||
|
||||
*Source : [Multi-instance & fédération](../../wiki/Multi-instance-et-f%C3%A9d%C3%A9ration.md) · § À toi de jouer.*
|
||||
*Source : [Multi-instance & fédération](Multi-instance-et-fédération) · § À toi de jouer.*
|
||||
|
||||
## Métriques & journaux
|
||||
|
||||
|
|
@ -184,10 +172,10 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
|---|---|---|---|---|
|
||||
| 1 | Interroge les métriques | (sur obs-01) — combien de nœuds scrapés, tous UP ? « commande » | 👁 observe | — |
|
||||
| 2 | Vois les journaux | : curl -s http://localhost:3100/loki/api/v1/label/host/values → les nœuds qui expédient leurs logs. | 👁 observe | — |
|
||||
| 3 | Ouvre Grafana | (https://observatoire.chezlepro.internal) — métriques *et* logs au même endroit. | 👁 observe | — |
|
||||
| 3 | Ouvre Grafana | (https://grafana.lab.chezlepro.internal) — métriques *et* logs au même endroit. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Arrête prometheus-node-exporter sur un nœud : dans Prometheus, sa cible passe up=0 (DOWN). Redémarre : elle repasse UP. Tu *sens* que c'est l'agent qui nourrit le serveur (modèle pull). | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Métriques & journaux](../../wiki/M%C3%A9triques-et-journaux.md) · § À toi de jouer.*
|
||||
*Source : [Métriques & journaux](Métriques-et-journaux) · § À toi de jouer.*
|
||||
|
||||
## PKI & confiance
|
||||
|
||||
|
|
@ -198,38 +186,38 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | Vois-le servir en vrai | Le LDAPS d'OpenLDAP utilise ce certificat : « commande » 0 (ok) = confiance vérifiée. | 👁 observe | — |
|
||||
| 4 | Casse & répare | Retire la racine du magasin système, refais un curl HTTPS interne : avertissement de certificat (plus de confiance). Réinstalle la racine : ça remarche. Tu viens de *sentir* pourquoi « faire confiance à la racine » est la clé de voûte. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [PKI & confiance](../../wiki/PKI-et-confiance.md) · § À toi de jouer.*
|
||||
*Source : [PKI & confiance](PKI-et-confiance) · § À toi de jouer.*
|
||||
|
||||
## Reverse-proxy & TLS
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Route par nom | Deux noms, un seul edge (10.17.16.11) : « commande » Change grafana en forge : même IP, backend différent. C'est le routage par SNI. | 👁 observe | — |
|
||||
| 2 | Vois la terminaison TLS | Le certificat présenté est celui de l'edge (avec les SAN des exposés) : openssl s_client -connect 10.17.16.11:443 -servername observatoire.chezlepro.internal \| openssl x509 -noout -text \| grep -A1 'Subject Alternative'. | 👁 observe | — |
|
||||
| 1 | Route par nom | Deux noms, un seul edge (192.168.15.21) : « commande » Change grafana en forge : même IP, backend différent. C'est le routage par SNI. | 👁 observe | — |
|
||||
| 2 | Vois la terminaison TLS | Le certificat présenté est celui de l'edge (avec les SAN des exposés) : openssl s_client -connect 192.168.15.21:443 -servername grafana.lab… \| openssl x509 -noout -text \| grep -A1 'Subject Alternative'. | 👁 observe | — |
|
||||
| 3 | Casse & répare | Arrête le backend (ex. systemctl stop grafana-server sur obs-01) et rouvre Grafana : l'edge répond 502 Bad Gateway (le proxy est là, le service non). Redémarre : ça remarche. Tu distingues le proxy de ce qu'il sert. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Reverse-proxy & TLS](../../wiki/Reverse-proxy-et-TLS.md) · § À toi de jouer.*
|
||||
*Source : [Reverse-proxy & TLS](Reverse-proxy-et-TLS) · § À toi de jouer.*
|
||||
|
||||
## Sauvegardes (3-2-1)
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Lance une sauvegarde | Sur un nœud avec client_backup : « commande » | 👁 observe | — |
|
||||
| 2 | Liste les instantanés | (le dépôt vit hors-nœud, et hors de l'écosystème) : « commande » *Le même dépôt est interrogé chaque nuit par setops-verifier-mon-depot.sh, qui rapporte à Icinga : c'est le nœud, seul détenteur de la clé, qui juge.* | 👁 observe | — |
|
||||
| 2 | Liste les instantanés | (le dépôt vit hors-nœud) : « commande » | 👁 observe | — |
|
||||
| 3 | Restaure — le vrai test | Restaure dans un dossier temporaire et compare : « commande » *(sur infra-pki-01 ; ailleurs, compare le dump correspondant.)* | 👁 observe | — |
|
||||
| 4 | Casse & répare | Supprime un fichier de donnée (une copie de test !), restaure-le depuis l'instantané, vérifie qu'il est identique. Tu viens de *sentir* que la valeur d'une sauvegarde est la restauration, pas la sauvegarde. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Sauvegardes (3-2-1)](../../wiki/Sauvegardes.md) · § À toi de jouer.*
|
||||
*Source : [Sauvegardes (3-2-1)](Sauvegardes) · § À toi de jouer.*
|
||||
|
||||
## Supervision & impact
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Ouvre Icinga Web 2 | (https://vigie.chezlepro.internal, via le SSO) : la liste des hôtes et services supervisés, avec leur état (vert/jaune/rouge). | 👁 observe | — |
|
||||
| 1 | Ouvre Icinga Web 2 | (https://icinga.lab.chezlepro.internal, via le SSO) : la liste des hôtes et services supervisés, avec leur état (vert/jaune/rouge). | 👁 observe | — |
|
||||
| 2 | Vois l'impact | Menu *Business Processes* → « Supervision Chezlepro » : un processus qui agrège des checks (load, procs, ping…) en un état roulé. C'est l'impact, pas une case. | 👁 observe | — |
|
||||
| 3 | Casse & répare | Provoque l'échec d'un check (ex. arrête un service surveillé) : l'état passe CRITICAL, et le processus BPM qui en dépend rougit (l'impact remonte). Répare : tout reverdit. Tu *sens* la différence entre *mesurer* et *superviser/alerter*. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Supervision & impact](../../wiki/Supervision-et-impact.md) · § À toi de jouer.*
|
||||
*Source : [Supervision & impact](Supervision-et-impact) · § À toi de jouer.*
|
||||
|
||||
## Sécurité & durcissement
|
||||
|
||||
|
|
@ -239,7 +227,7 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 2 | Moindre privilège | : PermitRootLogin est à no, l'accès se fait par le compte ansible + clé SSH. Vérifie : sshd -T \| grep -E 'permitrootlogin\|passwordauthentication'. | 👁 observe | — |
|
||||
| 3 | Casse & répare (avec prudence, en lab) | Assouplis un réglage sysctl, observe, puis remets-le. Tu *sens* que chaque ligne de durcissement ferme une porte précise. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Sécurité & durcissement](../../wiki/S%C3%A9curit%C3%A9-et-durcissement.md) · § À toi de jouer.*
|
||||
*Source : [Sécurité & durcissement](Sécurité-et-durcissement) · § À toi de jouer.*
|
||||
|
||||
## Virtualisation & clonage
|
||||
|
||||
|
|
@ -250,14 +238,14 @@ le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (c
|
|||
| 3 | Sépare les deux couches | cloud-init a posé *l'identité* ; tout le reste (paquets, services, durcissement) vient d'Ansible. Le template, lui, ne contient aucune donnée de clone. | 👁 observe | — |
|
||||
| 4 | Casse & répare (mentalement + lab) | Supprime un nœud non critique et reclone-le depuis le template, puis redéploie : il revient à l'identique. Tu *sens* que la machine est reconstructible. | 🔨 casse-répare | — |
|
||||
|
||||
*Source : [Virtualisation & clonage](../../wiki/Virtualisation-et-clonage.md) · § À toi de jouer.*
|
||||
*Source : [Virtualisation & clonage](Virtualisation-et-clonage) · § À toi de jouer.*
|
||||
|
||||
## Vérifier le déployé — quand la preuve statique ne suffit plus
|
||||
|
||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||
|---|---|---|---|---|
|
||||
| 1 | — | Lance les dix devis sur ta flotte — les cinq de service, puis les cinq d'infrastructure. Note le temps que ça prend : quelques minutes pour ce qui demandait une journée d'enquête à la main. | 👁 observe | — |
|
||||
| 1 | — | Lance les cinq devis sur ta flotte. Note le temps que ça prend : quelques minutes pour ce qui demandait une journée d'enquête à la main. | 👁 observe | — |
|
||||
| 2 | Casse quelque chose exprès | — arrête un service publié, change un port — et relance le devis concerné. S'il ne dit rien, c'est *lui* qu'il faut réparer, pas le service. | 👁 observe | — |
|
||||
| 3 | — | Cherche, dans ton propre outillage, une vérification qui n'a jamais échoué. Demande-toi si c'est parce que tout va bien, ou parce qu'elle ne regarde rien. \| Terme \| Ce que tu retiens \| \| \| \| \| preuve statique \| lit le code ; rapide, univ… | 👁 observe | — |
|
||||
|
||||
*Source : [Vérifier le déployé — quand la preuve statique ne suffit plus](../../wiki/V%C3%A9rifier-le-d%C3%A9ploy%C3%A9.md) · § À toi de jouer.*
|
||||
*Source : [Vérifier le déployé — quand la preuve statique ne suffit plus](Vérifier-le-déployé) · § À toi de jouer.*
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-23
|
||||
|
||||
> 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 (42 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 : 33 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 31 rôles, 82 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 | 14 hotes, 31 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 : 24 secret(s) exige(s), tous presents. Voute reelle : 25 cle(s), aucun manque. |
|
||||
| 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 : 7 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 52 regles, 15 routes, admin=10.0.0.0/24,10.17.0.0/24,192.168.254.2/32,192.168.255.2/32. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 48 groupe(s), 77 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (1 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, 33 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 25 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 14, 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 | 47 scripts expliques et atteignables, 98 cibles make documentees, 57 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 34 exigence(s) de role, toutes satisfaites (118 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 (33 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 41 document(s) declarent leur lecteur (21 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 : 32 role(s) serveur/client tous nommes, 33 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 : 43 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 |
|
||||
|
||||
## 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-23._
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-24
|
||||
|
||||
> 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 (42 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 : 36 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 34 rôles, 87 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, 35 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 : 25 cle(s), aucun manque. |
|
||||
| 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 : 7 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 68 regles, 15 routes, admin=10.0.0.0/24,10.17.0.0/24,10.29.19.41/32,192.168.254.2/32,192.168.255.2/32. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 51 groupe(s), 80 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 | 28 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 17, 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 | 49 scripts expliques et atteignables, 102 cibles make documentees, 60 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (125 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 (37 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (22 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 : 35 role(s) serveur/client tous nommes, 36 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 : 45 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 |
|
||||
|
||||
## 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-24._
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-25
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/production/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (46 OK · 0 echec · 1 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 : 36 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 34 rôles, 92 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_oauth2_proxy |
|
||||
| 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 18 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 14 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), 50 groupe(s), 79 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 5 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 | 28 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 17, 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 | 49 scripts expliques et atteignables, 102 cibles make documentees, 60 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 14 exigence(s) de role, toutes satisfaites (39 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 (16 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (23 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 0 application(s) exigeant une base l'ont toutes (0 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 2 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 : 35 role(s) serveur/client tous nommes, 36 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 : 45 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, 56 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). |
|
||||
|
||||
## 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-25._
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-26
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/production/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (48 OK · 0 echec · 1 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 : 37 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 35 rôles, 92 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_oauth2_proxy |
|
||||
| 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 18 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 14 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), 50 groupe(s), 79 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 5 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. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 50 scripts expliques et atteignables, 104 cibles make documentees, 61 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 14 exigence(s) de role, toutes satisfaites (39 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 (16 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (24 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 0 application(s) exigeant une base l'ont toutes (0 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 2 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. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 46 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, 56 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 (110 lignes). |
|
||||
|
||||
## 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-26._
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-27
|
||||
|
||||
> 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/production/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (52 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 : 37 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 35 rôles, 92 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_oauth2_proxy |
|
||||
| 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 | 5 hotes, 14 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 : 18 secret(s) exige(s), tous presents. Voute reelle : 21 cle(s), aucun manque. |
|
||||
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 14 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), 50 groupe(s), 79 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 5 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. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 51 scripts expliques et atteignables, 104 cibles make documentees, 61 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 14 exigence(s) de role, toutes satisfaites (39 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 (16 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (25 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 0 application(s) exigeant une base l'ont toutes (0 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 2 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. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 47 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, 56 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 (110 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 121 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-27._
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# 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).
|
||||
|
||||
- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (54 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 : 38 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 36 rôles, 95 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. |
|
||||
| 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 | 30 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 19, 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, 106 cibles make documentees, 62 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 (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 : 37 role(s) serveur/client tous nommes, 38 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, 60 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 (113 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 125 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. |
|
||||
|
||||
## 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._
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-30
|
||||
|
||||
> 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, 106 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 (27 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-08-30._
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-31
|
||||
|
||||
> 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, 106 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 (28 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-08-31._
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
# 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 (56 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 : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 98 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 : 12 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 | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 54 scripts expliques et atteignables, 108 cibles make documentees, 64 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 : 39 role(s) serveur/client tous nommes, 40 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 : 50 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 : 6 machine(s) du plan retrouvees, 80 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 (116 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 145 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. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
|
||||
## 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._
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-02
|
||||
|
||||
> 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** : ❌ NON CONFORME (55 OK · 1 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 : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 98 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 : 13 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 | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 54 scripts expliques et atteignables, 108 cibles make documentees, 64 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 41 exigence(s) de role, toutes satisfaites (131 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 (30 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 | — | ❌ ECHEC | 1 hote(s) detiennent de l'etat sans sauvegarde : site : site-mon-01 detient ['serveur_postgresql'] — ajouter `client_backup` a leurs `integrations` dans `plan/s |
|
||||
| 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 : 39 role(s) serveur/client tous nommes, 40 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 : 50 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 : 7 machine(s) du plan retrouvees, 101 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 (116 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 166 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. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
|
||||
## 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-02._
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-03
|
||||
|
||||
> 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** : ❌ NON CONFORME (55 OK · 1 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 : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 99 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 | 14 hotes, 35 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 : 13 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), 50 groupe(s), 80 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 55 scripts expliques et atteignables, 109 cibles make documentees, 65 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (131 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 (37 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (31 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) de l'ecosysteme et 3 du site 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 : 39 role(s) serveur/client tous nommes, 40 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 : 51 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 : 7 machine(s) du plan retrouvees, 104 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 | — | ❌ ECHEC | La carte d'orientation ne dit plus vrai :
|
||||
- « pieces d'audit » : la carte annonce 33, le depot en compte 34 |
|
||||
| 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 (117 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 169 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 | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
|
||||
## 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-03._
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-05
|
||||
|
||||
> 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** : ❌ NON CONFORME (56 OK · 1 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 : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 99 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 | 14 hotes, 35 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 : 13 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), 50 groupe(s), 80 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 57 scripts expliques et atteignables, 114 cibles make documentees, 65 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (131 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 (37 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 43 document(s) declarent leur lecteur (32 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) de l'ecosysteme et 3 du site 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 : 39 role(s) serveur/client tous nommes, 40 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 : 53 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 : 7 machine(s) du plan retrouvees, 104 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 (117 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 169 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 | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ❌ ECHEC | Des comptes ecrits en prose ne disent plus vrai :
|
||||
- docs/autorisation.md:13 annonce 29 roles, le depot en compte 65
|
||||
- wiki/Vérifier-le-déployé.md:30 annonce |
|
||||
|
||||
## 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-05._
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-06
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (56 OK · 0 echec · 1 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 : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 99 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 13 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), 50 groupe(s), 80 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 57 scripts expliques et atteignables, 114 cibles make documentees, 65 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (131 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 (37 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 43 document(s) declarent leur lecteur (33 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) de l'ecosysteme et 3 du site 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 : 39 role(s) serveur/client tous nommes, 40 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 : 53 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 : 7 machine(s) du plan retrouvees, 104 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 87 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 (117 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 169 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 | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (57 preuves, 65 roles, 40 groupes). |
|
||||
|
||||
## 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-06._
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-08
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (61 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 40 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 38 rôles, 99 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 13 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), 50 groupe(s), 80 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 58 scripts expliques et atteignables, 115 cibles make documentees, 65 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (131 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 (37 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 43 document(s) declarent leur lecteur (34 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) de l'ecosysteme et 3 du site 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 : 39 role(s) serveur/client tous nommes, 40 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 : 54 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 : 7 machine(s) du plan retrouvees, 104 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 89 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 (117 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 169 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 | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (62 preuves, 65 roles, 40 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `2887b57` (publie le 2026-09-08). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
|
||||
## 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-08._
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-09
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (64 OK · 0 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 103 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 | 14 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 : 13 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), 50 groupe(s), 82 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 58 scripts expliques et atteignables, 115 cibles make documentees, 67 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 39 exigence(s) de role, toutes satisfaites (131 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 | 44 document(s) declarent leur lecteur (35 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) de l'ecosysteme et 3 du site 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 : 40 role(s) serveur/client tous nommes, 41 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 : 54 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 : 7 machine(s) du plan retrouvees, 118 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 89 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 (121 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 183 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (64 preuves, 67 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `2d9dc86` (publie le 2026-09-09). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 1 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_pki/certificat. |
|
||||
|
||||
## 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-09._
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-10
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (66 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 107 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 29 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 : 13 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), 49 groupe(s), 87 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, 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 | 59 scripts expliques et atteignables, 116 cibles make documentees, 67 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (137 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 44 document(s) declarent leur lecteur (36 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) de l'ecosysteme et 3 du site 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 : 40 role(s) serveur/client tous nommes, 41 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 : 55 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 : 7 machine(s) du plan retrouvees, 155 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 89 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 (125 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 217 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (67 preuves, 67 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `d1ae41c` (publie le 2026-09-10). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 24 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
|
||||
## 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-10._
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-11
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (67 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 107 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 29 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 : 13 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), 49 groupe(s), 87 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 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, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, 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 | 60 scripts expliques et atteignables, 117 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (138 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 44 document(s) declarent leur lecteur (37 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) de l'ecosysteme et 3 du site 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 : 41 role(s) serveur/client tous nommes, 41 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 : 56 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 : 7 machine(s) du plan retrouvees, 151 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 89 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 (125 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 219 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (68 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `989398f` (publie le 2026-09-11). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 26 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
|
||||
## 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-11._
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-12
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (69 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 109 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 29 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 : 5 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 : 13 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), 49 groupe(s), 87 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 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 : 4 pool(s) Proxmox, 41 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, 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 | 62 scripts expliques et atteignables, 119 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (139 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 44 document(s) declarent leur lecteur (38 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) de l'ecosysteme et 3 du site 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 : 41 role(s) serveur/client tous nommes, 41 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 : 58 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 : 7 machine(s) du plan retrouvees, 153 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 89 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 (127 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 239 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (70 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `904ece3` (publie le 2026-09-12). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 26 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
|
||||
## 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-12._
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-13
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (74 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 109 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 29 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 29 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 : 6 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 : 13 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, 4 tenant(s), 51 groupe(s), 90 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 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 : 5 pool(s) Proxmox, 44 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, 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 | 65 scripts expliques et atteignables, 121 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (149 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 45 document(s) declarent leur lecteur (39 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) de l'ecosysteme et 3 du site 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 : 41 role(s) serveur/client tous nommes, 41 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 : 61 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 153 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 89 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 (127 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 240 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 — 14 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (75 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `904ece3` (publie le 2026-09-12). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 26 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 6 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
|
||||
## 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-13._
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-14
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (77 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 111 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 30 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 29 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 : 6 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 : 14 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, 4 tenant(s), 52 groupe(s), 96 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 : 5 pool(s) Proxmox, 43 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, 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 | 67 scripts expliques et atteignables, 123 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 36 exigence(s) de role, toutes satisfaites (142 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 36 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 46 document(s) declarent leur lecteur (40 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 8 hote(s) de l'ecosysteme et 3 du site 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 : 41 role(s) serveur/client tous nommes, 41 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, 96 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 : 63 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 8 machine(s) du plan retrouvees, 178 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 92 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 (129 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 268 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 — 13 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (78 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `392da4d` (publie le 2026-09-14). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 40 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 3 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (5 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 11 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 6 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 155 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
|
||||
## 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-14._
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-15
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (78 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 111 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 31 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 29 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 : 6 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 : 14 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, 4 tenant(s), 52 groupe(s), 96 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 : 5 pool(s) Proxmox, 43 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, 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 | 67 scripts expliques et atteignables, 123 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 36 exigence(s) de role, toutes satisfaites (144 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 36 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 46 document(s) declarent leur lecteur (41 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 8 hote(s) de l'ecosysteme et 3 du site 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 : 41 role(s) serveur/client tous nommes, 41 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, 96 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 : 63 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 8 machine(s) du plan retrouvees, 182 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 92 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 (129 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 272 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 — 13 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (79 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `392da4d` (publie le 2026-09-14). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 40 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 13 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 6 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 155 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ✅ OK | 6 ecosysteme(s) (instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0, SITE-Chezlepro) : pattes, edges, certificats, rechargemen |
|
||||
|
||||
## 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-15._
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-16
|
||||
|
||||
> 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 (82 OK · 0 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 42 groupes classes, aucun cycle, aucune arete en arriere ; playbooks/site.yml a jour. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 40 rôles, 119 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 | 13 hotes, 33 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 : 33 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 30 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 : 6 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 : 14 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, 4 tenant(s), 56 groupe(s), 104 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 : 5 pool(s) Proxmox, 44 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 34 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 23, 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 | 70 scripts expliques et atteignables, 131 cibles make documentees, 69 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (147 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 42 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 48 document(s) declarent leur lecteur (42 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 8 hote(s) de l'ecosysteme et 3 du site 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 : 42 role(s) serveur/client tous nommes, 42 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, 96 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 : 66 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 192 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 97 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 (137 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 282 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 — 13 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (82 preuves, 69 roles, 42 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `392da4d` (publie le 2026-09-14). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 52 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:8, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 41 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 13 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 9 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 163 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ✅ OK | 6 ecosysteme(s) (instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0, SITE-Chezlepro) : pattes, edges, certificats, rechargemen |
|
||||
| P80 | Remise au client : inscrite, nommee, et son second temps a l'heure | — | ✅ OK | Aucun ecosysteme remis a un client : rien a tenir. |
|
||||
| P81 | La console dit sa portee, et ne sert pas un inventaire vide en silence | — | ✅ OK | Portee `poste` derivee des symlinks, source d'inventaire `instance`, et 14 route(s) POST exigent toutes un pouvoir. |
|
||||
| P82 | DNS public : les zones publiees sont servies, signees avant d'etre exposees | — | ✅ OK | 2 zone(s) publique(s) declaree(s), toutes servies par le site ; 2 replication(s) declaree(s) des deux cotes ; transfert ouvert a la cle seule ; aucune expositio |
|
||||
|
||||
## 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-16._
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-17
|
||||
|
||||
> 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 (82 OK · 0 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 42 groupes classes, aucun cycle, aucune arete en arriere ; playbooks/site.yml a jour. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 40 rôles, 120 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 | 13 hotes, 33 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 : 33 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 30 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 : 6 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 : 14 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, 4 tenant(s), 56 groupe(s), 104 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 : 5 pool(s) Proxmox, 44 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 34 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 23, 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 | 72 scripts expliques et atteignables, 133 cibles make documentees, 69 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (147 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 42 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 49 document(s) declarent leur lecteur (43 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 8 hote(s) de l'ecosysteme et 3 du site 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 : 42 role(s) serveur/client tous nommes, 42 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, 96 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 : 68 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 200 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 101 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 (138 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 308 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 — 13 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (82 preuves, 69 roles, 42 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `392da4d` (publie le 2026-09-14). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 55 champ(s) sur 7 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:8, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 41 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 13 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 9 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 165 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ✅ OK | 6 ecosysteme(s) (instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0, SITE-Chezlepro) : pattes, edges, certificats, rechargemen |
|
||||
| P80 | Remise au client : inscrite, nommee, et son second temps a l'heure | — | ✅ OK | Aucun ecosysteme remis a un client : rien a tenir. |
|
||||
| P81 | La console dit sa portee, et ne sert pas un inventaire vide en silence | — | ✅ OK | Portee `poste` derivee des symlinks, source d'inventaire `instance`, et 14 route(s) POST exigent toutes un pouvoir. |
|
||||
| P82 | DNS public : les zones publiees sont servies, signees avant d'etre exposees | — | ✅ OK | 2 zone(s) publique(s) declaree(s), toutes servies par le site ; 2 replication(s) declaree(s) des deux cotes ; transfert ouvert a la cle seule ; aucune expositio |
|
||||
|
||||
## 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-17._
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-30
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ❌ NON CONFORME (81 OK · 1 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre : 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 42 groupes classes, aucun cycle, aucune arete en arriere ; playbooks/site.yml a jour. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 40 rôles, 121 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 32 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 30 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) et 7 zone(s) de site : adressage 100% derive du seed index. |
|
||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 5 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 : 14 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, 2 tenant(s), 40 groupe(s), 108 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 | 34 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 23, 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, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 77 scripts expliques et atteignables, 146 cibles make documentees, 69 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (142 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 44 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 49 document(s) declarent leur lecteur (44 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 7 hote(s) de l'ecosysteme et 3 du site detiennent de l'etat, tous porteurs de `client_backup` (8 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 : 42 role(s) serveur/client tous nommes, 42 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, 97 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 : 73 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 (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 202 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 101 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 (139 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 309 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 — 13 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), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (83 preuves, 69 roles, 42 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `3fe7e3c` (publie le 2026-09-29). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 56 champ(s) sur 7 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:9, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 47 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, client_s |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (7 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 14 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 10 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 185 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ❌ ECHEC | 1 repli(s) silencieux — une derivation vide rend un succes :
|
||||
- flux : OPS-Chezlepro-lab — perimees par rapport au plan : ops-01.nft |
|
||||
| P80 | Remise au client : inscrite, nommee, et son second temps a l'heure | — | ✅ OK | Aucun ecosysteme remis a un client : rien a tenir. |
|
||||
| P81 | La console dit sa portee, et ne sert pas un inventaire vide en silence | — | ✅ OK | Portee `poste` derivee des voutes portees, source d'inventaire `instance/inventories/principal/hosts.yml`, et 15 route(s) POST exigent toutes un pouvoir. |
|
||||
| P82 | DNS public : les zones publiees sont servies, signees avant d'etre exposees | — | ✅ OK | 2 zone(s) publique(s) declaree(s), toutes servies par le site ; 2 replication(s) declaree(s) des deux cotes ; transfert ouvert a la cle seule ; aucune expositio |
|
||||
| P83 | Assistants : le registre des runbooks ne prend pas de retard sur le Makefile | — | ✅ OK | 17 runbooks, 139 etapes, 145 cibles documentees : chacune portee par un assistant ou exemptee avec son motif. |
|
||||
|
||||
## 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-30._
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-10-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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (82 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre : 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 42 groupes classes, aucun cycle, aucune arete en arriere ; playbooks/site.yml a jour. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 40 rôles, 121 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 32 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 30 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) et 7 zone(s) de site : adressage 100% derive du seed index. |
|
||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 5 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 : 14 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, 2 tenant(s), 40 groupe(s), 108 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 | 34 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 23, 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, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 77 scripts expliques et atteignables, 146 cibles make documentees, 69 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (142 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 44 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 49 document(s) declarent leur lecteur (45 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 7 hote(s) de l'ecosysteme et 3 du site detiennent de l'etat, tous porteurs de `client_backup` (8 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 : 42 role(s) serveur/client tous nommes, 42 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, 97 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 : 73 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 (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 202 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 101 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 (139 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 309 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 — 13 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere : WAN muet, interfa |
|
||||
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (83 preuves, 69 roles, 42 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `3fe7e3c` (publie le 2026-09-29). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 56 champ(s) sur 7 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:9, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 47 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, client_s |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (7 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 14 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 10 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 185 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ✅ OK | 5 ecosysteme(s) (instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, SITE-Chezlepro) : pattes, edges, certificats, rechargements, jumeaux d' |
|
||||
| P80 | Remise au client : inscrite, nommee, et son second temps a l'heure | — | ✅ OK | Aucun ecosysteme remis a un client : rien a tenir. |
|
||||
| P81 | La console dit sa portee, et ne sert pas un inventaire vide en silence | — | ✅ OK | Portee `poste` derivee des voutes portees, source d'inventaire `instance/inventories/principal/hosts.yml`, et 15 route(s) POST exigent toutes un pouvoir. |
|
||||
| P82 | DNS public : les zones publiees sont servies, signees avant d'etre exposees | — | ✅ OK | 2 zone(s) publique(s) declaree(s), toutes servies par le site ; 2 replication(s) declaree(s) des deux cotes ; transfert ouvert a la cle seule ; aucune expositio |
|
||||
| P83 | Assistants : le registre des runbooks ne prend pas de retard sur le Makefile | — | ✅ OK | 17 runbooks, 139 etapes, 145 cibles documentees : chacune portee par un assistant ou exemptee avec son motif. |
|
||||
|
||||
## 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-10-01._
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-10-03
|
||||
|
||||
> 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** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ❌ NON CONFORME (81 OK · 1 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre : 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), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 42 groupes classes, aucun cycle, aucune arete en arriere ; playbooks/site.yml a jour. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 40 rôles, 121 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 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| 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 : 32 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 30 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) et 7 zone(s) de site : adressage 100% derive du seed index. |
|
||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 5 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 : 14 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, 2 tenant(s), 40 groupe(s), 108 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 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 | 34 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 23, 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, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 77 scripts expliques et atteignables, 146 cibles make documentees, 69 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (142 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 44 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 49 document(s) declarent leur lecteur (46 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 7 hote(s) de l'ecosysteme et 3 du site detiennent de l'etat, tous porteurs de `client_backup` (8 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 : 42 role(s) serveur/client tous nommes, 42 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, 97 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 : 73 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 (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 202 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 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 | — | ❌ ECHEC | La carte d'orientation ne dit plus vrai :
|
||||
- « pieces d'audit » : la carte annonce 50, le depot en compte 51 |
|
||||
| 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 (139 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 309 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 — 13 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere : WAN muet, interfa |
|
||||
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 13 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 13. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (83 preuves, 69 roles, 42 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `3fe7e3c` (publie le 2026-09-29). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 56 champ(s) sur 7 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:9, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 47 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, client_s |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (7 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 14 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
| P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. |
|
||||
| P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. |
|
||||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 10 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 185 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
| P79 | Replis silencieux : une derivation vide ne passe pas pour un succes | — | ✅ OK | 5 ecosysteme(s) (instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, SITE-Chezlepro) : pattes, edges, certificats, rechargements, jumeaux d' |
|
||||
| P80 | Remise au client : inscrite, nommee, et son second temps a l'heure | — | ✅ OK | Aucun ecosysteme remis a un client : rien a tenir. |
|
||||
| P81 | La console dit sa portee, et ne sert pas un inventaire vide en silence | — | ✅ OK | Portee `poste` derivee des voutes portees, source d'inventaire `instance/inventories/principal/hosts.yml`, et 15 route(s) POST exigent toutes un pouvoir. |
|
||||
| P82 | DNS public : les zones publiees sont servies, signees avant d'etre exposees | — | ✅ OK | 2 zone(s) publique(s) declaree(s), toutes servies par le site ; 2 replication(s) declaree(s) des deux cotes ; transfert ouvert a la cle seule ; aucune expositio |
|
||||
| P83 | Assistants : le registre des runbooks ne prend pas de retard sur le Makefile | — | ✅ OK | 17 runbooks, 139 etapes, 145 cibles documentees : chacune portee par un assistant ou exemptee avec son motif. |
|
||||
|
||||
## 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-10-03._
|
||||
|
|
@ -94,7 +94,7 @@ Binaires, vérifiables, définis **avant** le début. On ne les renégocie pas e
|
|||
| # | Critère | Vérification |
|
||||
|---|---|---|
|
||||
| **R1** | Instance créée depuis `socle`, plan renseigné à ses valeurs | `make inventaire-verifier` → rc=0 |
|
||||
| **R2** | Golden template Debian 13 construit et converti en template Proxmox | `make verifier-modele` OK ; template visible dans Proxmox **sous le nom que `make config` a enregistré** (`proxmox_clone_source_nom`, `modeleSetOPS` par défaut) |
|
||||
| **R2** | Golden template Debian 13 construit et converti en template Proxmox | `make verifier-modele` OK ; template `modele-debian13` visible dans Proxmox |
|
||||
| **R3** | Au moins une VM créée depuis le plan | `make creer-vm HOTE=…` OK ; VM jointe par Ansible (`ansible -m ping`) |
|
||||
| **R4** | Cet hôte déployé selon ses groupes | `make deployer HOTE=…` → rc=0 |
|
||||
| **R5** | Validation globale du dépôt passée par l'opérateur | `make verifier` → rc=0 |
|
||||
|
|
|
|||
|
|
@ -1,546 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Registres du plan Set-OPS",
|
||||
"description": "GENERE par scripts/schema_plan.py (make schema). Ne pas editer a la main. Decrit la FORME des registres ; la COHERENCE reste aux validateurs de inventory_rules.py.",
|
||||
"registres": {
|
||||
"serveurs": {
|
||||
"title": "Serveurs (VM)",
|
||||
"x-fichier": "plan/serveurs.yml",
|
||||
"x-racine": "serveurs",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fonction": {
|
||||
"type": "string",
|
||||
"title": "Fonction",
|
||||
"description": "Determine VMID, IP, VLAN et passerelle. Doit exister dans la nomenclature.",
|
||||
"x-source-valeurs": "nomenclature.fonctions"
|
||||
},
|
||||
"etat": {
|
||||
"type": "string",
|
||||
"title": "État",
|
||||
"description": "`planifie` = decrit mais pas deploye ; `actif` = joignable par Ansible.",
|
||||
"enum": [
|
||||
"actif",
|
||||
"planifie"
|
||||
]
|
||||
},
|
||||
"integrations": {
|
||||
"type": "array",
|
||||
"title": "Intégrations facultatives",
|
||||
"description": "Seulement les facultatives. Les universelles viennent du role et sont refusees ici.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-editeur": "matrice"
|
||||
},
|
||||
"noeud": {
|
||||
"type": "string",
|
||||
"title": "Nœud Proxmox",
|
||||
"description": "Surcharge le defaut de `make config`.",
|
||||
"x-defaut-intrant": "proxmox_clone_noeud",
|
||||
"x-source-valeurs": "intrants.proxmox_noeuds"
|
||||
},
|
||||
"stockage": {
|
||||
"type": "string",
|
||||
"title": "Stockage",
|
||||
"description": "Surcharge le defaut de `make config`.",
|
||||
"x-defaut-intrant": "proxmox_clone_stockage",
|
||||
"x-source-valeurs": "intrants.proxmox_stockages"
|
||||
},
|
||||
"disque": {
|
||||
"type": "string",
|
||||
"title": "Disque",
|
||||
"description": "Ex. `32G`. Vide = derive des empreintes des roles.",
|
||||
"x-defaut-derive": "disque_taille"
|
||||
},
|
||||
"memoire": {
|
||||
"type": "integer",
|
||||
"title": "Mémoire (Mo)",
|
||||
"description": "Vide = derive des empreintes des roles.",
|
||||
"x-defaut-derive": "memoire"
|
||||
},
|
||||
"coeurs": {
|
||||
"type": "integer",
|
||||
"title": "Cœurs",
|
||||
"description": "Vide = derive des empreintes des roles.",
|
||||
"x-defaut-derive": "coeurs"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"fonction"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Nom d'hôte",
|
||||
"description": "Le nom court de la VM. Le rang final derive l'adresse."
|
||||
}
|
||||
},
|
||||
"applications": {
|
||||
"title": "Applications",
|
||||
"x-fichier": "plan/applications.yml",
|
||||
"x-racine": "applications",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"groupe": {
|
||||
"type": "string",
|
||||
"title": "Groupe (rôle)",
|
||||
"description": "La capacite appliquee. Doit avoir un playbook homonyme.",
|
||||
"x-source-valeurs": "groupes_operationnels"
|
||||
},
|
||||
"hote": {
|
||||
"type": "string",
|
||||
"title": "Hôte",
|
||||
"description": "Une VM declaree au plan. Un hote inconnu est un « hote fantome » (P06).",
|
||||
"x-source-valeurs": "serveurs"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"title": "Port"
|
||||
},
|
||||
"expose": {
|
||||
"type": "array",
|
||||
"title": "Exposition publique",
|
||||
"description": "FQDN publies par l'edge. Derivent le vhost, les SAN et le plancher /etc/hosts.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"requiert": {
|
||||
"type": "array",
|
||||
"title": "Requiert",
|
||||
"description": "Dependance applicative. Indicative : l'ordre de deploiement vient des couches.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"liens": {
|
||||
"type": "array",
|
||||
"title": "Liens (bindings)",
|
||||
"description": "Roles acceptes par le role porteur (meta/liens.yml).",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vers": {
|
||||
"type": "string",
|
||||
"title": "Vers",
|
||||
"description": "L'application liee.",
|
||||
"x-source-valeurs": "applications"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"title": "Rôle",
|
||||
"description": "Le role du lien, accepte par meta/liens.yml."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"role",
|
||||
"vers"
|
||||
]
|
||||
},
|
||||
"x-editeur": "liens"
|
||||
},
|
||||
"websocket": {
|
||||
"type": "boolean",
|
||||
"title": "WebSocket",
|
||||
"description": "L'edge doit relayer la mise a niveau de connexion."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"groupe",
|
||||
"hote"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Identifiant",
|
||||
"description": "Nom de l'application dans le plan."
|
||||
}
|
||||
},
|
||||
"bases_donnees": {
|
||||
"title": "Bases de donnees",
|
||||
"x-fichier": "plan/bases-donnees.yml",
|
||||
"x-racine": "bases_donnees",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"serveur": {
|
||||
"type": "string",
|
||||
"title": "Serveur de BD",
|
||||
"x-source-valeurs": "serveurs_bd"
|
||||
},
|
||||
"base": {
|
||||
"type": "string",
|
||||
"title": "Base"
|
||||
},
|
||||
"proprietaire": {
|
||||
"type": "string",
|
||||
"title": "Propriétaire"
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"title": "Secret (Vault)",
|
||||
"description": "NOM d'une variable Vault, jamais une valeur. Le secret ne quitte pas le role."
|
||||
},
|
||||
"consommateur": {
|
||||
"type": "string",
|
||||
"title": "Consommateur",
|
||||
"description": "Qui utilise cette base. La liste depend de la portee.",
|
||||
"x-source-selon": {
|
||||
"champ": "portee",
|
||||
"cas": {
|
||||
"hote": "serveurs",
|
||||
"application": "applications"
|
||||
},
|
||||
"defaut": "groupes_operationnels"
|
||||
}
|
||||
},
|
||||
"portee": {
|
||||
"type": "string",
|
||||
"title": "Portée",
|
||||
"description": "Comment le consommateur est designe : par application, par groupe ou par hote.",
|
||||
"enum": [
|
||||
"application",
|
||||
"groupe",
|
||||
"hote"
|
||||
],
|
||||
"default": "groupe"
|
||||
},
|
||||
"usage": {
|
||||
"type": "string",
|
||||
"title": "Usage",
|
||||
"default": "principale"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"base",
|
||||
"consommateur",
|
||||
"proprietaire",
|
||||
"secret",
|
||||
"serveur"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Identifiant",
|
||||
"description": "Nom de l'entree au registre, ex. `bd_forgejo`."
|
||||
}
|
||||
},
|
||||
"serveurs_bd": {
|
||||
"title": "Serveurs de bases de donnees",
|
||||
"x-fichier": "plan/bases-donnees.yml",
|
||||
"x-racine": "serveurs_bd",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Moteur"
|
||||
},
|
||||
"hote": {
|
||||
"type": "string",
|
||||
"title": "Hôte",
|
||||
"x-source-valeurs": "serveurs"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"title": "Port"
|
||||
},
|
||||
"groupe": {
|
||||
"type": "string",
|
||||
"title": "Groupe",
|
||||
"x-source-valeurs": "groupes_operationnels"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"hote",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Nom",
|
||||
"description": "Nom du serveur de bases, ex. `pg-principal`."
|
||||
}
|
||||
},
|
||||
"domaines_publics": {
|
||||
"title": "Domaines publics",
|
||||
"x-fichier": "plan/domaines.yml",
|
||||
"x-racine": "domaines_publics",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"autorite": {
|
||||
"type": "string",
|
||||
"title": "Autorité DNS",
|
||||
"enum": [
|
||||
"auto-heberge",
|
||||
"delegue",
|
||||
"primaire-cache"
|
||||
]
|
||||
},
|
||||
"edge": {
|
||||
"type": "string",
|
||||
"title": "Edge",
|
||||
"description": "Le GROUPE Ansible qui sert cette zone (ex. serveur_nginx).",
|
||||
"x-source-valeurs": "groupes_edge"
|
||||
},
|
||||
"resolution_interne": {
|
||||
"type": "string",
|
||||
"title": "Resolution interne",
|
||||
"description": "`service` : le plancher et la zone menent au service lui-meme, l'edge ne sert qu'aux personnes.",
|
||||
"enum": [
|
||||
"edge",
|
||||
"service"
|
||||
]
|
||||
},
|
||||
"secondaires": {
|
||||
"type": "array",
|
||||
"title": "Secondaires",
|
||||
"description": "Serveurs DNS secondaires de la zone.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dnssec": {
|
||||
"type": "boolean",
|
||||
"title": "DNSSEC"
|
||||
},
|
||||
"exposition": {
|
||||
"type": "array",
|
||||
"title": "Expositions declarees",
|
||||
"description": "FQDN publies pour cette zone, vers un groupe cible.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nom": {
|
||||
"type": "string",
|
||||
"title": "Nom",
|
||||
"description": "Sous-domaine, ou `@` pour la zone elle-meme."
|
||||
},
|
||||
"cible": {
|
||||
"type": "string",
|
||||
"title": "Cible",
|
||||
"description": "Le groupe interne qui sert ce nom.",
|
||||
"x-source-valeurs": "groupes_operationnels"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Type",
|
||||
"default": "web"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"cible",
|
||||
"nom"
|
||||
]
|
||||
}
|
||||
},
|
||||
"enregistrements": {
|
||||
"type": "array",
|
||||
"title": "Enregistrements publics",
|
||||
"description": "MX, SPF, DMARC, DKIM, CAA et noms historiques de la zone.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nom": {
|
||||
"type": "string",
|
||||
"title": "Nom",
|
||||
"description": "Relatif a la zone : `@`, `mx`, `_dmarc`."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Type",
|
||||
"enum": [
|
||||
"A",
|
||||
"AAAA",
|
||||
"CAA",
|
||||
"CNAME",
|
||||
"MX",
|
||||
"TXT"
|
||||
]
|
||||
},
|
||||
"valeur": {
|
||||
"type": "string",
|
||||
"title": "Valeur",
|
||||
"description": "Adresse publique, nom d'hote complet ou texte (sans guillemets)."
|
||||
},
|
||||
"priorite": {
|
||||
"type": "integer",
|
||||
"title": "Priorite",
|
||||
"description": "Requise pour un MX."
|
||||
},
|
||||
"etiquette": {
|
||||
"type": "string",
|
||||
"title": "Etiquette CAA",
|
||||
"enum": [
|
||||
"iodef",
|
||||
"issue",
|
||||
"issuewild"
|
||||
]
|
||||
},
|
||||
"ttl": {
|
||||
"type": "integer",
|
||||
"title": "TTL",
|
||||
"description": "Secondes (60 a 604800) ; defaut de la zone sinon."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"nom",
|
||||
"type",
|
||||
"valeur"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"autorite",
|
||||
"edge"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Domaine",
|
||||
"description": "Le nom public, ex. `chezlepro.ca`."
|
||||
}
|
||||
},
|
||||
"acces_admin_vpn": {
|
||||
"title": "Acces d'administration (WireGuard)",
|
||||
"x-fichier": "plan/acces.yml",
|
||||
"x-racine": "acces_admin_vpn",
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cle_publique": {
|
||||
"type": "string",
|
||||
"title": "Cle publique",
|
||||
"description": "Cle WireGuard PUBLIQUE de l'appareil (44 caracteres). La privee ne quitte jamais l'appareil."
|
||||
},
|
||||
"adresse": {
|
||||
"type": "string",
|
||||
"title": "Adresse dans le tunnel",
|
||||
"description": "Un /32 du reseau derive `10.<index>.29.0/24`."
|
||||
},
|
||||
"etat": {
|
||||
"type": "string",
|
||||
"title": "Etat",
|
||||
"description": "`absent` revoque l'acces au prochain passage.",
|
||||
"enum": [
|
||||
"present",
|
||||
"absent"
|
||||
],
|
||||
"default": "present"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"adresse",
|
||||
"cle_publique"
|
||||
]
|
||||
},
|
||||
"x-clef": {
|
||||
"title": "Pair",
|
||||
"description": "`personne-appareil`, ex. `daniel-portable` — un pair par appareil, pour revoquer l'un sans l'autre."
|
||||
}
|
||||
},
|
||||
"nomenclature": {
|
||||
"title": "Nomenclature",
|
||||
"x-fichier": "plan/nomenclature.yml",
|
||||
"x-racine": null,
|
||||
"entite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"title": "Index (seed)",
|
||||
"description": "LE seul champ d'adressage. Tout en derive ; P20 refuse d'en stocker un autre.",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"cidr_hote": {
|
||||
"type": "integer",
|
||||
"title": "CIDR d'hôte",
|
||||
"description": "Masque des sous-reseaux de zone. 24 = 254 hotes par zone."
|
||||
},
|
||||
"categories": {
|
||||
"type": "object",
|
||||
"title": "Catégories (zones)",
|
||||
"description": "Une zone de securite par cle. Le numero derive le 3e octet et le VLAN.",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"libelle": {
|
||||
"type": "string",
|
||||
"title": "Libellé",
|
||||
"description": "Nom lisible de la zone (Frontiere, Identite...)."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"libelle"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fonctions": {
|
||||
"type": "object",
|
||||
"title": "Fonctions",
|
||||
"description": "categorie + service par fonction. VMID et IP en derivent.",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"categorie": {
|
||||
"type": "integer",
|
||||
"title": "Catégorie",
|
||||
"description": "La zone. Fixe le 3e octet (15 + categorie) et le VLAN.",
|
||||
"x-source-valeurs": "nomenclature.categories"
|
||||
},
|
||||
"service": {
|
||||
"type": "integer",
|
||||
"title": "Service",
|
||||
"description": "Fixe le bloc d'adresses de l'hote dans la zone."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"categorie",
|
||||
"service"
|
||||
]
|
||||
}
|
||||
},
|
||||
"reservations": {
|
||||
"type": "object",
|
||||
"title": "Réservations",
|
||||
"description": "Adresses soustraites a la derivation dans la zone.",
|
||||
"properties": {
|
||||
"passerelle": {
|
||||
"type": "integer",
|
||||
"title": "Passerelle",
|
||||
"description": "Dernier octet de la passerelle. P23 refuse un SVI qui s'en ecarte."
|
||||
},
|
||||
"reserve_min": {
|
||||
"type": "integer",
|
||||
"title": "Réserve (min)",
|
||||
"description": "Premier octet soustrait a la derivation."
|
||||
},
|
||||
"reserve_max": {
|
||||
"type": "integer",
|
||||
"title": "Réserve (max)",
|
||||
"description": "Dernier octet soustrait a la derivation."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"index"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
# Ecrit par `make wiki-publier`, lu par la preuve P60. Ne pas editer a la main.
|
||||
remote: ssh://git@eregion.chezlepro.ca:2222/Alliance-Boreale/Set-OPS-Public.wiki.git
|
||||
source: e9215a2
|
||||
date: 2026-10-07
|
||||
|
|
@ -98,23 +98,13 @@ Une directive qu'aucune garde ne vérifie finit par ne plus être vraie. Chaque
|
|||
| `socle-identite` | **est** la chaîne d'identité, ne peut pas se déléguer à elle-même | keycloak, openldap |
|
||||
| `ldap-direct` | protocole non-OIDC lié à LDAP | dovecot, postfix |
|
||||
| `interne-sans-auth` | interface joignable **dans** le tenant sans authentification, non exposée | prometheus, loki |
|
||||
| `sans-auth-humaine` | aucun point d'authentification humaine | 23 |
|
||||
| `sans-auth-humaine` | aucun point d'authentification humaine | 12 |
|
||||
|
||||
La preuve refuse **l'oubli et le mensonge** : un rôle sans déclaration, une portée
|
||||
inventée, un `web-sso` sans accès de secours ou sans posture de formulaire, un `web-sso
|
||||
natif` sans réglage `<rôle>_connexion_locale` **défini dans `defaults`**, et une
|
||||
déclaration que le code contredit.
|
||||
|
||||
**Et depuis le 2026-09-07, sa sœur `P58` garde les habilitations** — ce que P29 ne fait
|
||||
pas : elle tient les *positions* (qui s'authentifie comment), pas les *droits* (qui obtient
|
||||
quoi). Voir [`autorisation.md`](autorisation.md) §5.
|
||||
|
||||
**Depuis le 2026-09-06, elle refuse aussi que ce tableau mente.** Les chiffres et les listes
|
||||
de la troisième colonne sont confrontés aux déclarations réelles. Ils avaient cessé d'être
|
||||
vrais sans que rien ne le signale : la ligne `sans-auth-humaine` annonçait 12 rôles, il y en
|
||||
avait 21 — la preuve lisait les déclarations depuis le début, mais ne regardait pas ce que
|
||||
le document en disait.
|
||||
|
||||
> **Les indices doivent être nommés.** Une première version cherchait les mots « ldap » et
|
||||
> « oidc » dans le rôle. Le mot *LDAP*, présent dans un commentaire de `serveur_grafana`,
|
||||
> suffisait alors à valider une déclaration `ldap-direct` mensongère. La preuve exige
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@ Toute personne qui s'authentifie obtient donc le défaut du service — tout, ou
|
|||
aucun humain n'a de compte : `ou=people` est vide aussi. L'écosystème est déployé et
|
||||
personne ne peut y entrer autrement que par les comptes de secours en voûte.
|
||||
|
||||
> **Révisé le 2026-09-05 — le constat ci-dessus est daté, et il a bougé.** `ou=groups`
|
||||
> n'est plus un conteneur que personne ne lit : `amorcage_acces`, `serveur_keycloak` et
|
||||
> `serveur_icingaweb2` s'en servent. La mesure du 2026-08-07 est conservée telle quelle
|
||||
> parce qu'elle explique *pourquoi* ce document existe — mais elle ne décrit plus l'état
|
||||
> du dépôt. Le §2 et la suite, eux, restent la doctrine en vigueur.
|
||||
|
||||
## 2. Deux régimes, et la frontière entre eux
|
||||
|
||||
C'est la décision principale, et elle **contredit délibérément la doctrine du dépôt**.
|
||||
|
|
@ -176,22 +170,10 @@ ansible-vault view instance/inventories/principal/group_vars/all/vault.yml \
|
|||
opération sauf le changement lui-même (§3). C'est le premier geste de la reprise, et il
|
||||
n'est pas optionnel.
|
||||
|
||||
> **La clé de la voûte — une voûte, une clé (depuis le 2026-08-28).** Ce paragraphe a
|
||||
> longtemps désigné un `ANSIBLE_VAULT_PASSWORD_FILE` unique, `~/.config/setops-vault-pass`.
|
||||
> Ce n'est plus le mécanisme : un seul mot de passe ouvrait alors *toutes* les voûtes de la
|
||||
> flotte, celle de l'hébergeur comprise — compromettre le plus petit locataire, c'était
|
||||
> obtenir les secrets de tous. Chaque dépôt a désormais **sa** clé, nommée d'après lui :
|
||||
> `~/.config/setops-vault-<dépôt-en-minuscules>`. Le `Makefile` les rassemble tout seul dans
|
||||
> `ANSIBLE_VAULT_IDENTITY_LIST` (`scripts/voutes.py`), et Ansible les essaie toutes — il n'y
|
||||
> a **rien à exporter**. Pour voir ce que cette machine peut ouvrir :
|
||||
>
|
||||
> ```
|
||||
> python3 scripts/voutes.py etat
|
||||
> ```
|
||||
>
|
||||
> **Sans la clé de ton écosystème, rien de ce qui suit n'est possible** — c'est la clé de
|
||||
> voûte au sens propre, et la première chose à sortir de la machine
|
||||
> (`make cles-exporter`, cf. [`sortir-les-cles-du-poste.md`](sortir-les-cles-du-poste.md)).
|
||||
> Le mot de passe de la voûte est lu depuis `ANSIBLE_VAULT_PASSWORD_FILE`
|
||||
> (`~/.config/setops-vault-pass` par défaut). **Sans ce fichier, rien de ce qui suit n'est
|
||||
> possible** — c'est la clé de voûte au sens propre, et la première chose à sauvegarder
|
||||
> hors de la machine.
|
||||
|
||||
### 6.2 Deux consoles Keycloak, et la racine mène à la mauvaise
|
||||
|
||||
|
|
@ -393,34 +375,5 @@ quel dossier Nextcloud : ça se règle dans le service, à partir du groupe qu'i
|
|||
Set-OPS accorde l'entrée et le niveau ; il ne réimplémente pas le modèle de chaque
|
||||
application.
|
||||
|
||||
**Ce qui est construit, et ce qui ne l'est pas — mesuré le 2026-09-06.** Ce document s'est
|
||||
terminé jusqu'à cette date sur « **Rien n'est construit** [...] le rôle d'amorçage, les
|
||||
`meta/acces.yml` et la preuve restent à écrire ». C'était devenu faux au point de contredire
|
||||
le §3 du même document, qui rapporte des mesures **datées du 2026-08-11** prises sur le rôle
|
||||
en fonctionnement. L'état réel :
|
||||
|
||||
| | État |
|
||||
|---|---|
|
||||
| Le rôle d'amorçage | **`roles/amorcage_acces/`** — écrit, déployé, et c'est lui qui crée l'unique compte `sysadmin` du §3 |
|
||||
| Les `meta/acces.yml` | **écrits pour les cinq services `web-sso`** : `serveur_forgejo`, `serveur_grafana`, `serveur_icingaweb2`, `serveur_keycloak`, `serveur_nextcloud` |
|
||||
| La preuve | **écrite le 2026-09-07 — c'est P58** |
|
||||
|
||||
Le §5 de [`authentification.md`](authentification.md) formule la règle : *une directive
|
||||
qu'aucune garde ne vérifie finit par ne plus être vraie*. `meta/acces.yml` a été dans ce cas
|
||||
jusqu'au 2026-09-07 : rien ne vérifiait qu'un service `web-sso` en porte un. **P29** gardait
|
||||
les *positions* d'authentification ; **P58** garde désormais les *habilitations*.
|
||||
|
||||
Ce qu'elle exige :
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| tout rôle `web-sso` porte un `meta/acces.yml` | **sauf** `formulaire_local: aucun` — une passerelle authentifie devant, elle n'accorde rien. L'exemption est **dérivée** de la déclaration, jamais un nom en dur |
|
||||
| chaque entrée nomme `groupe`, `accorde`, `porte_par`, `raison` | `porte_par` existe pour rendre visible, dans la déclaration même, **ce qui n'est pas câblé** |
|
||||
| `groupe` est un groupe, pas un compte | ni `@`, ni `uid=` — c'est D-66, tenu par une machine |
|
||||
| une habilitation `porte_par: role-realm` est **réellement projetée** par `serveur_keycloak` | c'est le croisement qui porte la preuve : sans lui, un service peut annoncer une habilitation que rien ne transporte, et l'écran reste vide sans que personne sache pourquoi |
|
||||
|
||||
> **Ce qu'elle n'exige PAS, et c'est le point le plus important.** Elle ne demande pas que
|
||||
> les groupes nommés existent dans l'annuaire. Ce serait contredire le §2 : le dépôt
|
||||
> **amorce** un accès et se retire ; les appartenances appartiennent à une personne.
|
||||
> `amorcage_acces` ne crée qu'un groupe — `dev` et `personnel` sont créés par l'exploitant,
|
||||
> et leur absence du code n'est pas un défaut, c'est le régime.
|
||||
**Rien n'est construit.** Ce document fixe la direction ; le rôle d'amorçage, les
|
||||
`meta/acces.yml` et la preuve restent à écrire.
|
||||
|
|
|
|||
|
|
@ -2,14 +2,8 @@
|
|||
|
||||
> **Pour qui :** le **mainteneur** qui ajoute une relation service → service.
|
||||
|
||||
> Note de conception, 2026-07-02. Direction retenue : **liens déclarés côté application (le
|
||||
> consommateur déclare ses besoins)**.
|
||||
>
|
||||
> **Statut, revu le 2026-09-06 : ce n'est plus « à valider avant implémentation ».** Le
|
||||
> mécanisme est construit et déployé — le §9 en donne le phasage, et les §1 et §2 ci-dessous
|
||||
> décrivent l'**état de départ de juillet**, conservé parce qu'il explique *pourquoi* le
|
||||
> modèle est ce qu'il est. Ils ne décrivent pas le dépôt d'aujourd'hui : voir l'encadré au
|
||||
> §2. Ce qui reste ouvert est nommé au §7 (le graphe des liens) et au §10.
|
||||
> Note de conception, 2026-07-02. Décision d'architecture à valider avant implémentation.
|
||||
> Direction retenue : **liens déclarés côté application (le consommateur déclare ses besoins)**.
|
||||
|
||||
## 1. Problème
|
||||
|
||||
|
|
@ -25,9 +19,9 @@ Conséquence : **la topologie n'est pas déclarative**. Déplacer Dovecot sur un
|
|||
éditer des variables à la main. **Cela échoue à l'épreuve de la portabilité multi-tenant** —
|
||||
pourtant au cœur de la mission.
|
||||
|
||||
## 2. Ce qui existait déjà, en juillet 2026 (état de départ)
|
||||
## 2. Ce qui existe déjà (partiel)
|
||||
|
||||
Le concept était à moitié né, éclaté et non câblé :
|
||||
Le concept est à moitié né, mais éclaté et non câblé :
|
||||
|
||||
- **Bases** (`plan/bases-donnees.yml`) : `consommateur` + `portee` (groupe|hote|application) +
|
||||
`secret` (Vault) + `usage` + `proprietaire`. Un vrai binding base → consommateur, mais vide et
|
||||
|
|
@ -37,12 +31,6 @@ Le concept était à moitié né, éclaté et non câblé :
|
|||
- **Applications** (`plan/applications.yml`) : seulement `groupe` + `hote`. Aucun lien app → app.
|
||||
- **Rôles** : portent déjà une méta auto-descriptive (`meta/empreinte.yml`).
|
||||
|
||||
> **Aucune de ces quatre lignes n'est encore vraie (mesuré le 2026-09-06).** Le registre des
|
||||
> bases porte **4 bases** et un serveur, tous consommés ; **6 applications** déclarent un
|
||||
> `expose` ; `postfix` déclare **3 liens** app→app. La section est gardée au passé parce
|
||||
> qu'elle est le *problème* que le reste du document résout — la lire au présent donnerait
|
||||
> l'impression que rien n'a bougé.
|
||||
|
||||
## 3. Modèle proposé
|
||||
|
||||
**Un binding est une arête typée et dirigée**, d'un **consommateur** (une application) vers une
|
||||
|
|
@ -144,7 +132,7 @@ Pour chaque application portant des `liens`, pour chaque lien `{vers, role}` :
|
|||
applications:
|
||||
forgejo:
|
||||
groupe: serveur_forgejo
|
||||
hote: forge-01
|
||||
hote: git-01
|
||||
liens:
|
||||
- vers: git.chezlepro.ca # domaine public (domaines.yml)
|
||||
role: exposition
|
||||
|
|
@ -161,12 +149,9 @@ le lien app ↔ domaine relie enfin app + domaine + edge, aujourd'hui séparés.
|
|||
> app ». Mais le binding app→base **existe déjà et fonctionne**, par un mécanisme *différent* de
|
||||
> celui des liens app→app. Il **ne faut pas le dupliquer** dans `instancier`.
|
||||
|
||||
Réalité : les rôles **consommateurs** résolvent leur base **au déploiement, dans le rôle**,
|
||||
depuis le registre `plan/bases-donnees.yml`. Ils sont **cinq** aujourd'hui — `serveur_forgejo`,
|
||||
`serveur_icinga`, `serveur_icingaweb2`, `serveur_keycloak`, `serveur_nextcloud` — et passent
|
||||
tous par `roles/resoudre_base` (cf. le paragraphe qui suit). *`serveur_postgresql` figurait
|
||||
dans cette liste jusqu'au 2026-09-06 : il n'y a pas sa place. Il lit bien le registre, mais
|
||||
pour **créer** les bases et leurs comptes — il est le serveur, pas un consommateur.* :
|
||||
Réalité : quatre rôles (`serveur_postgresql`, `serveur_forgejo`, `serveur_keycloak`,
|
||||
`serveur_icinga`) résolvent leur base **au déploiement, dans le rôle**, depuis le registre
|
||||
`plan/bases-donnees.yml` :
|
||||
|
||||
```yaml
|
||||
- include_vars: bases-donnees.yml # charge le registre
|
||||
|
|
@ -190,12 +175,9 @@ annuaire, milter…) résolus par instancier. Les liens **app→base** restent *
|
|||
dans le rôle. Deux directions, **assumées**, chacune selon la nature de la cible et la sensibilité
|
||||
du secret. La §3.1 est donc raffinée, pas contredite.
|
||||
|
||||
~~Reste comme valeur réelle (non bloquant) : **factoriser** le bloc de résolution copié-collé
|
||||
dans les 4 rôles en un include partagé.~~ **Fait.** Le rôle utilitaire
|
||||
[`roles/resoudre_base/`](../roles/resoudre_base/README.md) porte la résolution, et **cinq**
|
||||
rôles consommateurs l'incluent (`serveur_forgejo`, `serveur_icinga`, `serveur_icingaweb2`,
|
||||
`serveur_keycloak`, `serveur_nextcloud`). Le DRY a bien été validé **en le déployant**, comme
|
||||
prévu — l'épreuve de Keycloak. Cf. §9.
|
||||
Reste comme valeur réelle (non bloquant) : **factoriser** le bloc de résolution copié-collé dans
|
||||
les 4 rôles en un include partagé (ex. `roles/_resoudre_base/`). À faire **avec l'épreuve de
|
||||
Keycloak** (qui utilise ce mécanisme), pour valider le DRY en le déployant. Cf. §9.
|
||||
|
||||
## 6. Règles de validation
|
||||
|
||||
|
|
@ -218,39 +200,27 @@ prévu — l'épreuve de Keycloak. Cf. §9.
|
|||
d'inventaire), très parlante pour la **démo de portabilité** (déplacer un nœud, voir les
|
||||
arêtes suivre).
|
||||
|
||||
## 8. Preuve de migration (les liens mail) — faite, mais pas comme prévu
|
||||
## 8. Preuve de migration (les 3 liens mail)
|
||||
|
||||
Premier cas concret. **Deux des trois lignes sont passées par les `liens`, la troisième
|
||||
non** — et l'écart est instructif, il fixe la frontière entre les deux mécanismes.
|
||||
Premier cas concret, à faire en régression (les mêmes variables doivent être générées) :
|
||||
|
||||
| Avant (codé en dur) | Après | Par quel mécanisme |
|
||||
|---|---|---|
|
||||
| `serveur_postfix_mailstore_hote` (group_var) | `postfix.liens: [mailstore → dovecot]` | **lien** ✅ (2026-07-03) |
|
||||
| `serveur_postfix_rspamd_milter` (group_var) | `postfix.liens: [milter → rspamd]` | **lien** ✅ (2026-07-03) |
|
||||
| `id-ldap-01` (defaults) | connexion LDAP dérivée du `domaine_interne` | **rôle utilitaire** `resoudre_annuaire` |
|
||||
|
||||
**Pourquoi l'annuaire n'est pas un lien.** Ce document a annoncé jusqu'au 2026-09-06
|
||||
`postfix.liens: [annuaire → openldap]` et l'équivalent pour Dovecot. Ni l'un ni l'autre
|
||||
n'existe : `roles/serveur_postfix/meta/liens.yml` n'accepte que `mailstore` et `milter`.
|
||||
L'annuaire est traité comme les bases (§5) — par un **rôle utilitaire** que quatre
|
||||
consommateurs incluent (`serveur_dovecot`, `serveur_postfix`, `serveur_keycloak`,
|
||||
`serveur_icingaweb2`, plus `amorcage_acces`), parce qu'il n'y a **qu'un** annuaire par
|
||||
écosystème et que sa connexion se dérive entièrement du `domaine_interne` : il n'y a pas de
|
||||
choix de topologie à déclarer, donc pas d'arête à porter dans le plan. Un lien exprime un
|
||||
choix ; ici il n'y en a pas.
|
||||
| Avant (codé en dur) | Après (déclaratif) |
|
||||
|---|---|
|
||||
| `serveur_postfix_mailstore_hote` (group_var) | `postfix.liens: [mailstore → dovecot]` |
|
||||
| `serveur_postfix_rspamd_milter` (group_var) | `postfix.liens: [milter → rspamd]` |
|
||||
| `id-ldap-01` (defaults) | `postfix.liens: [annuaire → openldap]`, `dovecot.liens: [annuaire → openldap]` |
|
||||
|
||||
## 9. Phasage
|
||||
|
||||
- **Phase 0** : cette note + décision. ✅ (direction : liens côté app pour app→app)
|
||||
- **Phase 1** : résolveur de liens dans `instancier.py` + `meta/liens.yml` des rôles mail ;
|
||||
migrer les liens mail ; régression DIFF VIDE. ✅ (2026-07-03 : `mailstore` + `milter` migrés)
|
||||
- **Phase 2** : bases. ✅ **complète** — binding **côté base** (registre + résolution en
|
||||
rôle), cf. §5. Ne PAS dupliquer dans instancier. La factorisation qui restait est faite :
|
||||
`roles/resoudre_base/`, inclus par cinq rôles consommateurs.
|
||||
- **Phase 3** : exposition / domaines. ✅ — **6 applications** déclarent un `expose`
|
||||
(keycloak, forgejo, grafana, oauth2_proxy, nextcloud, collabora), et `serveur_nginx` en
|
||||
dérive vhost, SAN et enregistrement A (`expositions_des_applications`). `make expositions-plan`
|
||||
vérifie ensuite que chacune répond réellement.
|
||||
- **Phase 2** : bases. ✅ **déjà en place** — binding **côté base** (registre + résolution en
|
||||
rôle, 4 rôles), cf. §5. Ne PAS dupliquer dans instancier. **Reste (reporté à la session
|
||||
Keycloak)** : factoriser le bloc de résolution copié-collé en un include partagé (DRY),
|
||||
validé en déployant Keycloak.
|
||||
- **Phase 3** : exposition / domaines (vhosts nginx dérivés des liens ; machinerie `expose` /
|
||||
`expositions_des_applications` déjà présente).
|
||||
- **Phase 4** : vue GUI des liens + graphe. 🟡 **éditeur fait** (2026-07-22, cf. §7) ;
|
||||
**graphe des liens reste à faire**.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,46 +10,29 @@ Point d'entrée vers le corpus documentaire, et **catalogue des mécanismes tran
|
|||
ceux qui vivent dans le code et qu'on *re-découvre* sinon. Créée le 2026-07-03 après un audit
|
||||
du dépôt, **revue le 2026-07-29**. But : ne plus re-déterrer ce qui existe.
|
||||
|
||||
Le dépôt est **déjà bien documenté**. Le manque n'était pas la doc du *modèle*, mais (a) un
|
||||
index « par où commencer » et (b) une carte des *mécanismes* (dispersés dans le code + les
|
||||
README de rôles). Cette page comble ces deux trous.
|
||||
|
||||
### Le dépôt en chiffres
|
||||
|
||||
> Ces valeurs sont **mesurées**, pas recopiées : **P48** les recompte et refuse tout écart.
|
||||
> Elles étaient toutes fausses le 2026-08-26 — de 6 rôles, de 12 pièces d'audit, de 8
|
||||
> décisions. Aucune ne faisait travailler personne ; mais une carte dont les faits
|
||||
> vérifiables sont faux cesse d'être consultée, et c'est alors ses **pointeurs** qu'on perd.
|
||||
|
||||
| Ce qu'on compte | Combien | Comment on le mesure |
|
||||
|---|---|---|
|
||||
| rôles | 69 | `roles/*/` |
|
||||
| README de rôles | 69 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| documents | 46 | `docs/*.md` |
|
||||
| pièces d'audit | 51 | `docs/audit/*` |
|
||||
| unités de wiki | 27 | `wiki/*.md` |
|
||||
| décisions en vigueur | 85 | lignes `\| **D-nn** \|` de `decisions-architecture.md` |
|
||||
| décisions renversées | 3 | lignes `\| **D-nn** —` du même document |
|
||||
Le dépôt est **déjà bien documenté** (34 docs + 15 pièces d'audit + 23 unités de wiki, et un
|
||||
README pour chacun des 54 rôles). Le manque n'était pas la doc du *modèle*,
|
||||
mais (a) un index « par où commencer » et (b) une carte des *mécanismes* (dispersés dans le
|
||||
code + les README de rôles). Cette page comble ces deux trous.
|
||||
|
||||
## 1. À lire d'abord (dans l'ordre)
|
||||
|
||||
| Sujet | Documents |
|
||||
|---|---|
|
||||
| **Autorité / gouvernance** | `AGENTS.md` (source d'autorité), `CLAUDE.md`, `docs/MISE-A-JOUR-CODEX-CLAUDE.md` |
|
||||
| **Le modèle (plan)** | `docs/architecture-set-ops.md` (survol) → `docs/plan-et-generation.md` (à fond) → `docs/meta-classe.md` (concept) → `docs/conception-contextes.md` (site et locataire : deux classes, un contrat) |
|
||||
| **Le modèle (plan)** | `docs/architecture-set-ops.md` (survol) → `docs/plan-et-generation.md` (à fond) → `docs/meta-classe.md` (concept) |
|
||||
| **Services, maturité, dette** | `docs/catalogue-services.md` (**la carte de maturité + la cruft y sont déjà**) |
|
||||
| **Exploitation / VM** | `docs/vm-lifecycle.md`, `docs/procedure-template-debian13-proxmox.md`, `docs/config-proxmox.md`, `docs/nomenclature-vm.md`, `docs/multi-instances.md` |
|
||||
| **Conceptions de domaine** | `docs/identite-sso.md`, `docs/courriel-conception.md`, `docs/bindings-conception.md`, `docs/dns-interne.md`, `docs/dimensionnement-ressources.md`, `docs/integrations-vm.md` |
|
||||
| **Supervision & métriques** | `docs/supervision-conception.md` (les verdicts, vers Icinga) → `docs/metriques-conception.md` (les séries, vers Prometheus et Grafana) — deux questions, deux fichiers `meta/`, un même patron : le rôle déclare, le moteur dérive |
|
||||
| **Réseau / pare-feu** | `docs/flux-conception.md` (le modèle) → `docs/registre-flux.md` (**généré**, matrice d'audit) → `docs/frontiere-opnsense.md` (la bordure nord/sud) ; underlay : `underlay.yml.example` + `make underlay` |
|
||||
| **Ordre de déploiement** | `docs/couches-deploiement.yml` (couches) + `docs/dependances-groupes.yml` (graphe) → `playbooks/site.yml` (**généré**, `make site`) |
|
||||
| **Conformité du déployé** | `docs/devis-services.md` — les **cinq devis de service** (`make identite-plan`, `certificats-plan`, `expositions-plan`, `postgresql-plan`, `courriel-plan`) **et les cinq devis d'infrastructure** (`frontiere-plan`, `proxmox-fw-plan`, `sdn-plan`, `underlay-plan`, `placement-plan`). Répondent à ce que `make prouver` ne demande jamais : *ce qui tourne correspond-il à ce qui est déclaré ?* |
|
||||
| **Conformité du déployé** | `docs/devis-services.md` — les **cinq devis de service** (`make identite-plan`, `certificats-plan`, `expositions-plan`, `postgresql-plan`, `courriel-plan`). Répondent à ce que `make prouver` ne demande jamais : *ce qui tourne correspond-il à ce qui est déclaré ?* |
|
||||
| **Preuve / recette** | `docs/audit/affirmations.md` (registre), `make prouver` → `docs/audit/preuve-<date>.md` — **statique** : lit le dépôt, aucun appel réseau ; la conformité du déployé est l'affaire des devis de service (ligne au-dessus), `docs/audit/plan-de-recette.md` (**généré** du wiki), `docs/audit/protocole-operateur-independant.md` |
|
||||
| **Décisions d'architecture** | `docs/decisions-architecture.md` — les décisions en vigueur (comptées ci-dessus), pourquoi, où lire le détail, et ce qui les garde ; plus les **décisions renversées** et leur cause |
|
||||
| **SDN / routage** | `docs/sdn-evpn.md` — décision du 2026-08-02 : le routage inter-zone passe des commutateurs aux hyperviseurs (zones EVPN = VRF). **En service** depuis le 2026-08-03 (`routage_tenants: sdn` dans l'`underlay.yml` du site) : les trunks ne portent plus que deux VLAN d'underlay au lieu de quinze, les passerelles `.1` sont anycast sur chaque hyperviseur. Écart mesuré par `make sdn-plan` |
|
||||
| **Migration de tenant** | `docs/migration-tenant.md` — recette en **neuf étapes (0 à 8)**, machine à états, gardes ; le receveur se construit **avant** tout gel |
|
||||
| **Décisions d'architecture** | `docs/decisions-architecture.md` — **70 décisions en vigueur** (D-01 → D-73, 3 renversées), pourquoi, où lire le détail, et ce qui les garde ; plus les **décisions renversées** et leur cause |
|
||||
| **SDN / routage** | `docs/sdn-evpn.md` — décision du 2026-08-02 : le routage inter-zone passe des commutateurs aux hyperviseurs (zones EVPN = VRF). **Non éprouvé** : spike avant génération |
|
||||
| **Migration de tenant** | `docs/migration-tenant.md` — recette en 8 étapes, machine à états, gardes ; le receveur se construit **avant** tout gel |
|
||||
| **Exploitation courante** | `docs/runbooks-exploitation.md`, `docs/intrants-communs.md`, `docs/intrants-base-gui-conception.md`, `docs/theme-forgejo-hors-flotte.md` |
|
||||
| **Pédagogie (le wiki)** | `wiki/` — les unités (comptées ci-dessus) publiées par `make wiki-publier` ; entrer par `wiki/Home.md` |
|
||||
| **Pédagogie (le wiki)** | `wiki/` — 21 unités (+ `_Sidebar`) publiées par `make wiki-publier` ; entrer par `wiki/Home.md` |
|
||||
| **Vision / positionnement** | `docs/ecosysteme-chezlepro.md`, `docs/positionnement.md`, `docs/pouvoirs-set-ops.md` |
|
||||
|
||||
## 2. Les mécanismes transverses (et OÙ ils vivent)
|
||||
|
|
@ -59,29 +42,25 @@ Ce que je re-découvre sinon. **Consulter avant de concevoir un nouveau mécanis
|
|||
| Mécanisme | Ce que c'est | Où, dans le code | Doc |
|
||||
|---|---|---|---|
|
||||
| Plan → inventaire | `plan/*.yml` → `hosts.yml` généré | `scripts/instancier.py`, `scripts/inventory_rules.py` | `plan-et-generation.md` |
|
||||
| **Schéma des registres** | la **forme** des six registres — champs, types, énumérations, requis — **dérivée** des validateurs, jamais écrite à la main. Sert à générer les formulaires plutôt qu'à les écrire. La **cohérence** reste aux `valider_*` : un schéma ne sait pas dire qu'un `consommateur` désigne une application inexistante | `scripts/schema_plan.py` (`make schema`) → `docs/audit/schema-plan.json` ; garde **P61** | `plan-et-generation.md` |
|
||||
| Nomenclature dérivée | VMID / IP / VLAN / FQDN dérivés | `inventory_rules.deriver_nomenclature` + `plan/nomenclature.yml` | `nomenclature-vm.md` |
|
||||
| Dimensionnement | RAM/CPU/disque sommés par logiciel | `roles/*/meta/empreinte.yml` → `deriver_ressources` | `dimensionnement-ressources.md` |
|
||||
| **Bindings app→app** | lien **côté app** (`liens`) résolu en host_vars | `plan/applications.yml` `liens:` + `roles/*/meta/liens.yml` + `instancier.resoudre_liens` | `bindings-conception.md` |
|
||||
| **Bindings app→base** | lien **côté base** (`consommateur`/`portee`) résolu **dans le rôle** | `plan/bases-donnees.yml` + rôle utilitaire `resoudre_base` (`lookup('vars', secret)`, `no_log`) inclus par le consommateur | `bindings-conception.md` §5 |
|
||||
| Résolution d'annuaire | connexion LDAP (uri/base DN/bind) **dérivée**, jamais recopiée | rôle utilitaire `resoudre_annuaire` (inclus par dovecot/postfix/keycloak/icingaweb2 et `amorcage_acces`) | `identite-sso.md` |
|
||||
| Résolution d'annuaire | connexion LDAP (uri/base DN/bind) **dérivée**, jamais recopiée | rôle utilitaire `resoudre_annuaire` (inclus par dovecot/postfix/keycloak/icingaweb2) | `identite-sso.md` |
|
||||
| Plancher de résolution | `/etc/hosts` généré depuis l'inventaire + alias d'`expose` → l'écosystème se résout **DNS éteint** | rôle `hosts_statiques` (appliqué dans la couche socle) | `dns-interne.md` |
|
||||
| Pont de certificat | cert step_ca → service, resync au renouvellement | script `*-cert-sync` + unité `.path`, dans chaque rôle serveur ; cert déposé par `client_pki` | — |
|
||||
| Ordonnancement socle-first | socle/durci avant les `client_*` | `serveur_debian`/`serveur_durci` d'abord (posent `/etc/hosts` via `hosts_statiques`) | — |
|
||||
| Sûreté check-mode | dry-run fiable | `when: not ansible_check_mode` sur les tâches de service + handlers | — |
|
||||
| Voûte au déploiement | secret jamais en clair ; **une voûte, une clé** depuis le 2026-08-28 | `ANSIBLE_VAULT_IDENTITY_LIST` construit par `scripts/voutes.py` (clé nommée `~/.config/setops-vault-<dépôt>`) ; déréférencé par `lookup('vars', <nom>)` | `autorisation.md` §6.1 |
|
||||
| Voûte au déploiement | secret jamais en clair | `ANSIBLE_VAULT_PASSWORD_FILE` / `~/.config/setops-vault-pass` ; déréférencé par `lookup('vars', <nom>)` | — |
|
||||
| Multi-instance | un dépôt par écosystème ; l'active = symlink `instance/`, les autres **découvertes par convention** (dossiers frères, aucun registre) | active : symlink `instance/` ; découverte : `scripts/instances.py` / `devis_reseau.py` (glob `../*/plan/nomenclature.yml` avec `index`) ; garde-fou collision : preuve **P21** | `multi-instances.md` |
|
||||
| Exposition → edge | app expose un FQDN public servi par un edge | `plan/domaines.yml` + `expose` (applications) | `bindings-conception.md` §4 |
|
||||
| Exploitation de l'hébergeur | ses **opérations** n'appartiennent à aucun tenant et restent **hors overlay** | **à moitié construit** : les *VM* du site ont leur inventaire (`scripts/site_inventaire.py`), leur socle, leur durcissement, leurs sauvegardes et leur supervision (`site-mon-01`, 2026-09-02). Les *équipements* : hyperviseurs et frontière **supervisés** depuis le 2026-09-17 — températures, ventilateurs, SMART, usure NVMe, un catalogue (`scripts/materiel.py`) lu par le tableau Grafana « Matériel » et le service Icinga `materiel` (`supervision-conception.md`). Les commutateurs restent sans supervision ; aucun équipement n'a de sauvegarde de configuration | `hebergeur-exploitation.md` |
|
||||
| Exploitation de l'hébergeur | ses **opérations** (supervision de la fabric, sauvegarde des configs, DNS d'underlay) n'appartiennent à aucun tenant et restent **hors overlay** | décidé, **non construit** : aucun équipement d'hébergeur n'est encore dans un inventaire | `hebergeur-exploitation.md` |
|
||||
| Authentification | web → Keycloak ; LDAP source unique ; secours par `sudo`, formulaire local non annoncé | `<rôle>_connexion_locale: false` (grafana, forgejo, nextcloud) ; garde de version Forgejo ≥ 10 | `authentification.md` |
|
||||
| Accès & habilitations | Set-OPS **amorce** un accès sysadmin puis se retire ; les appartenances aux groupes ne sont **jamais réconciliées** — c'est une personne qui gouverne | **construit et éprouvé** (2026-08-08) : rôle `amorcage_acces` (idempotence par existence, D-67), groupes projetés en rôles par `serveur_keycloak`, `meta/acces.yml` dans les 5 rôles web ; chaîne LDAP → Keycloak → groupe → service exercée de bout en bout sur Icinga Web 2 | `autorisation.md` (§6 = runbook de reprise) |
|
||||
| **DNS public** | le locataire **écrit** ses zones `autorite: primaire-cache`, le site les **sert** en secondaire, un site pair les réplique. Transfert ouvert **à la clé TSIG seule** (`allow-axfr-ips` et TSIG sont alternatifs dans PowerDNS) ; instance `pdns@public` à part pour que le site ne puisse jamais interroger la zone `.internal` | `roles/serveur_dns_public/`, `roles/serveur_powerdns/tasks/zones-publiques.yml` ; relations dérivées dans `scripts/site_inventaire.py` ; mots de flux `dns_public_site` / `primaires_dns_locataires` ; garde **P82** | `dns-interne.md` §Zones publiques |
|
||||
| **Remise au client** | remettre un écosystème se fait en **deux temps** : l'*identité* le jour de la livraison (sa clé de voûte, sa voûte, sa racine d'AC), la *machine* à l'échéance (sa clé SSH entre, la nôtre sort, la voûte est re-clétée). Le registre déclare enfin le **responsable désigné** (D-18) | `scripts/remise.py` (`make remise-paquet`, `remise-inscrire`, `remise-recleer`) ; registre `remise.yml` chez le locataire ; garde **P80** | `remise-au-client.md` |
|
||||
| SDN EVPN | ajouter un tenant implique **1 zone + 6 VNets + 6 sous-réseaux**, tous dérivés du seed | `scripts/devis_sdn.py` (`make devis-sdn`) ; nommage dérivé du seed (`t17`, `t17serv`), ≤ 8 caractères ; garde **P30** | `sdn-evpn.md` §2 |
|
||||
| SDN EVPN | ajouter un tenant implique **1 zone + 6 VNets + 6 sous-réseaux**, tous dérivés du seed | `scripts/devis_sdn.py` (`make devis-sdn`) ; nommage dérivé du tenant (`CHEZ17`, `chez174`), ≤ 8 caractères ; garde **P30** | `sdn-evpn.md` §2 |
|
||||
| Pools Proxmox | un pool par tenant : les noms courts de VM sont **volontairement identiques** d'un tenant à l'autre (même fonction, même nom), et seule la console Proxmox en souffrait | `scripts/devis_proxmox_pools.py` (`make devis-proxmox-pools`) ; nom dérivé de l'`index` ; garde de collision = preuve **P28** | `decisions-architecture.md` D-37 |
|
||||
| Routage | **aucun commutateur ne route** : la frontière est le seul équipement L3 ; les switches commutent | `passerelle` dit qui porte la passerelle, le SVI se dérive du rôle du porteur | `decisions-architecture.md` D-49/50 |
|
||||
| **Devis de service** | LIT le système en marche et le compare à ce que le plan dérive ; n'écrit rien (D-23/D-24 portés du réseau aux services). Le playbook **relève**, Python **compare** | `playbooks/maintenance/devis-*.yml` + `scripts/devis_*.py` ; cible `make <sujet>-plan` | `devis-services.md` |
|
||||
| Accès d'administration | un **tunnel WireGuard nominatif** (instance `admins`) : un pair par personne et par appareil, le réseau du tunnel devient un réseau d'administration dont les pare-feux d'hôte, le contrat vers les locataires et les règles de bordure **dérivent**. Le runner n'est pas le rebond, et le document dit pourquoi | `scripts/vpn_admin.py` (`make vpn-admin-plan`) ; déclaré dans `acces_admin_vpn` (plan du site) | `acces-administration.md` |
|
||||
| Frontière nord/sud | les flux `pair: externe` — **sautés** par le pare-feu d'hôte — sont la politique de bordure | `scripts/devis_opnsense.py` (`make devis-opnsense`) ; garde d'accès admin = preuve **P24** | `frontiere-opnsense.md` |
|
||||
|
||||
> ⚠️ **Deux directions de binding, assumées** : `app→app` côté app (instancier),
|
||||
|
|
@ -96,7 +75,7 @@ Ce que je re-découvre sinon. **Consulter avant de concevoir un nouveau mécanis
|
|||
- ✅ **soldé** — README de rôles : **tous les rôles en ont un** (les 12 manquants écrits le
|
||||
2026-07-29 : `serveur_debian`, `hosts_statiques`, `resoudre_base`, `resoudre_annuaire`,
|
||||
`serveur_dovecot`, `serveur_postfix`, `serveur_rspamd`, `client_backup`, `serveur_backup`,
|
||||
`client_resolveur`, `serveur_oauth2_proxy`, `serveur_icingaweb2`).
|
||||
`client_unbound`, `serveur_oauth2_proxy`, `serveur_icingaweb2`).
|
||||
- ✅ **soldé** — `expose` **est** consommé au déploiement : `plan/applications.yml` →
|
||||
filtre `expositions_des_applications` → vhosts nginx dérivés
|
||||
(`roles/serveur_nginx/tasks/main.yml`, template `expositions.conf.j2`, drapeau
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ groupe opérationnel -> playbooks/groupes/<groupe>.yml (P04 le prouve)
|
|||
```
|
||||
|
||||
Le **rôle porte le nom du groupe** : `serveur_keycloak`, `client_pki`. Il n'y a pas de nom
|
||||
court séparé. Seule exception, `serveur_durci` : un groupe dont le playbook **compose dix
|
||||
rôles** de durcissement, et dans cet ordre — `hardening_packages`, `sysctl_hardening`,
|
||||
`core_dumps`, `unattended_upgrades`, `apparmor`, `auditd`, `fail2ban_ssh`, `journald`,
|
||||
`ssh_hardening`, `nftables_baseline` — plutôt qu'un rôle homonyme.
|
||||
court séparé. Seule exception, `serveur_durci` : un groupe dont le playbook **compose**
|
||||
onze rôles de durcissement (`hardening_packages`, `sysctl_hardening`, `apparmor`,
|
||||
`auditd`, `fail2ban_ssh`, `ssh_hardening`, `nftables_baseline`…) plutôt qu'un rôle
|
||||
homonyme.
|
||||
|
||||
La nomenclature des VM et des VMID est documentée dans `docs/nomenclature-vm.md`.
|
||||
|
||||
|
|
@ -23,28 +23,21 @@ Un service central peut partager un hôte avec d'autres services de la même fon
|
|||
|
||||
## État d'implémentation des rôles
|
||||
|
||||
> **Mise à jour (2026-09-06), vérifiée groupe par groupe contre `roles/` et
|
||||
> `playbooks/groupes/`.** Les **40 groupes** `serveur_*` / `client_*` du tableau ci-dessous
|
||||
> ont **tous** leur rôle et leur playbook — 40 fichiers dans `playbooks/groupes/`, 40 noms
|
||||
> distincts cités ici. Aucune capacité annoncée n'est un point d'ancrage vide.
|
||||
>
|
||||
> *(Le chiffre lu ici jusqu'au 2026-09-06 était 29, mesuré le 2026-08-18 : le catalogue a
|
||||
> grandi de onze groupes — site, runners, résolveur, artefacts — sans que la phrase suive.
|
||||
> C'est ce genre d'écart que la preuve **P57** garde désormais.)*
|
||||
> **Mise à jour (2026-08-18), vérifiée rôle par rôle contre `roles/`.** Les 29 groupes
|
||||
> `serveur_*` / `client_*` de ce catalogue ont **tous** leur rôle et leur playbook. Aucune
|
||||
> capacité annoncée ici n'est un point d'ancrage vide.
|
||||
|
||||
**Éprouvés sur VM réelles.** Le 2026-08-13, la flotte a été **reconstruite depuis zéro** —
|
||||
43 groupes, 0 échec, 37 minutes. L'épreuve a été **rejouée deux fois le 2026-09-02**, sur
|
||||
un dépôt qui avait beaucoup bougé depuis : 15/15 hôtes puis 14/14, 0 échec, `make valider`
|
||||
à 0 échec sur 13 hôtes. Ce n'est donc plus « du code validé » : chaque rôle a repris une
|
||||
machine nue et l'a menée à l'état voulu — et il l'a refait après coup.
|
||||
**Éprouvés sur VM réelles.** Le 2026-08-13, la flotte a été **reconstruite depuis zéro**
|
||||
— 43 groupes, 0 échec, 37 minutes — puis remontée d'un seul trait. Ce n'est donc plus
|
||||
« du code validé » : chaque rôle a repris une machine nue et l'a menée à l'état voulu.
|
||||
|
||||
Ce que la reconstruction couvre, par capacité :
|
||||
|
||||
| Capacité | Rôles | Ce qui est éprouvé |
|
||||
|---|---|---|
|
||||
| Socle et durcissement | `serveur_debian`, `serveur_durci` (dix rôles composés) | clone du gabarit doré → machine conforme |
|
||||
| Socle et durcissement | `serveur_debian`, `serveur_durci` | clone du gabarit doré → machine conforme |
|
||||
| Confiance | `serveur_step_ca`, `client_pki` | mTLS avec SAN dérivés du plan, renouvellement |
|
||||
| Noms | `serveur_powerdns`, `client_resolveur` | autoritaire interne + résolveur local |
|
||||
| Noms | `serveur_powerdns`, `client_unbound` | autoritaire interne + résolveur local |
|
||||
| Identité | `serveur_openldap`, `serveur_keycloak` | LDAPS, SSO OIDC, **fédération LDAP automatisée** (`tasks/federation-ldap.yml`), exposé par le plan (`auth.<domaine>`) |
|
||||
| Passerelle SSO | `serveur_oauth2_proxy` | SSO devant une app sans OIDC natif (éprouvé sur Icinga Web 2) |
|
||||
| Données | `serveur_postgresql`, `serveur_redis` | bases et comptes dérivés du registre, TLS `verify-full` |
|
||||
|
|
@ -53,23 +46,13 @@ Ce que la reconstruction couvre, par capacité :
|
|||
| Observabilité | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` | métriques, journaux, tableaux sous SSO |
|
||||
| Supervision | `serveur_icinga`, `serveur_icingaweb2` | Icinga 2 + IcingaDB + Web 2 + BPM, au SSO |
|
||||
| Forge | `serveur_forgejo` | Git + PostgreSQL + SSO OIDC |
|
||||
| Collaboration | `serveur_nextcloud`, `serveur_collabora` (**natif**, plus de conteneur) | déployés par la reconstruction, base et client OIDC dérivés du plan — **usage** (dépôt de fichier, édition partagée) non consigné comme preuve |
|
||||
| Collaboration | `serveur_nextcloud`, `serveur_collabora` | déployés par la reconstruction, base et client OIDC dérivés du plan — **usage** (dépôt de fichier, édition partagée) non consigné comme preuve |
|
||||
| Plateforme webapp | `serveur_web_frontal`, `serveur_web_dorsal` | sites statiques et webapps natives (venv + systemd + nginx), **zéro conteneur** |
|
||||
| Sauvegardes | `serveur_backup`, `client_backup` | restic hors-nœud, **restauration éprouvée** (2026-08-12 : la donnée revient) |
|
||||
| Exploitation | `serveur_ops` | le poste depuis lequel l'ecosysteme se reconstruit : Ansible epingle, genome clone depuis **sa propre forge**, cle SSH propre — **sans** la voute ni son mot de passe |
|
||||
| Source d'artefacts | `serveur_artefacts`, `client_artefacts` | **deux faces sur un seul service.** Cache apt (apt-cacher-ng) : les paquets viennent de chez soi, pas de six serveurs etrangers — **mode hors ligne** pour prouver ce que le cache detient vraiment. Et **depot des binaires directs** (`LocalDirs`) : Forgejo, Keycloak, Nextcloud, oauth2-proxy ne vivent dans aucun depot apt et etaient tires d'Internet par CHAQUE runner — 570 Mo mesures le 2026-09-12. Meme port, meme regle de pare-feu, garde `P70` |
|
||||
| Runner de tenant | `serveur_ops_tenant` | le pouvoir de **configurer** : la voute de l'ecosysteme, deposee CHIFFREE sur son propre runner. Sans elle un runner calcule son inventaire et ne peut rien en faire — chaque role qui demande un secret echoue sur son assertion. N'atteint ni la fabric ni la frontiere |
|
||||
| Runner de site | `serveur_ops_site` | le pouvoir de **materialiser** : creer et detruire des VM sur la fabric. Detient la voute du SITE, chiffree, et n'entre JAMAIS chez un tenant — reserve a l'ecosysteme de l'hebergeur |
|
||||
| Resolution | `serveur_resolveur`, `client_resolveur` | UN resolveur recursif par tenant (Unbound), qui recurse depuis la racine et delegue la zone souveraine a PowerDNS. Remplace les N demons locaux d'avant le 2026-08-24 |
|
||||
| Cache du site | `serveur_cache_site` | designe LE cache que les ecosystemes voisins prennent comme amont : Debian telecharge une fois pour toute la fabric, et le cache ne voit que des requetes agregees. Reserve a l'ecosysteme de l'hebergeur |
|
||||
| Forge du genome du site | `serveur_forge_site` | designe LA forge dont les ecosystemes de ce site se reproduisent (D-81), et l'ouvre a eux. Marqueur : `serveur_forgejo` installe. |
|
||||
| Resolveur du site | `serveur_resolveur_site` | designe LE resolveur que les ecosystemes de ce site interrogent tant qu'ils n'ont pas le leur. Marqueur : `serveur_resolveur` installe. |
|
||||
| Depot de sauvegarde du site | `serveur_backup_site` | designe LE depot ou les ecosystemes de ce site posent leur etat tant qu'ils n'ont pas le leur. Marqueur : `serveur_backup` installe. |
|
||||
| DNS public du site | `serveur_dns_public` | **secondaire** public de toutes les zones `autorite: primaire-cache` des locataires du site : le locataire ecrit, le site sert. Transfert signe TSIG, aucune adresse de confiance, aucune zone `.internal`. Phase 1 : non expose a Internet |
|
||||
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp`, `client_sante` | collecte, relais et rapport de santé sur toute la flotte |
|
||||
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte |
|
||||
|
||||
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
||||
(→ Postfix), `client_dns` (→ plancher `/etc/hosts` + `client_resolveur`), `client_ldap`
|
||||
(→ Postfix), `client_dns` (→ plancher `/etc/hosts` + `client_unbound`), `client_ldap`
|
||||
(login LDAP au niveau OS, hors design).
|
||||
|
||||
> **Ce que « éprouvé » ne dit pas.** La reconstruction prouve que le moteur mène une
|
||||
|
|
@ -153,14 +136,9 @@ table décrit une répartition éprouvée, pas un minimum requis.
|
|||
| `mon-01` | Icinga 2, Icinga Web 2, oauth2-proxy |
|
||||
| `forge-01` | Forgejo |
|
||||
| `collab-01` | Nextcloud, Collabora |
|
||||
| `backup-01` | dépôt restic |
|
||||
| `web-frontal-01` | site statique |
|
||||
| `web-dorsal-01` | webapp native |
|
||||
| `ops-01` | runner de l'écosystème (`serveur_ops`, `serveur_ops_tenant`) |
|
||||
|
||||
> `ops-01` manquait de cette table jusqu'au 2026-09-06, alors que le compte annoncé
|
||||
> ci-dessus le comptait : quatorze hôtes, treize lignes. C'est le nœud depuis lequel
|
||||
> l'écosystème se reconstruit **sans le poste de l'exploitant** — la pièce la moins visible
|
||||
> et la plus structurante de la reconstruction autonome.
|
||||
|
||||
Le courriel occupe **deux** hôtes, et ce n'est pas un détail de taille : `edge-mta-01`
|
||||
porte ce qui parle à l'extérieur (Postfix, rspamd), `infra-mail-01` ce qui détient les
|
||||
|
|
@ -173,9 +151,7 @@ boîtes (Dovecot). La coupure suit l'exposition, pas le logiciel.
|
|||
| Confiance PKI / ACME | `client_pki` | **tout hôte** (universelle) |
|
||||
| Métriques Prometheus | `client_metrique` | **tout hôte** (universelle) |
|
||||
| Journaux vers Loki | `client_journal` | **tout hôte** (universelle) |
|
||||
| Résolution locale (Unbound) | `client_resolveur` | **tout hôte** (universelle) |
|
||||
| Source d'artefacts (cache apt) | `client_artefacts` | **tout hôte** (universelle) |
|
||||
| Santé du nœud (unités en échec) | `client_sante` | **tout hôte** (universelle) |
|
||||
| Résolution locale (Unbound) | `client_unbound` | **tout hôte** (universelle) |
|
||||
| Relais SMTP | `client_smtp` | déclaré par hôte, dans le plan |
|
||||
| Sauvegarde restic | `client_backup` | déclaré par hôte — **obligatoire pour tout détenteur d'état** (P36) |
|
||||
|
||||
|
|
@ -187,21 +163,14 @@ dans le plan.
|
|||
> **`client_supervision` n'existe pas.** Ce catalogue l'a longtemps annoncé ; il n'a
|
||||
> jamais eu ni rôle ni playbook, et rien ne l'attend. La supervision s'exerce **sans agent
|
||||
> sur les hôtes** : contrôles actifs depuis le cœur (`hostalive`) et résultats **passifs
|
||||
> poussés par l'API** par celui qui détient la vérité de terrain. Le nom est retiré plutôt
|
||||
> que réservé : une case vide dans un catalogue se lit comme une promesse.
|
||||
|
||||
> **Qui rapporte l'état des sauvegardes a changé le 2026-09-02.** Tant que le dépôt vivait
|
||||
> dans l'écosystème, il était le seul à voir ce qui était réellement arrivé, et il
|
||||
> rapportait pour tout le monde. Depuis que les écosystèmes déposent chez leur **hébergeur**
|
||||
> — qui héberge des octets chiffrés côté client et ne peut pas les juger — **chaque nœud
|
||||
> vérifie son propre dépôt distant** et le rapporte lui-même. La vérification suit la clé,
|
||||
> pas le stockage. `serveur_icinga` se branche sur les deux modèles ; `backup-01` a été
|
||||
> retiré du plan de Chezlepro, sa VM détruite.
|
||||
> poussés par l'API** par celui qui détient la vérité de terrain — ainsi l'état des
|
||||
> sauvegardes est-il rapporté par `backup-01`, seul à pouvoir lire ses dépôts. Le nom est
|
||||
> retiré plutôt que réservé : une case vide dans un catalogue se lit comme une promesse.
|
||||
|
||||
## Ordre de déploiement — le raisonnement
|
||||
|
||||
> **L'ordre exécutable n'est pas ici.** Il vit dans `docs/couches-deploiement.yml` et
|
||||
> `docs/dependances-groupes.yml`, que P08 prouve cohérents (42 groupes classés, aucun
|
||||
> `docs/dependances-groupes.yml`, que P08 prouve cohérents (30 groupes classés, aucun
|
||||
> cycle, aucune arête en arrière) et que `make reconstruire` suit. Ce qui suit en est le
|
||||
> **raisonnement**, utile pour comprendre pourquoi cet ordre-là — et pour placer un
|
||||
> service nouveau. Les phases sont franchies : les « intégrations à prévoir » ci-dessous
|
||||
|
|
@ -209,24 +178,10 @@ dans le plan.
|
|||
|
||||
L'ordre ci-dessous privilégie les dépendances structurantes avant les applications.
|
||||
|
||||
> **Ce raisonnement a été révisé le 2026-09-09 sur un point** : l'observabilité et la
|
||||
> supervision ne viennent plus en phases 3 et 4, mais **juste après la PKI** — donc avant
|
||||
> presque tout ce qu'elles surveillent. *On n'allume pas la lumière une fois la maison
|
||||
> finie.* Une reconstruction depuis zéro est précisément le moment où l'on a le plus
|
||||
> besoin de voir. Les phases ci-dessous gardent leur numérotation, qui dit une **parenté
|
||||
> logique** ; l'ordre exécutable, lui, est dans `docs/couches-deploiement.yml` (D-86).
|
||||
>
|
||||
> Ce qui reste tard, et à dessein : la **vigie** (`icingaweb2`, `oauth2_proxy`), qui
|
||||
> réclame LDAP et Keycloak. L'interface humaine peut attendre ; la mesure, non.
|
||||
|
||||
### Phase 1 - Fondations transversales
|
||||
|
||||
1. `serveur_powerdns`
|
||||
- Service central : DNS interne **autoritaire** de la zone souveraine. La *résolution*
|
||||
est une couche distincte (`serveur_resolveur` / `client_resolveur`, Unbound), et le
|
||||
**plancher `/etc/hosts`** posé par `hosts_statiques` précède les deux — c'est lui qui
|
||||
permet à l'écosystème de se résoudre DNS éteint. Trois couches, pas un choix de design
|
||||
(`docs/dns-interne.md`).
|
||||
- Service central : DNS interne autoritaire et/ou résolution interne selon le design retenu.
|
||||
- Raison : les autres intégrations auront besoin de noms stables plutôt que d'adresses IP.
|
||||
|
||||
2. `serveur_step_ca`
|
||||
|
|
@ -313,7 +268,7 @@ L'ordre ci-dessous privilégie les dépendances structurantes avant les applicat
|
|||
- Tout service exposé en HTTP(S) doit prévoir son intégration avec `serveur_nginx`.
|
||||
- Tout service avec authentification humaine doit prévoir son intégration avec `serveur_keycloak`, sauf justification contraire.
|
||||
- Tout service générant des alertes ou notifications doit prévoir `client_smtp`.
|
||||
- Toute VM de service rejoint `client_pki`, `client_metrique`, `client_journal`, `client_resolveur` et `client_artefacts` — **par dérivation, sans rien écrire** (les cinq intégrations marquées `universelle: true`, P26). La supervision, elle, ne pose rien sur l'hôte.
|
||||
- Toute VM de service rejoint `client_pki`, `client_metrique`, `client_journal` et `client_unbound` — **par dérivation, sans rien écrire** (intégrations universelles, P26). La supervision, elle, ne pose rien sur l'hôte.
|
||||
- Tout hôte qui **détient de l'état** doit porter `client_backup` (P36 le refuse sinon).
|
||||
- Tout service utilisant un certificat interne doit dépendre de `client_pki`.
|
||||
- Tout rôle serveur doit documenter ses ports, secrets, sauvegardes, dépendances et groupes clients associés.
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
# Contextes : un tronc commun, deux classes (SITE et LOCATAIRE)
|
||||
|
||||
> **Pour qui :** le **mainteneur** — comment le moteur sait s'il sert un site ou un locataire, et ce que les deux s'apprennent l'un à l'autre.
|
||||
|
||||
> **Statut : arrêtée avec l'exploitant le 2026-10-04.** Rien n'est encore construit ; les
|
||||
> décisions sont au §7, le chemin au §6.
|
||||
|
||||
## 1. Le problème, mesuré
|
||||
|
||||
Le moteur ne sait pas dans quel contexte il tourne : **chaque script le devine**. Relevé du
|
||||
2026-10-04 : **33 scripts** font leur propre déduction, à partir de cinq indices différents.
|
||||
|
||||
| Indice | Ce qu'on en déduit | Scripts |
|
||||
|---|---|---|
|
||||
| lien `instance/` ou `SETOPS_INSTANCE` | « un locataire est monté » | 23 |
|
||||
| lien `underlay.yml` ou `SETOPS_UNDERLAY` | « un site est monté » ; son plan est à côté | 8 |
|
||||
| `SETOPS_INVENTAIRE` | quel inventaire de locataire lire | 8 |
|
||||
| `../*/plan/nomenclature.yml` | « la fédération », les locataires frères | 11 |
|
||||
| `../SITE-*/underlay.yml` | « les sites » | 2 |
|
||||
|
||||
Les indices ne concordent pas toujours, et chaque désaccord a déjà produit un défaut silencieux :
|
||||
|
||||
- **2026-08-14** : `frontiere-plan` voulait poser sur la frontière de Technolibre les règles de
|
||||
Chezlepro. « La fédération » valait « les locataires de ce site », jusqu'au second site.
|
||||
- **2026-09-16** : la console du runner du site affichait zéro machine, sans erreur. Elle
|
||||
cherchait un inventaire de locataire là où il n'y en a pas.
|
||||
- **2026-10-04** : `make ci`, sur le poste, mélangeait le modèle public et les écosystèmes
|
||||
réels. P74 lisait `SETOPS_UNDERLAY` (le modèle) ; P82 lisait le lien `underlay.yml` et les
|
||||
dossiers frères (le site réel).
|
||||
|
||||
Les rôles Ansible, eux, ne posent pas ce problème : ils sont **déjà** le tronc commun. Un même
|
||||
`serveur_postgresql` sert au site et chez un locataire.
|
||||
|
||||
## 2. Le modèle
|
||||
|
||||
```
|
||||
Ecosysteme (tronc commun)
|
||||
/ \
|
||||
Site Locataire
|
||||
\ /
|
||||
`-- contrat --' (associations : un site A des locataires,
|
||||
un locataire A un site)
|
||||
```
|
||||
|
||||
### 2.1 Le tronc commun : `Ecosysteme`
|
||||
|
||||
Ce que tout écosystème possède, quel que soit son contexte :
|
||||
|
||||
- un **nom** et un **dépôt** (`SITE-Chezlepro`, `OPS-Technolibre`) ;
|
||||
- une **voûte** et sa clé (`~/.config/setops-vault-<dépôt>`) ;
|
||||
- un **plan** (`<dépôt>/plan/`) et un **index**, dont dérive son adressage (le site
|
||||
aussi depuis le 2026-09-20) ;
|
||||
- des **machines**, déployées par les **mêmes rôles** : socle, durcissement, PKI, journaux,
|
||||
métriques, supervision, sauvegarde de son propre état ;
|
||||
- une **filiation** : le moteur et le commit dont il descend ;
|
||||
- les **preuves communes** : lint, rendu des gabarits, adressage dérivé, etc.
|
||||
|
||||
Méthodes abstraites, que chaque classe **surcharge** : `inventaire()`, `machines()`,
|
||||
`preuves()`, `verbes()`, `console()`.
|
||||
|
||||
### 2.2 `Site(Ecosysteme)`
|
||||
|
||||
- **Déclaration** : `underlay.yml` (le matériel, les réseaux `site` et `fabric`) et `plan/`
|
||||
(`10-intrants.yml`, serveurs, applications, domaines, bases).
|
||||
- **Inventaire** : dynamique (`site_inventaire.py`). Le site ne dérive rien d'un plan de services ;
|
||||
sa déclaration est sa forme finale.
|
||||
- **Ce qu'il porte en propre** : le matériel (hyperviseurs, commutateurs, frontière),
|
||||
la matérialisation des VM (Proxmox), le SDN, le pare-feu Proxmox, la frontière OPNsense,
|
||||
le DNS public, le dépôt des sauvegardes des locataires, le cache et les artefacts, la forge
|
||||
du génome.
|
||||
- **Relation** : `locataires()`, la liste de `underlay.tenants` résolue en objets
|
||||
`Locataire`. Le site n'en lit que la **face réseau** (§2.4).
|
||||
|
||||
### 2.3 `Locataire(Ecosysteme)`
|
||||
|
||||
- **Déclaration** : `plan/` (nomenclature, serveurs, applications, bases, domaines).
|
||||
- **Inventaire** : généré (`instancier.py` → `hosts.yml`). C'est la **méta-classe** de
|
||||
[`meta-classe.md`](meta-classe.md) : une définition qui engendre toute la flotte.
|
||||
- **Ce qu'il porte en propre** : la configuration de ses services, la remise au client.
|
||||
- **Relation** : `site()`, l'hébergeur que nomme `parente.yml`, résolu en objet `Site`. Le
|
||||
locataire n'en lit que les **intrants exposés** (§2.4).
|
||||
|
||||
### 2.4 Ce que le site et le locataire s'apprennent l'un à l'autre
|
||||
|
||||
Les deux entités **s'informent mutuellement**. Relevé du 2026-10-04 : qui décide de chaque
|
||||
information, où elle vit, et comment elle parvient à l'autre.
|
||||
|
||||
**Ce que chacun a sous la main.** Le runner du site porte le moteur, son dépôt **et ceux de
|
||||
ses locataires** (sans leurs voûtes). Le runner d'un locataire ne porte que le moteur et
|
||||
**son propre** dépôt. Le poste porte tout.
|
||||
|
||||
#### Le site informe le locataire, par trois canaux
|
||||
|
||||
**Canal 1 : des copies écrites à la main** dans le dépôt du locataire.
|
||||
|
||||
| Information | Décidée par | Tenue chez le site dans | Copiée chez le locataire dans | Contrôle |
|
||||
|---|---|---|---|---|
|
||||
| son **index** | le site | `underlay.yml` → `tenants` | `plan/nomenclature.yml` (`index`) | `underlay valider` |
|
||||
| son **adresse publique** | le site | `opnsense.yml` → `opnsense_ips_publiques` | `10-intrants.yml` (`ip_publique`) | — |
|
||||
| les **10 intrants de service** : résolveur, cache, binaires, forge du génome, cible de sauvegarde, DNS public, plan d'administration, passerelle | le site (dérivés de son plan, par `site_intrants.py`) | son plan | `10-intrants.yml`, et `serveur_ops.yml` pour la forge | `site_intrants.py --verifier`, seulement là où les deux dépôts sont présents (le poste) |
|
||||
| sa **racine de confiance** | le site | `ac-racine-site.crt` | le même fichier, copié | — |
|
||||
| *hors contrat* : un dépôt de la forge du site désigné par son adresse | — | — | `serveur_web_dorsal.yml` (Chezlepro) | **aucun** |
|
||||
|
||||
**Canal 2 : une lecture directe, au moment de générer l'inventaire.** `instancier.py` ouvre
|
||||
l'`underlay.yml` et le plan du site pour écrire le `hosts.yml` du locataire. Mesuré sur
|
||||
Technolibre, inventaire généré avec puis sans le site monté : **quatre variables changent**.
|
||||
|
||||
| Variable du locataire | Avec le site monté | Sans le site |
|
||||
|---|---|---|
|
||||
| `chrony_serveurs` | `10.0.4.1` (la frontière) | absente |
|
||||
| `proxmox_pont` | `t23appl` (le VNet SDN) | absente |
|
||||
| `proxmox_etiquette_vlan` | aucune (le SDN étiquette) | `1236` |
|
||||
| `serveur_resolveur_zones_deleguees` | `genese.internal` → `10.37.34.11` | absente |
|
||||
|
||||
**Canal 3 : le réseau.** Le runner du locataire **tire** son génome de la forge du site ; il est
|
||||
né de l'**insémination** par le runner du site.
|
||||
|
||||
#### Le locataire informe le site : le site lit et recalcule
|
||||
|
||||
| Information | Décidée par | Tenue chez le locataire dans | Parvient au site par |
|
||||
|---|---|---|---|
|
||||
| ses **zones** et son adressage | dérivés de l'index | `plan/nomenclature.yml` | le site **lit le fichier** |
|
||||
| les **VM à matérialiser** | le locataire | `plan/serveurs.yml` → `hosts.yml` | le site **lit les fichiers** (placement, clonage, pools, SDN) |
|
||||
| ses **flux** | ses rôles et son plan | `meta/flux.yml` des rôles (moteur), croisés avec son `hosts.yml` | le site **recalcule** lui-même, avec **sa** version du moteur et la totalité de l'inventaire du locataire → frontière, NAT, pare-feu Proxmox |
|
||||
| ses **domaines publics** | le locataire | `plan/domaines.yml` (+ `applications.yml`, `serveurs.yml`) | le site **lit les fichiers** → DNS public secondaire |
|
||||
| sa **clé de sauvegarde** | le locataire | `inventories/*/group_vars/serveur_backup.yml` | le site **lit le fichier** → compte Unix sur le dépôt |
|
||||
| ses **accès d'administration** | le locataire | `plan/acces.yml`, `nftables_admin_ssh` | le site **lit les fichiers** → pairs WireGuard, règles d'administration |
|
||||
|
||||
Le SDN, lui, ne prend aucun flux : il ne filtre pas. Il ne reçoit que l'index, dont il dérive
|
||||
la zone, les 6 VNets et les 6 sous-réseaux.
|
||||
|
||||
#### À l'exécution, entre machines
|
||||
|
||||
Ces échanges-là passent par le réseau, pas par les dépôts. Ils sont déjà déclarés en flux :
|
||||
le locataire **dépose** ses sauvegardes chez le site (SFTP), **tire** ses paquets, ses binaires
|
||||
et son génome, **entre** par le tunnel d'administration du site ; le site **réplique** les
|
||||
zones publiques du locataire (AXFR signé TSIG).
|
||||
|
||||
#### Ce que le relevé montre
|
||||
|
||||
1. **Le site fouille l'intérieur du locataire.** Six fichiers de son plan et de son inventaire,
|
||||
`group_vars` compris. Rien ne dit ce que le locataire **accepte** de montrer. Renommer un
|
||||
champ chez le locataire casse le site sans bruit.
|
||||
2. **Les flux sont calculés deux fois**, par le locataire pour ses `nftables` et par le site pour
|
||||
la frontière et Proxmox, chacun avec **sa** version du moteur. Ils concordent tant que les deux
|
||||
runners tiennent le même commit (c'était le cas le 2026-10-04), mais rien ne l'impose.
|
||||
3. **Le locataire vit de copies** : douze valeurs et un certificat, recopiés à la main, plus une
|
||||
valeur hors contrat. La garde qui compare ne tourne que sur le poste ; le runner du locataire
|
||||
ne peut pas savoir que sa copie a vieilli.
|
||||
4. **L'inventaire d'un locataire dépend du site monté au moment de le générer.** Généré sur le
|
||||
runner du locataire, qui n'a pas le dépôt du site, il perdrait son serveur de temps, son SDN
|
||||
et sa délégation DNS. Ça ne s'est jamais vu, parce que l'inventaire est toujours généré sur le
|
||||
poste puis versionné.
|
||||
5. **Deux décisions du site** (l'index, l'adresse publique) vivent en double.
|
||||
|
||||
#### Proposition : deux fiches, une dans chaque sens
|
||||
|
||||
Chacun **publie** ce qu'il donne à l'autre, dans une fiche **générée** par le moteur, jamais
|
||||
écrite à la main. Chacun ne lit que la fiche que l'autre lui destine. Plus aucune lecture
|
||||
croisée, plus aucun recalcul.
|
||||
|
||||
- **La fiche du site pour un locataire.** Tout ce que le site lui **attribue** (index, adresse
|
||||
publique) et lui **offre** : les 10 intrants, sa racine de confiance, et ce que l'instancier
|
||||
allait lire en douce (serveur de temps, délégation DNS, mode SDN et nom des VNets). Une fiche
|
||||
**par locataire** : aucun ne voit le plan du site ni ses voisins. L'instancier ne lit plus que
|
||||
cette fiche, et l'inventaire devient **identique où qu'on le génère**.
|
||||
- **La face réseau du locataire.** Ce qu'il **demande** au site : VM à matérialiser, zones,
|
||||
domaines publics, clé de sauvegarde publique, accès d'administration, et **ses flux déjà
|
||||
résolus** (adresses, ports, protocoles), ceux avec l'extérieur pour la frontière et ceux de
|
||||
chaque VM pour Proxmox. Le locataire génère déjà ses flux résolus (`flux-genere/*.nft`,
|
||||
`*.connectivite.json`) : la face réseau en est la partie destinée au site. Le site ne
|
||||
recalcule plus rien : il applique ce que le locataire publie, après l'avoir confronté à sa
|
||||
propre politique.
|
||||
- **Chaque fiche porte l'empreinte de sa source**, et une preuve de chaque côté vérifie que la
|
||||
fiche reçue correspond à ce que l'autre a publié.
|
||||
|
||||
**Où en est l'étape 2 (2026-10-04).** La fiche du site (P84), les faits de la face réseau
|
||||
(P85) et les flux de chaque machine (P86) existent, et disent exactement ce que les lectures
|
||||
croisées produisent. La première mesure des flux a trouvé une information que le locataire
|
||||
jetait : les clients nommés d'un port aussi public, que Proxmox doit admettre nommément. Il
|
||||
les publie désormais (`sources_declarees`). La frontière, en trois temps : les identités
|
||||
(P87), les entrées publiques (P88), l'administration (P89) et les sorties (P90) sont faites.
|
||||
**L'étape 2 est terminée** (2026-10-05). Étape 3 : l'instancier lit la fiche déposée par le site, et
|
||||
l'inventaire d'un locataire se génère sans son site, à l'octet près (P91). Le locataire publie sa face
|
||||
réseau (P92) ; les comptes de sauvegarde, le DNS public, le pare-feu Proxmox, la frontière et la
|
||||
découverte des locataires du site la lisent. La matérialisation aussi : la face publie les
|
||||
paramètres de clonage (P93) ; `locataire-creer`, `locataire-raser` et `placement-plan TENANT=`
|
||||
nomment leur locataire au lieu de le monter, et visent les mêmes machines, à l'argument près de
|
||||
la ligne `ansible-playbook` (P94, `test_appels_locataire.py`) ; `reconstruire-locataire` les
|
||||
emploie. Reste : la preuve par reconstruction.
|
||||
|
||||
Méthodes du contrat : `site.fiche_pour(locataire)`, `locataire.face_reseau()`. Une classe
|
||||
n'ouvre jamais les fichiers de l'autre ; une preuve vérifiera la règle.
|
||||
|
||||
#### Ce que les fiches donnent : la portabilité
|
||||
|
||||
Un locataire qui change de site, pour un déménagement, un plan de reprise ou une émancipation,
|
||||
n'a plus qu'à **recevoir la fiche de son nouveau site**. Son dépôt ne contient plus rien
|
||||
d'interne à l'ancien : ni copie d'adresse, ni inventaire généré avec l'ancien site monté. En
|
||||
face, le nouveau site n'a qu'à lire sa **face réseau**. Aujourd'hui, la même bascule demande de
|
||||
corriger des copies dans plusieurs fichiers, puis de régénérer l'inventaire avec le nouveau site
|
||||
monté sur le poste.
|
||||
|
||||
## 3. Le poste : un sélecteur
|
||||
|
||||
Aujourd'hui, le poste monte les deux contextes **en même temps** (`instance/` + `underlay.yml`),
|
||||
et `ConsolePoste` hérite de `ConsoleLocataire`. Désormais :
|
||||
|
||||
- **Le poste choisit un contexte actif** : un site **ou** un locataire. La console ouvre celui-là,
|
||||
et seulement celui-là.
|
||||
- **Une opération qui traverse les deux** nomme ses objets au lieu de les deviner.
|
||||
`reconstruire-locataire` en est l'exemple : le site matérialise, puis le locataire monte.
|
||||
L'orchestration devient `site.materialiser(locataire)` puis `locataire.monter()`.
|
||||
- **Les runners ne changent pas** : celui du site n'a qu'un `Site`, celui d'un locataire qu'un
|
||||
`Locataire`. Leur contexte est désormais **dit**, plus déduit.
|
||||
|
||||
## 4. Qui surcharge quoi
|
||||
|
||||
| Méthode | Tronc commun | Site | Locataire |
|
||||
|---|---|---|---|
|
||||
| `inventaire()` | abstraite | script dynamique (`underlay.yml`) | `hosts.yml` généré du plan |
|
||||
| `machines()` | abstraite | VM du site + équipements | VM du plan |
|
||||
| `adressage()` | dérivé de l'index | zones `site` dérivées, liens `fabric` écrits | 6 zones dérivées |
|
||||
| `sauvegardes()` | son propre état, vérifié par restauration | + héberge les dépôts des locataires | dépose chez son site |
|
||||
| `supervision()` | sondes déclarées par les rôles | + matériel, fabric, frontière | — |
|
||||
| `raser()` / `reconstruire()` | — | `site_raser.py` | `raser.py`, `reconstruire_locataire.py` |
|
||||
| `preuves()` | preuves communes | + preuves de site (P23, P74, P82…) | + preuves de locataire |
|
||||
| `verbes()` | `verifier`, `publier`… | `site-*`, `frontiere-*`, `proxmox-*` | `appliquer`, `instancier`, `remise-*` |
|
||||
| `console()` | — | console SITE | console LOCATAIRE |
|
||||
|
||||
## 5. Ce qui ne change pas
|
||||
|
||||
- Les **rôles Ansible**, déjà communs.
|
||||
- Les **formats de plan** : pas dans ce chantier.
|
||||
- La **doctrine** d'`AGENTS.md`.
|
||||
|
||||
## 6. Le chemin, chaque pas prouvé avant le suivant
|
||||
|
||||
1. **`scripts/contexte.py`** : `Ecosysteme`, `Site`, `Locataire`, `contexte_actif()`,
|
||||
`Site.charger(nom)`, `Locataire.charger(nom)`. Tests unitaires. Rien ne l'utilise encore.
|
||||
2. **Les deux fiches**, générées à côté de l'existant sans rien remplacer :
|
||||
`site.fiche_pour(locataire)` et `locataire.face_reseau()`. Une preuve vérifie que chaque
|
||||
fiche dit **exactement** ce que les lectures croisées d'aujourd'hui produisent.
|
||||
3. **Les consommateurs basculent sur les fiches**, un par un : l'instancier sur la fiche du site
|
||||
(l'inventaire généré doit rester identique, octet pour octet) ; la frontière, Proxmox, le DNS
|
||||
public et les comptes de sauvegarde sur la face réseau (chaque devis doit rester inchangé).
|
||||
4. **`prouver.py`** : chaque preuve déclare son contexte (commun, site, locataire) et reçoit son
|
||||
écosystème du module.
|
||||
5. **Les autres scripts**, un par un, vérifiés par `make verifier` et par un devis inchangé.
|
||||
6. **Une preuve « aucune devinette, aucune lecture croisée »** : les indices du §1, et toute
|
||||
ouverture d'un fichier de l'autre contexte, interdits hors de `contexte.py`.
|
||||
7. **Les verbes du Makefile** rangés par contexte.
|
||||
8. **Les consoles** : le sélecteur et deux consoles (chantier suivant).
|
||||
9. **OPS-Modele** : un locataire modèle, et sans doute un site modèle, vérifiés chacun dans son
|
||||
contexte.
|
||||
|
||||
Après les étapes 3 et 5, une reconstruction prouve que la flotte n'a pas bougé.
|
||||
|
||||
## 7. Décisions et questions ouvertes
|
||||
|
||||
### Tranché par l'exploitant
|
||||
|
||||
- **Deux classes, `Site` et `Locataire`, qui héritent d'un tronc commun** (2026-10-04).
|
||||
- **Le poste est un sélecteur** : un contexte actif à la fois (2026-10-04).
|
||||
- **On commence par le moteur**, les consoles viennent ensuite (2026-10-04).
|
||||
- **Le site dépose sa fiche dans le dépôt du locataire** (2026-10-04), comme il y amorce déjà
|
||||
son runner. Le runner du locataire n'a besoin d'aucun accès au dépôt du site, et ne voit
|
||||
ni le plan du site ni ses voisins.
|
||||
|
||||
- **Le contexte actif se nomme dans un fichier `contexte` explicite** (2026-10-04), une seule
|
||||
valeur : `site:SITE-Chezlepro` ou `locataire:OPS-Technolibre`. Un sélecteur qui monte deux
|
||||
liens à la fois contredirait sa propre règle. Les liens `instance/` et `underlay.yml` restent
|
||||
le temps de la bascule, lus par le seul `contexte.py`.
|
||||
- **Un modèle SITE public, `SITE-Modele`**, à côté d'`OPS-Modele` (2026-10-04). Sans site, la
|
||||
CI ne peut exercer ni les preuves de site (P74, P81, P82) ni la fiche que le site dépose chez
|
||||
le locataire.
|
||||
- **Le site a aussi son `parente.yml`** (2026-10-04) : la filiation est dans le tronc commun.
|
||||
|
|
@ -13,7 +13,7 @@ la connexion au cluster Proxmox et les valeurs de clonage par défaut. Il pose
|
|||
Le chemin se **dérive** du symlink qui désigne déjà l'hébergeur — rien de nouveau
|
||||
n'est déclaré. Sans underlay monté, tout retombe dans le fichier du tenant et
|
||||
`make config` fonctionne comme avant.
|
||||
- Secrets → **voûte unique** `instance/inventories/<inventaire>/group_vars/all/vault.yml`
|
||||
- Secrets → **voûte unique** `instance/inventories/production/group_vars/all/vault.yml`
|
||||
(chiffrée par `ansible-vault`), qui contient **tous** les secrets de l'instance
|
||||
(token Proxmox + `vault_*`). Voir [§4](#4-secrets-de-linstance-).
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ sans tout retaper.
|
|||
| Invite | Variable | Défaut | Sens / quoi saisir |
|
||||
| --- | --- | --- | --- |
|
||||
| VMID du modèle Debian 13 | `proxmox_clone_vmid_modele` | `9000` | VMID de la VM-modèle existante à cloner pour chaque nouvelle VM. |
|
||||
| Nom logique du modèle | `proxmox_clone_source_nom` | `modeleSetOPS` | Nom de référence du template. **Il doit correspondre au nom réel du template Proxmox** : sinon le clonage ne trouve pas sa source. *(Ce tableau a annoncé `modele-debian13` jusqu'au 2026-09-06 — un défaut qui n'a jamais été celui du code.)* |
|
||||
| Nom logique du modèle | `proxmox_clone_source_nom` | `modele-debian13` | Nom de référence du template (lisibilité ; doit correspondre au modèle). |
|
||||
|
||||
> Le golden template est l'**actif central** : il est cloné pour chaque VM, jamais
|
||||
> jeté ni reconstruit à la légère.
|
||||
|
|
@ -68,17 +68,10 @@ Ces valeurs s'appliquent à toute VM clonée, **sauf** si l'hôte les surcharge
|
|||
## 4. Secrets de l'instance 🔒 *(voûte unique)*
|
||||
|
||||
Tous les secrets de l'instance vivent dans **une seule voûte chiffrée par
|
||||
instance** : `instance/inventories/<inventaire>/group_vars/all/vault.yml`. Un seul fichier
|
||||
par écosystème — fini les voûtes éparpillées.
|
||||
|
||||
> **Une voûte, une clé (2026-08-28).** « Un seul mot de passe » a été vrai, et c'était le
|
||||
> défaut : le même ouvrait *toutes* les voûtes de la flotte, celle de l'hébergeur comprise.
|
||||
> Chaque dépôt a maintenant **sa** clé — `~/.config/setops-vault-<dépôt-en-minuscules>` —
|
||||
> et le `Makefile` les rassemble dans `ANSIBLE_VAULT_IDENTITY_LIST` via
|
||||
> `scripts/voutes.py`. Créer une VM ouvre d'ailleurs **deux** voûtes dans la même
|
||||
> exécution : celle du tenant, et celle de l'hébergeur qui détient le jeton Proxmox. Gabarit committé :
|
||||
[`exemples/vault.exemple.yml`](../exemples/vault.exemple.yml) (les deux clés du token
|
||||
Proxmox + **15 clés `vault_*`** pour PKI, LDAP/SSO, bases, forge, observabilité).
|
||||
environnement** : `instance/inventories/<env>/group_vars/all/vault.yml`. Un seul
|
||||
fichier, un seul mot de passe — fini les voûtes éparpillées. Gabarit committé :
|
||||
[`exemples/vault.exemple.yml`](../exemples/vault.exemple.yml) (token Proxmox +
|
||||
17 clés `vault_*` pour PKI, LDAP/SSO, bases, forge, observabilité).
|
||||
|
||||
L'assistant demande « Configurer la voûte de secrets maintenant ». Si `oui` :
|
||||
|
||||
|
|
@ -103,16 +96,10 @@ L'assistant demande « Configurer la voûte de secrets maintenant ». Si `oui` :
|
|||
| **Saisir** | un **tiers** — le secret existe déjà ailleurs et ne s'invente pas (clé d'API OPNsense, jeton Proxmox) | `python3 scripts/voute.py saisir <clés>` |
|
||||
|
||||
```bash
|
||||
python3 scripts/voute.py saisir vault_opnsense_api_key vault_opnsense_api_secret
|
||||
ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass \
|
||||
python3 scripts/voute.py saisir vault_opnsense_api_key vault_opnsense_api_secret
|
||||
```
|
||||
|
||||
Il n'y a **rien à exporter** : `voute.py` trouve la clé de la voûte par la convention de
|
||||
nommage (`scripts/voutes.py etat` la montre). Il n'y a pas non plus de cible `make` pour ce
|
||||
geste — c'est délibéré : saisir un secret est une manœuvre rare et attentive.
|
||||
|
||||
*(`voute.py` au singulier manipule **le contenu** d'une voûte ; `voutes.py` au pluriel dit
|
||||
**où sont les clés**. Les deux existent, et ce n'est pas une faute de frappe.)*
|
||||
|
||||
Saisie **sans écho**, double confirmation, rien sur la ligne de commande — donc ni
|
||||
dans l'historique du shell, ni dans la liste des processus. Rien n'est écrit en clair
|
||||
sur disque : la voûte est déchiffrée en mémoire, complétée, reparsée et re-déchiffrée
|
||||
|
|
|
|||
|
|
@ -33,94 +33,40 @@ couches:
|
|||
groupes:
|
||||
- client_pki
|
||||
|
||||
- nom: observabilite
|
||||
raison: >-
|
||||
VOIR AVANT DE CONSTRUIRE. La mesure vient juste après la PKI, donc avant tout ce
|
||||
qu'elle devra surveiller — et non après, comme si l'on n'allumait la lumière qu'une
|
||||
fois la maison finie. Une reconstruction depuis zéro est précisément le moment où
|
||||
l'on a le plus besoin de voir ce qui se passe : chaque rôle déployé ensuite l'est
|
||||
sous l'œil de la supervision, et une unité qui casse se voit à la minute plutôt qu'à
|
||||
la fin. `obs` ne dépend de rien ; `serveur_icinga` n'exige que PostgreSQL, qui
|
||||
n'exige rien lui-même. Ce qui reste plus tard, c'est la CONSOLE (`icingaweb2`,
|
||||
`oauth2_proxy`), qui demande LDAP et Keycloak — l'interface humaine peut attendre,
|
||||
la mesure non.
|
||||
groupes:
|
||||
- serveur_postgresql
|
||||
- serveur_prometheus
|
||||
- serveur_loki
|
||||
- serveur_grafana
|
||||
- serveur_icinga
|
||||
|
||||
- nom: agents_supervision
|
||||
raison: >-
|
||||
Les agents qui FONT voir, poses juste apres leurs serveurs. Deplacer la supervision
|
||||
en amont sans eux n'aurait rien change : ce sont eux qui rapportent. Des cette
|
||||
couche, chaque hote expedie ses metriques, ses journaux, et l'etat de ses unites
|
||||
systemd — donc tout ce qui se deploie apres est mesure pendant qu'on le construit.
|
||||
groupes:
|
||||
- client_metrique
|
||||
- client_journal
|
||||
- client_sante
|
||||
|
||||
- nom: services
|
||||
raison: "Les services d'infrastructure dont dépendent les applications : bases, annuaire, DNS, cache, métriques, journaux, edge, courriel."
|
||||
groupes:
|
||||
- serveur_postgresql
|
||||
- serveur_openldap
|
||||
- serveur_powerdns
|
||||
# Le resolveur du tenant vient APRES son autoritatif : il le prend en stub-zone,
|
||||
# et sa validation exige que la zone souveraine reponde deja.
|
||||
- serveur_resolveur
|
||||
- serveur_redis
|
||||
- serveur_prometheus
|
||||
- serveur_loki
|
||||
- serveur_nginx
|
||||
- serveur_rspamd
|
||||
- serveur_dovecot
|
||||
- serveur_postfix
|
||||
- serveur_backup
|
||||
# La source d'artefacts vient AVANT ceux qui installent des paquets — c'est tout
|
||||
# son objet. Placee plus tard, elle serait remplie apres avoir servi.
|
||||
- serveur_artefacts
|
||||
# LA RACINE DE LA CHAINE DE CACHES, et elle appartient au SITE, pas au tenant :
|
||||
# VM du tenant -> cache du tenant -> cache du SITE -> Debian
|
||||
# Classee ici pour que son ordre soit dit, mais elle ne se deploie pas dans le meme
|
||||
# mouvement : elle vit dans l'underlay de l'hebergeur et se joint par
|
||||
# `scripts/site_inventaire.py`. Un tenant ne la deploie jamais — il la CONSOMME.
|
||||
- serveur_cache_site
|
||||
# La forge du genome, meme nature : elle vit dans l'underlay de
|
||||
# l'hebergeur et un tenant la CONSOMME sans jamais la deployer.
|
||||
- serveur_forge_site
|
||||
# Le resolveur du site, meme nature : prete aux locataires pendant leur jeunesse.
|
||||
- serveur_resolveur_site
|
||||
# Le depot de sauvegarde du site, meme nature : il recoit l'etat des locataires.
|
||||
- serveur_backup_site
|
||||
# Le DNS public du site vient APRES les services : il tire les zones que les
|
||||
# autoritatifs des locataires ecrivent, et n'a rien a servir avant eux.
|
||||
- serveur_dns_public
|
||||
|
||||
- nom: apps
|
||||
raison: "Les applications métier, qui consomment les services (base, SSO, courriel, edge)."
|
||||
groupes:
|
||||
- serveur_keycloak
|
||||
- serveur_oauth2_proxy
|
||||
- serveur_forgejo
|
||||
- serveur_icinga
|
||||
- serveur_icingaweb2
|
||||
- serveur_grafana
|
||||
- serveur_collabora
|
||||
- serveur_nextcloud
|
||||
- serveur_web_frontal
|
||||
- serveur_web_dorsal
|
||||
# Le poste d'exploitation vient APRES la forge : il clone le genome depuis
|
||||
# elle. Le placer plus tot le laisserait sans source.
|
||||
- serveur_ops
|
||||
# Le runner de TENANT vient APRES le poste : il suppose les depots clones et le lien
|
||||
# `instance` pose. Il n'ajoute qu'un pouvoir -- celui de CONFIGURER cet ecosysteme,
|
||||
# par sa voute deposee chiffree.
|
||||
- serveur_ops_tenant
|
||||
# Le runner de SITE vient APRES le runner de tenant : il suppose les depots
|
||||
# clones. Il n'ajoute qu'un pouvoir -- celui de materialiser sur la fabric.
|
||||
- serveur_ops_site
|
||||
|
||||
- nom: agents
|
||||
raison: "Les intégrations clientes qui expédient vers les services centraux (métriques, journaux, courriel, sauvegardes, résolution locale). Déployées en dernier, quand leurs cibles sont debout."
|
||||
groupes:
|
||||
- client_metrique
|
||||
- client_journal
|
||||
- client_smtp
|
||||
- client_backup
|
||||
- client_resolveur
|
||||
- client_artefacts
|
||||
- client_unbound
|
||||
|
|
|
|||
|
|
@ -2,15 +2,8 @@
|
|||
|
||||
> **Pour qui :** le **mainteneur** du service de courriel.
|
||||
|
||||
> **Statut, revu le 2026-09-06 — l'Étape A est LIVRÉE, l'Étape B reste du cadrage.**
|
||||
> Ce document annonçait « aucun rôle n'est encore écrit » : les trois rôles
|
||||
> (`serveur_postfix`, `serveur_dovecot`, `serveur_rspamd`) existent, sont déployés, et le
|
||||
> flux interne est **prouvé de bout en bout** — SMTP → validation LDAP → LMTP chiffré →
|
||||
> boîte → **lecture IMAP**, avec antispam et signature DKIM. Ce qui n'est **pas** livré,
|
||||
> c'est l'**Étape B** (§11) : la face publique — Let's Encrypt, reprise du MX `.53`,
|
||||
> enregistrements chez Namespro, tests de délivrabilité. Lire ce document ainsi : les
|
||||
> décisions du §1 sont **arrêtées et appliquées** ; la feuille de route du §11 est
|
||||
> **ouverte**.
|
||||
> **Statut : CONCEPTION (cadrage).** Aucun rôle n'est encore écrit. Ce document fixe
|
||||
> les décisions, les prérequis et la topologie avant toute implémentation.
|
||||
|
||||
## 1. Décisions arrêtées
|
||||
|
||||
|
|
@ -201,16 +194,13 @@ _dmarc TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@chezlepro.
|
|||
But : prouver **toute la pile en interne**, en **code de prod**, dans le bac à sable.
|
||||
Aucune dépendance au public.
|
||||
|
||||
1. **Pilier identité** : `serveur_openldap` (TLS **step_ca**) — ✅ **déployé et prouvé**.
|
||||
2. **`serveur_postfix` + `serveur_rspamd`** sur `edge-mta-01`, **`serveur_dovecot`** sur
|
||||
`infra-mail-01` : TLS **step_ca**, annuaire/auth **LDAP**, DKIM, nftables mail — ✅ **déployés**.
|
||||
1. **Pilier identité** : `serveur_openldap` (TLS **step_ca**) — ✅ **déployé et prouvé** en bac à sable.
|
||||
2. **`serveur_postfix` + `serveur_dovecot` + `serveur_rspamd`** sur `mail-01` : TLS **step_ca**,
|
||||
annuaire/auth **LDAP**, DKIM interne, nftables mail.
|
||||
3. **Prouver** : réception → boîte → accès **IMAP** → envoi **intra-écosystème**, le tout en
|
||||
TLS interne, auth LDAP — ✅ **prouvé de bout en bout**, et rejoué à la demande par
|
||||
`make courriel-plan`, file d'attente comprise.
|
||||
TLS interne, auth LDAP.
|
||||
|
||||
*(Ce paragraphe disait « Étape actuelle : identité OK ; on démarre `serveur_postfix` » et
|
||||
plaçait toute la pile sur un `mail-01` unique. La topologie retenue est celle du §3 révisé —
|
||||
le MTA en périphérie, les boîtes à l'intérieur — et l'Étape A est close.)*
|
||||
*(Étape actuelle : identité OK ; on démarre `serveur_postfix`.)*
|
||||
|
||||
### Étape B — Fonctionnement EXTERNE (transition prod, plus tard)
|
||||
|
||||
|
|
@ -226,5 +216,4 @@ But : brancher sur le monde **en reprenant l'existant** (voir §2).
|
|||
---
|
||||
|
||||
*Ce document est un cadrage vivant : il évolue à mesure que les décisions ouvertes se
|
||||
tranchent. **L'Étape A qu'il décrit est livrée et prouvée** ; la séquence ci-dessus, qui est
|
||||
l'Étape B, ne l'est pas.*
|
||||
tranchent. Il ne décrit pas encore de code livré.*
|
||||
|
|
|
|||
|
|
@ -55,14 +55,6 @@ sont les seules vérifiables.
|
|||
|
||||
| # | Décision | Pourquoi | Détail | Garde |
|
||||
|---|---|---|---|---|
|
||||
| **D-82** | **Patient 0 n'est le parent de personne.** Il est la **mise en œuvre de référence** du modèle `origine` — le plus petit écosystème complet — et un pair de la famille du génome, pas sa racine | trois faits l'ont retiré un par un : D-81 a donné l'autorité du génome à la forge du SITE (son dernier lecteur corrigé le 2026-08-26, Technolibre le 08-31) ; le dénominateur commun vit dans les modèles depuis le 08-24 ; et **l'ancêtre était locataire de son enfant** — index 29 sur la fabric de `SITE-Chezlepro`, qui descend de lui. `eregion` (`forge.alliance-boreale.ca`) n'est PAS sur le chemin du génome : le poste porte les commits en bundle au runner du site (`make genome-pousser`), qui pousse sur sa forge. `eregion` est une forge héritée, porte publique des contributions, qui ne fera jamais partie de Set-OPS — la redondance vient d'une forge par site, chacune inséminée du génome *(corrigé le 2026-09-28 : cette ligne l'avait dite « SPOF promu », sans mesurer le chemin)* | `OPS-Patient0/README.md`, `docs/filiation-emancipation.md` | — |
|
||||
| **D-83** | **Patient 0 a été retiré** — ses machines n'existent plus (constaté le 2026-09-06) | D-82 lui avait laissé une raison d'être : la mise en œuvre de référence du modèle `origine`, et un **témoin** de plus du génome. Le retrait solde les deux : l'une vit dans le modèle `origine`, l'autre revient aux forges de site. **Son plan a été effacé le 2026-09-27** : l'index 29 est libéré, et le site n'ouvre plus rien à `10.29.0.0/16` | `SITE-Chezlepro/underlay.yml` (`tenants`), `SITE-Chezlepro/flux-genere/` | **P21** (index), **P23** |
|
||||
| **D-84** | **Le plan de contrôle reste gelé — c'est la CARTE DES SEUILS qui était fausse** | La question « et si on retirait le gel ? » a mis à l'épreuve les cinq seuils de `positionnement.md`, et deux ne tenaient pas. **RBAC** : couvert depuis que trois classes d'acteurs aux pouvoirs disjoints existent — poste, runner de site, runners de tenant — séparés **cryptographiquement** (une voûte, une clé, 2026-08-28) et non par une table de permissions qu'une faille applicative contournerait ; adopter AWX pour ce besoin serait **régresser**. **IPAM** : sans objet par construction — rien ne s'alloue, tout dérive du seed, et P20/P21/P23/P28/P33 tiennent déjà ce qu'un IPAM vérifierait *a posteriori*. Les deux lignes sont retirées du tableau : les garder aurait fait adopter un outil pour un besoin déjà rempli. **Et un seuil manquait** — l'**émancipation** : le GUI est mono-utilisateur (`127.0.0.1` + jeton), or la trajectoire mène à plusieurs humains aux portées disjointes, sur des machines qui ne sont pas les nôtres. Ce seuil n'appelle pas AWX, il appelle une décision non prise. *Un seuil qu'on ne nomme pas est un seuil qu'on franchit sans le voir.* Corollaire consigné : le gel porte sur les **fonctions**, jamais sur les **vues** — montrer à l'écran ce que le moteur sait déjà ne franchit aucun seuil | `positionnement.md` §3, §4, §5 | — |
|
||||
| **D-85** | **cloud-init naît avec la VM et ne lui survit pas** | cloud-init n'est pas un logiciel d'installation : c'est une **source de vérité externe**, qui se réveille à *chaque* démarrage et relit le lecteur attaché par l'hyperviseur — lequel peut redéfinir comptes, clés SSH autorisées, mots de passe et réseau. Sur une machine que le plan possède, c'est un **second maître** : le plan ne le décrit pas, `make valider` ne le mesure pas, et il parle en premier. Sa tâche est pourtant finie à la première seconde — c'est parce qu'il a **réussi** à poser l'adresse et les clés qu'Ansible a pu entrer. **Trois moitiés, qui se défont séparément** : le **gabarit** le garde (sans lui un clone ne naît pas — P56) ; le **socle** ne l'installe plus (le garder produisait un va-et-vient à chaque déploiement : le socle installe, le durcissement retire, deux `changed` par passage) ; le **durcissement** le retire (`cloud_init_retrait`, en dernier). **Ce qui rend le retrait sûr est mesuré, pas supposé** (2026-09-09, `obs-01`) : `/etc/network/interfaces.d/50-cloud-init` n'appartient à aucun paquet — `dpkg -S` ne le trouve pas — et le `postrm` ne le nomme jamais, même en `purge`. L'adresse survit. Le rôle le **vérifie** malgré tout, avant et après : une VM qui perd ce fichier ne se plaint pas, elle repart sans adresse et plus personne ne peut entrer pour le constater. **⚠ CE QUE CETTE DÉCISION NE FERME PAS — et il faut le dire, sinon elle se lit comme une émancipation qu'elle n'est pas.** Retirer cloud-init **n'ôte aucun pouvoir à l'hébergeur**. `qemu-guest-agent` est au gabarit (P56 : il doit y être — c'est par lui que `creer-vm` confirme la matérialisation sans entrer chez le tenant), et l'API Proxmox expose sur son dos, sur toute VM vivante de la flotte, un pouvoir **strictement plus grand** que le lecteur cloud-init : `exec`, `file-write`, `file-read`, `set-user-password`, `shutdown` (relevé le 2026-09-09 sur `edge-mta-01`, jeton d'API du site). Ce que D-85 ferme est donc **précis et étroit** : (a) une réapplication **automatique, à chaque démarrage**, depuis un support que le plan ne possède pas et qu'aucune preuve ne lit ; (b) le code de cloud-init lui-même — un interpréteur Python complet, exécuté en root au démarrage, et ses ~29 dépendances. Elle ne ferme **pas** la mainmise de l'hyperviseur sur ses invités : celle-là est une propriété de la virtualisation, pas de cloud-init, et elle appelle sa propre décision — non prise. **Le seuil où le remplacer deviendrait juste** : le jour où une première seconde ne peut plus être amorcée par Proxmox (autre hyperviseur, métal nu, hébergeur sans API), le chemin par l'agent invite cesse d'être une réimplémentation d'un standard — que `positionnement.md` interdit — et devient **le chemin portable**. Tant que ce seuil n'est pas atteint, écrire soi-même l'amorçage serait échanger un standard éprouvé contre du code maison au moment le plus fragile, dont le mode de panne est le pire : une VM injoignable | `roles/cloud_init_retrait/`, `serveur_durci.yml`, `positionnement.md` | **P63** |
|
||||
| **D-86** | **La supervision se déploie juste après la PKI, pas à la fin** | L'observabilité (`prometheus`, `loki`, `grafana`) et le **moteur** de supervision (`icinga`) passent en couche 4, immédiatement après `client_pki` ; les agents qui les nourrissent (`client_metrique`, `client_journal`, `client_sante`) en couche 5. **Le raisonnement** : ce qui se déploie ensuite l'est *sous l'œil* de la supervision — une unité qui casse se voit à la minute, pas à la fin. Une **reconstruction depuis zéro** est précisément le moment où l'on a le plus besoin de voir, et c'était le seul moment où l'on ne voyait rien. **Déplacer les serveurs sans les agents n'aurait rien changé** : ce sont les agents qui rapportent, et ils étaient en dernière couche. **Ce que ça a coûté en dépendances** : `serveur_postgresql` monte aussi (il n'exige rien lui-même, et `icinga` l'exige). **Ce qui reste tard, à dessein** : `icingaweb2` et `oauth2_proxy` réclament LDAP et Keycloak — c'est la CONSOLE, pas la mesure. L'interface humaine peut attendre. **Limite dite franchement** : les *notifications* dépendent de `client_smtp`, encore en dernière couche — pendant une reconstruction, l'état est mesuré et consultable, mais rien ne part par courriel avant la fin. **CE QUI REND CE DÉPLACEMENT POSSIBLE**, et qui n'est pas un détail : le DNS (`powerdns`, `resolveur`) reste en couche 6, donc *après* la supervision. Or `icinga` joint sa base par un **nom** (`data-sql-01.chezlepro.internal`), et `client_sante` pousse vers un **nom**. Ça tient parce que le **plancher `/etc/hosts`**, posé dès la couche 1 par `hosts_statiques`, porte déjà les 34 entrées de l'écosystème — vérifié. C'est exactement ce pour quoi il existe : *« il ne s'installe pas, il rend installable »*. Sans lui, cette décision serait impossible. **P08** valide l'ordre : aucune arête en arrière | `docs/couches-deploiement.yml`, `catalogue-services.md` §Ordre | **P08** |
|
||||
| **D-87** | **Ce que l'hébergeur n'a pas le droit de VOIR** — l'observabilité découpée, la PKI tranchée | La question *« quels rôles ne dois-je pas embarquer dans le site ? »* a mis à l'épreuve la table de mutualisation de `filiation-emancipation.md`, et **une ligne contredisait ce qui tourne**. Elle disait « observabilité \| oui \| l'hébergeur surveille ses locataires » — or chaque écosystème a son propre Icinga, et celui du site ne voit que ses sept machines (mesuré). Surtout, elle autorisait en une case ce que la ligne du dessous interdit : **les journaux contiennent du contenu** — un mot de passe dans un message d'erreur, une donnée métier dans une trace. Un hébergeur qui ingère les journaux de son locataire en sait **plus** que s'il détenait son annuaire ; l'annuaire dit qui existe, les journaux disent ce qu'ils font. **Découpée en trois** : *disponibilité* oui (une VM tombée est un fait de la fabric), *métriques* oui avec réserve (elles disent quand et combien, ce qui suffit à lire l'activité d'une organisation), *journaux* **non**. **Et la ligne PKI, « à trancher », est tranchée : non.** Une AC intermédiaire signée par l'hôte lui donnerait le pouvoir d'émettre des certificats valides pour les noms du locataire, donc de se présenter comme n'importe lequel de ses services — devant les propres machines du locataire, qui les accepteraient, puisque c'est ce que la chaîne de confiance leur demande. Même pouvoir que l'annuaire, sous une forme **moins visible** : aucune trace côté locataire. Une PKI par écosystème, jamais dérivée de l'hôte. **Le revers, mesuré et assumé** : le site n'a NI `client_journal` NI `client_metrique`, aucun `loki` ni `prometheus` — à refuser de voir ceux des locataires, il s'est privé des siens. Le remède n'est pas d'assouplir la règle, c'est de lui donner sa propre pile | `filiation-emancipation.md` §mutualisable | — |
|
||||
| **D-88** | **Le nœud qui porte le gabarit est un point unique de défaillance — pour la REPRODUCTION, pas pour l'exploitation** | Question posée par l'exploitant le 2026-09-10 : *« le modèle vit sur vishnu, les clones sont sur asgard — qu'arriverait-il si vishnu tombait ? »*. **Mesuré, la réponse se coupe en deux.** Les **données** survivent : le pool `CephNVMe` est en `size=3 / min_size=2`, avec des OSD sur les trois hôtes, et `base-9006-disk-0/1` y sont répliquées — l'image reste lisible avec un nœud en moins, et les VM d'un écosystème tournent ailleurs sans s'apercevoir de rien. Mais la **configuration** du gabarit porte le nom du nœud dans son chemin (`/etc/pve/nodes/vishnu/qemu-server/9006.conf`), et le clonage appelle `nodes/vishnu/qemu/9006/clone` : nœud éteint, API muette, **aucune VM nouvelle ne peut naître**. Or la reproduction est ce que ce dépôt existe pour garantir. **La décision est d'ASSUMER cette dépendance et de la rendre courte**, pas de la supprimer : depuis que le disque du gabarit vit sur un stockage partagé (2026-09-10), la remise en route est un **déplacement de fichier de configuration** — quelques minutes, aucun mouvement de données — suivi de la déclaration `gabarit.noeud`. Sur stockage local il aurait fallu recopier 16 Go ou refabriquer. **Ce qui n'est PAS fait, et qui est dit** : rien ne *mesure* cette dépendance. `gabarit_etat` compare le déclaré au réel, il ne demande pas si le nœud du gabarit héberge autre chose que le gabarit. *Une dépendance qu'on documente sans la mesurer reste une dépendance qu'on découvrira au mauvais moment.* | `runbooks-exploitation.md` §7, `SITE-Chezlepro/plan/10-intrants.yml` §gabarit | — |
|
||||
| **D-81** | **La forge du SITE fait autorité pour le génome.** Toute autre copie — y compris celle d'où le moteur a été poussé jusqu'ici — est un **miroir**. Le poste de l'exploitant ne route pas jusqu'à elle : c'est le **runner du site** qui publie, par `make genome-pousser` | un écosystème se reproduit depuis la forge de son site : c'est de là qu'il clone son moteur, ses plans, ses modèles. Si l'autorité est ailleurs, cette forge devient un cache qu'on croit à jour — et le 2026-08-26 elle était **quatre commits en arrière** sans que rien ne le signale, dont le correctif qui désarme le pare-feu Proxmox. **Un écosystème qui se reproduit depuis une forge en retard reproduit ses défauts.** Le poste n'a de patte que sur l'administration, et on ne perce pas de chemin pour lui : le runner existe pour ce travail | `playbooks/maintenance/genome_pousser.yml`, `scripts/genome_colis.py`, `Makefile` §genome-pousser | — |
|
||||
| **D-13** | Un **hébergeur** sert plusieurs **tenants** et a son tenant par défaut | Chezlepro est les deux à la fois, ce qui masquait la distinction | `frontiere-opnsense.md` §2 | — |
|
||||
| **D-14** | `underlay.yml` appartient à l'**hébergeur**, monté par symlink | ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas | `sdn-evpn.md`, `underlay.yml.example` | — |
|
||||
| **D-15** | Ce symlink **ne suit pas** `make instance-utiliser` | basculer le tenant actif ne change pas la fabric | `frontiere-opnsense.md` §2 | — |
|
||||
|
|
@ -77,9 +69,9 @@ sont les seules vérifiables.
|
|||
| **D-48** | Les **hyperviseurs** sont gérables par Ansible ; « hors flotte » ne vaut que pour les **commutateurs** et la **frontière** | ce sont des Debian joignables en SSH ; c'est la seule façon d'y poser un exportateur de métriques | `hebergeur-exploitation.md` §5 | — |
|
||||
| **D-55** | Le dépôt réseau porte une **interface normalisée** vers les tenants de l'Alliance, et abstrait le matériel en les encapsulant dans des zones EVPN | un tenant qui ne nomme aucun équipement se déplace d'un hébergeur à l'autre sans rien changer ; le VRF borne ce qu'il a le droit de connaître | `hebergeur-exploitation.md` §7 | — |
|
||||
| **D-56** | Le **VNet d'une VM est dérivé** (`index` + zone), jamais déclaré ; l'étiquette VLAN est **vide** en SDN | déclaré, il faisait naître les VM sur `vmbr1` avec un tag — l'ancien monde, à rebrancher une par une | `instancier.py` | P02, P03 |
|
||||
| **D-53** | Le **réseau et l'underlay** de l'hébergeur ont leur **propre dépôt**, séparé de son tenant — **appliqué** : `SITE-Chezlepro` | `underlay.yml` et le cluster décrivent une infrastructure ; le dépôt de tenant décrit une organisation. Les mêler obligeait à trancher qui possède quoi à chaque commit. Le dépôt porte aussi, depuis, le **plan des VM du site** : l'hébergeur n'est pas qu'un porteur de fabric, c'est un exploitant | `hebergeur-exploitation.md` §8 | — |
|
||||
| **D-54** | Le **plan d'administration** est réservé à l'**IPAM, la gestion des équipements et l'OOB** — accès sysadmin | aucune VM, aucun trafic tenant ; c'est la raison d'être des VLAN de transport et de transit, qui l'en sortent. **Adresses révisées le 2026-09-06** : la décision citait `10.0.0.0/24` et « les VLAN 11 et 40 ». D-77 a déplacé ce plan en `10.<index>.0.0/24` (`10.17.0.0/24` chez l'hébergeur de référence, **sans VLAN** — segment physique, aucun pont ne le touche), et D-78 a fait passer le transport VXLAN au **VLAN 50**. Le principe est intact ; seules les adresses ont bougé | `underlay.yml` | P23 |
|
||||
| **D-57** | ~~La route par défaut d'un hyperviseur vit sur `vlan40`, vers la frontière~~ → **NON APPLIQUÉE, et gelée** | L'intention tient : le trafic tenant ne doit pas toucher la carte d'administration. Mais la bascule elle-même **n'a pas été faite et ne doit pas être proposée** : la route par défaut des hyperviseurs reste sur `vmbr0`, vers le routeur du site (`192.168.11.254`), et c'est un état **gelé**. Conséquence assumée, écrite noir sur blanc dans l'`underlay.yml` : ce que ce plan envoie dehors **ne passe pas par la frontière**. Déplacer la route par défaut d'un hyperviseur en service, c'est risquer de perdre l'hyperviseur *et* le chemin pour le réparer | `underlay.yml` | — |
|
||||
| **D-53** | Le **réseau et l'underlay** de l'hébergeur méritent leur **propre dépôt**, séparé de son tenant | `underlay.yml` et le cluster décrivent une infrastructure ; le dépôt de tenant décrit une organisation. Les mêler oblige à trancher qui possède quoi à chaque commit | `hebergeur-exploitation.md` §7 | — |
|
||||
| **D-54** | `10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB** — accès sysadmin | aucune VM, aucun trafic tenant ; c'est la raison d'être des VLAN 11 et 40 | `underlay.yml` | — |
|
||||
| **D-57** | L'interface **sysadmin** d'un hyperviseur (`vmbr0`) n'a **pas de route par défaut** ; celle-ci vit sur `vlan40`, vers la frontière | on n'atteint l'administration que depuis son propre domaine de diffusion — un accès distant doit être ouvert explicitement, il ne peut pas exister par accident. Et le trafic tenant ne touche plus la carte d'administration | `underlay.yml` | — |
|
||||
| **D-58** | Un hôte déclare **par quelle interface** (`via`) chaque réseau lui arrive ; le devis en dérive un **port par interface** et son **type** | un hyperviseur a plusieurs pattes ; les grouper remettait la gestion sur le trunk du transport | `devis_reseau.py` | P23 |
|
||||
| **D-59** | Un VLAN qui ne porte que des **adresses d'hôte** n'a **pas besoin de pont** | un pont sert à brancher des invités ; vide, il coûte une table MAC et un saut de plus sur le lien qui porte tout le trafic tenant | `underlay.yml` | — |
|
||||
| **D-18** | Chaque tenant a un **responsable désigné** | sans lui, « qui peut décider de déménager cette organisation ? » se pose au pire moment | `migration-tenant.md` §3 | — |
|
||||
|
|
|
|||
|
|
@ -12,12 +12,6 @@ groupes:
|
|||
raison: "Les exporters clients doivent etre collectes par Prometheus."
|
||||
surveillance: "Verifier targets Prometheus, scrape duration et erreurs de collecte."
|
||||
|
||||
client_sante:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_icinga
|
||||
raison: "Le rapport de sante depose un resultat passif sur l'API d'Icinga."
|
||||
surveillance: "Verifier que chaque noeud rapporte : un service `sante` EXPIRE vaut un echec."
|
||||
|
||||
client_journal:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_loki
|
||||
|
|
@ -74,83 +68,10 @@ groupes:
|
|||
requiert_groupes_actifs:
|
||||
- serveur_postgresql
|
||||
- serveur_nginx
|
||||
# UNE EXIGENCE PEUT ETRE CONDITIONNELLE (2026-08-22). Depuis que le role sait tenir sa
|
||||
# base dans un fichier, exiger un serveur PostgreSQL est faux pour qui a choisi SQLite
|
||||
# — et bloquait le deploiement d'un ecosysteme parfaitement coherent.
|
||||
sauf_si:
|
||||
serveur_postgresql: { variable: serveur_forgejo_bd, vaut: sqlite }
|
||||
# UTILISE SI PRESENT : Forgejo envoie des notifications quand un MTA existe, et s'en
|
||||
# passe sinon. Ce n'etait pas une EXIGENCE — le confondre avec une exigence obligeait
|
||||
# une forge a deployer une pile courriel pour exister.
|
||||
utilise_si_present:
|
||||
- serveur_postfix
|
||||
raison: "Forgejo depend d'une base (serveur ou fichier) et d'une publication HTTP(S) ; le courriel est un agrement."
|
||||
raison: "Forgejo depend d'une base, d'une publication HTTP(S) et d'un relais courriel (MTA Postfix)."
|
||||
surveillance: "Verifier HTTP(S), base, files Git et envoi courriel."
|
||||
|
||||
serveur_ops:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_forgejo
|
||||
# LE POSTE LIT LE GENOME SUR LA FORGE DE SON PROPRE ECOSYSTEME — c'est ce qui le rend
|
||||
# autonome : il ne redemande rien a son parent. Sans forge, il n'a aucune source.
|
||||
#
|
||||
# Une forge EXTERNE reste possible (un ecosysteme peut lire le genome ailleurs) : il
|
||||
# suffit de surcharger `serveur_ops_forge_url`. L'exigence tombe alors, comme pour
|
||||
# toute exigence conditionnelle du registre.
|
||||
sauf_si:
|
||||
serveur_forgejo: { variable: serveur_ops_forge_externe, vaut: true }
|
||||
# UTILISE SI PRESENT : sans confiance PKI, `git clone` refuse le certificat de la
|
||||
# forge — et il a raison de refuser. Ce n'est pas une exigence du groupe : une forge
|
||||
# a certificat public se cloner sans client_pki.
|
||||
utilise_si_present:
|
||||
- serveur_step_ca
|
||||
raison: "Le poste d'exploitation clone le genome depuis la forge de l'ecosysteme ; sans elle, il n'a pas de source."
|
||||
surveillance: "Verifier que les depots clones suivent leur amont et qu'ansible repond dans le venv."
|
||||
|
||||
client_artefacts:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_artefacts
|
||||
# L'INTEGRATION SUIT L'EXISTENCE DU SERVICE. Un ecosysteme sans source d'artefacts
|
||||
# prend ses paquets a l'amont : c'est un choix valide, pas une panne. Le role se
|
||||
# desactive alors seul (`client_artefacts_actif` derive de l'inventaire) et RETIRE la
|
||||
# direction posee auparavant -- sans quoi les hotes resteraient braques sur une
|
||||
# machine disparue.
|
||||
sauf_si:
|
||||
serveur_artefacts: { variable: client_artefacts_actif, vaut: false }
|
||||
raison: "Un hote ne peut prendre ses paquets chez lui que si l'ecosysteme heberge une source."
|
||||
surveillance: "Verifier que le cache repond sur 3142 et que les hotes le designent bien."
|
||||
|
||||
serveur_artefacts:
|
||||
requiert_groupes_actifs: []
|
||||
raison: "Un cache apt ne depend d'aucun service de l'ecosysteme : il ne fait que relayer et retenir."
|
||||
surveillance: "Verifier l'ecoute sur 3142, le taux de service depuis le journal, et l'espace du cache."
|
||||
|
||||
serveur_ops_tenant:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_ops
|
||||
# LE RUNNER DE TENANT EST ADDITIF, comme celui du site : il suppose le poste
|
||||
# d'exploitation en place, dont il reutilise la racine, l'utilisateur, les depots
|
||||
# clones et le lien `instance`. Seul, il ne ferait que deposer un secret sur une
|
||||
# machine qui n'a pas le plan qu'il ouvre.
|
||||
raison: "Le runner de tenant n'ajoute qu'un pouvoir a un poste d'exploitation existant : sans lui, rien a quoi l'attacher."
|
||||
surveillance: "Verifier que la voute de l'ecosysteme est presente ET CHIFFREE sous son dossier d'inventaire."
|
||||
|
||||
serveur_ops_site:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_ops
|
||||
# LE RUNNER DE SITE EST ADDITIF : il suppose le poste d'exploitation en place, dont il
|
||||
# reutilise la racine, l'utilisateur et les depots clones. Seul, il n'aurait ni carte
|
||||
# de la fabric ni moteur pour agir.
|
||||
raison: "Le runner de site n'ajoute qu'un pouvoir a un poste d'exploitation existant : sans lui, rien a quoi l'attacher."
|
||||
surveillance: "Verifier que la voute du site est presente ET CHIFFREE, et que la carte de la fabric est lisible."
|
||||
|
||||
serveur_resolveur:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_powerdns
|
||||
# Le resolveur prend la zone souveraine en STUB-ZONE : sans autoritatif, il ne saurait
|
||||
# resoudre aucun nom de l'ecosysteme, et sa propre validation echouerait.
|
||||
raison: "Le resolveur du tenant delegue la zone souveraine a l'autoritatif ; sans lui, il ne sait rien de l'ecosysteme."
|
||||
surveillance: "Verifier qu'il repond pour la zone interne ET pour un nom de l'Internet."
|
||||
|
||||
serveur_nextcloud:
|
||||
requiert_groupes_actifs:
|
||||
- serveur_postgresql
|
||||
|
|
|
|||
|
|
@ -16,21 +16,9 @@ make mtu-mesurer # l'invité porte-t-il le MTU de sa zone SDN ?
|
|||
make versions-mesurer # de combien nos épinglages ont-ils vieilli ?
|
||||
```
|
||||
|
||||
**Le patron a été porté sous les services, au monde physique** — mêmes pièces (un playbook
|
||||
qui relève, un script qui compare), même refus d'écrire. Ce document ne traite que la
|
||||
moitié haute ; ces cinq-là existent aussi :
|
||||
|
||||
```
|
||||
make frontiere-plan # les règles de la frontière OPNsense contre leur devis
|
||||
make proxmox-fw-plan # le pare-feu est-ouest de l'hyperviseur contre le registre des flux
|
||||
make sdn-plan # la zone EVPN, ses VNets, et la sortie des VRF
|
||||
make underlay-plan # l'underlay déclaré contre ce que le cluster porte vraiment
|
||||
make placement-plan # chaque VM est-elle là où le plan la met
|
||||
```
|
||||
|
||||
## Le trou qu'il comble
|
||||
|
||||
`scripts/prouver.py` porte 94 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
||||
`scripts/prouver.py` porte 35 preuves. Elles sont toutes **statiques** : elles lisent le
|
||||
dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est
|
||||
cohérent **avec lui-même** — que les handlers existent, que les intrants ont un
|
||||
propriétaire, que rien n'est codé en dur.
|
||||
|
|
@ -227,7 +215,7 @@ Une API est souvent préférable, mais pour une raison précise : elle rend la r
|
|||
l'attendu dans le réel ; si rien ne change, c'est conforme*. Une interface qui n'accepte
|
||||
que des écritures ne peut pas le soutenir.
|
||||
|
||||
**Ces devis sont cette relecture**, faite après coup et par une autre main que celle
|
||||
**Les cinq devis sont cette relecture**, faite après coup et par une autre main que celle
|
||||
qui a écrit. C'est ce qui les distingue d'un déploiement : `make deployer` réconcilie, les
|
||||
devis constatent.
|
||||
|
||||
|
|
@ -254,12 +242,5 @@ La forme correcte, reprise dans les deux devis :
|
|||
Ils ne corrigent pas — c'est `make deployer` qui réconcilie. Ils répondent à l'autre
|
||||
question, et sortent en code 1 s'il y a un écart.
|
||||
|
||||
**Ce paragraphe disait, en dernière ligne du document, que le courriel, la base de données
|
||||
et les expositions web « attendent le même traitement ».** Ils l'ont reçu — ce document
|
||||
décrit leurs trois devis quelques écrans plus haut, et le patron a même été porté sous les
|
||||
services, au monde physique (frontière, pare-feu de l'hyperviseur, SDN, underlay, placement).
|
||||
|
||||
Ce qui reste vraiment hors de leur portée, et qu'aucun ne mesure : la **tenue sous charge**
|
||||
et le **comportement dans la durée**. Un devis dit que le service rend son service à
|
||||
l'instant où on le lui demande, pas qu'il le rendra encore à mille utilisateurs, ni dans six
|
||||
mois.
|
||||
Ils couvrent l'identité et les certificats. Le courriel, la base de données et les
|
||||
expositions web attendent le même traitement ; le patron est là pour être repris.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ que recalculer la cible ; aucune dérive silencieuse.
|
|||
| Donnée | Emplacement | Remarque |
|
||||
| --- | --- | --- |
|
||||
| Empreinte d'un logiciel | `roles/<groupe>/meta/empreinte.yml` | Propriété du logiciel, voyage avec le rôle. Fichier *pur données* (parsable sans Jinja). |
|
||||
| Groupes sans rôle dédié | `EMPREINTES_SANS_ROLE` dans `scripts/inventory_rules.py` | **repli aujourd'hui vide d'effet** : tous les groupes de service ont leur rôle. Voir l'encadré plus bas. |
|
||||
| Groupes sans rôle dédié | `EMPREINTES_SANS_ROLE` dans `scripts/inventory_rules.py` | p. ex. `serveur_web_frontal`, `serveur_web_dorsal`. |
|
||||
| Repli ultime | `EMPREINTE_DEFAUT` | groupe inconnu : empreinte minimale. |
|
||||
| Socle SE | `SOCLE_SE` dans `inventory_rules.py` | coût de base Debian durci. |
|
||||
| Override par hôte | `serveurs.yml` (`coeurs`/`memoire`/`disque`) | déjà supporté par le générateur (`PLACEMENT`). |
|
||||
|
|
@ -65,35 +65,24 @@ la somme. Le socle (`serveur_debian`/`serveur_durci`) et les groupes d'état son
|
|||
5. `playbooks/proxmox/cloner_vm_debian.yml` — passe `cores`/`memory` à `proxmox_kvm`
|
||||
(avec `omit` si absent : aucune régression, on garde alors les specs du template).
|
||||
|
||||
## Les empreintes : ne pas les recopier, les mesurer
|
||||
## Empreintes actuelles
|
||||
|
||||
Ce document a porté jusqu'au 2026-09-06 un tableau de quatorze empreintes recopiées à la
|
||||
main. Il y en a **32** aujourd'hui, et l'une des quatorze avait cessé d'être vraie. Recopier
|
||||
une valeur qui vit ailleurs, c'est s'engager à la suivre — cette page ne s'y engage plus :
|
||||
|
||||
```bash
|
||||
# l'empreinte de chaque logiciel, telle qu'elle est déclarée
|
||||
python3 - <<'EOF'
|
||||
import yaml, pathlib
|
||||
for f in sorted(pathlib.Path('roles').glob('*/meta/empreinte.yml')):
|
||||
e = (yaml.safe_load(f.read_text()) or {}).get('setops_empreinte') or {}
|
||||
print(f"{f.parts[1]:26} {e.get('coeurs')} coeur(s) {e.get('memoire_mo')} Mo {e.get('disque_go')} Go")
|
||||
EOF
|
||||
|
||||
# ce que ça donne pour un hôte donné, une fois sommé et arrondi
|
||||
make hote-afficher HOTE=obs-01
|
||||
```
|
||||
|
||||
Ce qui mérite d'être écrit ici, c'est ce qui **façonne** le calcul et ne se lit pas dans un
|
||||
rôle — le socle, les marges, les paliers, les bornes. Ils sont au §« Règles d'agrégation »
|
||||
ci-dessus, et vivent en tête de `scripts/inventory_rules.py`.
|
||||
|
||||
> **Un repli devenu inutile.** `EMPREINTES_SANS_ROLE` couvrait `serveur_web_frontal` et
|
||||
> `serveur_web_dorsal` du temps où ces groupes n'avaient pas de rôle. Ils en ont un depuis,
|
||||
> avec leur propre `meta/empreinte.yml` — et comme la précédence est *rôle > repli > défaut*,
|
||||
> ces deux entrées ne sont plus jamais lues. Elles disent d'ailleurs autre chose que les
|
||||
> rôles (5 Go contre 10 pour le frontal) : c'est sans effet, mais c'est le genre d'écart
|
||||
> qu'on croit lire comme une vérité.
|
||||
| Rôle | cœurs | RAM (Mo) | disque (Go) |
|
||||
| --- | --- | --- | --- |
|
||||
| serveur_step_ca | 1 | 256 | 2 |
|
||||
| serveur_powerdns | 1 | 512 | 2 |
|
||||
| serveur_nginx | 1 | 512 | 3 |
|
||||
| serveur_openldap | 1 | 512 | 3 |
|
||||
| serveur_keycloak | 2 | 1536 | 5 |
|
||||
| serveur_postgresql | 2 | 2048 | 20 |
|
||||
| serveur_redis | 1 | 512 | 2 |
|
||||
| serveur_forgejo | 1 | 1024 | 20 |
|
||||
| serveur_prometheus | 1 | 1024 | 20 |
|
||||
| serveur_loki | 1 | 1024 | 20 |
|
||||
| serveur_grafana | 1 | 512 | 2 |
|
||||
| serveur_icinga | 2 | 1024 | 10 |
|
||||
| serveur_web_frontal (repli) | 1 | 512 | 5 |
|
||||
| serveur_web_dorsal (repli) | 1 | 1024 | 10 |
|
||||
|
||||
## Ajuster
|
||||
|
||||
|
|
|
|||
|
|
@ -8,42 +8,18 @@ Le service DNS interne est la premiere capacite de plateforme.
|
|||
> `serveur_debian`) genere `/etc/hosts` sur **chaque** VM depuis l'inventaire : tout
|
||||
> l'ecosysteme se resout par nom **meme serveur DNS eteint** (et au bootstrap, avant
|
||||
> que PowerDNS ne soit la). PowerDNS devient une **commodite** (zone, externe,
|
||||
> dynamique), plus un point de defaillance.
|
||||
|
||||
## Trois couches, et une seule est optionnelle
|
||||
|
||||
> **Ce paragraphe decrivait le modele d'avant le 2026-08-24** — un Unbound *sur chaque VM*,
|
||||
> en opt-in. Ce n'est plus le cas : `client_resolveur` **n'installe plus rien**, et son
|
||||
> integration est **universelle**, pas elective.
|
||||
|
||||
```text
|
||||
1. hosts_statiques le PLANCHER : /etc/hosts genere sur chaque VM depuis l'inventaire
|
||||
-> l'ecosysteme se resout DNS eteint. Jamais optionnel.
|
||||
2. serveur_powerdns l'AUTORITATIF de la zone souveraine (un par ecosysteme)
|
||||
3. serveur_resolveur le RECURSIF : UN seul Unbound pour tout le tenant, qui recurse
|
||||
depuis la racine et delegue la zone souveraine a PowerDNS
|
||||
client_resolveur l'integration : ecrit /etc/resolv.conf pour designer ce resolveur
|
||||
```
|
||||
|
||||
**`client_resolveur` n'installe plus de demon** (2026-08-24). Il en posait un par VM — N
|
||||
demons identiques de ~21 Mo pour quelques centaines de requetes. Il ne fait plus qu'une
|
||||
chose : **ecrire `/etc/resolv.conf`**. Son integration est marquee `universelle: true`, et
|
||||
elle n'a **aucune exemption, pas meme l'hote qui porte le resolveur** : il se sert
|
||||
lui-meme. L'ancien nom (`client_unbound`) mentait des lors qu'il n'installait plus Unbound.
|
||||
|
||||
**L'integration suit l'existence du service, elle ne se declare pas.** Aucun
|
||||
`serveur_resolveur` au plan rend `client_resolveur_actif` faux et le role ne touche a rien :
|
||||
l'ecosysteme garde la resolution d'amorcage de cloud-init. C'est un choix valide, pas une
|
||||
panne.
|
||||
> dynamique), plus un point de defaillance. Le resolveur local `client_unbound` est
|
||||
> **optionnel** (opt-in, avec bascule validee) : sans lui, le plancher `/etc/hosts` suffit.
|
||||
|
||||
## Groupes
|
||||
|
||||
```text
|
||||
serveur_powerdns -> autoritatif interne (PowerDNS Authoritative)
|
||||
serveur_resolveur -> LE recursif du tenant (Unbound), un seul
|
||||
client_resolveur -> integration universelle : designe ce resolveur dans /etc/resolv.conf
|
||||
serveur_powerdns -> service DNS central PowerDNS Authoritative
|
||||
client_unbound -> resolveur local optionnel (opt-in, bascule validee)
|
||||
```
|
||||
|
||||
`client_unbound` (résolveur local optionnel) peut viser PowerDNS en stub-zone + récursion.
|
||||
|
||||
## Zone initiale
|
||||
|
||||
La zone initiale est :
|
||||
|
|
@ -55,7 +31,7 @@ exemple.internal
|
|||
Elle est definie dans :
|
||||
|
||||
```text
|
||||
instance/inventories/<inventaire>/group_vars/serveur_powerdns.yml
|
||||
instance/inventories/production/group_vars/serveur_powerdns.yml
|
||||
```
|
||||
|
||||
## Enregistrement automatique
|
||||
|
|
@ -91,42 +67,22 @@ Raison :
|
|||
|
||||
Quand `serveur_postgresql` sera stable, il sera possible de migrer vers un backend SQL si le besoin operationnel le justifie.
|
||||
|
||||
## La bascule de `/etc/resolv.conf` est protegee
|
||||
## Résolveur local (optionnel)
|
||||
|
||||
Le role `client_resolveur` ne bascule `/etc/resolv.conf` qu'apres avoir verifie que le
|
||||
resolveur repond **deja** — pour l'interne *et* pour l'Internet :
|
||||
Le role `client_unbound` (opt-in) installe un résolveur récursif local qui, en stub-zone,
|
||||
délègue les noms internes à PowerDNS et récurse le reste.
|
||||
|
||||
La bascule du resolver local est **protegee** (le role valide qu'Unbound répond AVANT de
|
||||
basculer `/etc/resolv.conf`) :
|
||||
|
||||
```yaml
|
||||
client_resolveur_apply: true
|
||||
client_resolveur_confirm: true
|
||||
client_unbound_apply: true
|
||||
client_unbound_confirm: true
|
||||
```
|
||||
|
||||
Sans ces deux variables, le role prépare Unbound mais ne modifie pas `/etc/resolv.conf`.
|
||||
|
||||
Cette protection est volontaire : une mauvaise configuration DNS peut couper la resolution
|
||||
de noms. C'est la dependance la plus dangereuse du lot — basculer un hote sur un resolveur
|
||||
qui n'est pas encore pret le rend **muet**, et le runner qui devrait reparer tombe avec les
|
||||
autres. Le playbook de groupe applique donc l'hote qui *porte* le resolveur avant ceux qui
|
||||
s'y adressent, et la preuve **P44** refuse tout ecart entre cette declaration et lui.
|
||||
|
||||
## Le piege qui a coute deux jours : la racine signee nie notre TLD
|
||||
|
||||
`internal.` n'est **pas delegue dans la racine**, qui est signee : elle rend donc une preuve
|
||||
NXDOMAIN *validee* pour ce TLD. Or `harden-below-nxdomain` — **actif par defaut** dans
|
||||
Unbound — tient ce « non » pour prouve et repond NXDOMAIN pour **tout** nom sous
|
||||
`internal.` depuis son cache, **sans jamais interroger la `stub-zone`** declaree plus bas.
|
||||
|
||||
La delegation etait correcte. L'autoritatif repondait juste. Pas une requete ne lui
|
||||
parvenait.
|
||||
|
||||
Ce qui declenche l'empoisonnement : n'importe quelle question sur un nom inexistant sous
|
||||
`internal.` — y compris la zone d'**un autre ecosysteme**, que ce resolveur ne sert pas et
|
||||
va donc chercher a la racine. Sur un resolveur partage, ca arrive en permanence.
|
||||
|
||||
Et la panne parait **intermittente** : au redemarrage le cache est vide, tout fonctionne, on
|
||||
conclut que c'est regle. Le remede mesure (2026-09-02) est `harden-below-nxdomain: no` dans
|
||||
`roles/serveur_resolveur/templates/setops.conf.j2` — **pas** `aggressive-nsec`, qui traite
|
||||
un autre symptome.
|
||||
Cette protection est volontaire : une mauvaise configuration DNS peut couper la resolution de noms.
|
||||
|
||||
## Surveillance a prevoir
|
||||
|
||||
|
|
@ -139,103 +95,3 @@ Les premiers checks utiles :
|
|||
- enregistrements des hotes actifs presents ;
|
||||
- serial de zone attendu ;
|
||||
- latence de resolution.
|
||||
|
||||
## Zones publiques
|
||||
|
||||
> Ajouté le 2026-09-16. Le mode `primaire-cache` était **validé par le schéma et consommé par
|
||||
> rien** depuis sa création — *une autorité qu'on s'attribue sans l'exercer est une panne
|
||||
> différée*.
|
||||
|
||||
`plan/domaines.yml` porte un champ `autorite` par domaine. Deux valeurs sont exercées :
|
||||
|
||||
| `autorite` | Ce que ça veut dire | Qui sert la zone |
|
||||
|---|---|---|
|
||||
| `auto-heberge` | zone **interne** (`.internal`), servie à l'écosystème seul | l'instance principale de `serveur_powerdns`, sur la boucle locale |
|
||||
| `primaire-cache` | zone **publique** : le locataire l'écrit, le site la sert | `pdns@public` chez le locataire (primaire caché), `serveur_dns_public` au site (secondaire public) |
|
||||
|
||||
`delegue` est accepté par le validateur et **n'est consommé par aucun rôle**. Il ne faut pas
|
||||
le déclarer en croyant qu'il fait quelque chose.
|
||||
|
||||
**Le locataire écrit, le site sert, un site pair réplique.** Le primaire reste chez le
|
||||
locataire : quand il part, il emporte sa zone. Le détail — et ce que l'épreuve de PowerDNS a
|
||||
appris — est dans `roles/serveur_dns_public/README.md`.
|
||||
|
||||
Trois règles, chacune payée ou mesurée :
|
||||
|
||||
- **TSIG seul ouvre le transfert.** `allow-axfr-ips` et TSIG sont *alternatifs* : une adresse
|
||||
listée obtient la zone sans signature. L'instance publique n'autorise que la boucle locale.
|
||||
- **Une instance à part pour le public.** Faire écouter l'instance principale sur l'adresse
|
||||
de l'hôte aurait permis au serveur public du site d'interroger la zone `.internal`.
|
||||
- **Le serial suit le contenu.** Un serial figé ferait garder au secondaire l'ancienne zone
|
||||
pour toujours.
|
||||
|
||||
**Rien n'est exposé à Internet en phase 1.** P82 refuse d'exposer le serveur public tant
|
||||
que toutes ses zones ne sont pas signées DNSSEC.
|
||||
|
||||
### Ce qu'une zone publique porte
|
||||
|
||||
> Ajouté le 2026-09-16, phase 2. Une zone qui ne portait que ses expositions web aurait coupé
|
||||
> le courriel de production à la minute où le registraire l'aurait suivie.
|
||||
|
||||
Chaque zone `primaire-cache` déclare ses **enregistrements** au plan (`enregistrements:` dans
|
||||
`plan/domaines.yml`) : A, AAAA, CNAME, MX, TXT, CAA. `valider_domaines` refuse une adresse
|
||||
privée, une cible incomplète, un CNAME à l'apex ou à côté d'un autre type, un MX sans
|
||||
priorité, un TXT avec guillemets ou hors ASCII, un nom écrit en absolu (`mx.chezlepro.ca`
|
||||
sous `chezlepro.ca`), et tout enregistrement dans une zone que Set-OPS n'écrit pas.
|
||||
|
||||
Le **SOA et les NS** ne se déclarent pas : ils désignent le serveur de noms du site, sous le
|
||||
nom que **le site** déclare (`dns_public_nom`, contrat du site, reçu par chaque locataire).
|
||||
Seule la zone qui contient ce nom porte son A. Le premier choix, `ns1.<zone>`, aurait déplacé
|
||||
en silence un `ns1.chezlepro.ca` qui existe en production vers une autre adresse.
|
||||
|
||||
**Avant de basculer : `make dns-bascule-devis`.** Il compare le plan au DNS en service, nom
|
||||
par nom et type par type, sur les noms déclarés et une liste de sondes usuelles. Il rend ce
|
||||
qui serait **perdu**, **changé**, **ajouté**, ou **abandonné en connaissance de cause** (les
|
||||
marques `heritage=external-dns`), et les préalables : le serveur de noms doit déjà résoudre
|
||||
publiquement, et il en faut **deux** (exigence du registre `.ca`). Une mesure qui échoue rend
|
||||
« mesure impossible », jamais « absent ». Sa limite est écrite à chaque rapport : sans
|
||||
transfert de zone, un export chez le fournisseur actuel reste la seule preuve d'exhaustivité.
|
||||
|
||||
Au 2026-09-16 : `chezlepro.ca` reproduit la production (rien ne serait perdu) ;
|
||||
`technolibre.ca` est **préparée, pas basculée** — la décision revient au responsable désigné
|
||||
de TechnoLibre, et elle attend que `dns1.chezlepro.ca` soit publié.
|
||||
|
||||
### Signature DNSSEC : le locataire signe, avec la clé de sa voûte
|
||||
|
||||
> Ajouté le 2026-09-16, phase 2. Éprouvé sur les machines réelles avant d'être codé.
|
||||
|
||||
`dnssec: true` sur une zone `primaire-cache` la fait signer **par le primaire du locataire**,
|
||||
avec **une** clé CSK ECDSA P-256 tenue dans **sa** voûte (`vault_dnssec_<zone>`). Le site ne
|
||||
détient aucune clé : il reçoit la zone déjà signée et la sert telle quelle (PowerDNS pose
|
||||
`PRESIGNED` au transfert).
|
||||
|
||||
| Geste | Commande | Écrit ? |
|
||||
|---|---|---|
|
||||
| Créer la clé d'une zone | `python3 scripts/dnssec.py generer <zone>` | la voûte du locataire (copie de sûreté chiffrée, relue avant et après) |
|
||||
| Lire le DS à remettre au registraire | `make dnssec-ds` | rien — calculé depuis la voûte, sans la machine |
|
||||
| Voûte, plan et registre concordent-ils ? | `make dnssec-verifier` | rien |
|
||||
|
||||
Pourquoi la clé ne naît pas sur la machine : une reconstruction depuis zéro signerait avec
|
||||
une **autre** clé, le DS du registraire ne correspondrait plus, et le domaine deviendrait
|
||||
**BOGUS** pour tout résolveur validant, sans qu'aucune machine soit en panne.
|
||||
|
||||
Trois règles, chacune mesurée :
|
||||
|
||||
- **Changer la clé ou retirer la signature pendant qu'un DS est publié est refusé** par l'outil
|
||||
de la machine (`setops-dnssec-zone`) comme par `dnssec.py`. Une mesure du DS qui échoue vaut
|
||||
refus.
|
||||
- **Le serial servi est l'heure (`SOA-EDIT EPOCH`).** PowerDNS renouvelle ses signatures chaque
|
||||
semaine (inception au début de la semaine précédente, expiration deux semaines après le
|
||||
début de la semaine courante). `INCEPTION-EPOCH`, essayé d'abord, ne dépassait notre serial
|
||||
qu'au moment où les signatures du site expiraient. Avec l'heure, le site retire la zone à
|
||||
chaque rafraîchissement ; les signatures servies gardent 7 à 14 jours.
|
||||
- **L'état DNSSEC fait partie du corps de la zone.** Signer ne change aucun enregistrement :
|
||||
sans la ligne `; DNSSEC : …`, le serial n'aurait pas bougé et le site aurait gardé la version
|
||||
non signée.
|
||||
|
||||
La sonde `zones-publiques` du site alerte sur une zone signée servie **nue**, avertit sous
|
||||
6 jours de signature restante, et alerte sous 3.
|
||||
|
||||
**Ordre de la mise en service** : basculer les serveurs de noms (zone signée, sans DS : le
|
||||
domaine reste « non sécurisé », pas cassé), vérifier, **puis** remettre le DS au registraire.
|
||||
Jamais l'inverse.
|
||||
|
|
|
|||
|
|
@ -35,17 +35,13 @@ cohérent.
|
|||
| **Confiance** | Autorité de certification interne (PKI/ACME) : les serveurs se reconnaissent par certificats émis localement, sans acheter de confiance à l'extérieur. |
|
||||
| **Nommage** | DNS interne : des noms de machines stables et dérivables, plutôt que des adresses IP fragiles. |
|
||||
| **Données** | Bases relationnelles et cache, avec un registre des connexions applicatives. |
|
||||
| **Communication** | Service de courriel souverain : boîtes adossées à l'annuaire, réception SMTP, lecture IMAP, antispam et signature DKIM. Le relais des alertes système en est distinct. |
|
||||
| **Communication** | Relais courriel interne pour les alertes, notifications et réinitialisations. |
|
||||
| **Observabilité** | Métriques, journaux centralisés, tableaux de bord et supervision active. |
|
||||
| **Applicatif** | Services internes (forge logicielle, collaboration documentaire) derrière une couche web sécurisée. |
|
||||
|
||||
Tous ces services sont **libres** : OpenLDAP, Keycloak, step-ca, PowerDNS, Unbound,
|
||||
PostgreSQL, Redis, NGINX, Postfix, Dovecot, rspamd, Prometheus, Loki, Grafana, Icinga,
|
||||
Forgejo, Nextcloud, Collabora, restic. Aucun verrou propriétaire, aucune licence captive,
|
||||
**et aucun conteneur** — tout est installé nativement, en paquets et unités systemd.
|
||||
|
||||
*(Cette liste nommait Sendmail jusqu'au 2026-09-06 : ce rôle a été retiré le 2026-07-04,
|
||||
supersédé par Postfix.)*
|
||||
Tous ces services sont **libres** (OpenLDAP, Keycloak, step-ca, PowerDNS, PostgreSQL,
|
||||
Redis, NGINX, Prometheus, Loki, Grafana, Icinga, Forgejo, Sendmail…). Aucun verrou
|
||||
propriétaire, aucune licence captive.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -64,18 +60,12 @@ plan :
|
|||
reconstruit à l'identique depuis le plan et le code. L'infrastructure n'est pas un
|
||||
objet fragile patiemment bricolé — c'est un artefact reproductible.
|
||||
|
||||
> **Honnêteté sur le statut — mise à jour le 2026-09-06.** Ce paragraphe disait, bien après
|
||||
> que ce fut faux, que l'écosystème n'avait « pas encore été éprouvé sur des machines
|
||||
> réelles ». Il l'a été : la flotte entière a été **rasée et remontée depuis zéro** le
|
||||
> 2026-08-13, puis **deux fois le 2026-09-02** — 15/15 puis 14/14 machines, **zéro échec**,
|
||||
> et la validation complète à zéro échec sur treize hôtes. La reconstruction n'est donc pas
|
||||
> une promesse de conception : c'est une manœuvre exécutée, chronométrée et rejouée.
|
||||
>
|
||||
> Ce qui reste honnête à dire : la reconstruction prouve qu'un plan mène des machines nues à
|
||||
> l'état voulu. Elle ne dit rien de la tenue d'un service **sous charge**, ni de sa mise à
|
||||
> jour dans la durée — deux questions distinctes, et non traitées ici. Le degré de maturité,
|
||||
> service par service, est tenu à jour dans `docs/catalogue-services.md`, et nous ne
|
||||
> présentons jamais comme « en production » un service que ce tableau ne donne pas pour tel.
|
||||
> **Honnêteté sur le statut.** Une grande partie de l'écosystème est aujourd'hui
|
||||
> *définie, codée et validée* (vérification de syntaxe, analyse statique) mais n'a pas
|
||||
> encore été éprouvée sur des machines de production réelles. Le socle Debian durci et
|
||||
> son modèle sont la fondation établie ; les services applicatifs sont prêts en tant
|
||||
> que code et se déploient progressivement. Nous ne présentons jamais un service non
|
||||
> déployé comme « en production ».
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -110,8 +100,7 @@ Une trentaine de paramètres noyau resserrent le comportement du système et du
|
|||
|
||||
### 4. Pare-feu prêt, activé au bon moment
|
||||
|
||||
- **nftables** est **volontairement désactivé dans le modèle** — on n'active jamais un pare-feu générique sans connaître la fonction réelle de la machine — et **armé sur chaque serveur de la flotte**, en politique « tout refuser » par défaut.
|
||||
- **Ses règles ne sont pas écrites à la main.** Chaque rôle déclare ce qu'il écoute et ce à quoi il se connecte ; le jeu de règles en est **dérivé**. Le même registre alimente le pare-feu de l'hyperviseur et la frontière du site : trois couches de filtrage qui ne *peuvent pas* se contredire, parce qu'elles descendent d'une seule décision. C'est aussi ce qui produit la **matrice d'accès réseau** exigée par un audit de sécurité — source, destination, port, chiffrement et *raison*, pour chaque flux autorisé.
|
||||
- **nftables** est installé et préparé sur le socle, mais **volontairement désactivé dans le modèle**. Il est activé sur les serveurs finaux avec des règles adaptées à leur rôle (politique par défaut « tout refuser » en entrée et en transit). Principe : on n'active jamais un pare-feu générique sans connaître la fonction réelle de la machine, pour ne pas couper l'accès par accident.
|
||||
|
||||
### 5. Confinement et intégrité
|
||||
|
||||
|
|
@ -145,8 +134,6 @@ Au-delà des réglages machine, l'architecture elle-même est une mesure de séc
|
|||
- **Certificats internes** : les services se font confiance via la PKI interne, sans exposer de secrets à des autorités externes.
|
||||
- **Dépendances déclarées** : un service ne se déploie pas si ses prérequis (DNS, PKI…) ne sont pas réellement actifs — ce qui évite les états incohérents.
|
||||
- **Source unique de vérité** : l'état voulu vit dans des registres lisibles, versionnés par Git, qui sert de filet en cas d'erreur.
|
||||
- **Les sauvegardes sortent du site.** L'état non régénérable (clés de l'autorité, annuaire, bases, boîtes, forge) est sauvegardé **chiffré côté client** vers un dépôt hors-machine, puis emporté **hors du bâtiment**. Chaque nœud vérifie lui-même son propre dépôt distant : celui qui héberge les octets ne peut pas les lire, donc ne peut pas les juger.
|
||||
- **Ce qu'on affirme, on le prouve.** Un harnais de **57 vérifications** rejoue à la demande ce que le dépôt promet et produit une pièce justificative datée ; dix « devis » interrogent en plus le système *déployé* pour confirmer qu'il ressemble à ce qui est déclaré.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,269 +0,0 @@
|
|||
# Filiation, mutualisation, émancipation
|
||||
|
||||
> **Pour qui :** l'**architecte** de la lignée et l'**hébergeur** qui abrite des
|
||||
> écosystèmes tiers. Décrit les trois âges d'un écosystème et ce qu'ils impliquent au plan.
|
||||
|
||||
## Le constat
|
||||
|
||||
Un écosystème ne naît pas autoportant. Il lui faut une forge pour lire son génome, une
|
||||
source d'artefacts pour ses paquets, un dépôt pour ses sauvegardes — et rien de tout cela
|
||||
n'existe la première seconde. Il emprunte donc à son hôte.
|
||||
|
||||
Jusqu'ici, le moteur a rencontré ce besoin **trois fois sans le nommer** :
|
||||
|
||||
```
|
||||
client_artefacts_actif dérivé : « une source existe-t-elle chez moi ? »
|
||||
serveur_ops_forge_externe « je lis mon génome ailleurs »
|
||||
client_backup_cible « je sauvegarde chez le site » — mutualisé, en production
|
||||
```
|
||||
|
||||
Trois astuces, une seule notion. Ce document la déclare.
|
||||
|
||||
## Les trois âges
|
||||
|
||||
```
|
||||
FILIATION l'enfant emprunte à son parent ce qu'il ne porte pas encore
|
||||
MUTUALISATION un état DURABLE et CHOISI — on emprunte parce qu'on le veut
|
||||
ÉMANCIPATION l'écosystème porte tout lui-même
|
||||
```
|
||||
|
||||
**L'émancipation n'est pas une obligation.** Un petit organisme peut rester mutualisé
|
||||
toute sa vie ; ce qui compte est qu'il *puisse* s'émanciper, et que la sortie ne soit ni
|
||||
un piège ni une reconstruction. C'est la différence entre un locataire et un captif.
|
||||
|
||||
## Ce que ça veut dire pour les modèles
|
||||
|
||||
Un modèle sans forge n'est pas un modèle amputé : c'est un écosystème **au premier âge**,
|
||||
dont le runner lit le génome chez son hôte. L'ajout d'une forge n'est pas une correction,
|
||||
c'est une **émancipation**.
|
||||
|
||||
```
|
||||
premier âge serveur_ops_forge_externe: true + l'adresse de la forge de l'hôte
|
||||
émancipé serveur_ops_forge_externe: false + sa propre serveur_forgejo au plan
|
||||
```
|
||||
|
||||
## Ce que ça veut dire pour l'hébergeur
|
||||
|
||||
L'hébergeur ne vend plus seulement des machines : il vend **l'abri pendant la jeunesse**.
|
||||
Chaque service mutualisé est une ligne de facture, et chaque émancipation est une décision
|
||||
du client — jamais une rupture technique.
|
||||
|
||||
Pour une clientèle d'OBNL et de coopératives, c'est le bon geste : on entre à bas coût, on
|
||||
grandit vers l'autonomie, et **on n'est jamais captif**, puisque le génome est déjà chez
|
||||
soi dès le premier jour.
|
||||
|
||||
## Ce qui est mutualisable, et ce qui ne l'est pas
|
||||
|
||||
> **Le critère, à deux tranchants.** Un rôle appartient au SITE s'il sert la **relation**
|
||||
> avec les locataires, ou les machines du site elles-mêmes. Il appartient au TENANT s'il
|
||||
> sert des **utilisateurs** ou des **applications**. À l'usage, ça se décide sur deux
|
||||
> questions plus dures :
|
||||
>
|
||||
> 1. **Ce que le site ne doit pas pouvoir VOIR** — sinon l'hébergement n'est plus
|
||||
> souverain, quelles que soient les intentions.
|
||||
> 2. **Ce que le tenant doit pouvoir EMPORTER** — sinon l'émancipation est un mot.
|
||||
|
||||
| service | mutualisable | remarque |
|
||||
|---|---|---|
|
||||
| forge (génome) | oui | `serveur_ops_forge_externe` |
|
||||
| source d'artefacts | oui | `client_artefacts` se désactive seul si aucune n'existe |
|
||||
| sauvegarde | oui | `client_backup_cible` pointe déjà hors de l'écosystème ; le site héberge du **chiffré côté client** et ne peut rien en juger |
|
||||
| **disponibilité** (est-ce debout ?) | oui | c'est sa fabric : il doit savoir qu'une VM est tombée |
|
||||
| **métriques** (charge, disque) | oui, avec réserve | révèlent des **rythmes d'activité**, pas du contenu |
|
||||
| **journaux** | **non** | voir ci-dessous — plus intime que l'annuaire |
|
||||
| relais courriel | oui | l'enveloppe transite ; le contenu appartient au locataire |
|
||||
| DNS | partiellement | un sous-domaine délégué, pas la zone entière |
|
||||
| **PKI** | **non** (tranché le 2026-09-10) | voir ci-dessous |
|
||||
| **identité** (LDAP/SSO) | le plus intime | mutualiser l'annuaire, c'est confier ses gens |
|
||||
| **la voûte** | **jamais** | les secrets ne se mutualisent pas, à aucun âge |
|
||||
|
||||
### Pourquoi « observabilité » a été découpée en trois
|
||||
|
||||
La ligne disait : *« observabilité | oui | l'hébergeur surveille ses locataires »*. Elle
|
||||
était trop grossière, et elle **contredisait ce qui tourne** : chaque écosystème a son
|
||||
propre Icinga, et celui du site ne voit que ses sept machines (mesuré le 2026-09-10).
|
||||
|
||||
Surtout, elle autorisait en une case ce que la ligne du dessous interdit. **Les journaux
|
||||
contiennent du contenu** — un mot de passe dans un message d'erreur, une donnée métier dans
|
||||
une trace, qui a fait quoi et quand. Un hébergeur qui ingère les journaux de son locataire
|
||||
en sait **plus** que s'il détenait son annuaire : l'annuaire dit qui existe, les journaux
|
||||
disent ce qu'ils font.
|
||||
|
||||
La disponibilité, elle, est légitime : une VM tombée est un fait de la **fabric**, que
|
||||
l'hébergeur porte. Les métriques sont entre les deux — elles ne disent pas *quoi*, mais
|
||||
elles disent *quand* et *combien*, ce qui suffit à lire l'activité d'une organisation.
|
||||
|
||||
### Pourquoi la ligne PKI est tranchée : **non**
|
||||
|
||||
Elle disait « à trancher », en notant qu'*une AC intermédiaire signée par l'hôte est
|
||||
possible*. Elle l'est techniquement — et c'est précisément ce qu'il ne faut pas faire.
|
||||
|
||||
Une intermédiaire signée par l'hôte lui donne le pouvoir d'**émettre des certificats
|
||||
valides pour les noms du locataire**. Il peut alors se présenter comme n'importe lequel de
|
||||
ses services, y compris devant les propres machines du locataire, qui les accepteront sans
|
||||
sourciller — c'est exactement ce que la chaîne de confiance leur demande de faire.
|
||||
|
||||
C'est le même pouvoir que l'annuaire, sous une forme **moins visible** : rien n'apparaît
|
||||
dans la configuration du locataire, aucune trace côté locataire, et la vérification passe.
|
||||
|
||||
**Une PKI par écosystème, jamais dérivée de l'hôte.** C'est déjà ce qui tourne ; la ligne
|
||||
ne fait que cesser de laisser la porte entrouverte.
|
||||
|
||||
### Le revers : le site n'a pas d'observabilité du tout
|
||||
|
||||
Mesure du 2026-09-10 : le site ne porte **ni `client_journal` ni `client_metrique`**, et
|
||||
aucun `loki` ni `prometheus`. Ses sept machines n'expédient rien nulle part — l'hébergeur
|
||||
ne peut pas lire ses propres journaux.
|
||||
|
||||
C'est la conséquence directe de la frontière : à refuser de voir ceux des locataires, il
|
||||
s'est privé des siens. Le remède n'est pas d'assouplir la règle, c'est de lui donner **sa
|
||||
propre pile** — un `loki` et un `prometheus` du site, pour les machines du site, sans
|
||||
aucun lien avec ceux d'un tenant.
|
||||
|
||||
## L'insémination — le premier lien, et le seul que le SITE ait le droit d'avoir
|
||||
|
||||
Entre tenants, le zéro-confiance est le défaut : la frontière refuse tout ce qui n'est pas
|
||||
déclaré, et c'est cette règle qui rend l'hébergement mutualisé défendable. **La parenté est
|
||||
la seule exception, et elle est asymétrique** : un écosystème neuf ne peut pas s'amorcer
|
||||
lui-même. Quelqu'un doit poser sa première machine et lui donner de quoi continuer.
|
||||
|
||||
Ce geste s'appelle l'**insémination** :
|
||||
|
||||
```
|
||||
le SITE matérialise VNets, VM, routes, flux — le terrain
|
||||
le SITE amorce ops-01 la première machine, et elle seule
|
||||
le TENANT achève ses autres machines, ses rôles, ses intégrations
|
||||
```
|
||||
|
||||
**Le partage d'intelligence qui le rend possible.** Les deux runners portent le même
|
||||
moteur ; ils n'en consomment pas la même face :
|
||||
|
||||
| | ce qu'il lit du moteur | ce qu'il en fait |
|
||||
|---|---|---|
|
||||
| runner du **SITE** | `roles/*/meta/flux.yml` — la face **réseau** des rôles | SDN, pare-feu Proxmox, frontière OPNsense |
|
||||
| runner du **TENANT** | `tasks/`, `templates/`, `meta/integration.yml` — la face **machine** | configure ses services |
|
||||
|
||||
C'est ce partage qui permet au SITE de poser les bonnes règles pour un tenant **sans jamais
|
||||
lire sa configuration ni détenir sa voûte**. Il sait de quoi les rôles ont besoin sur le
|
||||
fil ; il ignore ce qu'ils font sur la machine.
|
||||
|
||||
**Ce que le lien doit être, pour ne pas devenir un pouvoir permanent :**
|
||||
|
||||
- **étroit** — vers le seul `ops-01` du tenant, jamais vers ses autres machines ;
|
||||
- **déclaré** — dans `meta/flux.yml`, comme tout le reste, pour que la frontière et
|
||||
`nftables` le connaissent au lieu de le subir ;
|
||||
- **borné par un acte**, et cet acte appartient à un humain (voir ci-dessous).
|
||||
|
||||
> **Ce qui existe déjà sans être déclaré, au 2026-08-28.** `make creer-vm` exige
|
||||
> `_instance-requise` : pour matérialiser les VM d'un tenant, le runner du SITE doit
|
||||
> basculer son symlink `instance` sur le dépôt de ce tenant — alors que son plan pose
|
||||
> `serveur_ops_instance: ""` et que la doctrine dit qu'un SITE n'a pas de plan monté par ce
|
||||
> lien. Le couplage est donc **déjà là**, et rien ne borne sur quels tenants il porte ni
|
||||
> jusqu'à quand. Le déclarer, c'est le rendre limitable ; le taire ne l'a jamais empêché.
|
||||
|
||||
## Ce que l'émancipation exige — et de qui
|
||||
|
||||
Basculer une déclaration ne suffit pas. Mais l'erreur symétrique est plus tentante encore :
|
||||
faire de l'émancipation un **automatisme**, que la machine déclencherait dès qu'elle
|
||||
constate que le tenant se reproduit sans son parent.
|
||||
|
||||
**L'émancipation exige toujours la gouverne d'un humain.** Le fruit de l'émancipation est
|
||||
livré à quelqu'un — un client qui reprend ses clés, sa forge, son écosystème. Sans
|
||||
quelqu'un pour le recevoir, il n'y a pas de livraison : seulement un lien qui tombe. *Une
|
||||
émancipation qui se déclencherait toute seule ne serait pas une émancipation, ce serait une
|
||||
expulsion.*
|
||||
|
||||
C'est déjà la forme des gestes lourds de ce dépôt : `raser` exige `CONFIRMER=true` et le
|
||||
nom de l'instance ; le mot de passe de la voûte se tape à l'exécution — « le seul objet que
|
||||
la reproduction exige d'un humain ». **L'émancipation est le deuxième objet de cette
|
||||
liste.**
|
||||
|
||||
D'où la répartition, qui ne se confond pas :
|
||||
|
||||
| | qui l'exécute |
|
||||
|---|---|
|
||||
| **le lien de filiation** — déclaré, étroit, visible au plan | le moteur |
|
||||
| **le constat d'aptitude** — « ce tenant se reproduit sans son parent » | la machine — elle **instruit**, elle n'agit pas |
|
||||
| **l'acte d'émancipation** — retirer le lien, remettre les clés | **l'humain**, garde `CONFIRMER=true` |
|
||||
| **la preuve que le lien est coupé** | la machine, **après** l'acte |
|
||||
|
||||
La quatrième ligne est celle qu'on oublie, et c'est elle qui décide si les trois autres ont
|
||||
servi. Sans elle, on croirait s'être émancipé en restant dépendant sans le savoir —
|
||||
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 — `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
|
||||
|
||||
Tout service mutualisable doit se déclarer de la même façon, pour qu'un exploitant lise
|
||||
l'état de son écosystème d'un coup d'œil plutôt qu'en fouillant chaque rôle :
|
||||
|
||||
```yaml
|
||||
<service>_externe: true # je l'emprunte
|
||||
<service>_amont: "https://…" # à qui
|
||||
```
|
||||
|
||||
`false` — ou l'absence du couple — signifie *chez moi*. Le rôle **refuse** de démarrer si
|
||||
le drapeau est levé sans que l'amont soit nommé : emprunter sans dire à qui, c'est ne rien
|
||||
déclarer du tout.
|
||||
|
||||
## Qui est le parent ? — tranché le 2026-08-31 (D-82)
|
||||
|
||||
**Personne. La forge du SITE fait autorité pour le génome** (D-81) ; toute autre copie est
|
||||
un miroir. Le dépôt l'avait suivi avant de le déclarer : `serveur_forge_site`, puis
|
||||
`serveur_cache_site`, puis `serveur_resolveur_site` — trois services prêtés par le site à
|
||||
ses locataires, un seul patron.
|
||||
|
||||
**Par où le génome arrive.** Le poste porte les commits en bundle au runner du site
|
||||
(`make genome-pousser`), qui les pousse sur sa forge. Aucune autre forge n'est sur ce
|
||||
chemin. `eregion` (`forge.alliance-boreale.ca`) est une forge **héritée** : la porte
|
||||
publique des contributions, qui ne fera jamais partie de Set-OPS. La redondance ne vient
|
||||
pas d'elle mais du nombre de sites — une forge par site, chacune inséminée du génome.
|
||||
|
||||
## État — revu le 2026-09-06
|
||||
|
||||
Seule la forge porte cette déclaration complète (`serveur_ops`). Les autres services
|
||||
mutualisés le sont par des mécanismes antérieurs, cohérents mais non uniformes. Les
|
||||
aligner sur la forme ci-dessus reste à faire.
|
||||
|
||||
Des trois manques que cette section listait au 2026-08-28, **deux sont comblés** :
|
||||
|
||||
| Manque du 2026-08-28 | Aujourd'hui |
|
||||
|---|---|
|
||||
| l'**instrument de preuve** de la dernière ligne — « le lien est coupé » — n'existe pas | **`make emancipation-prouver`**, depuis le 2026-09-01 (§ ci-dessus). Il *coupe* au lieu de sonder, et rend les deux verdicts. |
|
||||
| la séparation des voûtes est **organisationnelle, pas cryptographique** — un seul mot de passe ouvre celle du site et celles des tenants | **Séparé le 2026-08-28 même** : une voûte, **une clé** (`~/.config/setops-vault-<dépôt>`, `scripts/voutes.py`). L'ancien mot de passe unique n'ouvre plus rien. La séparation précédait nécessairement la distribution des clés aux runners : sinon, poser « la » clé sur le plus petit locataire lui donnait les secrets de l'hébergeur. |
|
||||
| le **lien d'insémination** n'est pas déclaré | **Toujours vrai.** Il existe, par le symlink `instance` du runner du SITE, sans borne ni visibilité. C'est le manque qui reste. |
|
||||
|
||||
*(Le paragraphe sur les voûtes se contredisait avec `scripts/voutes.py` — daté du même jour —
|
||||
et celui sur l'instrument avec la section qui le décrit, deux écrans plus haut. Un document
|
||||
qui se contredit lui-même à cette distance n'est plus lu comme une référence.)*
|
||||
|
|
@ -38,26 +38,9 @@ flux:
|
|||
| `externe` | hors flotte (frontière publique — géré à l'OPNsense, pas dans le nœud) |
|
||||
| `localhost` | boucle locale — aucune règle inter-nœud (nftables autorise `lo`) |
|
||||
| `expositions` | dérivé des `expose:` des applications (cas de l'edge → backends) |
|
||||
| `admin` | les **réseaux** d'administration (intrant `nftables_admin_ssh`) — l'exploitant n'est ni la flotte, ni l'Internet |
|
||||
| `voisins_site` | les **autres tenants fédérés** de la même fabric (leurs supernets) — le voisinage, quatrième chemin d'arrivée |
|
||||
| `fabric` | le **matériel de l'hébergeur** : hyperviseurs, frontière, commutateurs |
|
||||
| `runner_site` | le **runner du site**, seul autorisé à matérialiser des VM |
|
||||
| `derive` | résolu ailleurs que par le nœud (hors périmètre de sa règle) |
|
||||
|
||||
La résolution `pair → IP` réutilise le **plan** (registre IP/FQDN/zones) déjà en place.
|
||||
|
||||
> **Quatre de ces mots sont nés d'un piège, et pas d'un besoin de vocabulaire.**
|
||||
> `voisins_site` (2026-08-24) : sans lui, le chaînage des caches d'artefacts n'aurait pu se
|
||||
> déclarer qu'en `ingress` + `externe` — ce qui aurait **publié le cache à l'Internet
|
||||
> entier**. `fabric` (2026-08-25) : le runner de site déclarait son API Proxmox en
|
||||
> `externe`, or « externe » se rend par « tout sauf les espaces privés » et les hyperviseurs
|
||||
> *sont* en RFC 1918 — la règle avait l'air d'ouvrir le flux et l'excluait. Un flux qui a
|
||||
> l'air ouvert et qui ne l'est pas est pire qu'un flux fermé : il ne se cherche pas.
|
||||
>
|
||||
> Les cinq derniers ne rendent **pas des hôtes** : `admin`, `voisins_site`, `fabric` et
|
||||
> `runner_site` rendent des CIDR ou des sources extérieures à l'écosystème, `derive` ne rend
|
||||
> rien. Ils sont donc traités à part dans `scripts/resoudre_flux.py`.
|
||||
|
||||
## Génération
|
||||
Un **résolveur** (miroir de `instancier`) agrège, par serveur, les `flux.yml` de tous ses rôles
|
||||
(services + intégrations), résout les `pair`, et produit :
|
||||
|
|
@ -67,26 +50,14 @@ Un **résolveur** (miroir de `instancier`) agrège, par serveur, les `flux.yml`
|
|||
|
||||
## Activation prudente
|
||||
Activer nftables = **action destructive** (peut couper l'accès) → confirmation explicite +
|
||||
déploiement graduel (garder l'accès SSH/Ansible, tester par nœud).
|
||||
déploiement graduel (garder l'accès SSH/Ansible, tester par nœud). nftables reste *préparé mais
|
||||
non activé* tant que le registre n'est pas complet et validé.
|
||||
|
||||
> **Le pare-feu est armé sur la flotte depuis.** Ce paragraphe disait « nftables reste
|
||||
> *préparé mais non activé* tant que le registre n'est pas complet et validé ». Le registre
|
||||
> **est** complet — **P49** vérifie qu'il reproduit exactement ce que les `meta/flux.yml`
|
||||
> déclarent — et `nftables_baseline_enabled` vaut `true` pour `hotes_actifs` dans le plan.
|
||||
> Le rôle, lui, garde `false` par défaut : le pare-feu n'est pas une propriété du rôle,
|
||||
> c'est une décision de l'instance. Le gabarit doré reste à `false`, et c'est voulu.
|
||||
|
||||
## Séquence — franchie
|
||||
## Séquence
|
||||
1. ✅ figer le schéma (ce doc) + **piloter** sur postgresql / client_metrique / nginx ;
|
||||
2. ✅ le **résolveur** (`scripts/resoudre_flux.py`) — agrégation → règles + registre ;
|
||||
3. ✅ **remplir** tous les rôles (transcription du travail zéro-confiance) ;
|
||||
4. ✅ **générer** + registre d'audit (`docs/registre-flux.md`, gardé par **P49**) ; puis
|
||||
**activer** nftables nœud par nœud — fait.
|
||||
|
||||
Ce qui a suivi la séquence n'y était pas prévu : le même registre alimente désormais aussi
|
||||
le **pare-feu est-ouest de l'hyperviseur** (`make proxmox-fw-plan`, garde **P45**) et la
|
||||
**frontière nord/sud OPNsense** (`make frontiere-plan`). Trois couches, une seule décision —
|
||||
elles ne peuvent pas se contredire parce qu'elles dérivent de la même source.
|
||||
2. le **résolveur** (agrégation → règles + registre) ;
|
||||
3. **remplir** tous les rôles (large transcription du travail zéro-confiance déjà fait) ;
|
||||
4. **générer** + registre d'audit ; puis **activer** nftables nœud par nœud.
|
||||
|
||||
## `poste: false` — un service publié qui ne s'adresse pas à un humain
|
||||
|
||||
|
|
@ -122,9 +93,8 @@ Le registre confondait deux situations :
|
|||
| le rôle **ouvre** l'écoute | `serveur_dovecot` lie 12345 (SASL réseau) | absent |
|
||||
| le rôle **décrit** celle d'un autre | `serveur_backup` emprunte le sshd de `serveur_debian` | `true` |
|
||||
|
||||
Sans cette distinction, la seule co-location légitime de la flotte — `tcp/22` sur le
|
||||
dépôt de sauvegarde, `site-backup-01` depuis que les écosystèmes déposent chez leur
|
||||
hébergeur — serait signalée à tort. Une preuve qui crie sur un cas sain finit par être
|
||||
Sans cette distinction, la seule co-location légitime de la flotte — `tcp/22` sur
|
||||
`backup-01` — serait signalée à tort. Une preuve qui crie sur un cas sain finit par être
|
||||
ignorée, ce qui est pire que de ne pas l'avoir.
|
||||
|
||||
**Corollaire à retenir** : un port qu'on **subit** (le défaut amont d'un logiciel) doit être
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ mais elle décide de l'emplacement de chaque chose :
|
|||
|---|---|---|
|
||||
| plan des services, inventaire | le **tenant** | `OPS-<tenant>` |
|
||||
| `underlay.yml` (fabric physique) | l'**hébergeur** | `OPS-<hébergeur>`, monté par symlink |
|
||||
| `opnsense.yml` (frontière) | l'**hébergeur** | idem — **une** frontière pour tous ses tenants |
|
||||
| `group_vars/opnsense.yml` (frontière) | l'**hébergeur** | idem — **une** frontière pour tous ses tenants |
|
||||
|
||||
Conséquence pratique : `make instance-utiliser` bascule le **tenant** actif, jamais
|
||||
l'hébergeur. Le devis lit donc la fabric et les intrants de frontière chez l'hébergeur, quel
|
||||
|
|
@ -84,7 +84,7 @@ silence : chemin présent, politique absente, exactement le mode de panne du 202
|
|||
|
||||
Les alias d'hôtes sont préfixés du tenant (`SETOPS_CHEZ17_SERVEUR_NGINX`), et surtout
|
||||
**chaque tenant a son propre alias d'administration** : `SETOPS_ADMIN_CHEZ17` n'ouvre que
|
||||
`10.17.0.0/16`. Une union aurait laissé le plan de gestion d'un tenant entrer chez le voisin
|
||||
`10.27.0.0/16`. Une union aurait laissé le plan de gestion d'un tenant entrer chez le voisin
|
||||
— ce que les ACL de switch interdisent par ailleurs. La bordure ne doit pas rouvrir ce que
|
||||
l'isolation inter-tenant ferme.
|
||||
|
||||
|
|
@ -126,9 +126,7 @@ règle) et un tenant dont `nftables_admin_ssh` est vide (règle SSH omise — l'
|
|||
exposerait le SSH à Internet).
|
||||
|
||||
Le registre des flux distingue les pairs par **mot-clé**. Or `resoudre_flux.py` **saute
|
||||
volontairement** le pair `externe` (fonction de résolution des pairs, aux côtés de
|
||||
`localhost`, `expositions` et `derive` — chercher le nom, pas un numéro de ligne : celui
|
||||
qui figurait ici avait vieilli de quarante-sept lignes) : ces flux-là ne
|
||||
volontairement** le pair `externe` (`scripts/resoudre_flux.py:184`) : ces flux-là ne
|
||||
concernent pas le pare-feu d'hôte, ils relèvent de la bordure. Plusieurs `raison` le disent
|
||||
déjà noir sur blanc — « Frontière publique gérée à l'OPNsense ».
|
||||
|
||||
|
|
@ -167,9 +165,9 @@ alors **deux règles et deux alias**, chacun ne portant que les sources qui peuv
|
|||
emprunter ce chemin.
|
||||
|
||||
```
|
||||
SETOPS_ADMIN_CHEZ17_GESTION 10.17.0.0/24 → pass in on lan
|
||||
SETOPS_ADMIN_TECH23_GESTION 10.17.0.0/24 → pass in on lan
|
||||
SETOPS_ADMIN_TECH23_WAN 192.168.255.2/32, 192.168.254.2/32 → pass in on wan
|
||||
SETOPS_ADMIN_CHEZ17_GESTION 10.0.0.0/24 → pass in on lan
|
||||
SETOPS_ADMIN_TECH11_GESTION 10.0.0.0/24 → pass in on lan
|
||||
SETOPS_ADMIN_TECH11_WAN 192.168.255.2/32, 192.168.254.2/32 → pass in on wan
|
||||
```
|
||||
|
||||
**Le piège de la case à cocher.** Une source **RFC1918** qui arrive par le WAN se heurte à
|
||||
|
|
@ -181,13 +179,13 @@ entre par la gestion affaiblirait l'interface publique sans rien ouvrir du tout.
|
|||
|
||||
**Invariant du dernier octet.** Un point de routage porte **le même dernier octet sur tous
|
||||
les sous-réseaux où il participe** — on retient une adresse, pas treize. `le commutateur` est
|
||||
donc `.1` partout : `10.17.16.1`, `10.17.21.1`… Le chiffre n'est pas codé en dur,
|
||||
donc `.1` partout : `10.0.0.1`, `10.27.16.1`, `10.27.21.1`… Le chiffre n'est pas codé en dur,
|
||||
il vient de `reservations.passerelle` dans la nomenclature, et `make underlay` (**preuve
|
||||
P23**) refuse une passerelle qui s'en écarte.
|
||||
|
||||
Seule exception, assumée : le **lien de transit**, dont l'adressage est dicté par ses
|
||||
participants — les deux frontières occupent `.1` et `.2`, les hyperviseurs `.41`, `.43` et
|
||||
`.47`.
|
||||
Seule exception, assumée : les liens plus étroits qu'un `/24`. Sur le `/29` de transit,
|
||||
l'adressage est dicté par les participants du lien — les deux frontières occupent `.1` et
|
||||
`.2`, le switch prend `.6`.
|
||||
|
||||
## 5. La garde anti-lockout
|
||||
|
||||
|
|
@ -212,7 +210,7 @@ peut dériver d'aucun `index`. Sa place est l'underlay, cluster-global, au même
|
|||
management, l'iSCSI et Ceph.
|
||||
|
||||
**Une route par sous-réseau attribué, jamais une par supernet** (2026-08-09). Router
|
||||
`10.17.0.0/16` faisait porter à la frontière des destinations qui n'existent nulle part :
|
||||
`10.27.0.0/16` faisait porter à la frontière des destinations qui n'existent nulle part :
|
||||
elles atteignaient le nœud de sortie, y arrivaient dans la table *principale* — le VRF n'est
|
||||
atteint que par les `/24` annoncés en BGP — et repartaient vers la passerelle
|
||||
d'administration. Les alias `SETOPS_TENANT_*` énumèrent exactement les mêmes `/24`, et
|
||||
|
|
@ -230,24 +228,20 @@ sur ce lien :
|
|||
```yaml
|
||||
- nom: transit-frontiere
|
||||
vlan: 40
|
||||
sous_reseau: 10.0.4.0/24
|
||||
sous_reseau: 10.0.4.0/29
|
||||
passerelle: 10.0.4.6 # SVI du switch L3 (le commutateur)
|
||||
passerelle_sortie: 10.0.4.1 # bifrost-1 = sortie par défaut de la flotte
|
||||
```
|
||||
|
||||
**Le lien est passé d'un `/29` à un `/24`, et ce n'est pas cosmétique.** En SDN, les
|
||||
**hyperviseurs** sont eux-mêmes sur ce lien — ce sont eux les nœuds de sortie des VRF, ce
|
||||
n'était plus le SVI d'un commutateur. Huit adresses n'y suffisaient plus.
|
||||
**Plan du `/29`.** Les frontières occupent le bas de la plage, le SVI du switch le haut :
|
||||
|
||||
| Adresse | Qui |
|
||||
|---|---|
|
||||
| `10.0.4.1` | `bifrost-1` — frontière active, sortie par défaut de la flotte |
|
||||
| `10.0.4.2` | `bifrost-2` — seconde frontière |
|
||||
| `10.0.4.3` | libre, réservée à une IP virtuelle CARP si les deux passent en HA |
|
||||
| `10.0.4.41 / .43 / .47` | `asgard`, `gandalf`, `vishnu` — les hyperviseurs, via `bond3` |
|
||||
|
||||
*(Ce bloc annonçait `10.0.4.0/29` et une `passerelle: 10.0.4.6` — le SVI du commutateur —
|
||||
jusqu'au 2026-09-06. Le commutateur ne route plus les tenants : il transporte du VXLAN
|
||||
qu'il ne lit pas.)*
|
||||
| `10.0.4.4-.5` | libres |
|
||||
| `10.0.4.6` | SVI du switch routeur (`le commutateur`) |
|
||||
|
||||
Le jour où les deux OPNsense passent en haute disponibilité, `passerelle_sortie` devra
|
||||
pointer sur l'**IP virtuelle CARP** et non sur un boîtier nommé — c'est le seul changement
|
||||
|
|
@ -324,7 +318,7 @@ Même partage que Proxmox — l'anodin en clair, le secret dans la voûte :
|
|||
|
||||
| Quoi | Où | Réglable |
|
||||
|---|---|---|
|
||||
| URL de gestion, interfaces, prochain saut | `opnsense.yml` (en clair) | **panneau « Intrants de base » du GUI**, section *Frontière* |
|
||||
| URL de gestion, interfaces, prochain saut | `group_vars/opnsense.yml` (en clair) | **panneau « Intrants de base » du GUI**, section *Frontière* |
|
||||
| Clé et secret d'API | voûte unique de l'instance, sous `vault_opnsense_api_key` / `vault_opnsense_api_secret` | `ansible-vault edit` |
|
||||
|
||||
Les valeurs non sensibles sont de **vrais intrants** : `opnsense_api_url`,
|
||||
|
|
@ -426,7 +420,7 @@ Reste à trancher sur une interface **physique** : `ip ?`, `access-group ?`, `sp
|
|||
et `<PORT-TRUNK>` ; rien dans le modèle ne peut les deviner.
|
||||
- ~~**La sortie générale** n'est pas déclarée~~ — **réglé le 2026-08-02.** Elle est
|
||||
déclarée dans le registre, donc dérivée comme le reste : `serveur_debian` (le socle, porté
|
||||
par tous les hôtes) déclare 443, 80 et 123/udp — dépôts apt et horloge ; `client_resolveur`
|
||||
par tous les hôtes) déclare 443, 80 et 123/udp — dépôts apt et horloge ; `client_unbound`
|
||||
déclare 53 en UDP et TCP, la récursion depuis la racine que le choix souverain implique.
|
||||
Le `block out` de la section 5 est donc un vrai default-deny **assumé**, et le devis
|
||||
l'énonce désormais au lieu de le poser en silence.
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
# La frontière entre les deux mondes — physique et virtuel
|
||||
|
||||
> **Pour qui :** l'**exploitant** et le **mainteneur**. Ce document tranche une question qui
|
||||
> revenait à chaque décision : *qui possède quoi, et où ça vit ?*
|
||||
|
||||
Set-OPS manipule deux mondes qui se ressemblent et n'obéissent pas aux mêmes règles. Les
|
||||
confondre a coûté plusieurs soirées ; les séparer proprement rend la plupart des questions
|
||||
suivantes évidentes.
|
||||
|
||||
---
|
||||
|
||||
## Les deux mondes
|
||||
|
||||
| | **Monde PHYSIQUE** — l'underlay | **Monde VIRTUEL** — le tenant |
|
||||
|---|---|---|
|
||||
| Ce que c'est | commutateurs, hyperviseurs, frontière, stockage, transport VXLAN | VM, applications, données |
|
||||
| Possédé par | l'**hébergeur** | l'**organisation** |
|
||||
| Adressage | bande basse du site : `10.<index>.0-15.x` | zones : `10.<index>.16+.x` |
|
||||
| Décrit dans | `underlay.yml`, `proxmox-hebergeur.yml` | `plan/` |
|
||||
| Monté par | le symlink `underlay.yml` | le symlink `instance` |
|
||||
| Change quand | on touche au **matériel** | on touche au **service** |
|
||||
| Ses secrets | jeton d'API Proxmox, clé d'API de la frontière, accès aux commutateurs | LDAP, forge, SSO, bases |
|
||||
| Sa voûte | **la sienne**, chez l'hébergeur | `group_vars/all/vault.yml` |
|
||||
|
||||
**Les deux symlinks sont indépendants** (D-80) : `instance` dit *quel tenant*,
|
||||
`underlay.yml` dit *sur quelle fabric*. Un tenant se déplace d'une fabric à l'autre sans
|
||||
qu'on touche à son plan — c'est ce qui rend la portabilité possible.
|
||||
|
||||
---
|
||||
|
||||
## La règle qui rend la frontière opérante
|
||||
|
||||
> **Un tenant ne détient jamais un secret du monde physique.**
|
||||
|
||||
Chaque tenant a porté dans sa voûte le jeton d'API du cluster — chaque nouveau tenant devait le
|
||||
recopier pour exister. C'était exactement la faute des **neuf copies** de la résolution d'instance,
|
||||
appliquée aux secrets : une valeur qui vit à N endroits finit par diverger, et on ne peut
|
||||
plus révoquer l'une sans révoquer les autres.
|
||||
|
||||
**C'est réglé depuis le 2026-08-22** : l'underlay a **sa propre voûte**
|
||||
(`underlay.vault.yml`), chez l'hébergeur, à côté d'`underlay.yml`. Les opérations qui parlent
|
||||
au matériel — cloner une VM, poser une zone SDN, écrire sur la frontière — l'y lisent : le
|
||||
chemin se **dérive** du symlink `underlay.yml`, sans rien redéclarer
|
||||
(`playbooks/proxmox/cloner_vm_debian.yml`). Les tenants n'y ont pas accès et n'en ont pas
|
||||
besoin.
|
||||
|
||||
Deux compléments arrivés depuis, et qui achèvent la séparation :
|
||||
|
||||
- les **clés** aussi sont séparées (2026-08-28) : une voûte, **une clé**. Tant qu'un seul mot
|
||||
de passe les ouvrait toutes, la séparation était organisationnelle, pas cryptographique ;
|
||||
- **P25** refuse qu'une clé de l'hébergeur — nœuds, stockages, ponts, API — réapparaisse dans
|
||||
un `group_vars` de tenant. La garde attrape la rechute : un `make config` lancé d'un autre
|
||||
poste, une reprise à la main.
|
||||
|
||||
---
|
||||
|
||||
## Qui administre quoi, et depuis où
|
||||
|
||||
L'administration du monde physique se fait **depuis le tenant de l'hébergeur** : c'est lui
|
||||
qui porte les outils, les clés et les traces. Le plan de gestion du site
|
||||
(`10.<index>.0.0/24`) est la seule source autorisée à ouvrir SSH sur la flotte et à
|
||||
franchir la frontière.
|
||||
|
||||
Ce n'est pas un détail de commodité. Une machine qui administre depuis l'extérieur des deux
|
||||
mondes — un portable sur le réseau de la maison — n'est ni sauvegardée, ni reconstructible,
|
||||
ni prouvée. Le jour où elle disparaît, l'écosystème est intact et personne ne peut plus y
|
||||
entrer.
|
||||
|
||||
---
|
||||
|
||||
## Ce que la confusion a coûté
|
||||
|
||||
**Une règle qui ne peut jamais correspondre.** `devis_opnsense` dérive l'interface d'une
|
||||
règle de l'**attachement réel** de sa source (D-61), et cet attachement se lit dans
|
||||
`underlay.yml`. Un plan d'administration absent du fichier est classé « distant », et sa
|
||||
règle atterrit sur `wan`. Le 22 août, on s'apprêtait à poser 89 objets sur la frontière
|
||||
avec une règle d'admin qui n'aurait jamais laissé passer personne.
|
||||
|
||||
**Un fichier qui décrit un monde disparu.** Mesuré le même jour :
|
||||
|
||||
```
|
||||
management 10.0.0.0/24 déclaré → PERSONNE
|
||||
stockage 10.0.1.0/24 déclaré → PERSONNE
|
||||
ceph 10.0.2-3.0/24 déclaré → PERSONNE
|
||||
transit 10.0.4.0/24 déclaré → occupé (3 nœuds)
|
||||
vxlan 10.0.5.0/24 déclaré → occupé (3 nœuds)
|
||||
|
||||
et, portés par les nœuds sans être déclarés nulle part :
|
||||
192.168.11.x 10.11.5-7.x 192.168.50.x 10.1.110.254
|
||||
```
|
||||
|
||||
La frontière avait déjà migré vers `10.17.0.1` ; les hyperviseurs, non. Le fichier était
|
||||
resté au monde d'avant.
|
||||
|
||||
---
|
||||
|
||||
## L'instrument
|
||||
|
||||
```
|
||||
make underlay-plan # confronte le fichier au réel, n'écrit rien
|
||||
```
|
||||
|
||||
Il interroge l'API du cluster pour les adresses **réellement portées**, et sonde en TCP ce
|
||||
qui répond. Deux précautions y sont inscrites, toutes deux apprises en l'écrivant :
|
||||
|
||||
- **l'autorité dépend du rôle.** L'API de Proxmox connaît ses hyperviseurs, et eux seuls.
|
||||
Déclarer un commutateur « porté par personne » parce que le cluster l'ignore, c'est
|
||||
accuser le monde de ce que l'instrument ne voit pas ;
|
||||
- **« pas joignable d'ici » n'est pas « absent ».** Les réseaux de *chemin* (transit,
|
||||
transport VXLAN, stockage) ne sont **jamais** joignables depuis l'extérieur, par
|
||||
construction (D-78). Le premier jet du devis les déclarait morts.
|
||||
|
||||
---
|
||||
|
||||
## La séquence — faite, dans cet ordre
|
||||
|
||||
Cette section était une liste de tâches ouvertes. **Les trois sont faites** ; on la garde
|
||||
sous forme d'ordre parce que c'est l'ordre lui-même qui est la leçon — un site neuf le
|
||||
rejouera tel quel.
|
||||
|
||||
- [x] **Reconnaître** — `make underlay-plan` confronte l'underlay *déclaré* au réel (API du
|
||||
cluster + sondes). `underlay.yml` décrit ce qui **est**, pas ce qui était prévu :
|
||||
chaque réseau porté et non déclaré est un réseau que le moteur ne sait pas classer.
|
||||
- [x] **Séparer les voûtes** — fait le **2026-08-28**. Une voûte, une clé
|
||||
(`scripts/voutes.py`, `ANSIBLE_VAULT_IDENTITY_LIST`) : le jeton Proxmox et la clé de
|
||||
la frontière vivent dans `underlay.vault.yml`, hors de toute voûte de tenant. La
|
||||
séparation devait précéder la distribution des clés aux runners — sans elle, poser
|
||||
« la » clé sur le plus petit locataire lui donnait les secrets de l'hébergeur.
|
||||
- [x] **Appliquer la frontière** — ses règles dépendent des deux points ci-dessus, et
|
||||
elles en dérivent : P43 mesure que le devis de la frontière retrouve les machines du
|
||||
plan (7 machines, 104 règles du site).
|
||||
|
|
@ -34,23 +34,15 @@ poste de l'opérateur et sa forge. Rien à changer.
|
|||
|
||||
## 3. Ce qui n'a pas de maison aujourd'hui
|
||||
|
||||
Constaté le 2026-08-04, **révisé le 2026-09-05**. La moitié du constat a été levée
|
||||
entre-temps ; l'autre tient toujours, et il faut distinguer les deux.
|
||||
|
||||
**Ce qui a une maison désormais** : les **VM du site** ont leur inventaire Ansible
|
||||
(`scripts/site_inventaire.py` — 7 machines, 23 groupes, dérivé d'`underlay.yml` sans
|
||||
fichier intermédiaire), leur socle, leur durcissement, leurs sauvegardes et leur
|
||||
supervision (`site-mon-01`, depuis le 2026-09-02).
|
||||
|
||||
**Ce qui n'en a toujours pas** : les **équipements** eux-mêmes — hyperviseurs,
|
||||
commutateurs, frontière. C'est là que le constat d'origine reste entier.
|
||||
Constaté le 2026-08-04 : **aucun équipement de l'hébergeur n'est dans un inventaire
|
||||
Ansible**, et rien ne sauvegarde leurs configurations.
|
||||
|
||||
| Besoin | État |
|
||||
|---|---|
|
||||
| Supervision des hyperviseurs, commutateurs, frontière | personne — `site-mon-01` ne voit que les VM du site |
|
||||
| Supervision des hyperviseurs, commutateurs, frontière | personne |
|
||||
| Journaux de ces équipements | personne |
|
||||
| Sauvegarde de leurs configs (`running-config`, `config.xml`, `/etc/pve`) | personne — **vérifié le 2026-09-05, aucun rôle ne les touche** |
|
||||
| Résolution des noms d'underlay (`asgard`, `bifrost-3`, `bifrost-1`) | personne — **vérifié : `site-dns-01` ne les résout pas** |
|
||||
| Sauvegarde de leurs configs (`running-config`, `config.xml`, `/etc/pve`) | personne |
|
||||
| Résolution des noms d'underlay (`asgard`, `bifrost-3`, `bifrost-1`) | personne |
|
||||
| Certificats pour leurs interfaces web | personne |
|
||||
|
||||
Ce n'est pas un oubli de conception : ces besoins tombaient entre les chaises. Ils ne sont
|
||||
|
|
@ -62,9 +54,7 @@ d'aucun tenant, et `underlay.yml` ne décrit que du matériel, sans service.
|
|||
`proxmox-hebergeur.yml`. Ses services d'exploitation lui appartiennent au même titre que
|
||||
sa fabric ; les loger ailleurs recréerait la confusion qu'on vient de défaire.
|
||||
|
||||
Ce dépôt **a désormais son inventaire Ansible** — `scripts/site_inventaire.py`, dynamique
|
||||
plutôt que généré, parce qu'un site ne dérive de rien : sa déclaration *est* déjà sa forme
|
||||
finale. Ce qui reste à faire, c'est d'y loger les **équipements**, qui n'y sont pas.
|
||||
Ce dépôt n'a pas d'inventaire Ansible aujourd'hui : c'est ce que le chantier ajoutera.
|
||||
|
||||
**Rattachement réseau : un pont VLAN ordinaire, jamais un VNet du SDN.** C'est la
|
||||
contrainte qui découle du §1, et la seule qui distingue ces VM de celles d'un tenant.
|
||||
|
|
@ -153,48 +143,30 @@ emporter — mais il ne devrait pas non plus les nommer. L'interface normalisée
|
|||
`proxmox_noeuds` et `proxmox_stockages` sont déjà chez lui ; il manque la classe, pas le
|
||||
catalogue.
|
||||
|
||||
## 8. Un dépôt à part pour l'hébergeur — FAIT
|
||||
## 8. Un dépôt à part pour le réseau (décidé, non fait)
|
||||
|
||||
> **Cette section disait « Rien n'est fait » jusqu'au 2026-09-06.** Le dépôt existe :
|
||||
> **`SITE-Chezlepro`**, frère de `OPS-Chezlepro`, et le symlink `underlay.yml` du moteur y
|
||||
> pointe (`../SITE-Chezlepro/underlay.yml`).
|
||||
`underlay.yml` et `proxmox-hebergeur.yml` vivent aujourd'hui dans `OPS-Chezlepro`, qui est
|
||||
aussi le dépôt du **tenant** Chezlepro. C'est ce qui a permis de démarrer, et c'est ce qui
|
||||
oblige, à chaque commit, à trancher si l'on touche à l'infrastructure ou à l'organisation.
|
||||
|
||||
Le raisonnement qui l'a motivé, et qui vaut pour tout hébergeur : `underlay.yml` et
|
||||
`proxmox-hebergeur.yml` décrivent une **infrastructure** — des câbles, des VLAN, un
|
||||
cluster ; le plan d'un tenant décrit une **organisation** — ses serveurs, ses applications,
|
||||
ses comptes. Un tenant peut déménager ; une fabric ne déménage pas. Les garder dans le même
|
||||
dépôt obligeait, à chaque commit, à trancher lequel des deux on touchait.
|
||||
Ils décrivent des objets différents : l'un une **infrastructure** — des câbles, des VLAN,
|
||||
un cluster —, l'autre une **organisation** — ses serveurs, ses applications, ses comptes.
|
||||
Un tenant peut déménager ; une fabric ne déménage pas.
|
||||
|
||||
Ce que le dépôt de l'hébergeur porte aujourd'hui :
|
||||
Le dépôt réseau de l'hébergeur porterait donc `underlay.yml`, `proxmox-hebergeur.yml`, et
|
||||
plus tard l'inventaire des services d'exploitation (§4). Le symlink qui désigne
|
||||
l'hébergeur pointerait vers lui plutôt que vers son tenant — ce qui rendrait enfin la
|
||||
distinction visible dans les chemins eux-mêmes.
|
||||
|
||||
| Fichier | Ce qu'il décrit |
|
||||
|---|---|
|
||||
| `underlay.yml` | la fabric physique — réseaux, VLAN, MTU, commutateurs, hyperviseurs |
|
||||
| `proxmox-hebergeur.yml` | l'API du cluster, ses nœuds, ses stockages, ses ponts |
|
||||
| `opnsense.yml` | la frontière nord/sud (paramètres non sensibles) |
|
||||
| `underlay.vault.yml` | ses secrets à lui — **voûte séparée**, clé séparée (2026-08-28) |
|
||||
| `plan/` | ses **propres VM** : sept machines de service (pilotage, autorité, génome, cache, sauvegarde, supervision, DNS) |
|
||||
**Rien n'est fait.** La bascule demande de déplacer deux fichiers, de refaire le symlink,
|
||||
et de vérifier que les trois générateurs qui les lisent suivent.
|
||||
|
||||
C'est cette dernière ligne qui a le plus changé : l'hébergeur n'est plus seulement un
|
||||
porteur de fabric, **c'est un exploitant** — avec son inventaire (`scripts/site_inventaire.py`),
|
||||
son socle, son durcissement, ses sauvegardes et sa supervision.
|
||||
## 9. Le VLAN de gestion, et ce qu'il n'est pas
|
||||
|
||||
## 9. Le plan d'administration, et ce qu'il n'est pas
|
||||
`10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB/IPMI**. Accès
|
||||
sysadmin uniquement.
|
||||
|
||||
> **Les adresses de cette section ont toutes changé** avec la bascule D-77/D-78, terminée le
|
||||
> 2026-08-22. Elle désignait `10.0.0.0/24` et les « VLAN 11 (transport VXLAN) et 40 (sortie
|
||||
> tenant) » ; `10.0.0.0/24` n'existe plus, et le transport est passé au VLAN 50.
|
||||
Aucun hyperviseur n'y a d'adresse, aucune VM n'y est branchée, aucun trafic tenant ne le
|
||||
traverse — ni encapsulé, ni décapsulé. C'est précisément la raison d'être des VLAN 11
|
||||
(transport VXLAN) et 40 (sortie tenant) : les avoir sortis de ce domaine de diffusion.
|
||||
|
||||
Le plan d'**administration** — `10.17.0.0/24`, dans la bande basse du supernet du tenant
|
||||
Chezlepro (D-77) — est réservé aux **équipements** et à l'exploitant. Il n'a **aucun VLAN** :
|
||||
c'est un segment physique, et **aucun pont d'hyperviseur ne le touche**, donc aucune VM ne
|
||||
peut y naître. Le validateur refuse d'ailleurs qu'on y déclare une machine.
|
||||
|
||||
Distinct de lui, le **contrôle de la grappe** (`192.168.11.0/24`, `vmbr0`) porte l'interface
|
||||
web de Proxmox et le dialogue entre nœuds.
|
||||
|
||||
Aucun trafic tenant ne traverse ni l'un ni l'autre — ni encapsulé, ni décapsulé. C'est
|
||||
précisément la raison d'être du **VLAN 50** (transport VXLAN) et du **VLAN 40** (transit vers
|
||||
la frontière) : les avoir sortis de ces domaines de diffusion. *(Le transport a porté le
|
||||
numéro 11 jusqu'à la bascule ; `192.168.11.0/24` étant pris par le contrôle de la grappe, il
|
||||
est passé au 50.)*
|
||||
|
|
|
|||
|
|
@ -32,15 +32,9 @@
|
|||
|---|---|---|
|
||||
| **Apps web** (Forgejo, Grafana, Nextcloud…) | **Keycloak OIDC** (SSO) | un seul login, MFA, jetons |
|
||||
| **Mail** (Dovecot, Postfix) | **LDAP direct** (bind) | IMAP/SMTP ne parlent pas OIDC ; username + mot de passe |
|
||||
| **App web SANS OIDC natif** | **`oauth2-proxy` devant**, lui-même client OIDC | met au SSO ce qui ne sait pas y aller seul |
|
||||
| **Système / services** (SSSD, etc.) | **LDAP direct** | annuaire standard |
|
||||
| Tout | → **même OpenLDAP** | identité unifiée, aucun compte en double |
|
||||
|
||||
> **L'ouverture de session Unix n'est PAS dans ce tableau, et c'est délibéré.** Cette ligne
|
||||
> annonçait « Système / services (SSSD, etc.) → LDAP direct » jusqu'au 2026-09-06. Le dépôt
|
||||
> ne fait pas ça : le rôle `client_ldap` a été **retiré le 2026-07-04, hors conception**, et
|
||||
> aucun rôle n'installe SSSD. L'annuaire sert les **applications** ; l'accès à un hôte se
|
||||
> fait par **clé SSH**, et l'élévation par `sudo`. Voir `docs/authentification.md` §2.
|
||||
|
||||
## Sens de provisionnement
|
||||
|
||||
Les identités se créent et se gèrent dans **OpenLDAP**. Keycloak **fédère** (lit LDAP en
|
||||
|
|
@ -60,10 +54,7 @@ Keycloak : c'est LDAP la référence. Le mail bind directement sur LDAP.
|
|||
## Conséquences pour Set-OPS
|
||||
|
||||
- `serveur_openldap` (déployé) = le pilier annuaire, source de vérité.
|
||||
- `serveur_keycloak` = pilier SSO, **fédéré sur OpenLDAP** — et la fédération est
|
||||
*automatisée*, pas un geste manuel : `roles/serveur_keycloak/tasks/federation-ldap.yml`
|
||||
(avec ses mappeurs d'attributs, ses groupes et sa politique de mot de passe dans les
|
||||
tâches voisines). `make identite-plan` relit ensuite ce qui est réellement en place.
|
||||
- `serveur_keycloak` = pilier SSO, à **fédérer sur OpenLDAP** (User Federation → LDAP).
|
||||
- `serveur_dovecot` / `serveur_postfix` = auth **LDAP direct** (cohérent avec ce modèle).
|
||||
- Les apps web déclarées dans le plan → clients **OIDC** de Keycloak.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ par une **commande qui interroge le système**, jamais par une conviction.
|
|||
|
||||
## Phase 0 — au bureau, avant de partir
|
||||
|
||||
- [ ] **Recevoir la fiche de l'hébergeur** — les dix lignes du §7 de
|
||||
- [ ] **Recevoir la fiche de l'hébergeur** — les huit lignes du §7 de
|
||||
[`preparer-un-site-hebergeur.md`](preparer-un-site-hebergeur.md). Sans le nom exact
|
||||
du nœud et des stockages, la journée s'arrête à la phase 1.
|
||||
- [ ] **Fixer l'`index` du site = l'`index` de son tenant.** Il n'y a pas de second
|
||||
|
|
@ -44,13 +44,11 @@ par une **commande qui interroge le système**, jamais par une conviction.
|
|||
moteur de pare-feu.
|
||||
|
||||
> **Un site neuf se construit d'emblée dans l'adressage cible** — gestion en
|
||||
> `10.<index>.0.0/24`, chemins en `192.168.<vlan>.0/24` (D-77, D-78). Sur un site vierge, la
|
||||
> cible ne coûte rien — et deux sites qui porteraient le **même** plan de gestion rendraient
|
||||
> la **reprise mutuelle impossible** : deux réseaux identiques ne peuvent pas s'atteindre.
|
||||
>
|
||||
> *(État du site historique, mesuré le 2026-09-06 : sa gestion est **déjà** en `10.17.0.0/24`
|
||||
> et son transport VXLAN en `192.168.50.0/24` ; le transit et le stockage restent dans
|
||||
> l'ancien espace. Ce paragraphe le donnait entièrement « encore en `10.0.x` ».)*
|
||||
> `10.<index>.0.0/24`, chemins en `192.168.<vlan>.0/24` (D-77, D-78). Le site historique
|
||||
> est encore en `10.0.x` et migrera par [`runbooks-exploitation.md`](runbooks-exploitation.md) §6.
|
||||
> Sur un site vierge, la cible ne coûte rien — et deux sites en `10.0.0.0/24` rendraient
|
||||
> la **reprise mutuelle impossible** : deux plans de gestion identiques ne peuvent pas
|
||||
> s'atteindre.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -205,17 +203,17 @@ make frontiere-plan make sdn-plan make placement-plan
|
|||
```yaml
|
||||
---
|
||||
underlay:
|
||||
# UN SITE N'A PAS D'INDEX (depuis le 2026-08-25). Il en portait un ; c'était un vestige.
|
||||
# Un site ne dérive AUCUN adressage — ses machines vivent sur des réseaux de fabric.
|
||||
# Cette valeur ne disait qu'une chose : quel supernet de tenant est le sien. Et elle le
|
||||
# disait pour le site ENTIER alors qu'UN SEUL réseau est concerné.
|
||||
#
|
||||
# LES TENANTS QUE CE SITE PORTE — nom de dossier -> index. Les trois devis d'équipement
|
||||
# (frontière, commutateur, SDN) découvrent TOUTE la fédération : sans cette clé, le
|
||||
# second site se voit proposer les règles, les VLAN et les zones du premier. Le matériel
|
||||
# les accepte, aucune ne correspond jamais à un paquet, et rien ne le signale.
|
||||
tenants:
|
||||
OPS-<tenant>: <index>
|
||||
# LE SEED DU SITE — le même que celui de son tenant, et la clé qu'on oublie.
|
||||
# C'est elle qui dit au validateur que 10.<index>.0.0/16 est SON supernet, donc que
|
||||
# la bande basse lui appartient. Sans elle, `make underlay` refuse le réseau de
|
||||
# gestion en le prenant pour celui d'un AUTRE site — message déroutant, cause triviale.
|
||||
index: <index>
|
||||
# LES TENANTS QUE CE SITE PORTE — noms de dossier, pas de fantaisie. Les trois devis
|
||||
# d'équipement (frontière, commutateur, SDN) découvrent TOUTE la fédération : sans
|
||||
# cette clé, le second site se voit proposer les règles, les VLAN et les zones du
|
||||
# premier. Le matériel les accepte, aucune ne correspond jamais à un paquet, et rien
|
||||
# ne le signale. Un site UNIQUE n'a rien à déclarer ; c'est le second qui se nomme.
|
||||
tenants: [OPS-<tenant>]
|
||||
routeur: <nom-du-commutateur> # racine du spanning-tree, pas un routeur
|
||||
# `stp` exige `routeur` : ne pas le déclarer tant qu'aucun commutateur ne l'est.
|
||||
routage_tenants: sdn # le routage inter-zone vit sur l'hyperviseur
|
||||
|
|
@ -224,12 +222,7 @@ underlay:
|
|||
dialecte: cisco # cisco | binardat — propriété du MATÉRIEL
|
||||
stp: { mode: mstp, topologie: etoile }
|
||||
reseaux:
|
||||
# `bande_basse_de` DÉCLARE le chevauchement VOULU : ce /24 vit dans le /16 du tenant
|
||||
# nommé, dans la bande 0-15 que ses zones (3e octet >= 16) n'allouent jamais. Sans
|
||||
# cette clé, `make underlay` refuse le réseau — et il a raison : il ne peut pas
|
||||
# distinguer un chevauchement voulu d'un accident. Le nom est vérifié contre `tenants:`,
|
||||
# donc une faute de frappe est refusée.
|
||||
- { nom: management, vlan: 10, bande_basse_de: OPS-<tenant>, sous_reseau: 10.<index>.0.0/24, passerelle: 10.<index>.0.1, mtu: 1500 }
|
||||
- { nom: management, vlan: 10, sous_reseau: 10.<index>.0.0/24, passerelle: 10.<index>.0.1, mtu: 1500 }
|
||||
- { nom: transit-frontiere, vlan: 40, sous_reseau: 192.168.40.0/24, passerelle_sortie: 192.168.40.1, mtu: 1500 }
|
||||
- { nom: underlay-vxlan, vlan: 50, sous_reseau: 192.168.50.0/24, mtu: 1500 }
|
||||
# Stockage : uniquement les réseaux réellement câblés (fabric: stockage, mtu 9000).
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@
|
|||
|
||||
Une intégration est de l'un des deux genres, et ils ne se déclarent pas au même endroit.
|
||||
|
||||
**Universelle** — métriques, journaux, PKI, **résolution** et **source d'artefacts**
|
||||
(les cinq qui portent `universelle: true`). Il n'y a aucun choix de cible : un seul
|
||||
Prometheus, un seul Loki, une seule AC, un seul résolveur, un seul cache. Elle est déclarée
|
||||
**une fois, par le rôle**, dans `roles/<role>/meta/integration.yml`, et tout hôte la
|
||||
reçoit :
|
||||
**Universelle** — supervision, journaux, PKI. Il n'y a aucun choix de cible : un seul
|
||||
Prometheus, un seul Loki, une seule AC. Elle est déclarée **une fois, par le rôle**, dans
|
||||
`roles/<role>/meta/integration.yml`, et tout hôte la reçoit :
|
||||
|
||||
```yaml
|
||||
integration:
|
||||
|
|
@ -20,16 +18,8 @@ integration:
|
|||
sauf_role: serveur_step_ca # facultatif — voir « exemptions »
|
||||
```
|
||||
|
||||
**Facultative** — `client_backup` et `client_smtp`. Là il y a un vrai choix, et il se
|
||||
déclare par serveur, dans `plan/serveurs.yml : integrations`.
|
||||
|
||||
> **`client_resolveur` a changé de camp le 2026-08-24, et ce document l'annonçait encore
|
||||
> facultatif.** Il posait alors un Unbound sur chaque VM — un vrai coût, donc un vrai choix.
|
||||
> Il n'installe plus rien : il écrit `/etc/resolv.conf` pour désigner le résolveur du
|
||||
> tenant. Son intégration est devenue **universelle, sans aucune exemption — pas même
|
||||
> l'hôte qui porte le résolveur** : il se sert lui-même, et l'exempter reviendrait à dire
|
||||
> que le résolveur ne se fait pas confiance. Une machine restée sur la résolution
|
||||
> d'amorçage envoie chacune de ses questions dehors, sans que rien ne le signale.
|
||||
**Facultative** — `client_backup`, `client_smtp`, `client_unbound`. Là il y a un vrai choix,
|
||||
et il se déclare par serveur, dans `plan/serveurs.yml : integrations`.
|
||||
|
||||
**Pourquoi cette inversion.** Le plan portait 57 lignes d'intégration écrites à la main. 28
|
||||
d'entre elles disaient oui à quelque chose de vrai pour tous les hôtes — elles n'existaient
|
||||
|
|
|
|||
|
|
@ -9,34 +9,14 @@ de l'écosystème, en distinguant **constantes** et **défauts surchargeables**.
|
|||
> au même titre que le [dimensionnement](dimensionnement-ressources.md). Décidée le
|
||||
> 2026-06-26.
|
||||
|
||||
> **Statut, revu le 2026-09-06 : le panneau est construit.** Ce document reste la note de
|
||||
> *conception* — il explique les arbitrages, pas l'état. Trois écarts entre ce qui était
|
||||
> proposé et ce qui a été fait, et ils comptent :
|
||||
>
|
||||
> 1. **La migration en répertoires n'a eu lieu que pour `all/`.** `group_vars/all/` porte
|
||||
> bien `00-instance.yml` (tenu à la main) et `10-intrants.yml` (écrit par le GUI) ;
|
||||
> `proxmox.yml` et `modeles_vm.yml` sont restés des **fichiers plats**. Le §3 les
|
||||
> présente encore comme des répertoires.
|
||||
> 2. **Les constantes Proxmox ont déménagé chez l'hébergeur.** L'accès au cluster
|
||||
> (`proxmox_api_*`) n'est plus un intrant du tenant : il vit dans
|
||||
> `proxmox-hebergeur.yml`, à côté d'`underlay.yml`, parce qu'un cluster appartient à
|
||||
> qui possède le matériel. Cf. `config-proxmox.md`.
|
||||
> 3. **Les « points ouverts » du §8 sont tranchés** par ce qui a été bâti : la liste de
|
||||
> rappel des secrets attendus est bien dans le panneau, en lecture seule, et elle se
|
||||
> *recense* (`scripts/voute.py lister`) au lieu d'être recopiée — trois copies manuelles
|
||||
> avaient existé, toutes avaient divergé.
|
||||
|
||||
## 1. Décisions cadre (validées)
|
||||
|
||||
1. **Secrets : hors périmètre.** Le GUI n'affiche ni ne stocke aucun secret. Les
|
||||
`vault_*` et tokens Proxmox restent édités via Ansible Vault en ligne de commande.
|
||||
Le panneau peut, au plus, afficher une **liste de rappel en lecture seule** des
|
||||
secrets attendus (sans valeur).
|
||||
2. **Nomenclature : lecture seule** dans le panneau — à une exception près, l'**`index`**,
|
||||
qui est le seul champ d'adressage saisissable (panneau *Réseau*, écrit chirurgicalement).
|
||||
Tout le reste — supernet, sous-réseaux, passerelles, VLAN, VMID — se **dérive** et **P20
|
||||
refuse qu'on l'écrive**. *(Ce point disait « l'édition de `supernet`/VLAN/catégories reste
|
||||
dans `plan/nomenclature.yml` » : ces valeurs n'y sont plus du tout.)*
|
||||
2. **Nomenclature : lecture seule** dans le panneau. L'édition de `supernet`/VLAN/
|
||||
catégories reste dans `plan/nomenclature.yml` (autorité unique du plan réseau).
|
||||
3. **Conception avant code** (cette note).
|
||||
|
||||
## 2. Modèle : constante vs défaut surchargeable
|
||||
|
|
@ -108,10 +88,9 @@ supporté) ; par groupe, dans le `group_vars/<groupe>/` correspondant.
|
|||
- **Suite** : politiques de durcissement (défauts par groupe), DNS internes, relais
|
||||
SMTP, endpoints services centraux.
|
||||
|
||||
## 8. Points ouverts — tranchés par ce qui a été construit
|
||||
|
||||
| Question de juin | Réponse, telle que le code la donne |
|
||||
|---|---|
|
||||
| Migrer `group_vars/*.yml` → répertoires ? | **Pour `all/` seulement.** `proxmox.yml` et `modeles_vm.yml` sont restés plats — la migration n'a payé que là où le GUI écrivait vraiment. |
|
||||
| Afficher la liste de rappel des secrets ? | **Oui, en lecture seule** — et *recensée*, jamais recopiée (`scripts/voute.py lister`, gardée par **P18**). |
|
||||
| Périmètre MVP suffisant ? | Oui, et il a été dépassé : la couverture du GUI est désormais **prouvée** par **P19**, qui refuse un champ du plan que la console ne saurait pas éditer. |
|
||||
## 8. Points ouverts à confirmer
|
||||
- OK pour la **migration `group_vars/*.yml` → répertoires** (§3) ? (alternative :
|
||||
réécrire les fichiers existants en bloc, au prix des commentaires).
|
||||
- Le panneau affiche-t-il la **liste de rappel des secrets attendus** (lecture seule),
|
||||
ou on n'en parle pas du tout dans le GUI ?
|
||||
- Périmètre MVP (§7) suffisant pour une première itération ?
|
||||
|
|
|
|||
|
|
@ -18,22 +18,14 @@ fois**, depuis un endroit unique, puis les laisser se **dériver** ou se **propa
|
|||
- `setops_plan_dir` — chemin du plan.
|
||||
|
||||
### B. Réseau & nomenclature — `plan/nomenclature.yml`
|
||||
- **`index`** — le **seed**, et le seul champ d'adressage. Supernet, sous-réseaux,
|
||||
passerelles, VLAN et VMID en **dérivent** ; la preuve **P20** refuse qu'on les y écrive.
|
||||
- `cidr_hote`, `reservations`, `categories` (libellés de zones), `fonctions`
|
||||
(catégorie + service).
|
||||
|
||||
> Ce paragraphe listait `supernet` comme un intrant de la nomenclature jusqu'au
|
||||
> 2026-09-06. C'est exactement ce que **P20 interdit** : un adressage stocké est un
|
||||
> adressage qui peut contredire celui qu'on dérive.
|
||||
- `supernet`, `cidr_hote`, `reservations`, `categories` (VLAN/sous-réseau/passerelle),
|
||||
`fonctions` (catégorie + service).
|
||||
|
||||
### C. Hyperviseur Proxmox — `group_vars/proxmox.yml`
|
||||
- `proxmox_api_host`, `proxmox_api_user`, `proxmox_api_port`, `proxmox_validate_certs`.
|
||||
- 🔒 `proxmox_api_token_id`, `proxmox_api_token_secret` — dans la **voûte unique** de
|
||||
l'instance, `group_vars/all/vault.yml`. **`proxmox.vault.yml` n'est plus lue** (retirée le
|
||||
2026-08-03) : tolérée « en compatibilité », elle était restée le *seul* porteur du jeton
|
||||
chez un tenant — et comme `*.vault.yml` est gitignoré, ce jeton ne voyageait avec aucun
|
||||
dépôt. Une voûte unique qui ne l'était pas. Cf. `docs/config-proxmox.md`.
|
||||
l'instance, `group_vars/all/vault.yml`. (L'ancienne `proxmox.vault.yml` reste lue en
|
||||
compatibilité si elle existe encore ; cf. `docs/config-proxmox.md`.)
|
||||
- Golden template : `proxmox_clone_vmid_modele`, `proxmox_clone_source_nom`.
|
||||
- Placement par défaut : `proxmox_clone_noeud`, `proxmox_clone_stockage`,
|
||||
`proxmox_clone_pont`, format, complet, timeout, disque, interface, démarrer.
|
||||
|
|
@ -48,7 +40,7 @@ nftables baseline · fail2ban SSH · auditd · AppArmor · sysctl · unattended-
|
|||
journald (rétention) · core_dumps · systemd_ssh_auto.
|
||||
|
||||
### F. Endpoints des services centraux (les rôles `client_*` en dérivent)
|
||||
- DNS interne : plancher `/etc/hosts` (`hosts_statiques`) + PowerDNS (autoritatif) + `serveur_resolveur` (LE récursif du tenant), désigné sur chaque nœud par `client_resolveur` — intégration **universelle**, pas opt-in.
|
||||
- DNS interne : plancher `/etc/hosts` (`hosts_statiques`) + PowerDNS + `client_unbound` (opt-in).
|
||||
- AC/PKI (`client_pki_ca_url` → infra-pki, provisioner).
|
||||
- IdM/LDAP : annuaire résolu par `resoudre_annuaire` (hôte + base DN dérivés du domaine).
|
||||
- Relais courriel (`client_smtp_relais` → edge-mta, MTA Postfix, port 25).
|
||||
|
|
@ -92,13 +84,13 @@ domaines publics, `edge`, autorité DNS, FQDN exposés.
|
|||
| Intrant | Classe | Surcharge où ? |
|
||||
| --- | --- | --- |
|
||||
| `domaine_interne` | **Constante** | — |
|
||||
| Nomenclature (**`index`**, CIDR d'hôte, catégories, fonctions) | **Constante** | — (le seed est la loi ; l'adressage en dérive) |
|
||||
| Nomenclature (supernet, CIDR, catégories, fonctions) | **Constante** | — (le plan réseau est la loi) |
|
||||
| Accès Proxmox (API host/user/port/token) | **Constante** | — (un seul cluster) |
|
||||
| Golden template (vmid_modele, source_nom) | **Constante** | — |
|
||||
| Secrets Vault | **Constante** 🔒 | — (gérés à part, jamais en clair) |
|
||||
| `fuseau_horaire` | Défaut | par hôte (rare) |
|
||||
| `proxmox_clone_noeud` / `stockage` / `pont` | Défaut | par hôte (`serveurs.yml`) |
|
||||
| DNS internes (plancher `/etc/hosts` + PowerDNS) | Défaut | par hôte / groupe |
|
||||
| DNS internes (plancher `/etc/hosts` + PowerDNS + `client_unbound`) | Défaut | par hôte / groupe |
|
||||
| Politiques durcissement (SSH, nftables, fail2ban, journald…) | Défaut | par hôte / groupe |
|
||||
| Relais SMTP, TLS internes | Défaut | par hôte / groupe |
|
||||
| `ciuser`, compte `ansible` | Défaut | rarement surchargé |
|
||||
|
|
@ -117,17 +109,7 @@ dossier ; la forme plate `group_vars/all.yml` reste lue en compatibilité —, `
|
|||
`modeles_vm.yml`, `plan/nomenclature.yml`). À détailler dans la note de conception de la
|
||||
fonctionnalité GUI.
|
||||
|
||||
## 4. Incohérences repérées — soldées
|
||||
|
||||
Les deux écarts que cette section signalait n'existent plus (vérifié le 2026-09-06), et le
|
||||
modèle « deux environnements » qui les portait non plus :
|
||||
|
||||
- ~~`fuseau_horaire` défini en **lab** seulement, absent de **production**~~ — il est dans
|
||||
`group_vars/all/10-intrants.yml` (`America/Toronto`), avec `domaine_interne`.
|
||||
- ~~`group_vars/serveur_debian.yml` référencé mais absent~~ — plus aucune référence.
|
||||
|
||||
> **Il n'y a plus d'« environnements ».** Ce document parle de `<env>` par endroits : c'est
|
||||
> le vocabulaire d'avant la séparation par instance. Une instance = **un dépôt**, avec **un**
|
||||
> inventaire (le moteur en résout le nom, cf. `plan-et-generation.md`). « Lab » et
|
||||
> « production » ne sont pas deux environnements d'un même écosystème : ce sont deux
|
||||
> écosystèmes, chacun avec son plan, son inventaire et sa voûte.
|
||||
## 4. Incohérences repérées (à corriger)
|
||||
- `fuseau_horaire` défini en **lab** seulement, absent de **production**.
|
||||
- `group_vars/serveur_debian.yml` **référencé** (commentaire de prod `all.yml`) mais
|
||||
**absent** des deux environnements.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ flowchart TB
|
|||
subgraph INST["③ INSTANCES — la flotte (inventory hosts.yml)"]
|
||||
direction LR
|
||||
INV[["hosts.yml"]]
|
||||
VMS(("14 VM<br/>infra-pki · infra-dns · infra-mail · infra-edge · edge-mta<br/>idm · data-sql · obs · mon · forge · collab<br/>web-frontal · web-dorsal · ops"))
|
||||
VMS(("11 VM<br/>infra-pki · dns · mail · edge<br/>idm · data · obs · mon · forge · web"))
|
||||
end
|
||||
|
||||
ECO["④ ÉCOSYSTÈME souverain en service<br/>PKI · DNS · IdM/SSO · Données · Observabilité · Forge · Web"]
|
||||
|
|
@ -60,9 +60,8 @@ flowchart TB
|
|||
réseau (VMID·VLAN·IP·gw) · ressources (cœurs·RAM·disque) · groupes · DSN+DNS
|
||||
│ = instanciation
|
||||
▼
|
||||
③ INSTANCES — la flotte (inventory hosts.yml : 14 VM cohérentes)
|
||||
infra-pki · infra-dns · infra-mail · infra-edge · edge-mta · idm · data-sql
|
||||
obs · mon · forge · collab · web-frontal · web-dorsal · ops
|
||||
③ INSTANCES — la flotte (inventory hosts.yml : 11 VM cohérentes)
|
||||
infra-pki·dns·mail·edge · idm · data · obs · mon · forge · web
|
||||
▲ clone du golden template + identité cloud-init
|
||||
│ make deployer (rôles Ansible par groupe)
|
||||
▼
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
# Métriques dérivées des rôles
|
||||
|
||||
> **Pour qui :** celui qui ajoute un rôle à Set-OPS et se demande comment ses mesures
|
||||
> arrivent dans Prometheus — et celui qui exploite et veut savoir d'où sortent les
|
||||
> courbes qu'il regarde.
|
||||
|
||||
> **La règle en une phrase.** Un rôle déclare l'exportateur de ses propres mesures ; le
|
||||
> moteur en dérive la cible de scrutation et les panneaux. Comme `meta/flux.yml` engendre
|
||||
> nftables *et* OPNsense, comme `meta/supervision.yml` engendre les services Icinga.
|
||||
|
||||
## Pourquoi un second fichier
|
||||
|
||||
`meta/supervision.yml` et `meta/metriques.yml` répondent à des questions différentes, sur
|
||||
des données différentes, pour des consommateurs différents.
|
||||
|
||||
| | ce qu'il déclare | la question | le consommateur |
|
||||
|---|---|---|---|
|
||||
| `supervision.yml` | une **sonde** qui rend un verdict avec un TTL | *est-ce cassé ?* | Icinga |
|
||||
| `metriques.yml` | un **exportateur** qui expose une série | *depuis quand, et vers où ?* | Prometheus → Grafana |
|
||||
|
||||
Ce n'est pas une frontière inventée pour l'occasion. `docs/supervision-conception.md` la
|
||||
pose déjà dans l'autre sens :
|
||||
|
||||
> Une **métrique à seuil** — durée de collecte, volume de journaux, taux d'occupation —
|
||||
> appartient à Prometheus et Grafana. Icinga répond à une seule question : *est-ce cassé ?*
|
||||
> Mélanger les deux rendrait les deux moins lisibles.
|
||||
|
||||
Ce document est l'autre moitié de cette phrase.
|
||||
|
||||
## Le constat qui l'a rendu nécessaire
|
||||
|
||||
Mesure du 2026-09-14 : **aucune métrique de service n'était collectée.** Prometheus ne
|
||||
scrutait que les `node_exporter` — processeur, mémoire, disques, réseau. Rien de
|
||||
PostgreSQL, rien de l'annuaire, rien des boîtes, rien du cache.
|
||||
|
||||
Le crochet existait pourtant : `serveur_prometheus_cibles_supplementaires`, une liste
|
||||
libre, documentée, et que **personne ne remplissait**. Une facilité offerte à qui saurait
|
||||
qu'elle existe n'est pas un mécanisme ; c'est une note de bas de page.
|
||||
|
||||
## Ce qu'un rôle déclare
|
||||
|
||||
```yaml
|
||||
# roles/<rôle>/meta/metriques.yml
|
||||
exportateur:
|
||||
paquet: prometheus-postgres-exporter
|
||||
service: prometheus-postgres-exporter
|
||||
port: 9187
|
||||
job: postgresql
|
||||
|
||||
panneaux:
|
||||
- titre: "Taux de succès du cache"
|
||||
expr: "..."
|
||||
unite: ratio
|
||||
raison: "..."
|
||||
```
|
||||
|
||||
**L'exportateur** est ce que le rôle installe pour qu'il y ait quelque chose à lire. Le
|
||||
rôle le pose lui-même : il connaît ses chemins, son compte de service, sa vérité de
|
||||
terrain.
|
||||
|
||||
**Les panneaux** sont ce qui mérite d'être regardé dans le temps.
|
||||
|
||||
## Combien de panneaux : un par QUESTION QU'ON SE POSE
|
||||
|
||||
Ni un par métrique — un exportateur en publie couramment plus de deux cents — ni un par
|
||||
rôle. Un par question qu'on se pose vraiment quand quelque chose commence à aller moins
|
||||
bien.
|
||||
|
||||
**Le critère :** *une série a sa place ici si elle **précède** un verdict, ou si elle n'en
|
||||
aura **jamais**.*
|
||||
|
||||
- Les **connexions** précèdent un verdict : la sonde Icinga crie à 70 % et à 90 %, le
|
||||
graphe dit depuis *quand* ça monte. Une base qui passe de 20 à 60 connexions en trois
|
||||
semaines n'a rien cassé — elle annonce la date où elle cassera.
|
||||
- Le **taux de succès du cache** n'aura jamais de verdict, et c'est pourquoi il compte.
|
||||
Quand les données dépassent `shared_buffers`, la base va chercher sur disque de plus en
|
||||
plus souvent. Rien ne casse, rien n'alerte : tout devient lent. C'est exactement la panne
|
||||
qu'un graphe voit et qu'une sonde ne verra jamais.
|
||||
|
||||
Ce qui bascule d'un coup appartient à Icinga. Ce qui dérive lentement n'a que le graphe
|
||||
pour se faire voir.
|
||||
|
||||
## Ce que le moteur dérive
|
||||
|
||||
**La cible de scrutation.** Le nom du dossier du rôle *est* le nom du groupe ; les cibles
|
||||
sont donc les hôtes actifs de ce groupe. Un rôle déclaré sans hôte ne produit aucun job —
|
||||
Prometheus n'a pas à porter une cible qui n'existe pas, ni son journal à se remplir de
|
||||
refus prévisibles.
|
||||
|
||||
**Les panneaux**, pour le tableau de bord.
|
||||
|
||||
Les cibles **écrites au plan** (`serveur_prometheus_cibles_supplementaires`) complètent la
|
||||
dérivation, elles ne la remplacent pas : un équipement ou un service tiers n'a aucun rôle
|
||||
Set-OPS pour se déclarer.
|
||||
|
||||
## Ce que le moteur ne dérive PAS, et c'est délibéré
|
||||
|
||||
**Le flux.** Le port de l'exportateur doit s'ouvrir depuis l'observatoire, et c'est
|
||||
`meta/flux.yml` qui le déclare — là où vivent déjà tous les flux du rôle. Deux fichiers
|
||||
pour un même fait finissent par diverger, et ce dépôt en a assez d'exemples.
|
||||
|
||||
## Le compte de service : le moins de droits possible
|
||||
|
||||
Un exportateur lit des compteurs. Il n'a aucune raison de pouvoir lire des données.
|
||||
|
||||
Pour PostgreSQL, c'est le rôle `pg_monitor` — fourni par le moteur depuis la version 10 —
|
||||
qui donne accès aux vues de statistiques **et à elles seules**. Faire tourner un
|
||||
exportateur sous `postgres` serait donner les clés de la base pour lire des compteurs.
|
||||
|
||||
**Le mot de passe vient de la voûte.** Vide, l'exportateur n'est pas posé du tout : le rôle
|
||||
ne l'installe pas et ne crée pas le compte — jamais un mot de passe par défaut. **Prometheus,
|
||||
lui, dérive quand même la cible** (`:9187`) de tout hôte de `serveur_postgresql` : sans le
|
||||
secret, la collecte d'`obs-01` passe au rouge. C'est voulu — une base sans métriques doit se
|
||||
voir, et renseigner le secret est la façon de l'éteindre. *(Cette page promettait l'inverse
|
||||
jusqu'au 2026-09-28.)*
|
||||
|
||||
**La chaîne de connexion ne passe pas par la ligne de commande.** Un `DATA_SOURCE_NAME` en
|
||||
argument serait lisible dans `ps` par tout le monde sur la machine ; dans un fichier à
|
||||
`0600`, il ne l'est que par root et le service.
|
||||
|
||||
## Le chiffrement : ce qui est fait, ce qui ne l'est pas
|
||||
|
||||
`client_metrique` sert ses métriques en **TLS**, certificat synchronisé par `client_pki`.
|
||||
|
||||
Les exportateurs de service ne le font pas encore. La dette est écrite dans la `raison` du
|
||||
flux concerné, avec son remède — `--web.config.file` et l'abonnement au renouvellement.
|
||||
Elle n'est pas cachée derrière un silence.
|
||||
|
||||
## Où en est la couverture
|
||||
|
||||
| déclaration | rôles |
|
||||
|---|---|
|
||||
| `meta/flux.yml` | 39 |
|
||||
| `meta/authentification.yml` | 33 |
|
||||
| `meta/empreinte.yml` | 32 |
|
||||
| `meta/supervision.yml` | 28 |
|
||||
| **`meta/metriques.yml`** | **1** |
|
||||
|
||||
Le second versant commence. Un rôle sans `metriques.yml` n'est pas fautif — beaucoup n'ont
|
||||
aucune série qui mérite un graphe. Mais un service qui porte de l'état et n'en déclare
|
||||
aucune mérite qu'on se demande pourquoi.
|
||||
|
||||
## Quand relire ce document
|
||||
|
||||
- un rôle qui se met à porter de l'état → il lui faut probablement un exportateur
|
||||
- un exportateur qui passe en TLS → la dette du flux se referme, et cette page le dit
|
||||
- un panneau qu'on regarde sans jamais agir dessus → il n'avait pas sa place ici
|
||||
|
|
@ -82,15 +82,12 @@ VM destinée à devenir un template, pas un serveur de production
|
|||
Vérifications utiles depuis le poste Ansible :
|
||||
|
||||
```bash
|
||||
ssh ansible@<ip-de-la-VM-modele> # l'adresse est celle que tu lui as donnee, pas une derivee du plan
|
||||
ansible -i "$SETOPS_INVENTAIRE" modeles_vm -m ping
|
||||
ansible -i "$SETOPS_INVENTAIRE" modeles_vm -m setup
|
||||
ssh ansible@10.0.2.99
|
||||
ansible -i instance/inventories/lab/hosts.yml modeles_vm -m ping
|
||||
ansible -i instance/inventories/lab/hosts.yml modeles_vm -m setup
|
||||
```
|
||||
|
||||
`SETOPS_INVENTAIRE` est exporté par le `Makefile`, qui **résout** le nom de l'inventaire
|
||||
(`INVENTAIRE_LAB` essaie `lab`, puis `principal`, puis `production`) — cette instance-ci
|
||||
n'a pas de `lab/`, et un chemin écrit en dur y échouait. Adapter l'utilisateur et
|
||||
l'adresse IP selon l'inventaire ainsi résolu.
|
||||
Adapter l'utilisateur et l'adresse IP selon `instance/inventories/lab/hosts.yml`.
|
||||
|
||||
### Prérequis côté dépôt
|
||||
|
||||
|
|
@ -110,7 +107,7 @@ modeles_vm
|
|||
Les variables du template sont dans :
|
||||
|
||||
```text
|
||||
instance/inventories/<inventaire>/group_vars/modeles_vm.yml
|
||||
instance/inventories/production/group_vars/modeles_vm.yml
|
||||
```
|
||||
|
||||
### Commande de préparation
|
||||
|
|
@ -156,7 +153,7 @@ Un résultat `changed=0` à la relance est le signal que le playbook est idempot
|
|||
|
||||
| Symptôme | Cause probable | Action |
|
||||
| --- | --- | --- |
|
||||
| `UNREACHABLE` | IP, SSH, utilisateur ou clé SSH incorrecte. | Vérifier l'inventaire résolu (`$SETOPS_INVENTAIRE`), cloud-init et tester `ssh`. |
|
||||
| `UNREACHABLE` | IP, SSH, utilisateur ou clé SSH incorrecte. | Vérifier `instance/inventories/lab/hosts.yml`, cloud-init et tester `ssh`. |
|
||||
| échec `become` | Sudo NOPASSWD absent ou utilisateur non autorisé. | Corriger l'accès sudo initial, puis relancer `make preparer-modele`. |
|
||||
| échec APT | DNS, passerelle, miroir Debian ou verrou APT. | Vérifier réseau, DNS et processus APT en cours. |
|
||||
| erreur handler SSH | Handler manquant ou nom `notify` incohérent. | Vérifier les handlers du rôle SSH avant de relancer. |
|
||||
|
|
|
|||
|
|
@ -50,17 +50,11 @@ statut fédéré/local et production ; signale toute **collision d'index** :
|
|||
make instances
|
||||
```
|
||||
```
|
||||
INSTANCE INDEX VLAN FEDERE PROD
|
||||
OPS-Chezlepro-lab 13 1131-1136 LOCAL non
|
||||
* OPS-Chezlepro 17 1171-1176 oui oui
|
||||
OPS-Technolibre 23 1231-1236 oui non
|
||||
* = instance active (symlink 'instance'). Basculer : make instance-utiliser NOM=<depot>
|
||||
★ OPS-Chezlepro 13 1131-1136 fédérée prod
|
||||
OPS-Technolibre 2 1021-1026 fédérée
|
||||
OPS-Chezlepro-lab 1 1011-1016 local
|
||||
```
|
||||
|
||||
*(Sortie réelle du 2026-09-06. **Ne pas se fier aux index d'un exemple** : ils bougent —
|
||||
celui de Chezlepro a changé au moins une fois, Technolibre est passé de 11 à 23. Le seul
|
||||
endroit qui dit vrai est `plan/nomenclature.yml` de chaque dépôt, et cette commande.)*
|
||||
|
||||
**Basculer l'active** — le symlink, avec garde-fous (le dossier existe, `instance` est
|
||||
bien un symlink) :
|
||||
|
||||
|
|
@ -70,9 +64,8 @@ make instance-utiliser NOM=OPS-Technolibre # bascule (l'inventaire suit le
|
|||
```
|
||||
|
||||
Rien à « recharger » : l'inventaire vit **dans** le dépôt de l'instance, il suit le lien.
|
||||
Le GUI (onglet **Réseau**) montre la même flotte et les collisions — **et sait basculer**,
|
||||
par le bouton « Activer » de chaque instance. *(Ce paragraphe affirmait le contraire — « la
|
||||
bascule reste au CLI » — jusqu'au 2026-09-06 ; c'était vrai avant que le bouton n'existe.)*
|
||||
Le GUI (onglet **Réseau**) montre la même flotte et les collisions ; la bascule reste au
|
||||
CLI (chirurgie de symlink, mal placée dans une interface web).
|
||||
|
||||
**Deux réflexes.** (1) Avant tout déploiement, `make instance-courante` : la seule vraie
|
||||
façon de se tromper est de déployer sur la mauvaise flotte (la colonne `prod` est là pour
|
||||
|
|
@ -104,7 +97,7 @@ FRERES.glob("*/plan/nomenclature.yml") # tout dépôt frère ayant un plan
|
|||
Une instance **est** donc un dossier : (1) **frère** du moteur (`../OPS-Chezlepro`,
|
||||
`../OPS-Technolibre`…), (2) portant un **`plan/nomenclature.yml`**, (3) avec un **`index`**.
|
||||
De là : **active** = ce que résout le symlink `instance` ; **fédérée** = `index` présent
|
||||
*et* `federe ≠ false`. Les **modèles** (`exemples/modeles/…` ici, `Set-OPS-modeles/…` pour les modèles privés) sont un cran plus
|
||||
*et* `federe ≠ false`. Les **modèles** (`Set-OPS-Modeles/integral/…`) sont un cran plus
|
||||
profond — le glob ne les attrape pas, volontairement.
|
||||
|
||||
Conséquence : « inscrire » une instance = la déposer à côté des autres. Rien à éditer,
|
||||
|
|
@ -131,9 +124,6 @@ sable met `false` et affiche « bac à sable ».
|
|||
et la cible Proxmox (`proxmox.yml`).
|
||||
3. Créer la voûte unique depuis le gabarit (cf. [`config-proxmox.md`](config-proxmox.md)) :
|
||||
`cp exemples/vault.exemple.yml …/group_vars/all/vault.yml` puis `ansible-vault encrypt`.
|
||||
**Et poser SA clé** — une voûte, une clé : `~/.config/setops-vault-ops-clientx`, nom
|
||||
dérivé du dossier en minuscules. `python3 scripts/voutes.py etat` confirme que le moteur
|
||||
la trouve.
|
||||
4. `make instance-utiliser NOM=OPS-ClientX` puis `make instancier-appliquer`,
|
||||
`make inventaire-ui`.
|
||||
|
||||
|
|
@ -154,7 +144,7 @@ Pour que plusieurs écosystèmes **coexistent** sur une même fabric sans collis
|
|||
instance reçoit un **`index`** (unique champ d'adressage de `plan/nomenclature.yml` :
|
||||
`index: N`). **Rien d'autre n'est écrit à la main** — la nomenclature ne garde que le
|
||||
*modèle* (libellés de zones + placement des fonctions) ; supernet, sous-réseaux,
|
||||
passerelles, VLAN et VMID se **dérivent** (`scripts/inventory_rules.py` : `supernet_de`,
|
||||
passerelles, VLAN et VMID se **dérivent** (`scripts/inventory_rules` : `supernet_de`,
|
||||
`base3_de`, `passerelle_de`, `vlan_de`). Changer `index` rederive tout le réseau — et la
|
||||
preuve **P20** interdit tout adressage stocké.
|
||||
|
||||
|
|
@ -176,13 +166,10 @@ mêmes VLAN/VMID : c'est la collision que `make instances` et **P21** attrapent.
|
|||
`federe: false` : il est alors **exclu du réseau convergé** (devis, `make instances` le
|
||||
montre « local »). Il garde son adressage dérivé et reste déployable sur *son* infra.
|
||||
|
||||
**Plafond : le 2ᵉ octet IPv4.** `valider_index` borne l'index à **0–255**
|
||||
(`INDEX_MIN`/`INDEX_MAX`, `scripts/inventory_rules.py`) — la garde est posée **à la source
|
||||
de la dérivation**, donc aucune fonction ne peut fabriquer une adresse hors bornes, d'où
|
||||
qu'on l'appelle. En pratique, **éviter 0** : `10.0.x` porte déjà les réseaux de service du
|
||||
site. Le décalage de +10, retiré le 2026-08-12, confisquait dix valeurs — et surtout, il
|
||||
empêchait de lire l'index directement dans l'adresse. Le VLAN (≤ 4094) autoriserait
|
||||
jusqu'à 308, le VMID bien plus : c'est donc l'adressage IP qui plafonne. Au-delà d'une poignée d'instances
|
||||
**Plafond théorique : 255 écosystèmes fédérés** (index 1 à 255), borné par l'IPv4
|
||||
`10.<index>` (2ᵉ octet 1→255). Le décalage de +10, retiré le 2026-08-12, en confisquait
|
||||
dix — et surtout, il empêchait de lire l'index directement dans l'adresse. Le VLAN (≤ 4094) autorise jusqu'à 308, le VMID bien
|
||||
plus — c'est donc l'adressage IP qui plafonne. Au-delà d'une poignée d'instances
|
||||
co-localisées, confier l'allocation à un **IPAM** (NetBox) plutôt qu'au moteur — cf.
|
||||
[`positionnement.md`](positionnement.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -21,16 +21,14 @@ infra-pki-01
|
|||
infra-edge-01
|
||||
infra-mail-01
|
||||
infra-dns-01
|
||||
edge-mta-01
|
||||
idm-01
|
||||
data-sql-01
|
||||
data-01
|
||||
obs-01
|
||||
mon-01
|
||||
forge-01
|
||||
collab-01
|
||||
web-frontal-01
|
||||
web-dorsal-01
|
||||
ops-01
|
||||
```
|
||||
|
||||
La couche applicative web suit le même format. Le tier est porté par la fonction, au singulier puisqu'il nomme une instance :
|
||||
|
|
@ -50,13 +48,11 @@ Les anciens noms de test `web-01` et `web-02` sont retirés. Ils ne doivent pas
|
|||
| `infra-pki-01` | `serveur_step_ca` |
|
||||
| `infra-edge-01` | `serveur_nginx` |
|
||||
| `infra-mail-01` | `serveur_dovecot` (mail-store) |
|
||||
| `edge-mta-01` | `serveur_postfix`, `serveur_rspamd` (ce qui parle à l'extérieur) |
|
||||
| `infra-dns-01` | `serveur_powerdns`, `serveur_resolveur` |
|
||||
| `infra-dns-01` | `serveur_powerdns` |
|
||||
| `idm-01` | `serveur_openldap`, `serveur_keycloak` |
|
||||
| `data-sql-01` | `serveur_postgresql`, `serveur_redis` |
|
||||
| `data-01` | `serveur_postgresql`, `serveur_redis` |
|
||||
| `obs-01` | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` |
|
||||
| `mon-01` | `serveur_icinga`, `serveur_icingaweb2`, `serveur_oauth2_proxy` |
|
||||
| `ops-01` | `serveur_ops`, `serveur_ops_tenant` (le runner de l'écosystème) |
|
||||
| `mon-01` | `serveur_icinga` |
|
||||
| `forge-01` | `serveur_forgejo` |
|
||||
| `collab-01` | `serveur_nextcloud`, `serveur_collabora` |
|
||||
| `web-frontal-01`, `web-frontal-02` | `serveur_web_frontal` |
|
||||
|
|
@ -64,47 +60,45 @@ Les anciens noms de test `web-01` et `web-02` sont retirés. Ils ne doivent pas
|
|||
|
||||
Les groupes restent fins et composables. La cohabitation se fait en associant plusieurs groupes au même hôte.
|
||||
|
||||
## VMID et adressage : tout dérive du seed `index`
|
||||
## Plages VMID
|
||||
|
||||
> **Cette section décrivait le modèle d'avant le multi-instance**, et rien n'y était plus
|
||||
> vrai : un réseau unique `10.0.0.0/16`, des VLAN 11 à 15, des plages de VMID à cinq
|
||||
> chiffres (`91xxx`…`99xxx`). Il n'y a plus de plages à réserver, et il n'y a plus *un*
|
||||
> réseau : chaque écosystème dérive le sien.
|
||||
| Plage | Usage |
|
||||
| --- | --- |
|
||||
| `91xxx` | fondations transversales : PKI, reverse proxy, SMTP |
|
||||
| `92xxx` | identité : LDAP, SSO |
|
||||
| `93xxx` | données et cache : PostgreSQL, Redis |
|
||||
| `94xxx` | observabilité et supervision |
|
||||
| `95xxx` | applications internes |
|
||||
| `99xxx` | modèles, essais initiaux ou exceptions documentées |
|
||||
|
||||
Une instance reçoit **un seul champ d'adressage** : `index`, dans
|
||||
`instance/plan/nomenclature.yml`. Tout le reste s'en déduit — et la preuve **P20** interdit
|
||||
de stocker un adressage quelconque (`supernet`, `sous_reseau`, `passerelle`, `vlan`).
|
||||
## Plan d'adressage interne
|
||||
|
||||
Réseau interne unique : `10.0.0.0/16`. Segmentation par fonction, un `/24` et un VLAN par catégorie, **3ᵉ octet = VLAN** (L2 alignée sur L3).
|
||||
|
||||
| Catégorie | VLAN | Sous-réseau | Passerelle |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 — Fondations / infra | 11 | `10.0.11.0/24` | `10.0.11.1` |
|
||||
| 2 — Identité | 12 | `10.0.12.0/24` | `10.0.12.1` |
|
||||
| 3 — Données | 13 | `10.0.13.0/24` | `10.0.13.1` |
|
||||
| 4 — Observabilité | 14 | `10.0.14.0/24` | `10.0.14.1` |
|
||||
| 5 — Applications | 15 | `10.0.15.0/24` | `10.0.15.1` |
|
||||
|
||||
Adresse d'hôte (4ᵉ octet) : **`service × 10 + NN`**. `.1` = passerelle ; `.2`–`.9` réservés. Exemple : `web-dorsal-01` (catégorie 5, service 4, NN 01) → `10.0.15.41`.
|
||||
|
||||
Tout se dérive de la fonction de l'hôte, et la source unique machine-lisible est **`instance/plan/nomenclature.yml`** :
|
||||
|
||||
```text
|
||||
supernet = 10.<index>.0.0/16
|
||||
zone (3 oct.) = 10.<index>.(15 + catégorie)
|
||||
sous-réseau = 10.<index>.(15 + catégorie).0/24
|
||||
passerelle = 10.<index>.(15 + catégorie).1 ← premier hôte du /24
|
||||
VLAN = 1000 + index × 10 + zone ← unique sur tout le trunk convergé
|
||||
VMID = <VLAN><hôte sur 3 chiffres><rang sur 2> ← neuf chiffres, miroir de l'IP
|
||||
adresse IP = 10.<index>.(15 + catégorie).<hôte>
|
||||
hostname = <fonction>-<NN>
|
||||
VMID = 9 · catégorie · service · NN
|
||||
VLAN = catégorie.vlan
|
||||
IP = 10.0.<vlan>.(service × 10 + NN)
|
||||
```
|
||||
|
||||
Le VMID **est** l'adresse, relue : `117602101` se lit `1176` (VLAN) · `021` (hôte) · `01`
|
||||
(rang). On retrouve la VM depuis son adresse, et l'inverse, sans registre.
|
||||
`make inventaire-ui` lit ce registre et **propose** automatiquement VMID, VLAN, IP et passerelle quand on nomme un hôte. La sécurité entre zones se fera par règles inter-zones (nftables / edge), pas par l'adressage.
|
||||
|
||||
Exemple, l'écosystème de référence (`index: 17`) : supernet `10.17.0.0/16`, zones
|
||||
`10.17.16.0/24` à `10.17.21.0/24`, VLAN `1171` à `1176`. `infra-edge-01` y vaut
|
||||
`10.17.16.11`, et son VMID `117101101` se relit `1171` · `011` · `01`.
|
||||
Contrainte : `NN` de 01 à 09 par fonction (l'octet hôte reste dans le bloc du service). Au-delà, ouvrir une nouvelle fonction/service dans `instance/plan/nomenclature.yml`.
|
||||
|
||||
*(L'index se lit directement dans le second octet — c'est ce qui permet de reconnaître le
|
||||
tenant d'une adresse à l'œil. `valider_index` le borne, et la même borne protège un second
|
||||
plafond : à l'index 255, le VLAN vaut `3550 + zone`, sous les 4094 du 802.1Q.)*
|
||||
|
||||
**Changer `index` redérive tout le réseau de l'écosystème.** C'est ce qui rend un tenant
|
||||
portable d'un site à l'autre, et c'est pourquoi rien ne doit être écrit à la main. La preuve
|
||||
**P21** refuse que deux instances fédérées partagent un index. Détail complet :
|
||||
[`multi-instances.md`](multi-instances.md).
|
||||
|
||||
`make inventaire-ui` lit ce registre et **propose** VMID, VLAN, IP et passerelle quand on
|
||||
nomme un hôte. La sécurité entre zones ne repose pas sur l'adressage mais sur le registre
|
||||
des flux (nftables de l'hôte, pare-feu de l'hyperviseur, frontière) —
|
||||
[`flux-conception.md`](flux-conception.md).
|
||||
La segmentation `10.0.0.0/16` remplace l'ancienne plage d'essais `192.168.12.x`.
|
||||
|
||||
## Variables de provisioning d'hôte
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,9 @@ l'inventaire Ansible en est **généré**. Le dépôt est la définition ; chaqu
|
|||
est une instance. Ce document décrit le modèle, les registres, les commandes et
|
||||
le flux de travail.
|
||||
|
||||
> Règle d'or : **`instance/inventories/<inventaire>/hosts.yml` est GÉNÉRÉ. Ne jamais l'éditer
|
||||
> Règle d'or : **`instance/inventories/production/hosts.yml` est GÉNÉRÉ. Ne jamais l'éditer
|
||||
> à la main.** On édite le *plan* puis on régénère (`make instancier-appliquer`).
|
||||
|
||||
> **`<inventaire>` n'est pas un nom, c'est une place.** Le dépôt n'impose pas comment une
|
||||
> instance nomme son inventaire : **le moteur le cherche**, dans l'ordre `principal`, puis
|
||||
> `production` (`scripts/inventory_rules.py` : `ORDRE_INVENTAIRE` ; pour la construction du
|
||||
> gabarit, `ORDRE_INVENTAIRE_MODELE` essaie `lab` d'abord). La flotte utilise `principal` ;
|
||||
> le modèle public livré dans `exemples/modeles/socle/` utilise `production`, et le
|
||||
> QUICKSTART l'écrit tel quel parce que c'est ce que son lecteur a sous la main. Les
|
||||
> documents de doctrine, eux, écrivent `<inventaire>` : coder l'un des deux noms en dur y
|
||||
> serait faux pour la moitié des lecteurs — et ça l'a été jusqu'au 2026-09-06.
|
||||
|
||||
---
|
||||
|
||||
## 1. Le modèle : deux ancres, cinq liens
|
||||
|
|
@ -43,14 +34,11 @@ liaison — seulement une **capacité** qu'une VM fournit (le rôle appliqué).
|
|||
|
||||
## 2. Les registres (source unique de vérité)
|
||||
|
||||
Machine-lisibles, validés, consommés par le GUI, le CLI et Ansible. **Ils vivent dans
|
||||
l'instance** (`instance/plan/`), pas dans le moteur — c'est toute la séparation
|
||||
moteur/instance. Seul `dependances-groupes.yml` est sous `docs/`, parce qu'il décrit une
|
||||
propriété des *rôles*, la même pour toutes les instances.
|
||||
Tous sous `docs/`, machine-lisibles, validés, consommés par le GUI, le CLI et Ansible.
|
||||
|
||||
| Registre | Décrit | Champs clés |
|
||||
| --- | --- | --- |
|
||||
| `nomenclature.yml` | nommage & adressage | **`index`** (le seed, seul champ d'adressage), `fonctions` (catégorie/service), `categories` (libellés de zones), `cidr_hote`, `reservations`. **Ni `supernet`, ni `vlan`, ni `passerelle` : P20 les refuse** — ils se dérivent. |
|
||||
| `nomenclature.yml` | nommage & adressage | `fonctions` (catégorie/service), `categories` (VLAN/sous-réseau/passerelle), `supernet` |
|
||||
| `serveurs.yml` | les VM du plan | `fonction`, `etat` (actif/planifie), placement Proxmox (`noeud`/`stockage`/`disque`/`memoire`/`coeurs`), `integrations` (les `client_*` **facultatives** seulement — les universelles viennent du rôle, voir `integrations-vm.md`) |
|
||||
| `applications.yml` | les applications | `groupe` (capacité/rôle), `hote` (VM), `port`, `requiert`, `expose`, (+ bases via consommateur) |
|
||||
| `bases-donnees.yml` | serveurs de BD + bases | `serveurs_bd` ; `bases_donnees` : `serveur`/`base`/`proprietaire`/`secret`(Vault), `consommateur` + `portee` (`application`/`groupe`/`hote`), `usage` |
|
||||
|
|
@ -58,12 +46,9 @@ propriété des *rôles*, la même pour toutes les instances.
|
|||
| `dependances-groupes.yml` | prérequis entre groupes | `requiert_groupes_actifs` |
|
||||
|
||||
### Dérivations clés
|
||||
- **Nommage/adressage** : tout part du seed `index` et de la `fonction` de l'hôte.
|
||||
`supernet = 10.<index>.0.0/16`, `zone = 10.<index>.(15+catégorie)`,
|
||||
`VLAN = 1000 + index×10 + zone`, `VMID = <VLAN><octet-hôte><rang>` — neuf chiffres,
|
||||
miroir de l'IP. Voir `docs/nomenclature-vm.md`. *(Les formules à cinq chiffres et le
|
||||
réseau unique `10.0.x` qui figuraient ici décrivaient le modèle d'avant le
|
||||
multi-instance.)*
|
||||
- **Nommage/adressage** : tout part de la `fonction` de l'hôte (`web-frontal-03`).
|
||||
`VMID = 9·catégorie·service·NN`, `VLAN = catégorie.vlan`,
|
||||
`IP = 10.0.<vlan>.(service×10 + NN)`. Voir `docs/nomenclature-vm.md`.
|
||||
- **DSN** (lien application↔base) : `<type>://<proprietaire>:<secret>@<hôte>:<port>/<base>`.
|
||||
Une application reçoit les bases où `(portee=application ET consommateur=elle)`
|
||||
OU `(portee=groupe ET consommateur=son groupe)` OU `(portee=hote ET consommateur=son hôte)`.
|
||||
|
|
@ -98,33 +83,19 @@ c'est le feu vert pour appliquer.
|
|||
```
|
||||
|
||||
### Via le GUI — `make inventaire-ui`
|
||||
|
||||
**Douze vues**, éditables ou dérivées :
|
||||
Cinq vues :
|
||||
|
||||
| Vue | Rôle |
|
||||
| --- | --- |
|
||||
| **Serveurs** *(éditable)* | les VM du plan : fonction/état/placement/intégrations (VMID·IP·VLAN dérivés en direct) ; bouton **« Appliquer le plan »** |
|
||||
| **Applications** *(éditable)* | groupe/hôte/port/requiert/expose, et les **liens** acceptés par le rôle |
|
||||
| **Bases** *(éditable)* | serveurs de BD et bases (portée + consommateur), DSN affiché (secret masqué) |
|
||||
| **Domaines** *(éditable)* | zones publiques vs internes, autorité · edge · DNSSEC, expositions |
|
||||
| **Nomenclature** *(éditable)* | le modèle dont tout l'adressage dérive : zones, fonctions (catégorie · service), réservations. Chaque fonction affiche **ce qu'elle dérive** (VLAN, sous-réseau, bloc d'hôtes) et les VM qui la portent. L'`index` y est **montré, pas éditable** : il est alloué par le site |
|
||||
| **Intégrations** *(éditable)* | la matrice serveurs × intégrations ; les universelles en ✓ non décochables |
|
||||
| **Intrants** *(éditable)* | les intrants de base — identité, Proxmox, fabric, et la liste de rappel des secrets (lecture seule) |
|
||||
| **Inventaire** *(lecture seule)* | l'inventaire **généré** — vue d'ensemble des hôtes |
|
||||
| **Chaîne** *(lecture seule)* | vue holistique par hôte : groupes → rôles, et par application ses `expose` / `requiert` / bases |
|
||||
| **Flux** *(lecture seule)* | la matrice d'audit des flux — source de nftables et justification lisible |
|
||||
| **Couches** *(lecture seule)* | l'ordre de déploiement en six couches |
|
||||
| **Réseau** *(lecture seule + bascule)* | la flotte multi-instances, les collisions d'index, et le bouton **« Activer »** |
|
||||
| **Inventaire** | **lecture seule** (inventaire généré) — vue d'ensemble des hôtes |
|
||||
| **Serveurs** | éditer les VM du plan : fonction/état/placement/intégrations (VMID·IP·VLAN dérivés en direct) ; bouton **« Appliquer le plan »** |
|
||||
| **Chaîne** | vue holistique par hôte : groupes → rôles, et par application ses `expose` / `requiert` / bases (DSN) |
|
||||
| **Applications** | éditer les applications : groupe/hôte/port/requiert/expose |
|
||||
| **Bases** | éditer serveurs de BD et bases (portée + consommateur), DSN affiché |
|
||||
|
||||
Édition → **Sauvegarder** (écrit le registre) → **Appliquer le plan** (régénère
|
||||
`hosts.yml`). L'écriture directe de l'inventaire est refusée (409).
|
||||
|
||||
**Les formulaires des six registres sont générés** depuis `docs/audit/schema-plan.json`
|
||||
(`make schema`), dérivé des constantes du moteur. La sauvegarde en dérive aussi : un
|
||||
champ ajouté au plan apparaît à l'écran *et* arrive au fichier. Deux exceptions
|
||||
déclarées au schéma (`x-editeur`) : la matrice des intégrations et l'éditeur de liens
|
||||
gardent leur éditeur propre, plus riche que ce que le schéma sait dire.
|
||||
|
||||
### Via le CLI / `make`
|
||||
Chaque registre a son script miroir et ses cibles `make` :
|
||||
|
||||
|
|
@ -166,7 +137,7 @@ registres + **`node --check` du JS du GUI**).
|
|||
La règle de résolution vit **une seule fois**, en Python (`scripts/inventory_rules.py`),
|
||||
et est exposée à Ansible par un *filter plugin* (`filter_plugins/registres.py`) :
|
||||
`bases_de_application`, `applications_de_hote`, `expositions_des_applications`,
|
||||
`chaine_connexion`. Les playbooks par application (`serveur_web_dorsal`, `serveur_web_frontal`)
|
||||
`chaine_connexion`. Les playbooks par application (`serveur_web_dorsal`/`_frontaux`)
|
||||
itèrent ainsi sur les applications de l'hôte et résolvent leurs DSN.
|
||||
|
||||
---
|
||||
|
|
@ -188,7 +159,7 @@ C'est ainsi que le plan a été initialisé sans perte, avec diff vide vérifié
|
|||
## 8. Moteur et instance : deux dépôts
|
||||
|
||||
Le **moteur** (ce dépôt, `Set-OPS`) est générique et partageable ; il ne contient
|
||||
aucune donnée d'instance. Une **instance** (le plan + l'inventaire d'une organisation) vit
|
||||
aucune donnée d'instance. Une **instance** (le plan + l'inventaire d'un loup) vit
|
||||
dans son **propre dépôt** (ex. `OPS-monatelier`).
|
||||
|
||||
Le moteur localise l'instance via **`SETOPS_INSTANCE`** (défaut : `instance`). Deux
|
||||
|
|
@ -197,7 +168,7 @@ modèles :
|
|||
- **Modèle A — dépôts frères** (en cours) : moteur et instance côte à côte ; un
|
||||
symlink `instance -> ../OPS-monatelier` (gitignoré) fait que le défaut résout
|
||||
l'instance sans configuration. Idéal quand on développe le moteur *et* l'instance.
|
||||
- **Modèle B — moteur en sous-module** (futur, quand plusieurs écosystèmes vivront chez des exploitants distincts) : l'instance épingle
|
||||
- **Modèle B — moteur en sous-module** (futur, pour la meute) : l'instance épingle
|
||||
une version du moteur ; `SETOPS_INSTANCE` pointe la racine de l'instance.
|
||||
|
||||
Pour brancher une instance (modèle A) :
|
||||
|
|
|
|||
|
|
@ -44,13 +44,7 @@ Raisons assumées :
|
|||
1. **Souveraineté** — mission explicite du dépôt. NetBox + AWX + Backstage = trois
|
||||
applications lourdes à héberger et maintenir (Django + PostgreSQL, etc.). Set-OPS
|
||||
reste possédé en entier, sans dépendance.
|
||||
2. **Bon dimensionnement** — mais l'argument a bougé, et il faut le dire honnêtement.
|
||||
Ce document a longtemps écrit « ~13 VM ». Le moteur pilote aujourd'hui **quatre plans
|
||||
vivants** (mesuré le 2026-09-06) : Chezlepro 14 VM, Technolibre 15, le lab 15, plus les
|
||||
7 machines du site — **51 VM déclarées**, réparties sur des écosystèmes qui ne se parlent
|
||||
pas. Ça reste très loin de l'échelle entreprise pour laquelle NetBox est fait,
|
||||
et le seuil du §4 n'est pas franchi. Mais la courbe monte : c'est **le** chiffre à
|
||||
regarder quand on se demande si la décision tient encore.
|
||||
2. **Bon dimensionnement** — ~13 VM. NetBox est de la machinerie d'échelle entreprise.
|
||||
3. **Modèle sur-mesure** — NetBox exprimerait nos DSN / expositions à coups de
|
||||
*custom fields* et de plugins, moins naturellement que nos registres.
|
||||
4. **Maîtrise** — chaque ligne est comprise et auditable.
|
||||
|
|
@ -59,29 +53,13 @@ Raisons assumées :
|
|||
|
||||
## 3. Ce que le maison NE fait PAS (et que les outils mûrs ont)
|
||||
|
||||
> **Cette liste a été corrigée le 2026-09-08.** Deux de ses cinq lignes n'étaient plus
|
||||
> vraies : elles décrivaient des manques que le dépôt a comblés **autrement**, et les
|
||||
> garder en « manques » aurait fini par justifier d'adopter un outil pour un besoin déjà
|
||||
> couvert. Une carte des seuils qui se trompe ne fait pas perdre du temps : elle fait
|
||||
> franchir un seuil qui ne l'est pas.
|
||||
À garder en tête honnêtement — ce sont des fonctions qu'on n'a pas, pas des bugs :
|
||||
|
||||
Ce qui manque vraiment :
|
||||
|
||||
- **historique/audit des changements** au-delà de `git` — un journal applicatif, avec ses
|
||||
auteurs et ses motifs, que `git log` ne rend qu'imparfaitement ;
|
||||
- **webhooks / intégrations tierces, API riche** (REST/GraphQL) — il n'y en a aucune, et
|
||||
aucun consommateur ne la réclame aujourd'hui ;
|
||||
- **écosystème de plugins, communauté, correctifs de sécurité maintenus par d'autres.**
|
||||
C'est le seul point qui joue contre le maison **en permanence** : il ne dépend d'aucun
|
||||
seuil, il s'aggrave tout seul avec le temps. À relire chaque année, pas quand un besoin
|
||||
apparaît.
|
||||
|
||||
Ce qui était listé comme manquant et ne l'est plus :
|
||||
|
||||
| Ancien manque | Ce qui le couvre, et pourquoi c'est différent |
|
||||
|---|---|
|
||||
| ~~RBAC multi-utilisateurs~~ | **Résolu, et par un mécanisme plus fort.** Trois classes d'acteurs aux pouvoirs disjoints existent — le poste de l'exploitant, le **runner de site** (matérialiser ; ne rentre jamais chez un tenant) et les **runners de tenant** (configurer). La séparation est **cryptographique** — une voûte, une clé (2026-08-28) — et non applicative : c'est la *présence des fichiers* qui borne le pouvoir, jamais une table de permissions qu'une faille de l'application contournerait. |
|
||||
| ~~Détection de conflits IPAM, réservations~~ | **Sans objet par construction.** Un IPAM sert à *allouer* ; ici rien ne s'alloue, tout dérive du seed `index`. Et cinq preuves tiennent déjà ce qu'un IPAM vérifierait : **P20** (aucun adressage stocké), **P21** (collisions d'index), **P23** (chevauchement d'underlay), **P28** (pools), **P33** (ports co-localisés). Adopter un IPAM remplacerait une propriété *par construction* par un contrôle *a posteriori*. |
|
||||
- historique/audit des changements (au-delà de `git`) ;
|
||||
- RBAC multi-utilisateurs ;
|
||||
- détection de conflits IPAM, réservations, gestion d'adresses à grande échelle ;
|
||||
- webhooks / intégrations tierces, API riche (REST/GraphQL) ;
|
||||
- écosystème de plugins, communauté, correctifs de sécurité maintenus par d'autres.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -97,38 +75,16 @@ ne lui ajoute pas de fonctionnalités de type NetBox/AWX.
|
|||
Adopter l'outil du marché — **sans réécrire**, en branchant Ansible/notre GUI
|
||||
par-dessus — dès qu'un de ces besoins devient réel :
|
||||
|
||||
> **Ce tableau a été écrit avant que les runners existent**, et deux de ses lignes visaient
|
||||
> des besoins depuis couverts (§3). Corrigé le 2026-09-08.
|
||||
|
||||
| Besoin qui apparaît | Adopter |
|
||||
| --- | --- |
|
||||
| **Plusieurs HUMAINS, aux portées disjointes, sur des machines qui ne sont pas les tiennes** | à trancher — voir ci-dessous, c'est le seuil qui approche et qu'aucune ligne ne nommait |
|
||||
| Historique d'audit applicatif, secrets/credentials centralisés pour des tiers | **AWX** (exécution) |
|
||||
| Source de vérité **partagée** entre organisations, API/webhooks avec des consommateurs réels | **NetBox** (et `nb_inventory` remplacerait notre générateur) |
|
||||
| RBAC, plusieurs opérateurs, historique d'audit, secrets/credentials centralisés | **AWX** (exécution) |
|
||||
| IPAM sérieux, détection de conflits, source de vérité partagée, API/webhooks | **NetBox** (et `nb_inventory` remplace notre générateur) |
|
||||
| Catalogue de services / portail développeur, ownership, scaffolding | **Backstage** |
|
||||
| Provisionnement de VM déclaratif et reproductible à plus grande échelle | **Terraform** (Proxmox) ou **NixOS + Colmena** |
|
||||
|
||||
*Retiré de ce tableau : « RBAC » et « IPAM sérieux, détection de conflits ». Les deux sont
|
||||
couverts (§3), et les garder ici aurait fait adopter un outil pour un besoin déjà rempli.*
|
||||
|
||||
### Le seuil qui approche, et qu'aucune ligne ne nommait
|
||||
|
||||
**L'émancipation.** Le GUI écoute sur `127.0.0.1` avec un jeton par session : un modèle
|
||||
**mono-utilisateur**, parfait tant que l'exploitant est une personne à son poste. Le jour où
|
||||
un tenant est exploité par **son** organisation — c'est la trajectoire de
|
||||
[`filiation-emancipation.md`](filiation-emancipation.md), et l'offre destinée aux OBNL y
|
||||
mène — il y a plusieurs humains, aux portées disjointes, sur des machines qui ne sont pas
|
||||
les tiennes.
|
||||
|
||||
Ce n'est **pas** AWX qu'appelle ce seuil : les runners portent déjà la séparation des
|
||||
pouvoirs, cryptographiquement. Ce qu'il appelle, c'est une décision sur la **façon dont le
|
||||
GUI s'ouvre à quelqu'un d'autre** — et elle n'est pas prise. La nommer ici est le minimum :
|
||||
*un seuil qu'on ne nomme pas est un seuil qu'on franchit sans le voir.*
|
||||
|
||||
**Test simple** : si on se surprend à vouloir réimplémenter une de ces fonctions
|
||||
dans Set-OPS, c'est le signal d'adopter l'outil correspondant plutôt que de
|
||||
prolonger le maison. **Mais vérifier d'abord que le besoin n'est pas déjà couvert
|
||||
autrement** — c'est exactement l'erreur que ce tableau portait.
|
||||
prolonger le maison.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -136,15 +92,6 @@ autrement** — c'est exactement l'erreur que ce tableau portait.
|
|||
|
||||
- On **garde** le plan de contrôle maison : il fonctionne, il est souverain et
|
||||
bien dimensionné pour aujourd'hui. Pas de réécriture sur NetBox « par principe ».
|
||||
- On **gèle** son périmètre fonctionnel (voir §4). **Le 2026-09-08, c'est la carte des
|
||||
seuils qui a été corrigée, pas le gel qui a été levé** — la question « et si on retirait
|
||||
le gel ? » a montré que deux seuils étaient mal posés, pas que le gel était de trop.
|
||||
- On **gèle** son périmètre fonctionnel (voir §4).
|
||||
- On **réévalue** à l'échéance d'un seuil ci-dessus, ou si la charge de
|
||||
maintenance du maison dépasse le coût d'héberger l'outil mûr.
|
||||
|
||||
> **Ce que le gel n'interdit pas, et qu'on confond souvent avec lui.** Il porte sur les
|
||||
> *fonctions de type NetBox/AWX*, pas sur les **vues**. Montrer à l'écran ce que le moteur
|
||||
> sait déjà — l'écart des dix devis, l'état du diff entre « Sauvegarder » et « Appliquer »,
|
||||
> le périmètre sur lequel un ✅ a porté, les témoins du génome et lequel a décroché — ne
|
||||
> franchit aucun seuil : rien de tout cela n'existe dans NetBox ou AWX, parce que rien de
|
||||
> tout cela n'existe hors de ce modèle.
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
> **Pour qui :** qui **évalue le moteur** — ce qu'il sait faire, et ce qu'il ne sait pas encore.
|
||||
|
||||
> Bilan des capacités du moteur. Établi le 2026-07-02, **revu le 2026-09-06** : la
|
||||
> frontière ⭐/🔧 avait cessé de dire vrai — deux reconstructions depuis zéro (2026-08-13,
|
||||
> puis 2026-09-02) ont fait passer côté ⭐ presque tout ce que le §5 annonçait « à
|
||||
> éprouver ». Fondé sur l'état réel du dépôt (≈65 rôles, ≈60 playbooks, le moteur de plan,
|
||||
> la GUI). **Le détail rôle par rôle vit dans [`catalogue-services.md`](catalogue-services.md)** ;
|
||||
> ici on ne garde que le bilan.
|
||||
> Bilan des capacités du moteur, au 2026-07-02.
|
||||
> Fondé sur l'état réel du dépôt (≈50 rôles, ≈30 playbooks de groupe, le moteur de plan, la GUI).
|
||||
>
|
||||
> Deux niveaux de maturité sont distingués honnêtement :
|
||||
> - **⭐ Prouvé** — déployé et vérifié de bout en bout sur cluster Proxmox réel.
|
||||
|
|
@ -23,9 +19,8 @@
|
|||
complet — PKI, identité, DNS, web, courriel — cloné depuis un golden template durci, piloté par
|
||||
une GUI utilisable sans IA, en multi-tenant, tout en logiciel libre.**
|
||||
|
||||
Le cœur d'infrastructure **et** la couche des services applicatifs (SSO, données, forge,
|
||||
observabilité, supervision, collaboration) sont **prouvés de bout en bout** : la flotte a
|
||||
été rasée puis remontée d'un seul trait, deux fois, sans échec.
|
||||
Le cœur d'infrastructure est **prouvé de bout en bout** ; la couche des services applicatifs
|
||||
(SSO, données, forge, observabilité) est **outillée et prête à éprouver**.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -36,11 +31,8 @@ Pouvoir central : **plan déclaratif → écosystème vivant**. On décrit *quoi
|
|||
|
||||
- **`scripts/instancier.py`** — lit `nomenclature / serveurs / applications / bases-donnees.yml`
|
||||
et **dérive** tout : VMID, IP, groupes d'inventaire, dimensionnement. ⭐
|
||||
- **Nomenclature fédérée** — le seul champ saisi est l'**`index`** ; supernet, VLAN, IP,
|
||||
passerelle et **VMID** en dérivent, sans collision possible entre instances. Le VMID est le
|
||||
**miroir de l'adresse** sur neuf chiffres — `<VLAN><octet d'hôte><rang>`, soit `117602101`
|
||||
pour `1176` · `021` · `01` : on retrouve la VM depuis son adresse, et l'inverse, sans
|
||||
registre. **P20** interdit d'écrire un adressage au plan. ⭐
|
||||
- **Nomenclature fédérée** — un `index` + catégorie + service produit un **VMID
|
||||
`{index}{cat}{svc}{seq}`** et une IP déterministes, sans collision entre instances. ⭐
|
||||
- **Dimensionnement automatique** — chaque rôle porte une `meta/empreinte.yml`
|
||||
(cœurs / RAM / disque) ; l'outil **somme les empreintes et taille la VM** hôte. ⭐
|
||||
- **Multi-instance = multi-tenant** — le symlink `instance/` pointe vers un dépôt par écosystème,
|
||||
|
|
@ -55,10 +47,8 @@ Impératif fondateur : un sysadmin exploite l'outil **sans IA** ; l'IA n'assiste
|
|||
- **GUI souveraine** (`scripts/inventory_gui.py`, stdlib pure, aucune dépendance) : visualise le
|
||||
plan, **bouton « Pousser »** (crée la VM pour un serveur / déploie pour une app ou une base),
|
||||
**sonde de vivacité**, passage auto en « actif », jeton d'authentification. ⭐
|
||||
- **Voûte au déploiement** — secrets chiffrés par Ansible Vault, **jamais en clair** dans la
|
||||
GUI, qui n'en montre que les *noms*. **Une voûte, une clé** (2026-08-28) : chaque dépôt a la
|
||||
sienne, et le `Makefile` les rassemble tout seul (`scripts/voutes.py`), sans rien exporter
|
||||
ni rendre l'exécution interactive. ⭐
|
||||
- **Voûte au déploiement** — secrets chiffrés par Ansible Vault, saisis au déploiement, **jamais
|
||||
en clair** dans la GUI ; usage via le fichier de mot de passe, non interactif. ⭐
|
||||
- **Validation intégrée** — `syntax-check`, `ansible-lint` (profil *production*), `verifier`. ⭐
|
||||
|
||||
## 3. Le socle — un golden template durci
|
||||
|
|
@ -70,13 +60,9 @@ Impératif fondateur : un sysadmin exploite l'outil **sans IA** ; l'IA n'assiste
|
|||
`template_cleanup`.
|
||||
- **SSH** : `ssh_baseline` puis `ssh_hardening` (bascule mot de passe → clé, seulement après
|
||||
validation de l'accès par clé).
|
||||
- **Durcissement** — les **dix** rôles que compose le groupe `serveur_durci` :
|
||||
`hardening_packages`, `sysctl_hardening`, `core_dumps`, `unattended_upgrades`, `apparmor`,
|
||||
`auditd`, `fail2ban_ssh`, `journald`, `ssh_hardening`,
|
||||
`nftables_baseline` (**éteint dans le rôle, armé par le plan** : `hotes_actifs` le met à
|
||||
`true`, le gabarit doré le laisse à `false` — le pare-feu n'est pas une propriété du rôle,
|
||||
c'est une décision de l'instance). Le jeu de règles n'est pas écrit à la main : il est
|
||||
**dérivé du registre des flux** (`meta/flux.yml` → `scripts/resoudre_flux.py`).
|
||||
- **Durcissement** : `apparmor`, `auditd`, `fail2ban_ssh`, `sysctl_hardening`,
|
||||
`hardening_packages`, `unattended_upgrades`, `journald`, `core_dumps`,
|
||||
`nftables_baseline` (installé et préparé, **non activé par défaut**).
|
||||
- **Proxmox** : clonage depuis le golden template + redimensionnement disque (grow-only).
|
||||
|
||||
> Séparation nette : **Proxmox + cloud-init** donnent l'identité initiale de la VM ;
|
||||
|
|
@ -97,42 +83,20 @@ par annuaire LDAP, remise LMTP réseau chiffrée step_ca vers le stockage, accè
|
|||
LDAP, filtrage antispam et signature DKIM en milter. Topologie **MTA dédié en périphérie /
|
||||
boîtes à l'intérieur** (défense en profondeur).
|
||||
|
||||
## 5. Les services applicatifs — prouvés depuis ⭐
|
||||
## 5. Les services outillés — rôles présents 🔧
|
||||
|
||||
Ce paragraphe annonçait, jusqu'au 2026-09-06, des rôles « construits mais à éprouver ».
|
||||
Ils l'ont été : **la reconstruction depuis zéro les a tous repris sur machine nue**
|
||||
(2026-08-13, puis 15/15 et 14/14 hôtes le 2026-09-02, `make valider` à 0 échec).
|
||||
Construits et câblables par le plan ; **à éprouver** en déploiement réel avant de les déclarer
|
||||
prouvés.
|
||||
|
||||
- **SSO web** : `serveur_keycloak` — OpenLDAP source de vérité, fédération LDAP automatisée,
|
||||
courriel en bind LDAP direct. ⭐
|
||||
- **Passerelle SSO** : `serveur_oauth2_proxy` — met au SSO une application sans OIDC natif
|
||||
(éprouvé devant Icinga Web 2). ⭐
|
||||
- **Données** : `serveur_postgresql`, `serveur_redis` — bases et comptes dérivés du registre,
|
||||
TLS `verify-full` de bout en bout. ⭐
|
||||
- **Forge logicielle** : `serveur_forgejo` — Git + PostgreSQL + SSO OIDC. ⭐
|
||||
- **Observabilité** : `serveur_prometheus`, `serveur_grafana`, `serveur_loki`, avec les clients
|
||||
`client_metrique` et `client_journal`. ⭐
|
||||
- **Supervision** : `serveur_icinga`, `serveur_icingaweb2` — Icinga 2 + IcingaDB + Web 2 + BPM.
|
||||
**Sans agent sur les hôtes** : contrôles actifs depuis le cœur, résultats passifs poussés par
|
||||
l'API. ⭐
|
||||
- **Sauvegardes** : `serveur_backup`, `client_backup` — restic hors-nœud, chaque nœud vérifiant
|
||||
son **propre dépôt distant**, restauration éprouvée. ⭐
|
||||
- **Plateforme webapp** : `serveur_web_frontal`, `serveur_web_dorsal` — statique et natif
|
||||
(venv + systemd + nginx), zéro conteneur. ⭐
|
||||
- **Intégrations transverses** : `client_pki`, `client_backup`, `client_smtp`, `client_metrique`,
|
||||
`client_journal`, `client_resolveur`, `client_artefacts`. ⭐
|
||||
|
||||
Reste **🔧 outillé** — déployé, mais dont l'*usage* n'est pas consigné comme preuve :
|
||||
|
||||
- **Collaboration** : `serveur_nextcloud`, `serveur_collabora` (natif, plus de conteneur). Base
|
||||
et client OIDC dérivés du plan, posés par la reconstruction ; le dépôt d'un fichier et
|
||||
l'édition partagée à deux, eux, n'ont pas été consignés. 🔧
|
||||
|
||||
> Deux noms ont été retirés de ce paragraphe parce qu'ils n'ont jamais existé :
|
||||
> `client_supervision` (la supervision est sans agent — cf. `catalogue-services.md`) et les
|
||||
> « méta-rôles d'agrégation » `identity` / `storage`. La conformité passe par
|
||||
> `playbooks/groupes/`, un playbook par groupe ; les répertoires `playbooks/applications/`,
|
||||
> `database/`, `web/`, `monitoring/`, `backup/` ne contiennent qu'un README d'espace réservé.
|
||||
- **SSO web** : `serveur_keycloak` (architecture décidée : OpenLDAP source de vérité, Keycloak
|
||||
fédéré en OIDC, courriel en bind LDAP direct).
|
||||
- **Données** : `serveur_postgresql`, `serveur_redis`.
|
||||
- **Forge logicielle** : `serveur_forgejo`.
|
||||
- **Observabilité** : `serveur_prometheus`, `serveur_grafana`, `serveur_loki`, `serveur_icinga`,
|
||||
avec les clients `client_metrique`, `client_journal`, `client_supervision`.
|
||||
- **Intégrations transverses** : `client_pki`, `client_backup`, `client_smtp`, `client_metrique`, `client_journal`, `client_unbound`.
|
||||
- **Méta-rôles d'agrégation** : `identity`, `applications`, `database`, `web`, `monitoring`,
|
||||
`backup`, `storage`.
|
||||
|
||||
## 6. Les patrons d'ingénierie — la valeur invisible ⭐
|
||||
|
||||
|
|
@ -155,13 +119,9 @@ Reste **🔧 outillé** — déployé, mais dont l'*usage* n'est pas consigné c
|
|||
|
||||
## Prochaines frontières
|
||||
|
||||
- **Consigner l'usage** de la collaboration (dépôt de fichier, édition partagée) — le seul
|
||||
🔧 qui reste.
|
||||
- **Éprouver** les services outillés (Keycloak fédéré, observabilité, PostgreSQL, Forgejo).
|
||||
- **Courriel Étape B** (public) : DNS public (MX, SPF, **DKIM** — clé `setops._domainkey` déjà
|
||||
générée, DMARC), MX externe et réputation, PTR / FCrDNS.
|
||||
- **Les équipements de l'hébergeur** — hyperviseurs, commutateurs, frontière : ni inventaire,
|
||||
ni sauvegarde de configuration, ni supervision. Les *VM* du site en ont depuis le 2026-09-02,
|
||||
les *équipements* non (cf. `hebergeur-exploitation.md`).
|
||||
- **Seuils d'adoption** d'outils tiers (NetBox, AWX) plutôt que de réimplémenter le plan de
|
||||
contrôle maison, qui reste volontairement gelé.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,26 +25,13 @@ listes de stockages et de ponts différentes. Un hébergeur décrit son matérie
|
|||
|
||||
## 2. Le plan d'adressage — la seule chose à respecter à la lettre
|
||||
|
||||
**Un site prend son PROPRE `index`, comme un tenant.** Il n'y a pas de second registre à
|
||||
tenir : le seul seed de tout l'adressage reste l'`index`, et **sites et tenants se
|
||||
partagent la même classe A** — chacun le sien, aucun partagé.
|
||||
|
||||
> **Corrigé le 2026-09-12.** Cette règle disait « un site dérive du même index que son
|
||||
> tenant ». Conséquence mesurée chez l'hébergeur de référence : le plan d'administration
|
||||
> du site vivait dans `10.17.0.0/24`, **à l'intérieur du supernet du locataire
|
||||
> `OPS-Chezlepro`**. Ce n'était pas dangereux — les zones d'un tenant commencent à l'octet
|
||||
> 16 par construction — mais `10.17.0.0/16` désignait alors deux choses : un locataire, et
|
||||
> le plan d'administration de celui qui l'héberge. Deux sens pour une adresse.
|
||||
>
|
||||
> L'exception disparaît : **tout prend un index**. Le seul cas particulier restant serait
|
||||
> qu'il n'y en ait plus, et l'octet en offre 256.
|
||||
>
|
||||
> `make instances` compte désormais les sites avec les tenants — un site et un locataire
|
||||
> ne peuvent plus réclamer le même nombre sans que la garde le dise.
|
||||
**Un site dérive du même `index` que son tenant.** Il n'y a pas de second registre à
|
||||
tenir : le seul seed de tout l'adressage reste l'`index`, et l'underlay occupe la **bande
|
||||
basse** du supernet — celle que la dérivation des tenants n'alloue jamais.
|
||||
|
||||
| Rôle | VLAN | Sous-réseau | MTU | Nature |
|
||||
|---|---|---|---|---|
|
||||
| **Gestion** | 10 *(ou aucun — voir ci-dessous)* | `10.<index>.0.0/24` | 1500 | **destination** — unique par site |
|
||||
| **Gestion** | 10 | `10.<index>.0.0/24` | 1500 | **destination** — unique par site |
|
||||
| Sortie des tenants | 40 | `192.168.40.0/24` | 1500 | chemin — identique partout |
|
||||
| Transport VXLAN | 50 | `192.168.50.0/24` | 1500 | chemin — identique partout |
|
||||
| Stockage iSCSI | 20 | `192.168.20.0/24` | 9000 | chemin — identique partout |
|
||||
|
|
@ -54,12 +41,6 @@ partagent la même classe A** — chacun le sien, aucun partagé.
|
|||
Index 17 → gestion en `10.17.0.0/24` ; index 11 → `10.11.0.0/24`. Deux sites ne peuvent
|
||||
donc **pas** se chevaucher, sans qu'on ait rien de plus à décider.
|
||||
|
||||
> **Le VLAN de gestion peut n'exister pas du tout.** Chez l'hébergeur de référence, ce plan
|
||||
> est un **segment physique** — une patte dédiée sur la frontière, aucune étiquette, et
|
||||
> **aucun pont d'hyperviseur ne le touche**. Conséquence recherchée : *aucune VM ne peut y
|
||||
> naître*, et le validateur refuse qu'on y déclare une machine. Un port d'accès étiqueté 10
|
||||
> convient aussi ; ce qui compte est que rien du monde virtuel n'y ait de patte.
|
||||
|
||||
**Les VLAN ne changent jamais** d'un site à l'autre : ils sont locaux, ils ne traversent
|
||||
aucun lien inter-sites. Un seul modèle mental, et l'adresse de stockage porte son propre
|
||||
numéro de VLAN — `192.168.20.x` est sur le VLAN 20.
|
||||
|
|
@ -92,7 +73,7 @@ le seed de son site. Une seule règle, aucun cas particulier.
|
|||
|
||||
Le trafic des VM voyage encapsulé en VXLAN, ce qui coûte **50 octets**. Avec un transport
|
||||
à 1500, les VM tournent à **1450** — automatiquement, mais à condition que 1500 passe
|
||||
réellement de bout en bout sur les VLAN de transport et de transit — **50** et **40** (le tableau ci-dessus fait foi ; ce paragraphe disait « 11 et 40 », le numéro d'avant). Un MTU rogné en chemin donne le pire des
|
||||
réellement de bout en bout sur les VLAN 11 et 40. Un MTU rogné en chemin donne le pire des
|
||||
symptômes : les petites requêtes passent, les grosses meurent, et rien n'est signalé.
|
||||
|
||||
Le jumbo (9000) sur le stockage est facultatif. **À moitié configuré, il ne fonctionne
|
||||
|
|
@ -235,15 +216,8 @@ réseaux **défait en silence l'isolation inter-tenant**. WireGuard s'y prête b
|
|||
|
||||
## 9. Ce qu'il ne faut pas faire
|
||||
|
||||
- **Ne pas créer de VM à la main** pour le tenant. Elles sont toutes dérivées du plan ; une
|
||||
VM créée à côté est **invisible pour l'outil** — et le risque n'est pas celui qu'on croit.
|
||||
`make raser` dérive sa liste du plan : il ne la détruira **jamais**. Elle survit donc à
|
||||
tout, sans DNS, sans certificat, sans sauvegarde, sans politique de pare-feu, et **son
|
||||
VMID n'est gardé par aucune preuve contre une collision**. Un VMID oublié squatte le
|
||||
cluster sans que rien ne le signale. *(Cette ligne annonçait l'inverse — « l'outil la
|
||||
détruira sans le savoir » — jusqu'au 2026-09-06.)* Pour une machine d'épreuve jetable, il
|
||||
existe une voie prévue et documentée : `make cloner-vm`, hors plan, **à détruire à la
|
||||
main** (cf. `vm-lifecycle.md` §4bis).
|
||||
- **Ne pas créer de VM à la main** pour le tenant. Elles sont toutes dérivées du plan ;
|
||||
une VM créée à côté est invisible pour l'outil, qui la détruira sans le savoir.
|
||||
- **Ne pas configurer le SDN** : l'outil le fait entièrement.
|
||||
- **Ne pas écrire les secrets** dans un fichier partagé ni dans un dépôt git.
|
||||
- **Ne pas contourner un blocage en silence.** Un point resté ouvert et *signalé* se règle
|
||||
|
|
|
|||
|
|
@ -51,55 +51,13 @@ Créer une nouvelle VM dans Proxmox avec des paramètres sobres.
|
|||
Exemple :
|
||||
|
||||
```text
|
||||
Nom de la VM : modeleSetOPS ← voir l'encadré : ce nom N'EST PAS libre
|
||||
Nom de la VM : debian13-template
|
||||
VMID : 9000 ou autre ID réservé aux modèles
|
||||
OS : Debian 13
|
||||
BIOS : OVMF / UEFI
|
||||
Machine : q35
|
||||
```
|
||||
|
||||
> **Le nom du modèle est un contrat, pas une étiquette.** Le clonage cherche sa source
|
||||
> **par ce nom** : il doit être exactement celui que `make config` a enregistré sous
|
||||
> `proxmox_clone_source_nom` — **`modeleSetOPS`** par défaut. Un nom qui ne correspond pas
|
||||
> se solde par un clonage qui ne trouve rien, et le message ne dit pas que c'est le nom qui
|
||||
> est en cause. *(Cette procédure proposait `debian13-template` jusqu'au 2026-09-06 :
|
||||
> suivie à la lettre, elle produisait un gabarit que le moteur ne savait pas cloner.)*
|
||||
>
|
||||
> Le VMID, lui, est libre — c'est `proxmox_clone_vmid_modele` qui le retient.
|
||||
|
||||
### `q35` n'est pas un réglage — c'est la raison de cette procédure
|
||||
|
||||
**Ces deux lignes sont pourquoi l'installation est manuelle.** Elles expliquent aussi
|
||||
pourquoi Set-OPS n'utilise pas l'image cloud officielle de Debian.
|
||||
|
||||
`genericcloud` est livrée configurée pour **`i440fx`**, le défaut de Proxmox. La convertir
|
||||
en `q35` après coup ne change pas un paramètre : ça **remplace le matériel virtuel sous un
|
||||
système qui croit connaître le sien**. `i440fx` est un chipset PCI, `q35` est PCIe — la
|
||||
topologie des bus change, donc :
|
||||
|
||||
- les **noms d'interfaces prédictibles** changent, puisqu'ils dérivent du chemin PCI
|
||||
(`enp0s3` devient `enp1s0`) — la machine perd le réseau, et sa configuration réseau
|
||||
désigne une interface qui n'existe plus ;
|
||||
- les **chemins de disques** bougent, ce qui peut valoir un initramfs qui ne trouve plus
|
||||
sa racine ;
|
||||
- l'ordre d'énumération des périphériques n'est plus le même, et ce qui en dépend suit.
|
||||
|
||||
**Constat de l'exploitant, paye en anomalies** : une conversion `i440fx` → `q35` sur une
|
||||
machine déjà installée produit une série de pannes dont chacune ressemble à autre chose
|
||||
qu'à sa cause. *La conversion n'est pas une correction — c'est une transplantation.*
|
||||
|
||||
D'où la règle : **une machine naît `q35`, ou elle ne le sera jamais proprement.** C'est ce
|
||||
que cette installation depuis l'ISO garantit, et ce qu'une image préconfigurée pour
|
||||
`i440fx` interdit.
|
||||
|
||||
*Le gabarit hérite ces valeurs à chaque clonage — le vérifier avant de le convertir en
|
||||
modèle est le dernier moment où la correction est gratuite :*
|
||||
|
||||
```sh
|
||||
qm config <vmid> | grep -E '^machine|^bios'
|
||||
# attendu : machine: q35 / bios: ovmf
|
||||
```
|
||||
|
||||
### Disque EFI Proxmox
|
||||
|
||||
Avec OVMF/UEFI, Proxmox crée un petit disque EFI, par exemple :
|
||||
|
|
@ -771,13 +729,7 @@ Exemple :
|
|||
qm template 9000
|
||||
```
|
||||
|
||||
À partir de là, le modèle peut être cloné — **à condition que son nom et son VMID
|
||||
correspondent** à ce que `make config` a enregistré (`proxmox_clone_source_nom`,
|
||||
`proxmox_clone_vmid_modele`). Vérifier avant de s'en servir :
|
||||
|
||||
```bash
|
||||
make config # affiche la configuration lue par le moteur
|
||||
```
|
||||
À partir de là, le modèle peut être cloné.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -788,14 +740,14 @@ Après conversion du modèle, le flux normal passe par `make` et l'API Proxmox.
|
|||
Les paramètres communs Proxmox sont dans :
|
||||
|
||||
```text
|
||||
instance/inventories/<inventaire>/group_vars/proxmox.yml
|
||||
instance/inventories/production/group_vars/proxmox.yml
|
||||
```
|
||||
|
||||
Les secrets d'API vivent dans la voûte unifiée de l'instance (le token Proxmox aux côtés
|
||||
des autres `vault_*`), semée par `make config` :
|
||||
|
||||
```text
|
||||
instance/inventories/<inventaire>/group_vars/all/vault.yml
|
||||
instance/inventories/production/group_vars/all/vault.yml
|
||||
```
|
||||
|
||||
Créer le clone et l'ajouter à l'inventaire :
|
||||
|
|
@ -819,9 +771,7 @@ Les paramètres par VM (VMID, IP, VLAN, passerelle) ne sont plus saisis à la ma
|
|||
Après le premier démarrage, tester l'accès :
|
||||
|
||||
```bash
|
||||
# L'adresse n'est pas à retenir : elle est DÉRIVÉE, et le plan la donne.
|
||||
make hote-afficher HOTE=web-frontal-01 # VMID · IP · VLAN · passerelle
|
||||
ssh ansible@10.17.21.31 # (l'IP ainsi obtenue)
|
||||
ssh ansible@10.0.15.31
|
||||
```
|
||||
|
||||
Ensuite appliquer la conformité Ansible :
|
||||
|
|
|
|||
|
|
@ -11,70 +11,48 @@
|
|||
| `client_journal` | egress | 3100 | tcp | serveur_loki | tls | Expédition des journaux par Alloy vers le collecteur central Loki (HTTPS, cert step-ca). |
|
||||
| `client_metrique` | ingress | 9100 | tcp | serveur_prometheus | tls | Scrape des métriques par Prometheus (node_exporter en HTTPS). |
|
||||
| `client_pki` | egress | 8443 | tcp | serveur_step_ca | tls-requis | Émission/renouvellement des certificats par ACME et récupération de la racine auprès de l'AC interne. |
|
||||
| `client_resolveur` | egress | 53 | udp | serveur_resolveur | clair | Résoudre auprès du résolveur de l'écosystème, et de personne d'autre. |
|
||||
| `client_resolveur` | egress | 53 | tcp | serveur_resolveur | clair | Réponses longues et bascule TCP, obligatoires en DNS. |
|
||||
| `client_sante` | egress | 5665 | tcp | serveur_icinga | tls | Depot d'un resultat passif sur l'API Icinga (process-check-result) : les unites systemd en echec du noeud. Le pair est authentifie par l'AC d'Icinga. |
|
||||
| `client_smtp` | egress | 25 | tcp | serveur_postfix | starttls | Relais des notifications locales vers le MTA central (Postfix), STARTTLS. |
|
||||
| `serveur_artefacts` | ingress | 3142 | tcp | flotte | clair | Toute la flotte prend ses paquets ici. En clair, et c'est correct : l'intégrité d'un dépôt apt vient de ses signatures, qu'apt vérifie de toute façon — un intermédiaire ne peut pas altérer un paquet sans se faire prendre. |
|
||||
| `serveur_artefacts` | egress | 80 | tcp | externe | clair | Remplir le cache depuis les dépôts Debian amont (deb.debian.org, security). |
|
||||
| `serveur_artefacts` | egress | 3142 | tcp | voisins_site | clair | Prendre le cache du site comme amont, plutot que d'aller chez Debian. |
|
||||
| `client_unbound` | ingress | 53 | udp | localhost | clair | Résolveur local sur boucle locale (les processus du nœud interrogent 127.0.0.1). |
|
||||
| `client_unbound` | egress | 53 | tcp | serveur_powerdns | clair | Transfert des requêtes de la zone souveraine vers le DNS autoritatif interne (PowerDNS). |
|
||||
| `client_unbound` | egress | 53 | udp | externe | clair | Récursion DNS depuis la racine (UDP d'abord), validée par DNSSEC — la confidentialité du transport n'est pas l'enjeu, l'authenticité l'est. |
|
||||
| `client_unbound` | egress | 53 | tcp | externe | clair | Récursion DNS en TCP : repli obligatoire quand la réponse dépasse la taille UDP (fréquent avec DNSSEC). |
|
||||
| `serveur_backup` | ingress | 22 | tcp | client_backup | ssh | Dépôt restic servi par SSH (utilisateur restreint restic + clé) ; chaque client_backup pousse ses instantanés. |
|
||||
| `serveur_backup` | egress | 5665 | tcp | serveur_icinga | tls-requis | Rapport passif des sauvegardes vers l'API Icinga : le depot est le seul a voir ce qui est reellement arrive. |
|
||||
| `serveur_backup_site` | ingress | 22 | tcp | voisins_site | ssh | Les écosystèmes de ce site déposent leur état ici tant qu'ils n'ont pas leur propre dépôt. SFTP par un utilisateur restreint, jamais un compte d'administration : le site reçoit des octets chiffrés par restic, il ne peut pas les lire. |
|
||||
| `serveur_cache_site` | ingress | 3142 | tcp | voisins_site | clair | Servir les caches des écosystèmes voisins. Debian n'est ainsi téléchargé qu'une fois pour tout le site, et le cache ne voit que des requêtes AGRÉGÉES — jamais quelle machine installe quoi. |
|
||||
| `serveur_cache_site` | egress | 80 | tcp | externe | clair | Remplir le cache depuis les dépôts Debian amont. En clair parce que les dépôts apt sont signés : l'intégrité vient de la signature, pas du transport. |
|
||||
| `serveur_cache_site` | egress | 443 | tcp | externe | tls-requis | Les dépôts tiers qui n'existent qu'en HTTPS (smallstep, Grafana, Icinga). Le cache les relaie pour que la flotte n'ait pas à sortir elle-même. |
|
||||
| `serveur_collabora` | ingress | 9980 | tcp | edge | clair | Éditeur servi au navigateur via l'edge (WebSocket WOPI ; TLS terminé à l'edge). |
|
||||
| `serveur_collabora` | ingress | 9980 | tcp | localhost | clair | Vérifications WOPI serveur→Collabora depuis Nextcloud co-localisé. |
|
||||
| `serveur_debian` | ingress | 22 | tcp | flotte, externe | ssh | Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense). |
|
||||
| `serveur_debian` | ingress | echo-request | icmp | serveur_icinga | n-a | La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses notifications. |
|
||||
| `serveur_debian` | ingress | frag-needed | icmp | externe | n-a | ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent. |
|
||||
| `serveur_debian` | egress | 53 | udp | frontiere | n-a | Résolution de noms à l'amorçage, avant que le résolveur de l'écosystème n'existe. Sans elle, les machines d'un site neuf ne peuvent pas résoudre leurs dépôts de paquets — et rien ne peut donc s'installer, y compris le résolveur lui-même. |
|
||||
| `serveur_debian` | egress | 53 | tcp | frontiere | n-a | Réponses longues et bascule TCP, obligatoires en DNS. Déclarer l'UDP sans le TCP donne une résolution qui marche jusqu'à la première réponse tronquée. |
|
||||
| `serveur_debian` | egress | 80 | tcp | externe | clair | Dépôts apt en clair et redirections HTTP des miroirs (l'intégrité vient de la signature des paquets, pas du transport). |
|
||||
| `serveur_debian` | egress | 123 | udp | frontiere | n-a | Synchronisation d'horloge (NTP) contre la frontière, autorité de temps de l'écosystème. Une dérive fait échouer la validation des certificats step-ca et l'authentification SSO. |
|
||||
| `serveur_debian` | egress | 123 | udp | externe | n-a | Synchronisation d'horloge (NTP). Une dérive fait échouer la validation des certificats step-ca et l'authentification SSO. |
|
||||
| `serveur_debian` | egress | 443 | tcp | externe | tls-requis | Dépôts apt en HTTPS (Debian, Smallstep, Grafana, Icinga, Forgejo, Nextcloud) — sans quoi aucun correctif de sécurité n'entre. |
|
||||
| `serveur_debian` | egress | frag-needed | icmp | externe | n-a | ICMP « fragmentation nécessaire » sortant : c'est ainsi que nos hôtes signalent l'overlay à 1450 aux correspondants distants. |
|
||||
| `serveur_dns_public` | ingress | 53 | udp | voisins_site | clair | NOTIFY des primaires des locataires du site : une zone publique a change. Le contenu d'une zone publique n'a rien de secret ; ce qui compte est QUI peut le modifier, et c'est TSIG qui le garde, sur le transfert. |
|
||||
| `serveur_dns_public` | ingress | 53 | tcp | voisins_site | clair | Repli TCP des notifications des primaires des locataires. |
|
||||
| `serveur_dns_public` | ingress | 1053 | udp | externe | clair | Les resolveurs de l'Internet interrogent les zones publiques des locataires du site. Les reponses sont signees DNSSEC : leur integrite ne depend ni du transport, ni de nous. |
|
||||
| `serveur_dns_public` | ingress | 1053 | tcp | externe | clair | Repli TCP : reponses tronquees par dnsdist (ANY, debit) et reponses signees trop grosses pour l'UDP. |
|
||||
| `serveur_dns_public` | egress | 5300 | tcp | primaires_dns_locataires | clair | AXFR signe TSIG vers l'autoritatif de chaque locataire du site (5300 : il partage sa machine avec le resolveur, qui tient le 53). La signature authentifie, elle ne chiffre pas — et une zone publique n'a rien a cacher. |
|
||||
| `serveur_dns_public` | egress | 5300 | udp | primaires_dns_locataires | clair | SOA demande au primaire avant chaque transfert : c'est lui qui dit si la zone a change. Sans lui, le secondaire garde sa premiere copie pour toujours. |
|
||||
| `serveur_dovecot` | ingress | 24 | tcp | serveur_postfix | tls-requis | Remise LMTP depuis Postfix (edge-mta -> mailstore), en TLS vérifié (lmtp_tls_security_level=verify). |
|
||||
| `serveur_dovecot` | ingress | 993 | tcp | externe | tls-requis | Accès courriel des utilisateurs (IMAPS). Frontière publique gérée à l'OPNsense. |
|
||||
| `serveur_dovecot` | ingress | 12345 | tcp | serveur_postfix | tls | Authentification SASL déléguée : Postfix valide les identifiants de soumission contre Dovecot. |
|
||||
| `serveur_dovecot` | egress | 636 | tcp | serveur_openldap | tls-requis | userdb/passdb : Dovecot résout et authentifie les comptes sur l'annuaire (LDAPS). |
|
||||
| `serveur_forge_site` | ingress | 443 | tcp | voisins_site | tls-requis | Servir le génome aux écosystèmes de ce site : c'est de cette forge qu'ils clonent leur moteur, leurs modèles et la carte de la fabric (D-81). Sans ce flux, un écosystème neuf ne peut pas se reproduire. |
|
||||
| `serveur_forgejo` | ingress | 3000 | tcp | edge | clair | Interface web + Git HTTP derrière un edge : le nginx termine le TLS et parle en clair à la forge. C'est le cas de tout tenant. |
|
||||
| `serveur_forgejo` | ingress | derive | tcp | flotte, admin | tls | Sans edge devant elle — la forge du SITE — elle sert son propre TLS sur le port du schéma (443), avec le certificat de la machine. `derive` parce que le port vient de `serveur_forgejo_http_port` : écrire 3000 en dur ici serait faux pour elle, et rien ne le signalerait puisque ce flux ne traverse pas la frontière. |
|
||||
| `serveur_forgejo` | ingress | 3000 | tcp | edge | clair | Interface web + Git HTTP servis via l'edge (TLS terminé à l'edge). |
|
||||
| `serveur_forgejo` | egress | 25 | tcp | serveur_postfix | starttls | Notifications courriel (relais via le MTA Postfix). |
|
||||
| `serveur_forgejo` | egress | 443 | tcp | edge | tls-requis | Découverte OIDC et jetons auprès de Keycloak (via son FQDN publié à l'edge). |
|
||||
| `serveur_forgejo` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base de données Forgejo (verify-full). |
|
||||
| `serveur_grafana` | ingress | 3000 | tcp | edge, admin | clair | Interface web : servie via l'edge la ou il y en a un, joignable depuis le plan d'administration partout — sans quoi un deploiement sans edge n'a plus de console. |
|
||||
| `serveur_grafana` | ingress | 3000 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). |
|
||||
| `serveur_grafana` | egress | 443 | tcp | edge | tls-requis | Authentification OIDC auprès de Keycloak (via son FQDN publié à l'edge). |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | localhost | clair | API Icinga 2 consommée en local par Icinga Web 2 co-localisé. |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | client_backup | tls-requis | Rapport passif de chaque detenteur d'etat sur SON depot distant : le depot du site heberge du chiffre et ne peut pas le juger. |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | serveur_backup | tls-requis | Le depot de sauvegarde depose ses resultats passifs (portee : process-check-result sur « sauvegarde: * »). |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | serveur_debian | tls-requis | Rapport passif de sante de chaque noeud (unites systemd en echec). |
|
||||
| `serveur_icinga` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base relationnelle du moteur Icinga (verify-full). |
|
||||
| `serveur_icinga` | egress | echo-request | icmp | serveur_debian | n-a | La supervision verifie que ses hotes repondent (hostalive) : sans ce flux, elle les tient tous pour morts et supprime leurs notifications. |
|
||||
| `serveur_icinga` | egress | echo-request | icmp | fabric | n-a | La supervision verifie que la frontiere sert encore chaque zone. Aucun agent ne peut vivre sur un pare-feu : le controle ACTIF est le seul chemin, et il n'existait pas. |
|
||||
| `serveur_icingaweb2` | ingress | 8080 | tcp | edge, admin | clair | Interface web servie via l'edge (TLS terminé à l'edge ; SSO possible via oauth2-proxy), et joignable depuis le plan d'administration là où il n'y a pas d'edge. |
|
||||
| `serveur_icingaweb2` | ingress | 8080 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge ; SSO possible via oauth2-proxy). |
|
||||
| `serveur_icingaweb2` | egress | 636 | tcp | serveur_openldap | tls-requis | Authentification des utilisateurs sur l'annuaire (LDAPS). |
|
||||
| `serveur_icingaweb2` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Lecture d'IcingaDB (base relationnelle, verify-full). |
|
||||
| `serveur_keycloak` | ingress | 8080 | tcp | edge | clair | Console et endpoints OIDC servis au navigateur et aux applications via l'edge (TLS terminé à l'edge). |
|
||||
| `serveur_keycloak` | egress | 636 | tcp | serveur_openldap | tls-requis | Fédération de l'annuaire OpenLDAP (LDAPS). |
|
||||
| `serveur_keycloak` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Persistance Keycloak dans PostgreSQL (verify-full). |
|
||||
| `serveur_loki` | ingress | 1514 | tcp | fabric | clair | Journaux de la frontiere. Elle n'accueille aucun agent et n'emet que du syslog ; sans ce flux, le seul equipement qui voit passer TOUT le trafic n'ecrit nulle part. |
|
||||
| `serveur_loki` | ingress | 3100 | tcp | client_journal | tls | Réception des journaux poussés par Alloy (client_journal) en HTTPS (http_tls_config, cert step-ca). |
|
||||
| `serveur_loki` | ingress | 3100 | tcp | localhost | clair | Requêtes de Grafana co-localisé (datasource Loki en localhost). |
|
||||
| `serveur_loki` | ingress | 3100 | tcp | fabric | tls | Journaux des hyperviseurs. La machine qui PORTE les VM ecrivait ses journaux nulle part ailleurs que sur son propre disque — invisibles le jour ou c'est elle qui casse. |
|
||||
| `serveur_nextcloud` | ingress | 80 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). |
|
||||
| `serveur_nextcloud` | egress | 443 | tcp | edge | tls-requis | Découverte OIDC auprès de Keycloak (via son FQDN publié à l'edge). |
|
||||
| `serveur_nextcloud` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base de données Nextcloud (verify-full). |
|
||||
| `serveur_nextcloud` | egress | 9980 | tcp | serveur_collabora | tls-cible | Vérifications WOPI serveur->Collabora (édition en ligne). TLS interne = feuille de route edge->backends. |
|
||||
| `serveur_nginx` | ingress | 80 | tcp | admin | clair | HTTP depuis le reseau d'administration — redirection permanente vers HTTPS. |
|
||||
| `serveur_nginx` | ingress | 80 | tcp | externe | clair | HTTP entrant — redirection permanente vers HTTPS. |
|
||||
| `serveur_nginx` | ingress | 443 | tcp | externe | tls-requis | HTTPS entrant — services exposés (terminaison TLS à l'edge). |
|
||||
| `serveur_nginx` | ingress | 443 | tcp | flotte | tls-requis | HTTPS depuis le tenant : les FQDN publiés vivent à l'edge (découverte OIDC, appels inter-services par nom). |
|
||||
| `serveur_nginx` | ingress | 443 | tcp | admin | tls-requis | HTTPS depuis le reseau d'administration : l'exploitant administre les services par leur interface web, servie par l'edge. |
|
||||
| `serveur_nginx` | egress | derive | tcp | expositions | tls-cible | Proxy vers les backends exposés (host:port dérivés des expose ; TLS interne = roadmap edge→backends). |
|
||||
|
|
@ -83,55 +61,35 @@
|
|||
| `serveur_oauth2_proxy` | egress | 8080 | tcp | localhost | clair | Relais vers l'application co-localisée protégée (upstream en localhost). |
|
||||
| `serveur_openldap` | ingress | 389 | tcp | flotte | starttls | LDAP + STARTTLS pour les clients internes qui préfèrent la mise à niveau TLS sur 389. |
|
||||
| `serveur_openldap` | ingress | 636 | tcp | serveur_keycloak, serveur_dovecot, serveur_icingaweb2, serveur_postfix | tls-requis | LDAPS : fédération (Keycloak), userdb courriel (Dovecot), auth web (Icinga Web 2), tables virtuelles (Postfix). |
|
||||
| `serveur_ops` | ingress | 8090 | tcp | edge, admin | clair | Console d'exploitation servie par l'edge (TLS terminé à l'edge), et joignable depuis le plan d'administration là où il n'y a pas d'edge. Le GUI lui-même reste sur la boucle locale : c'est nginx qui authentifie devant. |
|
||||
| `serveur_ops` | egress | 22 | tcp | flotte | ssh | Piloter la flotte — c'est la raison d'être du poste. |
|
||||
| `serveur_ops` | egress | 443 | tcp | edge | tls-requis | Cloner et resynchroniser le génome depuis la forge de l'écosystème. |
|
||||
| `serveur_ops_site` | egress | 22 | tcp | fabric | ssh | Shell des hyperviseurs : ce que l'API ne couvre pas — configuration reseau, ponts, deplacement de disques. Un pouvoir distinct de l'API, donc declare a part. |
|
||||
| `serveur_ops_site` | egress | 22 | tcp | serveur_ops_tenant | ssh | Insemination : amorcer le runner d'un tenant — socle, moteur, plan, plancher de resolution — pour qu'il prenne ensuite le relais sur ses propres machines. Ne transporte aucun secret : la voute est remise par un humain. |
|
||||
| `serveur_ops_site` | egress | 443 | tcp | fabric | tls-requis | API de la frontiere OPNsense : poser les alias et les regles qui ouvrent les flux du tenant qu'on materialise. Preparer le terrain sans cela laisserait un terrain injoignable. |
|
||||
| `serveur_ops_site` | egress | 8006 | tcp | fabric | tls-requis | API de l'hyperviseur : créer, cloner et détruire les VM de la fabric. Le seul flux par lequel un écosystème peut en matérialiser un autre. |
|
||||
| `serveur_ops_tenant` | ingress | 22 | tcp | runner_site | ssh | Insémination : le runner du SITE amorce ce runner-ci — socle, moteur, plan, plancher de résolution — jusqu'à ce qu'un humain lui remette sa voûte. Vers cette machine seule, jamais vers le reste de l'écosystème. |
|
||||
| `serveur_postfix` | ingress | 25 | tcp | externe, client_smtp | starttls | SMTP entrant : courrier externe (MX) et notifications internes (client_smtp). |
|
||||
| `serveur_postfix` | ingress | 465 | tcp | flotte, externe | tls-requis | Soumission authentifiée en TLS direct (submissions, RFC 8314) : clients de courriel des utilisateurs. |
|
||||
| `serveur_postfix` | ingress | 587 | tcp | flotte, externe | starttls | Soumission authentifiée (STARTTLS obligatoire) : agents internes et clients de courriel des utilisateurs. |
|
||||
| `serveur_postfix` | ingress | 587 | tcp | flotte | starttls | Soumission authentifiée (submission) pour les agents internes qui envoient du courrier. |
|
||||
| `serveur_postfix` | egress | 24 | tcp | serveur_dovecot | tls-requis | Remise finale par LMTP au mailstore (Dovecot), en TLS vérifié. |
|
||||
| `serveur_postfix` | egress | 25 | tcp | externe | starttls | Relais sortant vers les MX distants (STARTTLS opportuniste). |
|
||||
| `serveur_postfix` | egress | 636 | tcp | serveur_openldap | tls-requis | Tables virtuelles (domaines/alias/boîtes) résolues sur l'annuaire (LDAPS). |
|
||||
| `serveur_postfix` | egress | 11332 | tcp | localhost | clair | Filtre milter rspamd co-localisé (antispam + signature DKIM). |
|
||||
| `serveur_postfix` | egress | 12345 | tcp | serveur_dovecot | tls | Validation SASL des identifiants de soumission contre Dovecot. |
|
||||
| `serveur_postgresql` | ingress | 5432 | tcp | serveur_keycloak, serveur_forgejo, serveur_icinga, serveur_nextcloud | tls-requis | Connexions applicatives à PostgreSQL (verify-full ; pg_hba hostssl). |
|
||||
| `serveur_postgresql` | ingress | 9187 | tcp | serveur_prometheus | clair | Metriques PostgreSQL lues par l'observatoire. Series, pas verdicts : ce qui derive lentement — cache qui decroche, connexions qui montent, bases qui grossissent — n'a que le graphe pour se faire voir. En clair pour l'instant, contrairement a `client_metrique` : dette inscrite, a fermer par `--web.config.file` + `client_pki`. |
|
||||
| `serveur_powerdns` | ingress | 5300 | tcp | dns_public_site | clair | AXFR signe TSIG par le serveur DNS public du site, vers l'instance publique uniquement. |
|
||||
| `serveur_powerdns` | ingress | 5300 | udp | dns_public_site | clair | Interrogation du SOA par le secondaire du site avant chaque transfert. |
|
||||
| `serveur_powerdns` | ingress | derive | udp | flotte | clair | Zone souveraine. 53 seul sur son hôte, 5300 sur la loopback derrière le résolveur. |
|
||||
| `serveur_powerdns` | ingress | derive | tcp | flotte | clair | Idem en TCP (réponses volumineuses, AXFR restreint par allow_axfr_ips). |
|
||||
| `serveur_powerdns` | ingress | 53 | udp | flotte | clair | Résolution DNS interne (zone souveraine). DoT/DoH = feuille de route (chiffrement DNS). |
|
||||
| `serveur_powerdns` | ingress | 53 | tcp | flotte | clair | Résolution DNS interne en TCP (réponses volumineuses, AXFR restreint par allow_axfr_ips). |
|
||||
| `serveur_powerdns` | egress | 53 | udp | externe | clair | Résolution sortante du serveur autoritatif POUR SES PROPRES besoins (apt, NTP) — il ne récurse pour aucun autre hôte. |
|
||||
| `serveur_powerdns` | egress | 53 | tcp | externe | clair | Repli TCP de la résolution sortante du serveur autoritatif (réponses dépassant la taille UDP). |
|
||||
| `serveur_prometheus` | ingress | 9090 | tcp | localhost | clair | Console Prometheus consommée en local par Grafana co-localisé (pas d'exposition inter-nœud). |
|
||||
| `serveur_prometheus` | egress | 9100 | tcp | client_metrique | tls | Scrape des node_exporter (HTTPS via cert step-ca) sur chaque nœud instrumenté. |
|
||||
| `serveur_prometheus` | egress | 9100 | tcp | frontiere | clair | Scrape de l'exportateur de la frontiere (os-node_exporter), sur sa seule patte de supervision : l'equipement qui voit passer TOUT le trafic n'avait ni temperature ni charge dans la supervision. |
|
||||
| `serveur_prometheus` | egress | 9100 | tcp | fabric | clair | Scrape des hyperviseurs : la machine qui PORTE les VM etait invisible de la supervision qui les surveille. Elle ne peut pas pousser (route par defaut gelee, D-57), donc on la tire. |
|
||||
| `serveur_redis` | ingress | 6379 | tcp | localhost | clair | Cache/verrous consommés uniquement par l'application co-localisée (ex. Nextcloud). Aucune exposition inter-nœud. |
|
||||
| `serveur_resolveur` | ingress | 53 | udp | flotte | clair | Toute la flotte du tenant résout ici — et nulle part ailleurs. |
|
||||
| `serveur_resolveur` | ingress | 53 | tcp | flotte | clair | Réponses longues et bascule TCP, obligatoires en DNS. |
|
||||
| `serveur_resolveur` | egress | 53 | udp | externe | clair | Récursion depuis les serveurs racine. Aucun transitaire : l'écosystème ne confie ses questions à personne. |
|
||||
| `serveur_resolveur_site` | ingress | 53 | udp | voisins_site | clair | Les écosystèmes de ce site résolvent ici tant qu'ils n'ont pas leur propre résolveur. `client_resolveur` les bascule chez eux dès qu'il existe ; retirer cet emprunt est alors une émancipation. |
|
||||
| `serveur_resolveur_site` | ingress | 53 | tcp | voisins_site | clair | Réponses longues et bascule TCP, obligatoires en DNS. |
|
||||
| `serveur_rspamd` | ingress | 11332 | tcp | localhost | clair | Protocole milter consommé par Postfix co-localisé (analyse + signature DKIM). Local uniquement. |
|
||||
| `serveur_rspamd` | ingress | 11334 | tcp | localhost | clair | Interface de contrôle rspamd (statistiques, apprentissage) en local. |
|
||||
| `serveur_step_ca` | ingress | 8443 | tcp | flotte | tls-requis | ACME + API step-ca : chaque nœud (client_pki) émet/renouvelle ses certificats et récupère la racine. |
|
||||
| `serveur_web_dorsal` | ingress | 80 | tcp | edge, serveur_web_frontal | clair | Front nginx local des webapps et des sites statiques, relaye par l'edge (noms internes) et par le web frontal (noms publics). |
|
||||
| `serveur_web_dorsal` | ingress | 80 | tcp | edge | clair | Front nginx local des webapps, proxie par l'edge (TLS termine a l'edge). |
|
||||
| `serveur_web_dorsal` | egress | 443 | tcp | flotte | tls | git clone/pull du depot de chaque app (Forgejo souverain) au deploiement. |
|
||||
| `serveur_web_frontal` | ingress | 80 | tcp | externe | clair | HTTP public, relaye a travers le WAF — et le defi HTTP d'ACME ; renverra vers HTTPS quand le certificat sera la. |
|
||||
| `serveur_web_frontal` | ingress | 443 | tcp | externe | tls-requis | HTTPS public — les sites et services que le locataire publie sur l'Internet (attend son certificat Let's Encrypt). |
|
||||
| `serveur_web_frontal` | egress | 80 | tcp | serveur_web_dorsal | clair | Relais vers le web dorsal, qui porte les sites et les applications publiques. |
|
||||
| `serveur_web_frontal` | ingress | 80 | tcp | edge | clair | Contenu statique servi au navigateur via l'edge (TLS termine a l'edge). |
|
||||
| `serveur_web_frontal` | egress | 443 | tcp | flotte | tls | git clone/pull du depot du site (Forgejo souverain) au deploiement. |
|
||||
|
||||
## Synthèse chiffrement
|
||||
|
||||
- **clair** : 51 flux
|
||||
- **n-a** : 8 flux
|
||||
- **ssh** : 8 flux
|
||||
- **clair** : 29 flux
|
||||
- **n-a** : 3 flux
|
||||
- **ssh** : 3 flux
|
||||
- **starttls** : 6 flux
|
||||
- **tls** : 10 flux
|
||||
- **tls** : 8 flux
|
||||
- **tls-cible** : 2 flux
|
||||
- **tls-requis** : 34 flux
|
||||
- **tls-requis** : 26 flux
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
> **Pour qui :** l'hébergeur, le jour où il remet un écosystème à celui qui en est le
|
||||
> propriétaire. À lire **avant** de fabriquer le paquet, pas pendant.
|
||||
|
||||
# Remettre un écosystème à son propriétaire
|
||||
|
||||
## 1. Le problème que ce document ferme
|
||||
|
||||
La livraison se terminait par une phrase : *« tes clés te seront remises séparément »*.
|
||||
Ce qui se passait ensuite n'était écrit nulle part — ni ce qu'on remet, ni dans quel
|
||||
ordre, ni **ce qu'on garde**. Le geste qui donne le contrôle d'une organisation était le
|
||||
seul geste lourd du dépôt sans procédure, sans outil et sans garde.
|
||||
|
||||
Et il portait une faute silencieuse : les clés vivent toutes dans le même dossier
|
||||
(`~/.config/setops-vault-*`). Remettre « les clés » d'un revers de main, c'est remettre
|
||||
celles du site et celles des autres locataires. Personne ne s'en apercevrait — ni celui
|
||||
qui donne, ni celui qui reçoit.
|
||||
|
||||
## 2. Les deux temps, et pourquoi ils ne se confondent pas
|
||||
|
||||
> **Temps 1 — l'identité.** Le client reçoit de quoi gouverner **ses gens** tout de suite.
|
||||
> **Temps 2 — la machine.** À une date convenue, il reçoit le pouvoir sur **ses serveurs**,
|
||||
> et l'hébergeur le perd.
|
||||
|
||||
Ce découpage n'est pas une précaution d'hébergeur : c'est ce qui rend les deux gestes
|
||||
honnêtes.
|
||||
|
||||
| | Temps 1 | Temps 2 |
|
||||
|---|---|---|
|
||||
| Ce qui passe | la clé de **sa** voûte, sa voûte chiffrée, la racine de **son** AC | sa clé SSH entre, celle de l'hébergeur sort, la voûte change de mot de passe, les secrets tournent |
|
||||
| Ce que le client peut | créer, retirer, habiliter des personnes — sans nous | tout, y compris se passer de nous |
|
||||
| Ce que l'hébergeur garde | l'accès **machine**, parce qu'il exploite encore | rien qui ne lui soit redonné |
|
||||
| Quand | le jour de la livraison | à l'échéance inscrite (30 jours par défaut) |
|
||||
|
||||
Le second temps applique à une livraison ce que
|
||||
[`migration-tenant.md`](migration-tenant.md) §6 étape 8 applique déjà à un départ :
|
||||
**révoquer, pas transmettre**. Sans lui, l'hébergeur garde **à vie** l'accès aux secrets
|
||||
d'un client qui se croit chez lui — et aucune procédure ne rattrape ça après coup.
|
||||
|
||||
## 3. Temps 1 — le paquet
|
||||
|
||||
```
|
||||
make ca-racine # la racine de SON AC, et son empreinte
|
||||
make ca-empreinte # la même, lue SUR l'AC : le témoin à comparer
|
||||
|
||||
make remise-recenser # ce qui partirait, sans rien écrire
|
||||
make remise-paquet VERS=/media/…/CLE
|
||||
make remise-inscrire RECU_PAR="Prénom Nom" COURRIEL="…"
|
||||
```
|
||||
|
||||
**Lance `remise-paquet` toi-même**, dans ton terminal : `gpg` demande une phrase de passe,
|
||||
et elle ne doit passer ni par un journal, ni par le contexte d'un assistant.
|
||||
|
||||
L'outil **refuse** quatre choses, et chacune ferme une faute réelle :
|
||||
|
||||
- **une destination dans l'infrastructure** — le dépôt de sauvegarde est chiffré par un
|
||||
mot de passe qui vit dans la voûte que ce paquet ouvre ; l'y déposer ferait un coffre
|
||||
dont la clé est à l'intérieur ;
|
||||
- **écraser un paquet existant** — c'est peut-être celui qu'on vient de vérifier ;
|
||||
- **partir sans la racine de l'AC** — sans elle, le client apprend à cliquer sur
|
||||
« continuer quand même », ce qui vaut pire que pas de TLS du tout ;
|
||||
- **un paquet qu'il n'arrive pas à rouvrir** — il est alors supprimé. Un paquet de remise
|
||||
qu'on ne sait pas rouvrir donne le sentiment d'avoir remis.
|
||||
|
||||
Il n'emporte **que l'écosystème monté** : la clé du site et celles des autres locataires
|
||||
vivent dans le même dossier, et c'est une seule ligne de code qui les en écarte
|
||||
(`remise.py:_cle_de_voute`). Il n'affiche jamais le contenu d'un secret — noms, tailles,
|
||||
empreintes SHA256, rien d'autre.
|
||||
|
||||
**Deux gestes restent, et ils n'ont pas d'outil :** transmettre la phrase de passe par un
|
||||
**autre canal** que le support, et transmettre l'empreinte de l'AC de la même façon.
|
||||
Séparés, le support et la phrase ne valent rien l'un sans l'autre.
|
||||
|
||||
## 4. Temps 2 — le re-clé
|
||||
|
||||
**L'ordre ne se permute pas.** Retirer sa propre clé avant que celle du client soit posée
|
||||
ferme l'écosystème à tout le monde, et le seul moyen de le réparer est justement celui
|
||||
qu'on vient de retirer.
|
||||
|
||||
1. **La clé du client entre** — son entrée dans `ssh_baseline_cles_admin`, `etat: present`.
|
||||
2. **Celle de l'hébergeur sort** — `etat: absent` sur son entrée. On ne la supprime pas du
|
||||
plan : une entrée retirée n'est plus appliquée, donc la clé **resterait** sur les
|
||||
machines. `absent` la fait *retirer*.
|
||||
3. **Déployer**, pour que le plan devienne l'état des machines.
|
||||
4. **La voûte change de mot de passe** — `ansible-vault rekey`, la nouvelle clé étant
|
||||
choisie par le client. Le mot de passe de l'hébergeur ne se *communique* pas.
|
||||
5. **Les secrets applicatifs tournent** — `voute.py saisir --remplacer`, sans écho, puis
|
||||
déploiement.
|
||||
6. **Estampiller** : `make remise-recleer CONFIRMER=true`.
|
||||
|
||||
`remise-recleer` **mesure avant d'estampiller**, et refuse si l'un des trois faits manque :
|
||||
une clé présente, une clé révoquée, une clé de voûte différente de celle remise au temps 1.
|
||||
Un registre qui dirait « révoqué » pendant que le plan garde la clé de l'hébergeur serait
|
||||
le seul mensonge que ce fichier puisse porter sans que personne ne s'en aperçoive — parce
|
||||
qu'il flatte tout le monde.
|
||||
|
||||
## 5. Le registre — `remise.yml` chez le locataire
|
||||
|
||||
Généré, versionné, dans le dépôt du locataire, à côté de `parente.yml` : *de qui il
|
||||
descend* d'un côté, *à qui il appartient* de l'autre.
|
||||
|
||||
Il porte l'organisation, la date du temps 1, qui a remis, **qui a reçu**, les empreintes
|
||||
SHA256 des pièces remises, l'échéance du temps 2 et son constat. **Aucun secret**, par
|
||||
construction : une empreinte prouve qu'on a remis *ce fichier-là* sans rien révéler de son
|
||||
contenu.
|
||||
|
||||
> **Il déclare enfin le responsable désigné.** D-18 décide depuis longtemps que chaque
|
||||
> locataire en a un ; `migration-tenant.md` §9 laissait ouverte la question « **où est-il
|
||||
> déclaré ?** ». La réponse est ici, et elle est la seule qui ne devine rien : c'est la
|
||||
> personne qui **reçoit**.
|
||||
|
||||
## 6. La garde
|
||||
|
||||
**P80** lit les registres de tous les écosystèmes et refuse trois états :
|
||||
|
||||
- un registre incomplet — remis à personne, ou sans échéance ;
|
||||
- un temps 2 **échu** et non fait : l'hébergeur garde l'accès machine d'un client qui se
|
||||
croit chez lui, et le silence le laisserait devenir un état de fait ;
|
||||
- un temps 2 déclaré fait pendant que le plan ne révoque **aucune** clé.
|
||||
|
||||
Elle ne juge **pas** un écosystème sans registre : tous ne sont pas remis, et beaucoup ne
|
||||
le seront jamais — le lab, l'écosystème de l'hébergeur lui-même.
|
||||
|
||||
## 7. Ce que cette procédure ne couvre pas
|
||||
|
||||
- **Ce que le client fait de son paquet.** Une clé remise sur un support qu'il laisse
|
||||
dans un tiroir déverrouillé n'est plus notre affaire, et le LISEZ-MOI le lui dit.
|
||||
- **La rotation des secrets applicatifs**, qui reste un geste humain : `voute.py` ne
|
||||
génère pas les valeurs, il les reçoit sans écho. Un script qui engendrerait et écrirait
|
||||
tout seul connaîtrait ce qu'il écrit.
|
||||
- **La preuve que l'hébergeur ne peut plus entrer.** Le plan déclare la révocation et le
|
||||
déploiement l'applique ; le vérifier *depuis l'extérieur* demande d'essayer d'entrer,
|
||||
donc une machine vivante. C'est le même partage que partout ici : le dépôt prouve ce
|
||||
qu'il a **demandé**, `make emancipation-prouver` prouve ce qui **tient**.
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
> **Pour qui :** le locataire **et** l'hébergeur — les deux lisent le même texte, et c'est
|
||||
> le but. Un partage de responsabilités dont chaque partie aurait sa version est un
|
||||
> désaccord qui attend son incident.
|
||||
|
||||
# Responsabilités du locataire et de l'hébergeur
|
||||
|
||||
## 1. Le principe : une responsabilité est la conséquence d'un pouvoir
|
||||
|
||||
Ce document ne distribue pas des devoirs par bonne volonté. Il les **dérive** :
|
||||
|
||||
> **Qui peut, doit. Qui ne peut pas, ne peut pas être tenu — et personne ne peut se
|
||||
> décharger sur celui qui ne peut pas.**
|
||||
|
||||
Trois règles en découlent, et elles décident de tout le reste :
|
||||
|
||||
1. **À chaque pouvoir sa responsabilité.** Un pouvoir sans devoir correspondant est une
|
||||
prise sur autrui ; un devoir sans pouvoir correspondant est un piège.
|
||||
2. **Une responsabilité sans mécanisme est un vœu.** Chaque ligne du tableau nomme
|
||||
l'endroit du système qui la rend vraie — un rôle, une garde, une commande. Ce qui n'a
|
||||
pas de mécanisme est écrit au §5, parmi les points non tranchés, plutôt que promis.
|
||||
3. **Le silence ne crée pas de responsabilité.** Si une ligne n'a pas de vis-à-vis, ce
|
||||
n'est pas une zone partagée : c'est un trou, et il se voit.
|
||||
|
||||
## 2. La ligne de partage : trois portées, et aucun pouvoir total
|
||||
|
||||
C'est la structure même du moteur, pas une convention de contrat
|
||||
(`roles/serveur_ops_tenant/README.md`) :
|
||||
|
||||
```
|
||||
calculer plan → inventaire aucune voûte n'importe quel runner
|
||||
configurer des rôles sur ses machines voûte du TENANT le runner du locataire
|
||||
matérialiser créer / détruire des VM voûte du SITE le runner de l'hébergeur
|
||||
```
|
||||
|
||||
**Aucun runner n'est omnipotent.** Celui de l'hébergeur matérialise le terrain et n'entre
|
||||
jamais chez un locataire ; celui du locataire configure son écosystème et ne touche jamais
|
||||
la fabric. Tout ce qui suit découle de cette coupure.
|
||||
|
||||
## 3. Le tableau
|
||||
|
||||
<!-- TABLE:RESPONSABILITES — canonique. Le PDF remis aux locataires LIT ce tableau ;
|
||||
il n'en porte pas de copie. Le modifier ici le modifie partout. -->
|
||||
|
||||
| Le pouvoir | Qui le détient | La responsabilité qui en découle | Ce que l'autre ne peut donc pas faire à sa place |
|
||||
|---|---|---|---|
|
||||
| **Créer et détruire les machines** (`raser`, `site-raser`, clonage du gabarit) | l'hébergeur | Que les machines existent, démarrent et tiennent ; qu'aucune destruction n'ait lieu sans confirmation explicite ni sans sauvegarde hors du site au préalable | Le locataire ne peut pas garantir l'existence de ses machines, ni se les rendre lui-même |
|
||||
| **Le réseau, la frontière et le stockage** (SDN, VLAN, OPNsense, Ceph) | l'hébergeur | Que le chemin existe et que la bordure refuse ce qui n'est pas déclaré ; prévenir avant toute coupure planifiée | Le locataire ne peut pas ouvrir un flux vers l'extérieur sans que l'hébergeur l'applique |
|
||||
| **Déclarer ses flux** (`roles/*/meta/flux.yml`, pare-feu d'hôte) | le locataire | Déclarer ce que ses services ont besoin d'échanger ; ce qui n'est pas déclaré est refusé, et c'est voulu | L'hébergeur ne peut pas deviner un flux applicatif ; il n'ouvrira rien « au cas où » |
|
||||
| **Configurer les services** (son runner, sa voûte) | le locataire | L'état de ses services : ce qui est déployé, à jour, et conforme à son plan | L'hébergeur ne peut pas configurer un service du locataire : il n'a pas sa voûte |
|
||||
| **L'annuaire et l'entrée unique** (LDAP, Keycloak) | le locataire | Qui entre, qui sort, et **quand** — créer, désactiver, révoquer sans attendre personne | L'hébergeur ne peut retirer l'accès de personne : mutualiser l'annuaire serait lui confier ses gens |
|
||||
| **Les habilitations** (appartenance aux groupes, D-66/D-67) | le locataire | Tenir ses groupes à jour. Le moteur amorce **un** accès puis se retire : il ne réconcilie jamais les appartenances | Ni l'hébergeur ni l'outillage ne corrigeront un groupe : un redéploiement n'efface pas le compte créé la veille |
|
||||
| **Son autorité de certification** (step-ca, D-87) | le locataire | Renouveler ses certificats, et **recharger** les services qui les servent — un certificat renouvelé sur disque reste servi périmé en mémoire | L'hébergeur ne peut pas émettre de certificat au nom du locataire, et c'est délibéré : il pourrait sinon se faire passer pour n'importe lequel de ses services |
|
||||
| **Ses journaux** (Loki, D-87) | le locataire | Les lire, les conserver, et les fournir s'il demande de l'aide | L'hébergeur ne voit pas les journaux du locataire : il ne peut donc pas diagnostiquer un incident applicatif à sa place |
|
||||
| **La disponibilité de la fabric** (une VM est-elle debout ?) | l'hébergeur | Constater qu'une machine est tombée et le dire — c'est sa fabric | Le locataire n'a pas de vue sur l'hyperviseur qui porte ses VM |
|
||||
| **Les métriques d'hébergement** (charge, disque) | l'hébergeur, **avec réserve** | S'en servir pour dimensionner et prévenir, pas pour lire l'activité d'une organisation — elles disent *quand* et *combien* | — (elles ne disent pas *quoi* : le contenu reste hors de portée) |
|
||||
| **Le dépôt de sauvegarde** (`serveur_backup`, chiffré côté client) | l'hébergeur | Que le dépôt **accepte encore une écriture** : espace, droits, système de fichiers en lecture seule. Sa sonde constate l'endroit, jamais le contenu | L'hébergeur **ne peut pas** juger un instantané : il héberge des octets qu'il ne peut pas ouvrir |
|
||||
| **La clé de ses sauvegardes** (restic) | le locataire | Juger que ses instantanés sont **récents, complets et restaurables**, et éprouver une restauration. *La vérification suit la clé* | Personne ne peut vérifier une sauvegarde à la place de qui détient la clé — et sa perte les rend définitivement illisibles |
|
||||
| **Sa voûte et ses secrets** (jamais mutualisée) | le locataire | Garder sa clé hors de l'infrastructure qu'elle ouvre, en double, et faire tourner ses secrets | L'hébergeur ne peut pas recouvrer une clé de voûte perdue : il n'en a aucune copie, par construction |
|
||||
| **Le courriel** (relais à la bordure) | partagé | L'**enveloppe** transite chez l'hébergeur, qui en répond ; le **contenu** appartient au locataire, qui en répond | L'hébergeur ne lit pas le contenu ; le locataire ne tient pas la réputation de la bordure |
|
||||
| **La forge du génome** (D-81) | l'hébergeur | Tenir l'autorité du génome disponible : c'est de là qu'un écosystème se reproduit | Le locataire ne dépend pas d'elle pour **vivre** — seulement pour se reconstruire ; il peut en garder son propre miroir |
|
||||
| **L'accès de secours par `sudo`** (D-40) | l'hébergeur, **tant qu'il exploite** | Le dire plutôt que de le taire : exploiter une machine, c'est pouvoir tout y lire. C'est la raison d'être du second temps de la remise, et de sa date | Le locataire ne peut pas le supprimer sans reprendre lui-même l'exploitation — d'où une **échéance écrite**, pas une confiance indéfinie |
|
||||
| **La remise des clés** (`make remise-*`, garde **P80**) | l'hébergeur remet, le locataire reçoit | Remettre en deux temps : l'identité le jour de la livraison, la machine à l'échéance — sa clé entre, la nôtre sort, la voûte est re-clétée | Un accès qu'on oublie de rendre ne devient pas légitime en vieillissant : la garde signale tout retard |
|
||||
| **Nommer le responsable désigné** (D-18, `remise.yml`) | le locataire | Nommer la personne qui engage l'organisation, et la maintenir à jour | L'hébergeur ne peut pas la désigner : ce serait choisir qui a le droit de le quitter |
|
||||
| **Décider de partir** (migration, émancipation) | le locataire | Mandater, et emporter ce qui est à lui : son plan, ses données, sa voûte, ses domaines | L'hébergeur ne peut ni retenir ni décider à sa place ; la machine **instruit**, l'humain décide |
|
||||
| **Libérer un partant** (`migration-tenant.md` §6) | l'hébergeur | **Révoquer, pas transmettre** : ses accès tombent, puis rétention convenue, puis purge | Le locataire ne peut pas vérifier lui-même la purge ; elle est un engagement daté, pas une mesure |
|
||||
|
||||
<!-- /TABLE:RESPONSABILITES -->
|
||||
|
||||
## 4. Ce que personne ne peut déléguer
|
||||
|
||||
Trois choses ne se confient à aucune des deux parties, parce que les confier les annule :
|
||||
|
||||
- **La phrase de passe d'un paquet de remise** ne voyage jamais avec le support qu'elle
|
||||
ouvre. Séparés, ils ne valent rien l'un sans l'autre ; ensemble, ils valent l'écosystème.
|
||||
- **La seconde copie** d'une clé, dans un autre lieu physique. Un support unique dans un
|
||||
tiroir unique n'est pas une sauvegarde, c'est le même risque déplacé de quelques mètres.
|
||||
- **La comparaison d'une empreinte** avant d'installer une autorité de certification.
|
||||
Installer une AC, c'est lui donner le droit de signer n'importe quel nom : la
|
||||
comparaison est ce qui distingue sa racine d'une racine interceptée.
|
||||
|
||||
## 5. Ce qui n'est pas tranché — et qui reste donc à convenir
|
||||
|
||||
Nommer un point ouvert vaut mieux qu'une ligne rassurante sans mécanisme derrière.
|
||||
|
||||
- **Le recouvrement de la clé du responsable désigné.** Elle se perd, se compromet, ou la
|
||||
personne quitte l'organisation. Sans procédure, un locataire devient *inmigrable* :
|
||||
captif non par contrat mais par accident. Piste retenue : un **contact de secours nommé
|
||||
en même temps que le responsable**, tant que personne n'est en situation d'urgence.
|
||||
- **Comment on change de responsable désigné** — acte au moins aussi sensible que la
|
||||
migration, puisqu'il décide qui pourra la mandater ensuite.
|
||||
- **La durée de rétention** avant purge chez un hébergeur sortant : elle se convient, elle
|
||||
ne se dérive pas.
|
||||
- **La preuve que l'hébergeur ne peut plus entrer.** Le plan déclare la révocation et le
|
||||
déploiement l'applique ; le vérifier depuis l'extérieur demande d'essayer d'entrer, donc
|
||||
une machine vivante (`make emancipation-prouver`).
|
||||
|
||||
## 6. Comment chaque ligne se vérifie
|
||||
|
||||
Aucune de ces responsabilités n'est laissée à la parole :
|
||||
|
||||
```
|
||||
make prouver le dépôt est-il cohérent avec lui-même (94 preuves, zéro réseau)
|
||||
make remise-verifier le second temps de la remise est-il fait, ou en retard ? (P80)
|
||||
make certificats-plan ce que le disque porte contre ce que la mémoire sert
|
||||
make expositions-plan chaque service publié répond-il, et depuis où
|
||||
make identite-plan royaume, fédération, politique de mot de passe, comptes
|
||||
make emancipation-prouver un lien d'hébergement est-il réellement coupé
|
||||
```
|
||||
|
||||
`make deployer` **répare**, les devis **constatent** : les confondre fait perdre
|
||||
l'information au moment où elle sert.
|
||||
|
||||
---
|
||||
|
||||
*Le partage décrit ici n'est pas une politique commerciale : c'est la forme du système.
|
||||
Chaque fois qu'il a été possible de donner un pouvoir au locataire, il lui a été donné —
|
||||
et chaque fois que l'hébergeur a conservé un pouvoir, c'est parce que quelqu'un doit porter
|
||||
la fabric, et cela s'écrit ici plutôt que de se découvrir le jour d'un incident.*
|
||||
|
|
@ -1,990 +0,0 @@
|
|||
---
|
||||
# LES RUNBOOKS DE CONSTRUCTION — l'ordre des gestes, et pourquoi celui-la.
|
||||
#
|
||||
# CE QUE CE FICHIER AJOUTE AU MAKEFILE, ET CE QU'IL NE REPETE PAS.
|
||||
#
|
||||
# Les 132 cibles documentees du Makefile disent chacune CE QU'ELLE FAIT. Aucune ne dit
|
||||
# dans quel ORDRE, ni pourquoi maintenant, ni ce qu'il faut avoir mesure avant. Cette
|
||||
# connaissance-la vivait en prose dans `docs/runbooks-exploitation.md`,
|
||||
# `docs/implanter-un-tenant-sur-un-site.md` et `docs/preparer-un-site-hebergeur.md` — et
|
||||
# la console offrait des boutons sans sequence.
|
||||
#
|
||||
# ON NE RECOPIE PAS LE LIBELLE D'UNE CIBLE. `scripts/runbooks.py` va le lire dans le
|
||||
# Makefile au moment de servir. Un libelle recopie ici serait une seconde liste, et une
|
||||
# liste qui suit une autre prend du retard sur elle. Ce fichier ne porte donc que ce que
|
||||
# le Makefile ne peut pas porter : l'ordre, la nature du geste, la portee, le pourquoi.
|
||||
#
|
||||
# LA GARDE EST ECRITE AVEC LA LISTE, PAS APRES. `python3 scripts/runbooks.py verifier`
|
||||
# (et P83) refusent qu'une cible documentee ne soit ni portee par un runbook ni exemptee
|
||||
# avec un motif. Sans cela, la console cacherait des pouvoirs que le moteur possede.
|
||||
#
|
||||
# NATURE D'UNE ETAPE
|
||||
# mesure n'ecrit rien, rejouable sans consequence, proposee meme apres un echec
|
||||
# ecriture change l'etat du monde ; exige que l'etape precedente ait reussi
|
||||
# destructif detruit ; exige une confirmation ecrite en plus de CONFIRMER=true
|
||||
#
|
||||
# PORTEE D'UN RUNBOOK — ce que la MACHINE porte, au sens de `contexte()` :
|
||||
# tenant un ecosysteme est monte (`instance/`) : on le configure
|
||||
# site une fabric est montee (`underlay.yml`) : on materialise
|
||||
# poste les deux — l'atelier du mainteneur
|
||||
# toute ni l'un ni l'autre n'est requis
|
||||
#
|
||||
# LA PORTEE SE PESE A L'ETAPE (2026-09-20). Un runbook donne le defaut ; une etape qui
|
||||
# exige davantage le declare avec son propre `portee:`. Mesure faite sur la console de
|
||||
# TechnoLibre : declaree au seul runbook, une unique etape qui materialise (`flotte-creer`,
|
||||
# `creer-vm`) faisait basculer TOUTE la sequence en `poste` — et un locataire ne pouvait
|
||||
# plus deployer sa propre flotte, ce qui est exactement son metier. Le locataire conduit
|
||||
# donc sa sequence, et bute precisement la ou il faut : sur la machine a engendrer.
|
||||
|
||||
# CE QUE LA CONSOLE DOIT DEMANDER A L'EXPLOITANT. Une variable absente d'ici est refusee
|
||||
# par la garde : la console ne saurait pas quoi afficher, et un champ libre sans invite
|
||||
# est une invitation a se tromper.
|
||||
variables:
|
||||
HOTE:
|
||||
invite: "La machine"
|
||||
source: hotes # la liste vient de l'inventaire actif
|
||||
GROUPE:
|
||||
invite: "Le groupe (role)"
|
||||
source: groupes
|
||||
NOM:
|
||||
invite: "Nom du dossier de l'ecosysteme"
|
||||
exemple: "OPS-Machin"
|
||||
MODELE:
|
||||
invite: "Modele de depart"
|
||||
source: modeles
|
||||
INSTANCE:
|
||||
invite: "Nom de l'ecosysteme a detruire (il doit etre ecrit en toutes lettres)"
|
||||
SITE:
|
||||
invite: "Depot du site a detruire (ecrit en toutes lettres)"
|
||||
TENANT:
|
||||
invite: "Dossier du locataire a amorcer"
|
||||
exemple: "OPS-Machin"
|
||||
DEPOT:
|
||||
invite: "Un seul depot (vide = tous ceux que le runner porte)"
|
||||
facultatif: true
|
||||
VERS:
|
||||
invite: "Repertoire de destination (une cle chiffree, hors du poste)"
|
||||
ARCHIVE:
|
||||
invite: "Fichier d'archive a restaurer"
|
||||
CIBLE:
|
||||
invite: "Hote ou adresse a sonder"
|
||||
SERVICE:
|
||||
invite: "Le lien dont on prouve la coupure"
|
||||
valeurs: [artefacts, genome, resolveur]
|
||||
ROLE:
|
||||
invite: "Un seul role (vide = tous)"
|
||||
facultatif: true
|
||||
LIMITE:
|
||||
invite: "Motif d'hotes"
|
||||
facultatif: true
|
||||
RECU_PAR:
|
||||
invite: "Qui recoit la remise (nom complet)"
|
||||
COURRIEL:
|
||||
invite: "Courriel de la personne qui recoit"
|
||||
DANS:
|
||||
invite: "Jours avant le second temps"
|
||||
facultatif: true
|
||||
JEU:
|
||||
invite: "Le jeu d'etat (openldap, postgresql, nextcloud... — voir restauration-etat)"
|
||||
DEPUIS:
|
||||
invite: "Reprendre a cette etape (vide = depuis le debut)"
|
||||
valeurs: [sauvegarder, raser, creer, inseminer, armer, monter, parefeu, bilan]
|
||||
facultatif: true
|
||||
ARMER:
|
||||
invite: "Armer sans marquer la pause"
|
||||
valeurs: [oui]
|
||||
facultatif: true
|
||||
SOURCE:
|
||||
invite: "Comparer a quel instantane (vide = celui d'avant rasage)"
|
||||
valeurs: [candidat]
|
||||
facultatif: true
|
||||
VMID:
|
||||
invite: "Identifiant Proxmox de la VM"
|
||||
DIALECTE:
|
||||
invite: "Dialecte du commutateur"
|
||||
valeurs: [cisco, binardat]
|
||||
facultatif: true
|
||||
PARALLELE:
|
||||
invite: "Combien de VM a la fois"
|
||||
facultatif: true
|
||||
|
||||
runbooks:
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: site-premier-jour
|
||||
titre: "Le premier jour d'un site"
|
||||
portee: site
|
||||
doc: docs/runbooks-exploitation.md
|
||||
but: >-
|
||||
Faire naitre un site hebergeur depuis une fabric nue : les machines, puis le moteur,
|
||||
puis la forge qui permettra aux locataires de se reproduire. L'ordre n'est pas une
|
||||
preference — le premier passage s'ARRETE sur une forge vide, et c'est normal.
|
||||
etapes:
|
||||
- cible: underlay
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
La fabric declaree tient-elle debout toute seule ? Rien ne sert de creer des
|
||||
machines sur un plan d'adressage qui se contredit.
|
||||
- cible: underlay-plan
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Le declare face au REEL, par l'API du cluster et une sonde TCP. C'est ici qu'on
|
||||
apprend qu'un pont n'existe pas, pas au milieu de la creation des VM.
|
||||
- cible: site-creer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
duree: "~9 min"
|
||||
pourquoi: >-
|
||||
Les machines du site, depuis l'underlay. Elles naissent ; elles ne repondent pas
|
||||
encore.
|
||||
- cible: site-deployer-tout
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
duree: "~20 min"
|
||||
pourquoi: >-
|
||||
Premier passage. Il va jusqu'a `serveur_ops` et s'arrete sur la forge vide :
|
||||
ce n'est pas un echec, c'est le maillon suivant.
|
||||
- cible: forge-amorcer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
duree: "~45 s"
|
||||
pourquoi: >-
|
||||
La forge tourne et n'a ni organisation ni depot. Sans cet amorcage, le runner
|
||||
clone le vide et le deploiement ne finira jamais.
|
||||
- cible: site-deployer-tout
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
duree: "~4 min"
|
||||
pourquoi: >-
|
||||
Second passage. Celui-ci doit finir a zero echec ; s'il n'y arrive pas, c'est
|
||||
un vrai defaut, plus un maillon manquant.
|
||||
- cible: publier
|
||||
nature: ecriture
|
||||
variables: [DEPOT]
|
||||
pourquoi: >-
|
||||
Le geste quotidien : eregion ET la forge du site, puis la verification. Un
|
||||
`git push` seul laisse la forge du site en retard sans que rien le dise.
|
||||
- cible: genome-pousser
|
||||
nature: ecriture
|
||||
variables: [DEPOT]
|
||||
pourquoi: >-
|
||||
La forge du site FAIT AUTORITE : tant qu'elle est en retard, tout ecosysteme qui
|
||||
s'y reproduit reproduit un moteur perime.
|
||||
- cible: routes-fabric-etat
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Les hyperviseurs routent-ils toutes les zones ? Une zone non routee ne se voit
|
||||
qu'au moment ou un locataire y pose sa premiere machine.
|
||||
- cible: gabarit-etat
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Sans gabarit conforme, aucun locataire ne pourra cloner quoi que ce soit ici.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: site-tenir
|
||||
titre: "Tenir un site en etat"
|
||||
portee: site
|
||||
doc: docs/hebergeur-exploitation.md
|
||||
but: >-
|
||||
Les gestes reguliers de l'hebergeur : porter le genome a jour, appliquer un role aux
|
||||
machines du site, et regarder ce que la fabric fait vraiment.
|
||||
etapes:
|
||||
- cible: genome-etat
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Ce que la forge porte, face au poste. Un ecart ici se paie chez TOUS les
|
||||
locataires qui clonent ensuite.
|
||||
- cible: genome-pousser
|
||||
nature: ecriture
|
||||
variables: [DEPOT]
|
||||
pourquoi: "Remettre la forge au niveau du poste, depot par depot si besoin."
|
||||
- cible: site-appliquer
|
||||
nature: ecriture
|
||||
variables: [GROUPE]
|
||||
pourquoi: >-
|
||||
Rejouer un role sur les machines du site — c'est ainsi qu'un runner reprend le
|
||||
genome courant, rien ne le tire tout seul.
|
||||
- cible: site-decrire
|
||||
nature: mesure
|
||||
pourquoi: "Ce que l'underlay declare comme machines de l'hebergeur."
|
||||
- cible: site-inventaire
|
||||
nature: mesure
|
||||
pourquoi: "L'inventaire dynamique du site, tel qu'Ansible le voit — pas tel qu'on l'imagine."
|
||||
- cible: site-intrants
|
||||
nature: mesure
|
||||
pourquoi: "Ce que ce site expose a ses locataires, derive et non declare deux fois."
|
||||
- cible: fiches-site-deposer
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Deposer chez chaque locataire la fiche que le site lui destine. Son runner n'a pas
|
||||
le depot du site : c'est par elle que son inventaire se genere sans lui. A commiter
|
||||
dans le depot de chaque locataire.
|
||||
- cible: site-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le playbook du site correspond-il encore aux couches declarees ?"
|
||||
- cible: site
|
||||
nature: ecriture
|
||||
pourquoi: "Regenerer playbooks/site.yml quand les couches ont bouge."
|
||||
- cible: routes-fabric-etat
|
||||
nature: mesure
|
||||
pourquoi: "Une route de zone manquante est invisible jusqu'au premier invite qui la traverse."
|
||||
- cible: locataire-creer
|
||||
nature: ecriture
|
||||
variables: [TENANT, PARALLELE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Cloner les VM d'un locataire NOMME, d'apres la face reseau qu'il publie : le
|
||||
runner du site materialise sans monter son depot. Memes machines, memes
|
||||
parametres que `flotte-creer` sur l'instance montee (P93, P94).
|
||||
- cible: locataire-raser
|
||||
nature: destructif
|
||||
variables: [TENANT, INSTANCE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Detruire les VM d'un locataire NOMME, d'apres sa face : les memes VMID que le
|
||||
plan derive (P94). Son nom court s'ecrit en toutes lettres, comme pour `raser`.
|
||||
- cible: site-raser
|
||||
nature: destructif
|
||||
variables: [SITE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Detruire les VM du SITE. A ne faire que sur un site de chantier : la
|
||||
reconstruction est prouvee, elle n'est pas gratuite.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: locataire-naitre
|
||||
titre: "Faire naitre un ecosysteme de locataire"
|
||||
portee: tenant
|
||||
doc: docs/multi-instances.md
|
||||
but: >-
|
||||
Du modele au plan monte : creer le dossier de l'ecosysteme, le rendre actif, puis
|
||||
generer son inventaire. Tout l'adressage descend du seed `index` — on ne l'ecrit
|
||||
jamais a la main.
|
||||
etapes:
|
||||
- cible: instance-modeles
|
||||
nature: mesure
|
||||
pourquoi: "Quels modeles de depart existent, avant d'en choisir un."
|
||||
- cible: instances
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Les ecosystemes deja decouverts, et surtout les COLLISIONS d'index : deux
|
||||
ecosystemes sur le meme seed se marcheraient dessus en silence.
|
||||
- cible: instance-creer
|
||||
nature: ecriture
|
||||
variables: [NOM, MODELE]
|
||||
pourquoi: "Le dossier de l'ecosysteme, depuis un modele, avec son index reserve."
|
||||
- cible: instance-utiliser
|
||||
nature: ecriture
|
||||
variables: [NOM]
|
||||
pourquoi: >-
|
||||
Basculer le symlink `instance/`. C'est lui qui decide QUEL ecosysteme la console
|
||||
configure — et quelle voute elle ouvrira.
|
||||
- cible: instance-courante
|
||||
nature: mesure
|
||||
pourquoi: "Verifier vers quoi on pointe avant d'ecrire quoi que ce soit."
|
||||
- cible: intrants-verifier
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Tout intrant qu'un role EXIGE est-il fourni ? Un intrant manquant ne se voit
|
||||
sinon qu'au milieu d'un deploiement.
|
||||
- cible: ports-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Deux roles co-localises qui reclament le meme port ne cohabiteront pas."
|
||||
- cible: instancier
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Generer hosts.yml depuis le plan SANS l'appliquer — on regarde le diff avant de
|
||||
le prendre.
|
||||
- cible: instancier-appliquer
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Prendre l'inventaire genere. `hosts.yml` est un ARTEFACT : on edite le plan, on
|
||||
ne le corrige jamais a la main.
|
||||
- cible: inventaire-verifier
|
||||
nature: mesure
|
||||
pourquoi: "L'inventaire se parse-t-il, voute dechiffree ? Sinon rien ne partira."
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: locataire-materialiser
|
||||
titre: "Preparer le terrain d'un locataire sur la fabric"
|
||||
portee: site
|
||||
doc: docs/implanter-un-tenant-sur-un-site.md
|
||||
but: >-
|
||||
Tout ce que l'HEBERGEUR pose avant qu'un locataire puisse exister : le placement, les
|
||||
pools, le SDN, les pare-feux et la frontiere. Chaque devis se mesure avant de
|
||||
s'appliquer — un devis qu'on applique sans l'avoir lu est un pari.
|
||||
etapes:
|
||||
- cible: placement-plan
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Le noeud, le stockage, le pont et le gabarit existent-ils VRAIMENT sur ce
|
||||
cluster ? C'est la premiere chose qui manque, et la derniere qu'on regarde.
|
||||
- cible: devis-proxmox-pools
|
||||
nature: mesure
|
||||
pourquoi: "Un pool par locataire, derive du plan — la cloison la plus simple."
|
||||
- cible: devis-proxmox-pools-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le devis tient-il ses propres regles avant qu'on le pose ?"
|
||||
- cible: devis-sdn
|
||||
nature: mesure
|
||||
pourquoi: "Zone, VNets et sous-reseaux, derives du seed de l'ecosysteme."
|
||||
- cible: devis-sdn-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Relire le devis SDN avant de toucher au reseau du cluster."
|
||||
- cible: sdn-plan
|
||||
nature: mesure
|
||||
pourquoi: "L'ecart entre le SDN en service et ce devis — ce qui manque, ce qui est perime."
|
||||
- cible: sdn-appliquer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Reconcilier : creer ce qui manque et RETIRER ce qui est perime. Le retrait est la
|
||||
moitie qu'on oublie.
|
||||
- cible: flux
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Regenerer le registre des flux et les regles nftables depuis les `meta/flux.yml`
|
||||
des roles. Tout ce qui suit en descend.
|
||||
- cible: face-reseau-publier
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Publier ce que ce locataire demande a son site (`face-reseau.yml`) : ses machines,
|
||||
ses zones, ses flux deja resolus. Le site ne lit plus que ce fichier. Apres `flux`,
|
||||
et a commiter dans le depot du locataire.
|
||||
- cible: devis-proxmox-fw
|
||||
nature: mesure
|
||||
pourquoi: "Le pare-feu est-ouest intra-locataire, derive du registre des flux."
|
||||
- cible: devis-proxmox-fw-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Relire ce devis avant de le poser sur le cluster."
|
||||
- cible: proxmox-fw-plan
|
||||
nature: mesure
|
||||
pourquoi: "L'ecart entre le pare-feu en service et le devis."
|
||||
- cible: proxmox-fw-appliquer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: "Poser IPSets, groupes et affectations — et retirer ce qui ne se declare plus."
|
||||
- cible: proxmox-fw-eprouver
|
||||
nature: mesure
|
||||
variables: [TENANT, HOTE]
|
||||
pourquoi: >-
|
||||
Avant d'activer le pare-feu d'UNE VM : les regles du devis tiennent-elles face aux
|
||||
flux que la VM recoit REELLEMENT ? Une VM reconstruite a perdu ses options.
|
||||
- cible: proxmox-fw-activer-vm
|
||||
nature: ecriture
|
||||
variables: [TENANT, HOTE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Activer une VM a la fois, matrice avant et apres, Icinga lu. Chaque defaut du
|
||||
2026-09-28 ne s'est montre qu'a l'activation d'UNE VM.
|
||||
- cible: devis-opnsense
|
||||
nature: mesure
|
||||
pourquoi: "La frontiere nord/sud, derivee du meme registre de flux."
|
||||
- cible: devis-opnsense-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Relire le devis de frontiere : c'est la porte de l'exterieur."
|
||||
- cible: frontiere-plan
|
||||
nature: mesure
|
||||
pourquoi: "Ce que la frontiere porte aujourd'hui, face a ce devis."
|
||||
- cible: frontiere-appliquer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Reconcilier la frontiere. Les routes creees ETEINTES ont deja coute une journee :
|
||||
une route eteinte compte « posee » et ne route rien.
|
||||
- cible: devis-reseau
|
||||
nature: mesure
|
||||
variables: [DIALECTE]
|
||||
pourquoi: >-
|
||||
Le devis des commutateurs physiques (VLANs, SVIs, ACLs). Il se pose a la main sur
|
||||
le materiel — le moteur ne configure pas les switches.
|
||||
- cible: frontiere-mesurer
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
La frontiere refuse-t-elle ce qui n'est pas declare ? Un connect() qui aboutit ne
|
||||
prouve rien : seule la LIVRAISON compte.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: locataire-deployer
|
||||
titre: "Materialiser et deployer la flotte d'un locataire"
|
||||
portee: tenant
|
||||
doc: docs/vm-lifecycle.md
|
||||
but: >-
|
||||
Des VM au service rendu : creer les machines manquantes, deployer couche par couche,
|
||||
puis mesurer. Rien n'est « pret » avant la recette.
|
||||
etapes:
|
||||
- cible: flotte-creer
|
||||
nature: ecriture
|
||||
portee: poste
|
||||
variables: [PARALLELE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Les VM manquantes, clonees depuis le gabarit dore. VMID, IP et VLAN sont DERIVES
|
||||
du plan : on ne les saisit nulle part.
|
||||
- cible: monter-flotte
|
||||
nature: ecriture
|
||||
duree: "long"
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Des VM qui viennent de naitre (ou de renaitre) a la flotte recettee : flux, AC et
|
||||
DNS d'abord, tout le reste, puis `valider`. C'est la sequence du runner apres une
|
||||
reconstruction ; chaque proprietaire d'etat y remet celui d'avant.
|
||||
- cible: deployer-tout
|
||||
nature: ecriture
|
||||
duree: "long"
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Toute la flotte, dans l'ordre des couches. L'ordre vient du graphe de
|
||||
dependances, pas d'une liste tenue a la main.
|
||||
- cible: deployer-groupe
|
||||
nature: ecriture
|
||||
variables: [GROUPE]
|
||||
facultative: true
|
||||
pourquoi: >-
|
||||
Un seul role, sur toute la flotte. C'est le geste d'apres : quand un role a change
|
||||
et qu'on ne veut pas tout rejouer.
|
||||
- cible: appliquer
|
||||
nature: ecriture
|
||||
variables: [GROUPE]
|
||||
facultative: true
|
||||
pourquoi: >-
|
||||
Appliquer un groupe a la flotte. Meme usage que `deployer-groupe`, par le chemin
|
||||
court — sans le graphe des couches.
|
||||
- cible: verifier-deploiement
|
||||
nature: mesure
|
||||
pourquoi: "L'etat de la flotte apres coup — avant de croire que c'est fini."
|
||||
- cible: valider
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
La recette de validation sur la flotte. C'est elle qui autorise le mot « pret »,
|
||||
pas l'absence d'erreur rouge.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: machine-une
|
||||
titre: "Ajouter ou reprendre une seule machine"
|
||||
portee: tenant
|
||||
doc: docs/vm-lifecycle.md
|
||||
but: >-
|
||||
Le cycle d'UNE machine, du plan au service : la voir derivee, la creer, la deployer,
|
||||
la verifier. Le meme chemin sert pour une machine neuve et pour une machine a
|
||||
reprendre.
|
||||
etapes:
|
||||
- cible: hote-afficher
|
||||
nature: mesure
|
||||
variables: [HOTE]
|
||||
pourquoi: >-
|
||||
Tout ce que le plan derive pour cette machine — avant de la creer, pour verifier
|
||||
qu'on va bien poser ce qu'on croit.
|
||||
- cible: creer-vm
|
||||
nature: ecriture
|
||||
portee: poste
|
||||
variables: [HOTE]
|
||||
duree: "~4 min 30"
|
||||
pourquoi: "Cloner depuis le gabarit et ATTENDRE que la machine reponde."
|
||||
- cible: deployer
|
||||
nature: ecriture
|
||||
variables: [HOTE]
|
||||
pourquoi: "Les roles de cette machine, couche par couche, dans l'ordre du graphe."
|
||||
- cible: verifier-hote
|
||||
nature: mesure
|
||||
variables: [HOTE]
|
||||
pourquoi: "Le playbook de verification sur cette machine seule."
|
||||
- cible: cloner-vm
|
||||
nature: ecriture
|
||||
portee: poste
|
||||
variables: [HOTE, VMID]
|
||||
facultative: true
|
||||
pourquoi: >-
|
||||
Cloner SANS passer par le plan. Chemin de reprise : a n'emprunter que lorsque le
|
||||
plan ne peut pas encore deriver la machine.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: flotte-refaire
|
||||
titre: "Raser et reconstruire un ecosysteme"
|
||||
portee: tenant
|
||||
doc: docs/vm-lifecycle.md
|
||||
but: >-
|
||||
La preuve la plus dure du moteur : detruire un ecosysteme et le refaire depuis le
|
||||
code seul. A ne lancer que sur un chantier — la prod vit ailleurs.
|
||||
etapes:
|
||||
- cible: reconstruire-locataire
|
||||
nature: destructif
|
||||
portee: poste
|
||||
duree: "long"
|
||||
variables: [TENANT, DEPUIS, ARMER]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
TOUT, d'une commande, depuis le poste : sauvegarder, raser et recreer (runner du
|
||||
site), amorcer, armer, monter (runner du locataire, etat remis), pare-feu, bilan.
|
||||
Arret a la premiere etape en echec ; `DEPUIS` reprend. Les etapes ci-dessous sont
|
||||
les memes, une a une.
|
||||
- cible: sauvegarder-maintenant
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Deposer l'etat de chaque noeud JUSTE AVANT de raser. La reconstruction remet le
|
||||
dernier instantane anterieur a la naissance des machines : sans ce depot, c'est
|
||||
celui de la nuit, et la journee est perdue. Il est etiquete « avant-raser » et
|
||||
garde jusqu'a la reconstruction suivante : c'est contre lui que jugent les temoins.
|
||||
- cible: raser
|
||||
nature: destructif
|
||||
portee: poste
|
||||
variables: [INSTANCE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Detruire les VM derivees du plan. Le nom de l'ecosysteme s'ecrit en toutes
|
||||
lettres : c'est le seul garde-fou qui resiste a un clic distrait.
|
||||
- cible: reconstruire
|
||||
nature: ecriture
|
||||
portee: poste
|
||||
duree: "long"
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Refaire tout depuis zero : les VM, puis le deploiement complet — ou chaque role
|
||||
proprietaire REMET l'etat de l'incarnation precedente (AC, annuaire, bases,
|
||||
Nextcloud, courriel, DKIM). Si le code ne suffit pas, c'est ici qu'on l'apprend.
|
||||
- cible: restauration-etat
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Ce que chaque jeu est devenu : restaure (et depuis quel instantane), neuf, en
|
||||
place. Un jeu EN ATTENTE bloque la sauvegarde de son noeud — c'est voulu.
|
||||
- cible: temoins-etat
|
||||
nature: mesure
|
||||
variables: [HOTE, SOURCE]
|
||||
pourquoi: >-
|
||||
L'etat vivant est-il celui d'avant le rasage ? Une perte, ou une identite changee
|
||||
(cles de l'AC, DKIM, instanceid de Nextcloud, mot de passe d'une entree de
|
||||
l'annuaire), est un ecart. Le bilan dit ce que les roles ont fait ; les temoins
|
||||
mesurent ce qui est revenu.
|
||||
- cible: restauration-renoncer
|
||||
nature: destructif
|
||||
variables: [HOTE, JEU]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Ecarter l'etat d'avant d'un jeu, sans le remettre. La sauvegarde reprend, et
|
||||
l'ancien etat sortira de la retention : une decision, pas une reparation.
|
||||
- cible: valider
|
||||
nature: mesure
|
||||
pourquoi: "Une reconstruction sans recette n'a rien prouve."
|
||||
- cible: parefeu-verifier-flotte
|
||||
nature: mesure
|
||||
portee: poste
|
||||
variables: [INSTANCE]
|
||||
pourquoi: >-
|
||||
La sonde `connectivite` est-elle saine sur chaque VM ? C'est le prealable : une VM
|
||||
clonee nait sans ses options de pare-feu, et on n'active que sur une flotte saine.
|
||||
- cible: parefeu-activer-flotte
|
||||
nature: ecriture
|
||||
portee: poste
|
||||
variables: [INSTANCE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Tout activer d'un coup, puis lire la sonde `connectivite` que chaque VM rapporte a
|
||||
la minute : seul ce qui change apres l'activation compte. Environ trois minutes.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: gabarit
|
||||
titre: "Le gabarit dore"
|
||||
portee: site
|
||||
doc: docs/procedure-template-debian13-proxmox.md
|
||||
but: >-
|
||||
La VM de reference que toute la flotte clone. Elle nait en q35/OVMF et ne se convertit
|
||||
jamais : convertir depuis i440fx casse interfaces et disques.
|
||||
etapes:
|
||||
- cible: gabarit-etat
|
||||
nature: mesure
|
||||
pourquoi: "Le gabarit porte-t-il ce que le SITE declare ? A regarder avant d'y toucher."
|
||||
- cible: preparer-modele
|
||||
nature: ecriture
|
||||
duree: "long"
|
||||
pourquoi: "Preparer la VM de reference, celle qui sera clonee pour chaque hote."
|
||||
- cible: verifier-modele
|
||||
nature: mesure
|
||||
pourquoi: "Le gabarit tient-il ses promesses avant qu'on le capture ?"
|
||||
- cible: nettoyer-modele
|
||||
nature: destructif
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Nettoyer avant capture. Destructif pour la VM de reference : ce qui est efface ne
|
||||
se retrouve pas.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: mesurer
|
||||
titre: "Mesurer sans rien ecrire"
|
||||
portee: tenant
|
||||
doc: docs/devis-services.md
|
||||
but: >-
|
||||
Les devis de service : ils confrontent ce que le plan derive a ce que le systeme rend
|
||||
VRAIMENT, et sortent en erreur s'il y a un ecart. Une tache verte ne prouve pas qu'un
|
||||
service rend son service.
|
||||
etapes:
|
||||
- cible: identite-plan
|
||||
nature: mesure
|
||||
pourquoi: "L'identite deployee face a ce que le plan derive."
|
||||
- cible: certificats-plan
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Les certificats sur disque face a ceux reellement SERVIS. Un cert renouvele mais
|
||||
non recharge reste perime en memoire.
|
||||
- cible: expositions-plan
|
||||
nature: mesure
|
||||
pourquoi: "Chaque exposition du plan repond-elle, depuis l'edge et depuis le poste ?"
|
||||
- cible: expositions-etat
|
||||
nature: mesure
|
||||
pourquoi: "Chaque exposition est-elle servie sous un certificat qui la porte ?"
|
||||
- cible: courriel-plan
|
||||
nature: mesure
|
||||
pourquoi: "La chaine Postfix → LDAP → Dovecot → IMAP, de bout en bout."
|
||||
- cible: postgresql-plan
|
||||
nature: mesure
|
||||
pourquoi: "Le chiffrement impose et la portee reelle des acces."
|
||||
- cible: mtu-mesurer
|
||||
nature: mesure
|
||||
pourquoi: "L'invite porte-t-il le MTU de sa zone ? Un MTU faux ne se voit qu'aux gros paquets."
|
||||
- cible: versions-mesurer
|
||||
nature: mesure
|
||||
pourquoi: "De combien nos epinglages ont-ils vieilli face aux amonts ?"
|
||||
- cible: ports-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Deux roles co-localises revendiquent-ils le meme port ?"
|
||||
- cible: intrants-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Un intrant exige et non fourni est une panne differee."
|
||||
- cible: flux-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le registre des flux correspond-il encore aux `meta/flux.yml` des roles ?"
|
||||
- cible: site-intrants-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le locataire monte suit-il encore les intrants de son site ?"
|
||||
- cible: sonder
|
||||
nature: mesure
|
||||
variables: [CIBLE]
|
||||
pourquoi: >-
|
||||
Sonder une cible et DIRE ce qui distingue absence, politique et frontiere. Un
|
||||
« echec » nu ecrase ces trois causes et envoie chercher la panne ailleurs.
|
||||
- cible: faits
|
||||
nature: mesure
|
||||
variables: [LIMITE]
|
||||
pourquoi: "Les faits Ansible de la flotte, quand une hypothese demande un fait."
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: recette
|
||||
titre: "La recette — avant de dire « pret »"
|
||||
portee: tenant
|
||||
doc: docs/audit/plan-de-recette.md
|
||||
but: >-
|
||||
Ce qui separe « ca a tourne sans erreur » de « c'est livrable ». Les preuves lisent le
|
||||
depot ; la recette interroge la flotte. Il faut les deux.
|
||||
etapes:
|
||||
- cible: verifier
|
||||
nature: mesure
|
||||
pourquoi: "Rejouer les preuves sans reecrire le rapport — la verification rapide."
|
||||
- cible: prouver
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Executer les preuves et ECRIRE le rapport date. C'est le document qu'on montre,
|
||||
et celui qu'on relit dans six mois.
|
||||
- cible: valider
|
||||
nature: mesure
|
||||
pourquoi: "La recette de validation sur la flotte reelle."
|
||||
- cible: verifier-deploiement
|
||||
nature: mesure
|
||||
pourquoi: "L'etat de la flotte, apres coup."
|
||||
- cible: inventaire
|
||||
nature: mesure
|
||||
pourquoi: "L'inventaire se verifie et se montre — lab puis production."
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: acces-admin
|
||||
titre: "L'acces d'administration (tunnel WireGuard)"
|
||||
portee: site
|
||||
doc: docs/acces-administration.md
|
||||
but: >-
|
||||
Un tunnel nominatif par locataire, declare par lui et borne a lui. Sans garde, un
|
||||
ecosysteme s'ouvrirait un acces chez son voisin depuis son propre plan.
|
||||
etapes:
|
||||
- cible: vpn-admin-plan
|
||||
nature: mesure
|
||||
pourquoi: "Ce que la frontiere porte aujourd'hui, face aux pairs declares au plan."
|
||||
- cible: vpn-admin-appliquer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: "Poser l'instance et les pairs declares — et retirer ceux qui ne le sont plus."
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: dns-public
|
||||
titre: "Le DNS public et sa signature"
|
||||
portee: tenant
|
||||
doc: docs/dns-interne.md
|
||||
but: >-
|
||||
Publier les zones du locataire, signees avant d'etre exposees. Les DS partent chez le
|
||||
registraire : c'est le seul maillon que le moteur ne peut pas poser lui-meme.
|
||||
etapes:
|
||||
- cible: dnssec-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Une cle en voute pour chaque zone signee, et des DS conformes au registre."
|
||||
- cible: dnssec-ds
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Les DS a remettre au registraire, calcules depuis la voute du locataire. A porter
|
||||
a la main chez le registraire : aucun automate ne le fera.
|
||||
- cible: dns-bascule-devis
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Basculer nos serveurs de noms changerait-il quelque chose ? Le plan face au DNS
|
||||
reellement en service, avant de toucher a la delegation.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: filiation
|
||||
titre: "Filiation, insemination, emancipation"
|
||||
portee: tenant
|
||||
doc: docs/filiation-emancipation.md
|
||||
but: >-
|
||||
D'ou vient cet ecosysteme, de quoi depend-il encore, et que faut-il couper pour qu'il
|
||||
tienne seul. L'emancipation se mesure ; elle ne se decrete pas.
|
||||
etapes:
|
||||
- cible: genome
|
||||
nature: mesure
|
||||
pourquoi: "Les depots requis a la reproduction de cet ecosysteme, et leur etat."
|
||||
- cible: genome-inscrire
|
||||
nature: ecriture
|
||||
pourquoi: "Inscrire la parente dans l'instance — sans quoi la filiation n'est qu'un souvenir."
|
||||
- cible: genome-verifier
|
||||
nature: mesure
|
||||
pourquoi: "La parente inscrite tient-elle encore ? Le code de sortie repond."
|
||||
- cible: inseminer
|
||||
nature: ecriture
|
||||
variables: [TENANT, HOTE]
|
||||
pourquoi: >-
|
||||
Le SITE amorce le runner d'un locataire, SANS ses secrets. Trois murs connus :
|
||||
apt, pip, et le genome lui-meme.
|
||||
- cible: depots-perimes
|
||||
nature: destructif
|
||||
fixes: {CONFIRMER: "true"}
|
||||
# FACULTATIVE, SINON ELLE BARRE LA PREUVE QUI LA SUIT. La console
|
||||
# debloque d'office une etape « mesure » ; toute autre attend que la
|
||||
# precedente non facultative ait REUSSI dans la session. Un menage
|
||||
# qu'on peut ne pas avoir a faire — aucun depot perime ce jour-la —
|
||||
# rendrait alors l'emancipation injouable. Le ménage n'est pas un
|
||||
# prealable a la preuve : il nettoie ce que la filiation a laisse.
|
||||
facultative: true
|
||||
pourquoi: >-
|
||||
Un depot raye du plan reste sur le disque du runner, qui garde de quoi lire un
|
||||
ecosysteme qu'il ne declare plus. Trois choses ne sont jamais retirees : ce qui
|
||||
n'est pas un depot git, ce qui porte des modifications non validees, et ce qui
|
||||
porte des commits qu'aucun distant ne porte.
|
||||
- cible: emancipation-prouver
|
||||
# ECRITURE, ET NON MESURE, MALGRE LE MOT « PROUVER ». La cible COUPE
|
||||
# l'amont quelques secondes pour mesurer : elle refuse d'ailleurs sans
|
||||
# CONFIRMER=true, et le dit. Le fait qu'elle rapporte un constat ne la
|
||||
# rend pas inerte. Declarée « mesure », elle se serait offerte a tout
|
||||
# outil qui ne propose que ce qui n'agit pas.
|
||||
nature: ecriture
|
||||
variables: [SERVICE, HOTE]
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Prouver qu'un lien est coupe, en le coupant. La coupure est retiree quoi
|
||||
qu'il arrive, mais la fonction eprouvee peut echouer pendant ce temps.
|
||||
La mesure instruit ; l'humain decide. Une emancipation automatique serait
|
||||
une expulsion.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: remise
|
||||
titre: "La remise au client"
|
||||
portee: poste
|
||||
doc: docs/remise-au-client.md
|
||||
but: >-
|
||||
Deux temps qui ne se confondent pas : le paquet qu'on remet, puis le re-cle qui
|
||||
mesure la revocation reelle. Tant que le temps 2 n'est pas fait, on detient encore
|
||||
les cles de quelqu'un d'autre.
|
||||
etapes:
|
||||
- cible: remise-recenser
|
||||
nature: mesure
|
||||
pourquoi: "Ce qu'une remise emporterait, sans rien ecrire. A lire avant de fabriquer."
|
||||
- cible: remise-paquet
|
||||
nature: ecriture
|
||||
variables: [VERS]
|
||||
pourquoi: "Temps 1 : le paquet chiffre, et relu apres ecriture."
|
||||
- cible: remise-inscrire
|
||||
nature: ecriture
|
||||
variables: [RECU_PAR, COURRIEL, DANS]
|
||||
pourquoi: >-
|
||||
Inscrire la remise chez le locataire : qui a recu, quand, et dans combien de
|
||||
jours le second temps est du.
|
||||
- cible: remise-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Temps 1 fait ? Temps 2 du, ou echu ? La seule reponse qui compte est datee."
|
||||
- cible: remise-recleer
|
||||
nature: ecriture
|
||||
fixes: {CONFIRMER: "true"}
|
||||
pourquoi: >-
|
||||
Temps 2 : mesurer la revocation REELLE, puis estampiller. On ne coche pas cette
|
||||
case, on la mesure.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: cles
|
||||
titre: "Les cles hors du poste"
|
||||
portee: poste
|
||||
doc: docs/sortir-les-cles-du-poste.md
|
||||
but: >-
|
||||
Ce qui n'existe QUE sur le poste meurt avec lui : les CLES, et les VOUTES qu'elles
|
||||
ouvrent (gitignorees, donc sur aucune forge). Les deux sortent, sur un support qu'on
|
||||
relit — et on refait l'operation a chaque voute nouvelle ou modifiee.
|
||||
etapes:
|
||||
- cible: cles-recenser
|
||||
nature: mesure
|
||||
pourquoi: "Ce qui n'existe que sur ce poste, sans rien ecrire. La liste fait peur, c'est le but."
|
||||
- cible: cles-exporter
|
||||
nature: ecriture
|
||||
variables: [VERS]
|
||||
pourquoi: "Sortir les cles, chiffrees, et les RELIRE apres ecriture."
|
||||
- cible: voutes-recenser
|
||||
nature: mesure
|
||||
pourquoi: "Les voutes de ce poste, et leur empreinte. Une voute en clair est refusee avant tout."
|
||||
- cible: voutes-exporter
|
||||
nature: ecriture
|
||||
variables: [VERS]
|
||||
pourquoi: >-
|
||||
Sortir les voutes, deja chiffrees, dans leur propre archive, et la RELIRE. Sans
|
||||
elles, les cles restaurees n'ouvrent rien (2026-09-28).
|
||||
- cible: cles-compagnons
|
||||
nature: ecriture
|
||||
variables: [VERS]
|
||||
pourquoi: >-
|
||||
Deposer le script de restauration sur la cle. Une archive qu'on ne sait pas
|
||||
rouvrir dans cinq ans n'est pas une sauvegarde.
|
||||
- cible: cles-restaurer
|
||||
nature: ecriture
|
||||
variables: [ARCHIVE]
|
||||
pourquoi: "Remettre les cles en place. A eprouver AVANT d'en avoir besoin."
|
||||
- cible: depot-hors-site
|
||||
nature: ecriture
|
||||
variables: [VERS]
|
||||
pourquoi: >-
|
||||
Copier le depot de sauvegarde HORS du site. Le site heberge du chiffre et ne peut
|
||||
pas le juger : la verification suit la cle.
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: lire-le-plan
|
||||
titre: "Lire le plan et ce qu'il derive"
|
||||
portee: tenant
|
||||
doc: docs/plan-et-generation.md
|
||||
but: >-
|
||||
Tout ce qui se regarde sans rien changer : les registres du plan, leur validation, et
|
||||
l'inventaire qui en descend. Le premier geste devant un ecosysteme qu'on ne connait pas.
|
||||
etapes:
|
||||
- cible: serveurs
|
||||
nature: mesure
|
||||
pourquoi: "Les serveurs declares au plan."
|
||||
- cible: serveurs-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le registre des serveurs tient-il ses regles ?"
|
||||
- cible: applications
|
||||
nature: mesure
|
||||
pourquoi: "Les applications declarees au plan."
|
||||
- cible: applications-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le registre des applications tient-il ses regles ?"
|
||||
- cible: bases
|
||||
nature: mesure
|
||||
pourquoi: "Les bases de donnees declarees au plan."
|
||||
- cible: bases-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le registre des bases tient-il ses regles ?"
|
||||
- cible: domaines
|
||||
nature: mesure
|
||||
pourquoi: "Les domaines declares au plan."
|
||||
- cible: domaines-verifier
|
||||
nature: mesure
|
||||
pourquoi: "Le registre des domaines tient-il ses regles ?"
|
||||
- cible: inventaire-lister
|
||||
nature: mesure
|
||||
pourquoi: "L'inventaire complet, en JSON — la verite generee."
|
||||
- cible: inventaire-graphe
|
||||
nature: mesure
|
||||
pourquoi: "Le graphe des groupes : qui herite de quoi."
|
||||
- cible: inventaire-hote
|
||||
nature: mesure
|
||||
variables: [HOTE]
|
||||
pourquoi: "Les variables derivees d'une machine, telles qu'Ansible les verra."
|
||||
- cible: inventaire-lab
|
||||
nature: mesure
|
||||
pourquoi: "Le graphe de l'inventaire de laboratoire."
|
||||
- cible: inventaire-production
|
||||
nature: mesure
|
||||
pourquoi: "Le graphe de l'inventaire de production."
|
||||
- cible: config
|
||||
nature: mesure
|
||||
pourquoi: "La configuration Proxmox telle que le moteur la lit."
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
- id: depot-verifier
|
||||
titre: "Verifier le depot avant de livrer"
|
||||
portee: toute
|
||||
doc: AGENTS.md
|
||||
but: >-
|
||||
Ce que le depot se doit a lui-meme : syntaxe, lint, tests, schema, fiches. Rien n'est
|
||||
« pret » sans au moins la syntaxe du playbook touche et l'entree de CHANGELOG.
|
||||
etapes:
|
||||
- cible: syntaxe
|
||||
nature: mesure
|
||||
pourquoi: "La syntaxe de TOUS les playbooks. Jamais declarer pret si elle echoue."
|
||||
- cible: lint
|
||||
nature: mesure
|
||||
pourquoi: "`ansible-lint` sur tout le depot."
|
||||
- cible: test
|
||||
nature: mesure
|
||||
pourquoi: "Les tests unitaires de derivation — nomenclature et inventaire."
|
||||
- cible: ci
|
||||
nature: mesure
|
||||
pourquoi: >-
|
||||
Verifier le depot comme la CI, sur un modele public monte a l'ecart : sans jamais
|
||||
toucher a l'ecosysteme monte.
|
||||
- cible: schema
|
||||
nature: ecriture
|
||||
pourquoi: >-
|
||||
Regenerer le schema du plan depuis les registres et les validateurs. C'est lui qui
|
||||
construit les formulaires : un champ nouveau apparait sans toucher a l'interface.
|
||||
- cible: fiches
|
||||
nature: ecriture
|
||||
variables: [ROLE]
|
||||
pourquoi: "Regenerer la fiche de chaque role, depuis le role lui-meme."
|
||||
- cible: plan-recette
|
||||
nature: ecriture
|
||||
pourquoi: "Regenerer le plan de recette depuis le wiki."
|
||||
- cible: wiki-publier
|
||||
nature: ecriture
|
||||
pourquoi: "Publier le wiki vers la forge, une fois qu'il dit vrai."
|
||||
|
||||
# CE QUE LA CONSOLE N'OFFRE PAS, ET POURQUOI. Une exemption muette serait un oubli
|
||||
# deguise : chaque ligne porte son motif, et la garde refuse une exemption vide.
|
||||
hors_assistant:
|
||||
aide: >-
|
||||
Aide en ligne de commande. La console porte la meme information autrement — chaque
|
||||
etape affiche le libelle lu dans le Makefile.
|
||||
ansible-runtime: >-
|
||||
Prerequis interne, appele par les cibles qui deploient. L'offrir seul donnerait un
|
||||
bouton qui ne fait rien de visible.
|
||||
inventaire-ui: >-
|
||||
C'est cette console elle-meme. Un bouton qui la relance depuis elle-meme n'a pas d'objet.
|
||||
syntaxe-modele: "Couverte par `syntaxe`, qui passe tous les playbooks d'un coup."
|
||||
syntaxe-verification-modele: "Couverte par `syntaxe`."
|
||||
syntaxe-nettoyage: "Couverte par `syntaxe`."
|
||||
syntaxe-verification-hote: "Couverte par `syntaxe`."
|
||||
syntaxe-groupes: "Couverte par `syntaxe`."
|
||||
syntaxe-proxmox: "Couverte par `syntaxe`."
|
||||
model-creer: >-
|
||||
Fabrique un MODELE d'ecosysteme — un geste de mainteneur du moteur, pas d'exploitant.
|
||||
Il se fait au poste, en connaissance du catalogue des modeles.
|
||||
serveurs-bootstrap: >-
|
||||
Chemin de REPRISE : reconstitue le plan depuis un inventaire existant. Il ecrit par
|
||||
dessus le plan, et ne doit pas etre a un clic d'un exploitant qui explore.
|
||||
applications-bootstrap: >-
|
||||
Meme raison que `serveurs-bootstrap` : amorcage de reprise, pas geste courant.
|
||||
cacher-paquets: >-
|
||||
Se fait EN LIGNE depuis le poste, avant de partir sur un site hors ligne. Le runner,
|
||||
lui, est deja derriere la frontiere : le bouton serait au mauvais endroit.
|
||||
ca-racine: >-
|
||||
Recupere la racine de l'AC pour l'installer sur un poste. L'empreinte doit etre
|
||||
verifiee A LA MAIN avant installation — un bouton encouragerait a sauter ce controle.
|
||||
ca-empreinte: >-
|
||||
Le temoin de comparaison de `ca-racine`. Meme raison : il se lit, il ne se clique pas.
|
||||
|
|
@ -20,10 +20,10 @@ l'edge) **n'a pas été rechargé** → il sert l'ancien cert en mémoire.
|
|||
**Diagnostic-réflexe** — comparer le cert *servi* au cert *fichier* :
|
||||
```bash
|
||||
# SERVI (en mémoire par nginx)
|
||||
echo | openssl s_client -connect infra-edge-01.chezlepro.internal:443 -servername keycloak.chezlepro.internal 2>/dev/null \
|
||||
echo | openssl s_client -connect infra-edge-01…:443 -servername keycloak.lab… 2>/dev/null \
|
||||
| openssl x509 -noout -enddate
|
||||
# FICHIER (sur disque)
|
||||
openssl x509 -in /etc/step/certs/infra-edge-01.chezlepro.internal.crt -noout -enddate
|
||||
openssl x509 -in /etc/step/certs/infra-edge-01….crt -noout -enddate
|
||||
```
|
||||
Dates différentes (servi < fichier) ⇒ nginx sert un cert périmé.
|
||||
|
||||
|
|
@ -50,15 +50,8 @@ Par défaut, un utilisateur SSO est **Viewer** (dashboards seulement, pas Explor
|
|||
3. L'utilisateur doit **se déconnecter/reconnecter** (Grafana applique le rôle à la connexion).
|
||||
|
||||
Mapping (défaut du rôle grafana) : `grafana-admin`→Admin, `grafana-editor`→Editor, sinon Viewer
|
||||
(`serveur_grafana_oidc_role_path`).
|
||||
|
||||
> **Préférer le groupe à la personne — et c'est construit, pas un idéal.** Les groupes LDAP
|
||||
> sont projetés dans Keycloak et émis en claim (`tasks/groupes-ldap.yml`, `claim-groupes.yml`),
|
||||
> et `roles/serveur_grafana/meta/acces.yml` déclare déjà `sysadmin ⇒ Admin`,
|
||||
> `personnel ⇒ Viewer`. Ajouter quelqu'un au **groupe** lui ouvre Grafana, Forgejo, Icinga et
|
||||
> le courriel d'un seul geste — alors qu'une assignation nominative crée une dette qu'on
|
||||
> découvre le jour du départ, service par service. L'assignation explicite ci-dessus reste
|
||||
> le geste de dépannage, pas la façon normale d'accorder un accès. Voir `docs/autorisation.md` §5.
|
||||
(`serveur_grafana_oidc_role_path`). Idéal souverain : piloter par un **groupe d'annuaire** plutôt
|
||||
qu'un utilisateur explicite. Voir l'unité wiki *Autorisation & RBAC*.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -112,64 +105,6 @@ Note : ne s'applique qu'aux Forgejo **gérées par Set-OPS**.
|
|||
|
||||
## 5. Restaurer — et d'abord : prouver qu'on peut
|
||||
|
||||
### 5.0 La reconstruction remet l'état (depuis le 2026-09-30)
|
||||
|
||||
Jusqu'au 2026-09-30, **une reconstruction repartait d'un état neuf** : nouvelle racine
|
||||
d'AC, annuaire vierge (`sysadmin` au mot de passe d'amorçage), bases, Nextcloud et courriel
|
||||
vides. Les instantanés se restauraient pour *prouver* qu'ils s'ouvrent, jamais pour être
|
||||
remis en service.
|
||||
|
||||
Désormais **chaque rôle propriétaire remet l'état de l'incarnation précédente**, au moment
|
||||
où il le créerait neuf — la bonne séquence vient des couches de déploiement :
|
||||
|
||||
| Rôle | Quand | Condition « vierge » |
|
||||
|---|---|---|
|
||||
| `serveur_step_ca` | avant `step ca init` | pas de `ca.json` — et la voûte doit ouvrir les clés restaurées |
|
||||
| `serveur_postgresql` | juste après la création des bases | base sans table (hors `nextcloud`, `icinga`) |
|
||||
| `serveur_openldap` | en fin de rôle (schéma et `ppolicy` chargés) | aucune entrée hors la racine |
|
||||
| `serveur_dovecot` | après le répertoire des boîtes | répertoire vide |
|
||||
| `serveur_rspamd` | avant la génération DKIM | pas de clé DKIM |
|
||||
| `serveur_forgejo` | avant l'arborescence de données | répertoire vide |
|
||||
| `serveur_nextcloud` | **en fin de rôle** : base + fichiers + config ensemble, puis `occ upgrade` | pas de `config.php` au départ, base vide |
|
||||
| `serveur_web_dorsal` | après sa racine | racine vide |
|
||||
|
||||
**L'instantané remis** est le dernier pris **avant la naissance de la machine** (date de
|
||||
sa clé d'hôte SSH). Un état en place n'est jamais écrasé d'office. Chaque jeu laisse un
|
||||
marqueur dans `/etc/setops/restauration/<jeu>` ; ce qui a été remplacé est mis de côté dans
|
||||
`/var/backups/setops-avant-restauration/`.
|
||||
|
||||
**La sauvegarde refuse de déposer** (code 3) tant qu'un état d'avant n'a été ni remis ni
|
||||
écarté : sinon `restic forget --keep-daily` chasserait l'instantané d'avant au profit de
|
||||
l'état neuf du même jour.
|
||||
|
||||
**L'instantané d'avant rasage est étiqueté** `avant-raser` (depuis le 2026-10-07) et la
|
||||
rétention le garde **jusqu'à la reconstruction suivante**, qui lui retire l'étiquette. Sans
|
||||
elle, le premier dépôt de la machine reconstruite le chassait le jour même : après la
|
||||
reconstruction de Technolibre (M4), il ne restait rien à quoi comparer l'état remis.
|
||||
|
||||
**Les témoins** comparent l'état vivant à cet instantané. Un écart, c'est une **perte**
|
||||
(fichier, courriel, entrée de l'annuaire, base, rôle, table, ligne d'historique, ou une
|
||||
base dont plus aucune ligne d'avant ne subsiste) ou une **identité changée** : clés et
|
||||
certificats de l'AC, AC d'Icinga et environnement d'Icinga DB, clé DKIM, `instanceid` de
|
||||
Nextcloud, mot de passe d'une entrée de l'annuaire. PostgreSQL se compare par clé (première
|
||||
colonne de chaque table), pas par nombre de lignes. Le reste (un courriel reçu depuis, une table de sessions, le bayes de rspamd,
|
||||
un cache) est listé sans être un écart. La reconstruction les lance à son étape `bilan`.
|
||||
|
||||
Les gestes :
|
||||
|
||||
```
|
||||
make sauvegarder-maintenant # JUSTE AVANT de raser : étiqueté « avant-raser »
|
||||
make restauration-etat [HOTE=...] # ce que chaque jeu est devenu, et si son instantané est encore au dépôt
|
||||
make temoins-etat [HOTE=...] # l'état vivant contre l'instantané d'avant rasage
|
||||
make temoins-etat SOURCE=candidat # faute d'étiquette : contre le dernier d'avant la naissance
|
||||
make restauration-renoncer HOTE=.. JEU=.. CONFIRMER=true # écarter un état, sans le remettre
|
||||
-e client_backup_restauration_instantane=<id> # imposer un instantané, par hôte
|
||||
```
|
||||
|
||||
Sur un nœud, sans Ansible : `setops-restaurer etat`, `setops-restaurer temoins`, et les répétitions qui ne touchent à
|
||||
rien — `setops-restaurer annuaire --essai <base_dn>`, `base <nom> --vers epreuve`,
|
||||
`fichiers --vers /var/tmp/epreuve <chemin>`.
|
||||
|
||||
> Éprouvé le 2026-08-12 sur Chezlepro. `make valider` rejoue la partie automatisable ;
|
||||
> la restauration d'une base reste manuelle, et porte un piège décrit plus bas.
|
||||
|
||||
|
|
@ -201,11 +136,10 @@ la base badger de step-ca, qui avance à **chaque** émission de certificat.
|
|||
|
||||
`pg_dumpall` écrit `CREATE DATABASE <suivante>` **avant** le `\connect` correspondant.
|
||||
Découper « du `\connect X` au `\connect` suivant » emporte donc un ordre qui vise une
|
||||
**autre** base. Couper aussi sur `CREATE DATABASE` et sur `DROP DATABASE` (avec `--clean`,
|
||||
la section de `nextcloud` finissait par `DROP DATABASE postgres;` — 2026-09-30), et **vérifier avant de rejouer** :
|
||||
**autre** base. Couper aussi sur `CREATE DATABASE`, et **vérifier avant de rejouer** :
|
||||
|
||||
```
|
||||
awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /||/^DROP DATABASE /){exit} f' \
|
||||
awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /){exit} f' \
|
||||
toutes-bases.sql > section.sql
|
||||
|
||||
grep -qE '^(DROP|CREATE|ALTER) DATABASE|^\\connect' section.sql \
|
||||
|
|
@ -223,286 +157,49 @@ Mesuré sur `forgejo` : rejeu en **0 erreur**, **130 tables**, et les comptes r
|
|||
**Ne jamais rejouer un `pg_dumpall` entier sur un cluster vivant** : il contient les
|
||||
`DROP DATABASE` de toutes les bases. Une restauration réelle se fait sur un cluster neuf.
|
||||
|
||||
## 6. La bascule d'adressage de la fabric (D-77) — FAITE
|
||||
## 6. La frontière porte deux adresses de gestion (transition D-77)
|
||||
|
||||
> **Cette section décrivait une transition en cours jusqu'au 2026-09-06.** Elle est
|
||||
> terminée : mesuré le 2026-08-22, **`10.0.0.0/24` n'existe plus** — ni `10.0.0.1`, ni
|
||||
> `10.0.0.41` ne répondent. Le plan d'administration est `10.17.0.0/24` : la frontière y
|
||||
> répond en `10.17.0.1` sur un port physique à elle, les commutateurs sont en `10.17.0.3`
|
||||
> et `.4` avec cette passerelle par défaut, le poste de l'exploitant en `10.17.0.17`. Le
|
||||
> renumérotage du tenant (`10.27` → `10.17`) est fait lui aussi.
|
||||
>
|
||||
> On garde la **méthode**, parce qu'elle est ce qui a permis de le faire sans coupure, et
|
||||
> qu'un autre site la rejouera :
|
||||
>
|
||||
> **Ajouter avant de retirer, jamais l'inverse.** Un point de routage qui change d'adresse
|
||||
> d'un coup coupe simultanément l'exploitant, les commutateurs qui l'ont en passerelle par
|
||||
> défaut, et l'outil qui devait faire la bascule. La seconde adresse a donc vécu à côté de
|
||||
> l'ancienne (`ipalias`), et l'ancienne n'est tombée qu'en **dernier**.
|
||||
>
|
||||
> **Distinguer une destination d'un chemin (D-78).** Un réseau qui n'est **jamais** une
|
||||
> destination — seulement un chemin — n'a aucune raison d'être unique entre deux hébergeurs :
|
||||
> il sort de l'espace dérivé, vers `192.168.<vlan>.0/24`. Seule la **gestion** doit rester
|
||||
> unique d'un site à l'autre, parce que le poste de l'exploitant, un VPN et demain un lien
|
||||
> inter-sites doivent l'atteindre.
|
||||
>
|
||||
> **Appliqué pour le transport VXLAN seulement, à ce jour** (mesuré le 2026-09-06) :
|
||||
> `underlay-vxlan` est bien en `192.168.50.0/24`. Le **transit** (`10.0.4.0/24`, VLAN 40) et
|
||||
> le **stockage** (`10.11.5-7.x`, VLAN 5/6/7) sont encore dans l'ancien espace. Ce n'est pas
|
||||
> une urgence — ces réseaux ne quittent jamais leur site — mais la carte doit dire ce qui est,
|
||||
> pas ce qui a été décidé. Un **site neuf** se monte directement au schéma final : il n'a
|
||||
> aucune transition à subir.
|
||||
>
|
||||
> **Un plan d'adressage ne doit pas dépendre de l'ordre d'une migration.** Le VLAN de
|
||||
> transport est passé de 11 à 50 — non parce que 11 était mauvais, mais parce que
|
||||
> `192.168.11.0/24` est occupé par le contrôle de la grappe. Faire dépendre un plan
|
||||
> d'adressage de l'**ordre** d'une migration est exactement la dette qui se paie un an
|
||||
> plus tard.
|
||||
|
||||
### Ce qui reste, et qui n'est PAS un reliquat de la bascule
|
||||
|
||||
Les hyperviseurs gardent **deux** plans, et c'est voulu :
|
||||
|
||||
| Plan | Réseau | Ce qu'il porte |
|
||||
|---|---|---|
|
||||
| administration | `10.17.0.0/24` (`vmbr3`, segment physique) | les **équipements** et l'exploitant ; **aucune VM ne peut y naître** (aucun pont ne le touche, et le validateur refuse qu'on y déclare une machine) |
|
||||
| contrôle de la grappe | `192.168.11.0/24` (`vmbr0`, carte dédiée) | l'**interface web Proxmox** et le dialogue entre nœuds — c'est par là qu'on atteint `ansible@192.168.11.4x` |
|
||||
|
||||
> **Le `/24` de gestion vit à l'intérieur du `/16` du tenant, et ce n'est pas un conflit.**
|
||||
> Les zones d'un tenant commencent au 3ᵉ octet 16 ; la bande 0-15 est libre pour la fabric,
|
||||
> et la route connectée du `/24` est plus spécifique que celle du `/16` — la règle du
|
||||
> préfixe le plus long, pas une coïncidence. Il faut cependant le **déclarer**
|
||||
> (`bande_basse_de:` dans `underlay.yml`), sinon le validateur ne peut pas distinguer ce
|
||||
> chevauchement voulu d'un chevauchement accidentel.
|
||||
|
||||
## 7. Le nœud qui porte le gabarit tombe — la reproduction s'arrête
|
||||
|
||||
**Symptôme.** Aucune VM nouvelle ne peut naître. `make creer-vm`, `make flotte-creer` et
|
||||
`make reconstruire` échouent au clonage. Les machines existantes, elles, ne bronchent pas.
|
||||
|
||||
**Ce qui se passe.** Le gabarit doré vit sur un nœud nommé — `vishnu` chez l'hébergeur de
|
||||
référence — et sa configuration porte ce nom dans son chemin même :
|
||||
> Posée le 2026-08-12 par l'API (`interfaces/vip_settings`, mode `ipalias`). **Ce n'est
|
||||
> pas une anomalie** : c'est une bascule d'adressage en cours.
|
||||
|
||||
```
|
||||
/etc/pve/nodes/vishnu/qemu-server/9006.conf
|
||||
10.0.0.1/24 adresse historique — TOUJOURS ACTIVE, rien ne l'a quittée
|
||||
10.17.0.1/24 adresse cible (D-77 : underlay dans la bande basse du /16 du site)
|
||||
```
|
||||
|
||||
Le clonage appelle `nodes/vishnu/qemu/9006/clone` : c'est l'API de **ce nœud-là** qui doit
|
||||
répondre. Nœud éteint, API muette, aucune naissance.
|
||||
**Ajouter avant de retirer**, jamais l'inverse. Un point de routage qui change d'adresse
|
||||
d'un coup coupe simultanément l'exploitant, les commutateurs qui l'ont en passerelle par
|
||||
défaut, et l'outil qui devait faire la bascule.
|
||||
|
||||
**Ce qui ne se passe PAS, et qu'il faut savoir avant de paniquer.** Les données du gabarit
|
||||
ne sont pas perdues. Mesure du 2026-09-10 :
|
||||
### Ce qui reste à déplacer, et dans quel ordre
|
||||
|
||||
```
|
||||
pool CephNVMe size=3 min_size=2
|
||||
OSD sur les trois hôtes : asgard, gandalf, vishnu
|
||||
base-9006-disk-0, base-9006-disk-1 présentes dans le pool
|
||||
```
|
||||
| # | À déplacer | Vers | Nature (D-78) |
|
||||
|---|---|---|---|
|
||||
| 1 | le poste de l'exploitant | `10.17.0.x/24` (seconde adresse) | — |
|
||||
| 2 | les commutateurs `10.0.0.3/.4` **et leur `ip default-gateway`** | `10.17.0.3/.4`, passerelle `10.17.0.1` | destination |
|
||||
| 3 | l'administration des hyperviseurs (`vmbr0`, aujourd'hui `192.168.11.x`), l'OOB/IPMI | `10.17.0.41/.43/.47` | destination |
|
||||
| 4 | le transit `10.0.4.x` | **`192.168.40.x`** | chemin |
|
||||
| 5 | le transport VXLAN `10.0.5.x`, **VLAN 11 → 50** | **`192.168.50.x`** | chemin |
|
||||
| 6 | le stockage `10.0.1–3.x` | **`192.168.20/30/31.x`** | chemin |
|
||||
| 7 | **en dernier seulement**, retirer `10.0.0.1` | — | — |
|
||||
|
||||
L'image est répliquée trois fois et reste lisible avec un nœud en moins. Et les quatorze VM
|
||||
d'un écosystème tournent ailleurs, sur leurs propres disques Ceph : elles ne s'aperçoivent
|
||||
de rien.
|
||||
Les étapes 4 à 6 sortent définitivement de l'espace dérivé (D-78) : ces réseaux ne sont
|
||||
jamais des destinations, seulement des chemins. Une fois faites, **seule la gestion** doit
|
||||
rester unique d'un site à l'autre.
|
||||
|
||||
> **`vishnu` n'est pas un point unique de défaillance pour l'exploitation. Il l'est pour la
|
||||
> reproduction** — et la reproduction est ce que ce dépôt existe pour garantir.
|
||||
> **L'étape 5 change aussi le numéro de VLAN**, côté commutateur (trunk) et côté
|
||||
> hyperviseurs (`bond3.11` → `bond3.50`). Le 11 est écarté parce que `192.168.11.0/24` est
|
||||
> occupé par l'étape 3 tant qu'elle n'est pas faite — et un plan d'adressage ne doit pas
|
||||
> dépendre de l'ordre d'une migration.
|
||||
|
||||
**La manœuvre.** Deux gestes, quelques minutes, aucun mouvement de données :
|
||||
### Ce qui ne dépend PAS de cette bascule
|
||||
|
||||
```bash
|
||||
# 1. re-héberger la configuration sur un nœud debout
|
||||
mv /etc/pve/nodes/vishnu/qemu-server/9006.conf \
|
||||
/etc/pve/nodes/asgard/qemu-server/9006.conf
|
||||
**Le renumérotage du tenant** (`10.27` → `10.17`) est **indépendant**. La frontière route
|
||||
le supernet du tenant vers le même prochain saut, quelle que soit sa propre adresse de
|
||||
gestion : il suffit que la route et les alias suivent. Les deux chantiers peuvent donc
|
||||
être menés séparément — et c'est préférable.
|
||||
|
||||
# 2. le déclarer, sinon la garde refusera
|
||||
# SITE-Chezlepro/plan/10-intrants.yml : gabarit.noeud: asgard
|
||||
```
|
||||
|
||||
Puis vérifier :
|
||||
|
||||
```bash
|
||||
make gabarit-etat # doit rendre « Conforme »
|
||||
```
|
||||
|
||||
**Pourquoi c'est aussi court.** Parce que le disque du gabarit vit sur un stockage
|
||||
**partagé** depuis le 2026-09-10 : le déplacement ne bouge qu'un fichier de configuration.
|
||||
Sur un stockage local, il aurait fallu recopier 16 Go — ou refabriquer le gabarit.
|
||||
|
||||
**L'ordre compte.** Déplacer sans déclarer laisse `gabarit_etat` en écart ; déclarer sans
|
||||
déplacer fait échouer le clonage sur un nœud qui ne détient pas le modèle. Faire les deux,
|
||||
dans cet ordre.
|
||||
|
||||
**Ce que cette section ne fait pas.** Elle ne supprime pas la dépendance : elle la rend
|
||||
connue et courte. Deux remèdes de fond existent, aucun n'est appliqué :
|
||||
|
||||
- **déplacer le gabarit là où vivent déjà les VM** — ça ne supprime pas le point unique,
|
||||
ça cesse d'en avoir *deux* (le nœud des VM et celui du modèle) ;
|
||||
- **une garde** qui refuse quand le nœud du gabarit n'héberge aucune machine de la flotte,
|
||||
c'est-à-dire quand la reproduction dépend d'un nœud qui ne porte rien d'autre.
|
||||
|
||||
*Une dépendance qu'on documente sans la mesurer reste une dépendance qu'on découvrira au
|
||||
mauvais moment.*
|
||||
|
||||
## 8. Les contrôles à la demande — ce que `make prouver` ne peut pas voir
|
||||
|
||||
`make prouver` est **statique** : il lit le dépôt, zéro appel réseau. C'est ce qui le rend
|
||||
rejouable partout, par n'importe qui, et présentable comme pièce justificative. Le prix de
|
||||
cette propriété : il ne peut rien dire de ce qui ne s'observe qu'en ouvrant une connexion.
|
||||
|
||||
Quatre contrôles comblent ce creux. Aucun ne corrige quoi que ce soit — ils regardent, et
|
||||
rendent `0` si tout concorde.
|
||||
|
||||
| Contrôle | Ce qu'il compare | Le piège qu'il attrape |
|
||||
| --- | --- | --- |
|
||||
| `make expositions-etat` | Expositions du plan ↔ **SAN du certificat servi** ↔ code du vhost | Un renommage déployé partout **sauf** dans le certificat |
|
||||
| `make gabarit-etat` | Gabarit déclaré ↔ VM modèle réelle | Le modèle a dérivé de ce que le plan promet |
|
||||
| `make routes-fabric-etat` | Zones déclarées ↔ routes déclarées ↔ routes vivantes | Une route **vivante mais non déclarée** — elle part au redémarrage |
|
||||
| `make frontiere-plan` | Registre des flux ↔ règles de la frontière | Une règle posée à la main, qu'aucune déclaration ne porte |
|
||||
|
||||
Ajouter `SITE=1` à `expositions-etat` pour interroger l'écosystème du SITE plutôt que
|
||||
l'instance montée.
|
||||
|
||||
### Pourquoi `expositions-etat` existe
|
||||
|
||||
Renommer une exposition touche cinq choses. Quatre suivent au déploiement ; la cinquième,
|
||||
non :
|
||||
|
||||
```
|
||||
serveur_powerdns la zone publie le nouveau nom ✓
|
||||
serveur_keycloak le client OIDC accepte le retour ✓
|
||||
le service il fabrique ses URL avec le bon nom ✓ (P67)
|
||||
serveur_nginx le vhost répond sur le nouveau nom ✓
|
||||
client_pki le SAN du certificat porte le nom ✗ il faut le rejouer
|
||||
```
|
||||
|
||||
Le symptôme est trompeur : le site répond, la page s'affiche, et c'est le **navigateur**
|
||||
qui refuse — avec une erreur de certificat que personne ne relie à un renommage fait la
|
||||
veille. Mesuré deux fois le 2026-09-10, sur `grafana → observatoire` puis `icinga → vigie`.
|
||||
|
||||
Le contrôle regarde **dans les deux sens**. Un nom resté dans le SAN après avoir quitté le
|
||||
plan est un nom que le certificat continue d'authentifier : c'est exactement ce qu'avait
|
||||
laissé le premier renommage, jusqu'au passage de `client_pki`.
|
||||
|
||||
> Un `INJOIGNABLE` ne condamne pas le service : il dit que **ce poste** n'a pas pu ouvrir
|
||||
> la connexion. Les zones du SITE ne sont pas routées depuis le plan d'administration du
|
||||
> locataire — le mur est la frontière, pas le vhost.
|
||||
|
||||
## 9. Le premier jour d'un site — la séquence, et les dix-huit murs
|
||||
|
||||
> **Écrit le 2026-09-12**, au sortir de la première reconstruction d'un site depuis zéro.
|
||||
> Avant elle, `SITE-Chezlepro` n'avait jamais été rasé : il avait été monté par ajouts
|
||||
> successifs, sur des semaines, avec un service déjà debout à chaque étape.
|
||||
>
|
||||
> La limite qu'on répétait — « l'infrastructure d'accueil n'a jamais été reconstruite
|
||||
> depuis zéro » — se lisait comme de la prudence. C'était **dix-huit défauts** que rien
|
||||
> d'autre n'aurait pu révéler.
|
||||
>
|
||||
> **Trois reconstructions complètes** ont été nécessaires : la première pour les trouver,
|
||||
> la deuxième pour vérifier les correctifs — elle en a révélé deux de plus, invisibles
|
||||
> tant que l'état n'était pas assez neuf — et la troisième pour prouver la séquence.
|
||||
>
|
||||
> | | Passages | Durée | Défauts trouvés |
|
||||
> |---|---|---|---|
|
||||
> | Tour 1 | 8 | ~2 h 30 | 16 |
|
||||
> | Tour 2 | 3 | 53 min | 2 |
|
||||
> | Tour 3 | **2** | **37 min 24** | **0** |
|
||||
>
|
||||
> La séquence ci-dessous est celle du troisième tour. Elle est mesurée, pas reconstituée.
|
||||
|
||||
### Ce qui rend un site différent d'un locataire
|
||||
|
||||
Un locataire naît dans un monde déjà peuplé : le site lui fournit les paquets, les noms,
|
||||
le génome, l'heure et le dépôt de sauvegarde. **Un site n'a personne au-dessus de lui**,
|
||||
sauf sa frontière. Tout ce qu'un locataire reçoit, un site doit se le donner — et pendant
|
||||
qu'il se le donne, il ne l'a pas.
|
||||
|
||||
C'est de là que viennent onze des quinze murs.
|
||||
|
||||
### La séquence, dans l'ordre
|
||||
|
||||
```bash
|
||||
# 0. AVANT TOUT — l'état sort du bâtiment
|
||||
make depot-hors-site VERS=<support hors site>
|
||||
|
||||
# 1. Le résolveur d'amorçage, DÉCLARÉ dans le plan du site AVANT de créer quoi que ce soit
|
||||
# plan/10-intrants.yml : dns_amorcage: <patte de la frontière>
|
||||
# Sans lui, les machines pointent sur le DNS du site — qui est l'une d'elles.
|
||||
# Et `nftables_admin_ssh: [<plan d'administration>]`, sans quoi l'exploitant ne
|
||||
# pourra pas atteindre ce qu'il vient de construire.
|
||||
|
||||
# 2. Les VM, depuis l'underlay ~9 min
|
||||
make site-creer CONFIRMER=true
|
||||
|
||||
# 3. ATTENDRE QU'ELLES REPONDENT — `site-creer` ne le fait pas ~3 min
|
||||
until ansible -i scripts/site_inventaire.py all,'!<hyperviseurs>' -m ping >/dev/null 2>&1
|
||||
do sleep 10; done
|
||||
|
||||
# 4. Passage 1 — va jusqu'à `serveur_ops`, s'arrête sur la forge vide ~20 min
|
||||
V="$(dirname "$(readlink -f underlay.yml)")/underlay.vault.yml"
|
||||
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml -e "@$V"
|
||||
|
||||
# 5. Amorcer la forge — elle tourne, elle est vide ~45 s
|
||||
export SETOPS_FORGE_MDP=… # jamais en argument de ligne de commande
|
||||
make forge-amorcer CONFIRMER=true
|
||||
|
||||
# 6. Passage 2 — doit finir à 0 échec ~4 min
|
||||
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml -e "@$V"
|
||||
```
|
||||
|
||||
**Total mesuré : 37 min 24** pour sept machines, de rien du tout à un site complet.
|
||||
|
||||
**La voûte se passe en `-e @`** — `make site-appliquer` la dérive du symlink `underlay.yml`,
|
||||
mais il n'existe aucune cible qui déploie le site EN ENTIER. Un `ansible-playbook` direct
|
||||
l'oublie, et l'échec parle d'une assertion, jamais d'un fichier manquant.
|
||||
|
||||
**Deux passages, et le second ne sert qu'à la forge.** Le premier va jusqu'à la couche 45
|
||||
sur 46 ; seul `serveur_ops` manque, faute de génome dans une forge neuve.
|
||||
|
||||
> **Il en fallait TROIS avant le 2026-09-12.** `client_pki` posait les droits de la clé
|
||||
> d'hôte pour le groupe `git`, que le paquet de Forgejo crée dans une couche postérieure :
|
||||
> au premier passage la clé restait fermée, Forgejo ne démarrait pas — après **300 secondes
|
||||
> d'attente perdue** — et il fallait un passage entier pour qu'il démarre, un autre pour la
|
||||
> forge. Aucun ordre de couches ne dénoue ce cycle ; c'est la **propriété du geste** qui a
|
||||
> changé de main : `serveur_forgejo` revendique désormais la clé au moment où il crée le
|
||||
> groupe. `client_pki` garde la sienne et la repose à chaque passage, parce que `step`
|
||||
> réécrit la clé à chaque renouvellement.
|
||||
|
||||
### Les quinze murs, et ce que chacun enseigne
|
||||
|
||||
| # | Le mur | Ce qu'il enseigne |
|
||||
|---|---|---|
|
||||
| 1 | Aucun moyen de raser le site | `make site-raser` — la limite était un trou d'outillage, pas une fatalité |
|
||||
| 2 | La forge naît vide, le runner y clone | `make forge-amorcer` — un amorçage vient de l'extérieur de ce qu'il amorce |
|
||||
| 3 | `dns_amorcage` pointe sur le DNS du site | le mécanisme existait, la **surcharge** n'avait jamais été posée |
|
||||
| 4 | La garde du résolveur teste l'adresse écrite | **une adresse écrite ne prouve pas qu'elle répond** |
|
||||
| 5 | Aucune cible « déployer tout le site » | la voûte n'était jointe nulle part à la séquence complète |
|
||||
| 6 | `client_pki` bloque sur un groupe absent | blocage **circulaire** : l'échec empêchait d'atteindre ce qui créait le groupe |
|
||||
| 7 | PostgreSQL du site sans TLS de l'AC | un écart de **sécurité**, révélé par le premier client exigeant `verify-full` |
|
||||
| 8 | `pg_hba` n'autorisait personne | le site ne déclarait aucun réseau client |
|
||||
| 9 | `/etc/setops` absent sur le dépôt | un rôle supposait qu'une couche ultérieure était déjà passée |
|
||||
| 10 | Forgejo attend 300 s une clé illisible | une attente devrait abandonner quand la cause est déjà au journal |
|
||||
| 11 | La clé SSH de l'exploitant inconnue de la forge | une forge neuve ne connaît personne |
|
||||
| 12 | L'accès de l'exploitant était **accidentel** | il tenait au chevauchement d'adressage que le renumérotage a supprimé |
|
||||
| 13 | Le devis reconnaît l'administration à son port | `"22" in ports` plutôt que `"admin" in pairs` |
|
||||
| 14 | Un flux à deux paires n'obtient qu'une branche | la chaîne de `elif` rangeait `[flotte, admin]` dans un seul cas |
|
||||
| 15 | Dépôts créés privés, runner anonyme | `could not read Username` — un message qui pointe ailleurs que sa cause |
|
||||
| 16 | Le wiki n'existe pas sur une forge neuve | Forgejo ne crée `<dépôt>.wiki.git` qu'à la **première page**, posée à la main dans l'interface |
|
||||
| 17 | `site-creer` rend la main avant que les machines répondent | mesuré à **3 min 20** — un enchaînement automatique échouerait sur `UNREACHABLE` |
|
||||
| 18 | Les clés d'hôte changent à chaque reconstruction | `accept-new` couvre la première rencontre, **jamais un changement** : `forge-amorcer` purge l'entrée périmée de l'hôte que `git` va contacter |
|
||||
|
||||
### Le motif
|
||||
|
||||
Douze des dix-huit sont **du code juste en régime établi**, faux le premier jour : un résolveur
|
||||
qui se pointe sur lui-même, une clé dont le consommateur n'existe pas encore, un répertoire
|
||||
créé par une couche ultérieure, une forge vide qu'on croit remplie.
|
||||
|
||||
Trois sont des **gardes qui vérifiaient la forme au lieu du résultat**. C'est la famille la
|
||||
plus coûteuse : elles donnent l'apparence d'une vérification.
|
||||
|
||||
Un seul touchait la sécurité — et il était invisible tant qu'aucun client n'exigeait la
|
||||
vérification. **Le défaut n'a pas cassé la construction : la construction a révélé le
|
||||
défaut.**
|
||||
|
||||
> **Pour le prochain site.** Poser `dns_amorcage` dès le départ, prévoir deux passages,
|
||||
> amorcer la forge entre les deux, déclarer `nftables_admin_ssh` — sans quoi l'exploitant ne
|
||||
> peut pas atteindre ce qu'il vient de construire — et créer la première page du wiki dans
|
||||
> l'interface avant `make wiki-publier`.
|
||||
> **Longueur de préfixe.** Une fois les deux faits, la gestion (`10.17.0.0/24`) vit
|
||||
> *à l'intérieur* du supernet du tenant (`10.17.0.0/16`). Aucun conflit : la route
|
||||
> connectée du `/24` est plus spécifique que celle du `/16`. C'est la règle du préfixe le
|
||||
> plus long, pas une coïncidence.
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
# Les pages publiées — où elles sont, et ce qu'elles montrent
|
||||
|
||||
> **Pour qui :** le **mainteneur**, quand il cherche une page existante ou qu'il en
|
||||
> écrit une neuve. Les pages elles-mêmes visent d'autres lecteurs — ce tableau le dit
|
||||
> pour chacune.
|
||||
|
||||
**Ce fichier est la liste entière**, pas seulement celle de la série des flux. Une page
|
||||
publiée qui n'y figure pas est une page qu'on ne retrouvera pas : elle vit sur une adresse
|
||||
que personne ne devine, et elle vieillira sans que quiconque s'en aperçoive.
|
||||
|
||||
La série des flux, elle, obéit à une règle propre : ces pages expliquent **comment les
|
||||
choses sont configurées et interconnectées**. Ce ne sont pas des comptes rendus — elles ne
|
||||
relatent aucun incident, ne datent aucune panne et ne comptent aucune machine tombée. Ce
|
||||
travail-là appartient au `CHANGELOG.md` et à `docs/decisions-architecture.md`. Une page de
|
||||
cette série répond à une seule question : *par où passe telle chose, et pourquoi par là*.
|
||||
|
||||
## La série
|
||||
|
||||
| # | Page | Ce qu'elle montre |
|
||||
|---|---|---|
|
||||
| 1 | [La chaîne de l'heure](https://claude.ai/code/artifact/fd050fd8-12b1-4db0-91a3-eac7cf19ee65) | Du satellite aux machines par la frontière — une seule sortie |
|
||||
| 2 | [La vie d'un certificat](https://claude.ai/code/artifact/77f067e0-336d-445a-ac31-c6f8eab10d73) | Racine, émission, renouvellement, consommateurs, contrôle |
|
||||
| 3 | [Comment un nom devient une adresse](https://claude.ai/code/artifact/8cdc642f-f1ea-4ff2-ab87-41d2e1d93fd3) | Le plancher, le résolveur, l'autoritatif — dans cet ordre |
|
||||
| 4 | [D'où vient un paquet](https://claude.ai/code/artifact/5c669f67-ad83-4b4f-9255-98dc0040db7b) | Le cache, ses deux faces, et les deux langues d'apt |
|
||||
| 5 | [Une identité, un mot de passe](https://claude.ai/code/artifact/3ea86b63-893f-4795-b391-1f75876dace7) | L'annuaire, la fédération, la passerelle — et qui a droit à quoi |
|
||||
| 6 | [Ce qu'on ne peut pas refaire](https://claude.ai/code/artifact/fd0073dc-6ad8-401d-a5a6-f31d304e7092) | La sauvegarde : ce qui part, ce qui ne part pas, qui vérifie |
|
||||
| 7 | [Trois canaux, trois sens](https://claude.ai/code/artifact/306bde34-f29c-4396-b43c-7dfeefc259aa) | Verdicts poussés, chiffres tirés, journaux expédiés |
|
||||
| 8 | [Le trajet d'un courriel](https://claude.ai/code/artifact/0b9b96ea-8d7d-4c40-8461-897a2c11a829) | Deux machines, un annuaire, une remise vérifiée |
|
||||
|
||||
## Ce qui les relie
|
||||
|
||||
| Page | Ce qu'elle montre |
|
||||
|---|---|
|
||||
| [Ce qui relie un locataire à son site](https://claude.ai/code/artifact/28f81e5d-4e71-48ee-aadc-43c4ba9353f9) | Les six liens de la filiation, et ce que coupe chacun |
|
||||
|
||||
## Pages destinées à quelqu'un d'autre que le mainteneur
|
||||
|
||||
Registre différent : pas de nom de logiciel en titre, pas de vocabulaire de doctrine.
|
||||
|
||||
| Page | Pour qui |
|
||||
|---|---|
|
||||
| [La maison TechnoLibre](https://claude.ai/code/artifact/bc2b441f-3f39-4659-8399-e61615f778cf) | Le propriétaire d'un écosystème — ce qu'il ouvre, où sont ses affaires |
|
||||
| [Où commence le système](https://claude.ai/code/artifact/3bffbc9c-018e-4e42-95d1-103b98139589) | Positionnement face à Coolify et Cloud in a Bottle |
|
||||
| [Deux sites, un tunnel](https://claude.ai/code/artifact/8cbc0ddc-6110-4bc6-906d-90e6a0eba987) | Plan de niveau 3 des deux sites reliés |
|
||||
| [Un écosystème Set-OPS](https://claude.ai/code/artifact/6ca4514e-6227-4bb6-b07e-ef6690cc156a) | Article promotionnel — résultat et capacités |
|
||||
| [Inventaire libre](https://claude.ai/code/artifact/ee444b6a-0605-4dce-972d-b9f090f2011e) | La liste des logiciels libres de la solution |
|
||||
|
||||
## La page commerciale
|
||||
|
||||
Une seule page en quatre parties — le moteur, les offres, les tarifs, la valeur. C'est la
|
||||
seule qui **affirme un état** (« éprouvé ») et **un prix** ; elle se relit donc chaque fois
|
||||
qu'une capacité change de camp.
|
||||
|
||||
| Page | Ce qu'elle porte |
|
||||
|---|---|
|
||||
| [Capacités, offres, tarifs et valeur](https://claude.ai/code/artifact/28369c71-c7c4-43f3-abfe-8dcf0fc50c8b) | Dix piliers, huit offres, la grille tarifaire, le calculateur et les réserves |
|
||||
|
||||
Les chiffres qu'elle avance se **mesurent** : nombre de rôles, de preuves, d'affirmations au
|
||||
registre, de lignes de documentation. Les recompter avant de republier, plutôt que de les
|
||||
reconduire.
|
||||
|
||||
## Plus anciennes, gardées pour mémoire
|
||||
|
||||
Elles n'ont pas été revues depuis leur publication et peuvent décrire un état dépassé.
|
||||
|
||||
| Page | Ce qu'elle montrait | Publiée |
|
||||
|---|---|---|
|
||||
| [Plan, dérivation, preuve](https://claude.ai/code/artifact/c7d996bf-8d99-4377-b5a8-91ad7d63ab39) | La méthode du moteur, en une page | 2026-08-26 |
|
||||
| [Préparer ton site pour TechnoLibre](https://claude.ai/code/artifact/1edbb622-6bb5-4560-9e93-8953b5caec42) | La préparation du site d'un partenaire | 2026-08-12 |
|
||||
| [Réseau Chezlepro — les trois plans](https://claude.ai/code/artifact/78584a01-7b45-4e2a-b8bd-e4aa3ecd5341) | Les trois plans du réseau, avant la fusion du lien de sortie | 2026-08-04 |
|
||||
|
||||
## La règle d'écriture
|
||||
|
||||
**Garder le mécanisme et sa raison. Retirer l'anecdote, la date, la durée, le nombre de
|
||||
machines touchées.**
|
||||
|
||||
Un réglage mérite souvent son explication — `harden-below-nxdomain: no` n'a aucun sens sans
|
||||
savoir que la racine signée nie le domaine `internal.`. Ça reste. Ce qui ne reste pas, c'est
|
||||
combien de temps il a fallu pour le comprendre.
|
||||
|
||||
Les chiffres sont admis quand ils donnent un **ordre de grandeur utile** (« un écosystème
|
||||
de quatorze machines demande environ 1,3 Go de paquets »), pas quand ils racontent une
|
||||
soirée particulière.
|
||||
|
||||
## Quand les relire
|
||||
|
||||
Une page décrit une configuration : elle vieillit quand la configuration change. Les points
|
||||
à revérifier après une modification d'architecture sont, dans l'ordre :
|
||||
|
||||
- un **service prêté** ajouté ou retiré → pages 4, 6 et celle des liens
|
||||
- un **renumérotage** → toutes les pages qui portent une adresse
|
||||
- une **bascule** (résolveur, cache, sauvegarde) → pages 3, 4, 6
|
||||
- un **rôle neuf avec une sonde** → page 7
|
||||
- une **capacité qui passe de la feuille de route au service** → la page commerciale, où
|
||||
elle porte un badge d'état et parfois un prix. C'est la relecture la plus facile à
|
||||
oublier, parce qu'une bonne nouvelle ne ressemble pas à une tâche.
|
||||
|
|
@ -27,38 +27,26 @@ C'est le VRF qu'on regrettait de ne pas avoir dans le matériel, obtenu en logic
|
|||
|
||||
## 2. La projection du modèle
|
||||
|
||||
Vérifiée sur chaque tenant fédéré (ils étaient deux au moment de la décision, ils sont
|
||||
trois), elle ne demande **aucun changement de dérivation** :
|
||||
Vérifiée sur les deux tenants fédérés, elle ne demande **aucun changement de dérivation** :
|
||||
|
||||
| Objet Proxmox SDN | Vient de | Exemple (Chezlepro, zone Services-infra) |
|
||||
|---|---|---|
|
||||
| **zone** (un VRF) | `zone_de(index)` → `t<index>` | `t17` |
|
||||
| **VNet** | `vnet_de(index, libellé)` → `t<index><zone abrégée>` | `t17serv` |
|
||||
| **zone** (un VRF) | le tenant | `CHEZ17` |
|
||||
| **VNet** | la zone de sécurité | `chez174` |
|
||||
| **tag** (VNI) | `vlan_de(index, zone)` | `1174` |
|
||||
| **subnet** | `sous_reseau_de(index, zone)` | `10.17.19.0/24` |
|
||||
| **gateway** | `passerelle_de(index, zone)` | `10.17.19.1` |
|
||||
|
||||
Les six VNets d'un tenant à l'index 17 : `t17fron`, `t17iden`, `t17donn`, `t17serv`,
|
||||
`t17obse`, `t17appl`.
|
||||
|
||||
> **Rectification du 2026-08-03.** Ce tableau annonçait `chez17-services-infra`, qui
|
||||
> aurait été **refusé à l'application** : zones et VNets sont limités à **8 caractères**
|
||||
> par Proxmox — l'identifiant sert de base aux noms de bridge, veth et tap. Message
|
||||
> amont : *« zone ID … can't be more length than 8 characters »*.
|
||||
>
|
||||
> **Le nommage a changé une seconde fois, et ce tableau ne l'avait pas suivi.** Il annonçait
|
||||
> `CHEZ17` / `chez174`, dérivés de `devis_reseau.prefixe()` — le nom court du *dossier* du
|
||||
> tenant. La forme en vigueur est plus simple et ne dépend que du seed :
|
||||
> **`t<index>`** pour la zone, **`t<index><zone abrégée>`** pour le VNet
|
||||
> (`scripts/devis_sdn.py` : `zone_de`, `vnet_de`). Même préfixe `t` que les IPSets du
|
||||
> pare-feu Proxmox, donc un seul vocabulaire d'un bout à l'autre de la fabric ; minuscules,
|
||||
> parce que cet identifiant devient une base de nom d'interface.
|
||||
>
|
||||
> La contrainte qui avait motivé la première rectification tient toujours : zones et VNets
|
||||
> sont limités à **8 caractères** par Proxmox — l'identifiant sert de base aux noms de
|
||||
> bridge, veth et tap. Message amont : *« zone ID … can't be more length than 8
|
||||
> characters »*. Avec `t<index>`, la marge est confortable jusqu'à l'index 255 (`t255` = 4,
|
||||
> `t255serv` = 8). **P30** refuse tout dépassement, sur les deux objets.
|
||||
> Le nommage dérive du tenant, comme tout le reste : `<PRÉFIXE><index>` pour la zone,
|
||||
> `<préfixe><index><zone>` pour le VNet. Le préfixe vient de `devis_reseau.prefixe()` —
|
||||
> la même fonction que le devis des commutateurs, donc un seul endroit fabrique le nom
|
||||
> court d'un tenant. Éprouvé jusqu'au pire cas de la fédération : `COOP245` = 7,
|
||||
> `coop2459` = 8. **P30** refuse tout dépassement, sur les deux objets.
|
||||
>
|
||||
> **Les zones créées à la main (`VRF0011`, `VRF0017`) sont remplacées.** Ce nommage ne
|
||||
> disait ni de quel tenant il s'agissait, ni rien qu'on puisse relier au plan : il
|
||||
|
|
@ -77,9 +65,8 @@ porteur** : du SVI d'un commutateur vers la passerelle **anycast** du VNet, pré
|
|||
chaque hyperviseur — donc plus proche de la VM, et sans point unique de défaillance.
|
||||
|
||||
Effet de bord favorable : un VNI est codé sur 24 bits là où un VLAN plafonne à 4094. La limite
|
||||
du nombre de tenants n'est plus l'espace de VLAN mais le **second octet IPv4** du supernet :
|
||||
`valider_index` borne l'index à **0–255**, et 0 est à éviter (les réseaux de service du site
|
||||
y vivent). Le plafond reste, sa cause change.
|
||||
du nombre de tenants n'est plus l'espace de VLAN mais le second octet IPv4 du supernet — le
|
||||
plafond de 245 tenants reste, sa cause change.
|
||||
|
||||
## 3. Le partage des responsabilités
|
||||
|
||||
|
|
@ -94,10 +81,8 @@ Deux conséquences qui méritent d'être dites.
|
|||
|
||||
**L'inter-tenant ne peut plus être « oublié ».** Il ne circule pas latéralement : il doit
|
||||
sortir du VRF, donc traverser la bordure, qui est en `block` par défaut. Un flux inter-tenant
|
||||
légitime doit donc être **déclaré** pour exister — et le registre des flux a désormais le
|
||||
mot-clé qui manquait : **`voisins_site`**, « les tenants d'à côté »
|
||||
(`scripts/resoudre_flux.py`, `MOTS_PAIR`). Ce paragraphe l'annonçait comme un point ouvert
|
||||
jusqu'au 2026-09-06 ; il est fermé.
|
||||
légitime devra être **déclaré** pour exister — le registre des flux n'a pas encore de mot-clé
|
||||
pour ça, c'est un point ouvert.
|
||||
|
||||
**La défense est en profondeur, sans coût de maintenance.** Le filtrage est-ouest est appliqué
|
||||
deux fois : par l'hyperviseur, puis par l'hôte destinataire. Une VM compromise doit franchir
|
||||
|
|
@ -183,31 +168,29 @@ Lecture seule par l'API Proxmox. **Le plan de contrôle existe, le plan de donn
|
|||
| VNets | **aucun** |
|
||||
| Nœuds de sortie | **aucun** — un VRF sans sortie n'a aucun chemin vers la frontière |
|
||||
|
||||
### Deux blocages — levés
|
||||
### Deux blocages à lever avant d'aller plus loin
|
||||
|
||||
> **Cette section décrivait l'état du 2026-08-02.** Les deux sont levés ; on la garde parce
|
||||
> que le *raisonnement* explique le plan d'adressage actuel, qui paraîtrait arbitraire sans
|
||||
> lui.
|
||||
**Les VTEP sont adressés dans un tenant.** `vmbr3` porte `10.27.19.{41,43,47}` — le
|
||||
sous-réseau *Services-infra de Chezlepro*. Le transport du cluster dérive donc de l'index d'un
|
||||
tenant : un changement d'index le casse, une migration l'emporte. Et une VM de cette zone
|
||||
partage son sous-réseau avec les trois VTEP, ce qui perce l'isolation à l'endroit même que
|
||||
l'EVPN devait fermer.
|
||||
|
||||
**Les VTEP étaient adressés dans un tenant.** `vmbr3` portait `10.27.19.{41,43,47}` — le
|
||||
sous-réseau *Services-infra de Chezlepro*. Le transport du cluster dérivait donc de l'index
|
||||
d'un tenant : un changement d'index le cassait, une migration l'emportait. Et une VM de cette
|
||||
zone partageait son sous-réseau avec les trois VTEP, ce qui perçait l'isolation à l'endroit
|
||||
même que l'EVPN devait fermer.
|
||||
Le modèle **refuse d'ailleurs d'exprimer cet état** : déclarer `10.27.19.0/24` comme réseau
|
||||
d'underlay ferait échouer **P23**, qui interdit tout chevauchement avec un supernet tenant. La
|
||||
garde détecte la faute avant qu'on ne la documente.
|
||||
|
||||
Le modèle **refusait d'ailleurs d'exprimer cet état** : déclarer `10.27.19.0/24` comme réseau
|
||||
d'underlay faisait échouer **P23**, qui interdit tout chevauchement accidentel avec un
|
||||
supernet tenant. La garde a détecté la faute avant qu'on ne la documente.
|
||||
`underlay.yml` déclare donc les trois hyperviseurs à leur adresse **cible** — `10.0.0.{41,43,47}`,
|
||||
dernier octet conservé comme sur `vmbr0`. Le déplacement réel de l'adresse sur les nœuds reste
|
||||
à faire : c'est une modification du réseau d'un hyperviseur en service.
|
||||
|
||||
**Aujourd'hui** : les VTEP vivent sur `underlay-vxlan` — `192.168.50.{41,43,47}`, VLAN 50,
|
||||
sur une interface étiquetée dédiée (`bond3.50`). Le transport ne dérive plus d'aucun index.
|
||||
*Pourquoi 50 et pas 11 : sous la règle `192.168.<vlan>`, le VLAN 11 aurait produit
|
||||
`192.168.11.0/24` — déjà occupé par le contrôle de la grappe. Un plan d'adressage ne doit pas
|
||||
dépendre de l'ordre d'une migration.*
|
||||
**Le pont `vmbr3` n'est pas *VLAN-aware*** (pas de `bridge_vlan_aware`, contrairement à
|
||||
`vmbr2`). L'adresse du VTEP y est donc **non étiquetée** : elle vit dans le VLAN natif du port
|
||||
de commutateur. Déplacer le VTEP vers l'underlay suppose soit un VLAN natif 10, soit une
|
||||
interface étiquetée dédiée (`bond3.10`) — ce n'est pas qu'un changement d'adresse.
|
||||
|
||||
**Le pont `vmbr3` n'était pas *VLAN-aware***, et l'adresse du VTEP y vivait donc dans le VLAN
|
||||
natif du port. C'est ce qui rendait le déplacement plus qu'un changement d'adresse — d'où
|
||||
l'interface étiquetée dédiée retenue.
|
||||
**`vishnu` n'est pas câblé.** Son `vmbr3` n'a **aucun port physique** : le pont existe, porte
|
||||
une adresse, et ne mène nulle part. Un pair VXLAN pointé sur lui ne fonctionnera jamais.
|
||||
|
||||
## 8. Ce qui reste à trancher
|
||||
|
||||
|
|
@ -216,8 +199,9 @@ l'interface étiquetée dédiée retenue.
|
|||
- **Le contrôleur EVPN** : ASN, voisins, et si l'on fait du BGP avec la bordure ou des routes
|
||||
statiques comme aujourd'hui.
|
||||
- **Le nombre de nœuds de sortie** et leur redondance.
|
||||
- ~~**Le mot-clé d'un flux inter-tenant** dans le registre.~~ **Tranché** : c'est
|
||||
`voisins_site` (2026-08-24), né du besoin de chaîner les caches d'artefacts. Cf. §3.
|
||||
- **Le mot-clé d'un flux inter-tenant** dans le registre. Aujourd'hui aucun ne l'exprime, donc
|
||||
tout inter-tenant tombe dans le `block` de la bordure — un défaut sûr, mais qui rend
|
||||
impossible de *déclarer* une exception légitime.
|
||||
- **La migration depuis l'existant** : le tenant Chezlepro tourne déjà sur des VLAN. Passer à
|
||||
EVPN est un changement de plan de transport pour des VM en service — la recette de
|
||||
`docs/migration-tenant.md` s'applique-t-elle, ou faut-il un chemin plus court ?
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
> **Pour qui :** l'exploitant, le jour où il réalise que 1 644 octets valent toute
|
||||
> l'installation. À faire une fois, puis à refaire quand une clé change.
|
||||
|
||||
# Sortir les clés du poste
|
||||
|
||||
## Ce qui est en jeu
|
||||
|
||||
Le **code** de Set-OPS est répliqué **deux fois** : `eregion` et la forge du site. Les
|
||||
**voûtes chiffrées**, elles, n'y sont **pas** — elles sont gitignorées. Chacune vit sur
|
||||
ce poste et sur le runner de son écosystème, qui en reçoit une copie chiffrée par
|
||||
`serveur_ops_tenant`. *(Corrigé le 2026-09-28 : cette page les disait sur les forges.)*
|
||||
|
||||
**Elles sortent donc aussi, dans une archive à part** (`make voutes-exporter VERS=<support>`,
|
||||
`scripts/exporter_voutes.py`) : déjà chiffrées par `ansible-vault`, elles n'ont pas besoin
|
||||
d'une seconde couche ; le script refuse tout fichier dont l'en-tête n'est pas
|
||||
`$ANSIBLE_VAULT`, garde le chemin de chaque voûte (elles s'appellent presque toutes
|
||||
`vault.yml`) et relit l'archive empreinte par empreinte. Restauration :
|
||||
`tar -xf "$(ls -t setops-voutes-*.tar | head -1)" -C <dossier des dépôts>` (la plus récente).
|
||||
Chaque export porte sa date (`setops-voutes-<date>-<poste>.tar`, l'heure en plus au second
|
||||
export du jour) et n'écrit rien si les voûtes n'ont pas changé depuis la dernière archive.
|
||||
**À refaire après toute écriture
|
||||
dans une voûte**, comme les clés après toute voûte nouvelle.
|
||||
|
||||
> **Ce chiffre était trois, et il a baissé sans que rien ne le signale.** Un coffre répliqué deux fois reste
|
||||
> solide — mais c'est la **redondance du génome** qui a diminué, pas le chiffrement, et
|
||||
> c'est exactement ce que la page *Filiation, signatures & témoins* appelle la vraie mesure
|
||||
> de résistance d'une lignée : combien de copies **vivantes**, sur combien de machines
|
||||
> distinctes.
|
||||
|
||||
Les **clés** qui l'ouvrent vivent dans neuf fichiers de ce poste — 1 644 octets — sans
|
||||
aucune copie ailleurs. S'y ajoutent les clés SSH par lesquelles on entre sur les
|
||||
hyperviseurs, la frontière et chaque machine.
|
||||
|
||||
Ce qu'on perd avec le poste, par ordre de gravité :
|
||||
|
||||
| Ce qui disparaît | Conséquence |
|
||||
|---|---|
|
||||
| Le poste seul | Les mots de passe restic restent lisibles sur les machines vivantes (`/etc/setops/restic.pass`) — récupérable, mais douloureux, et plus aucun déploiement possible entre-temps |
|
||||
| Le poste **et** une machine | L'état de cette machine devient illisible |
|
||||
| Le poste **et** le site | Terminal |
|
||||
|
||||
## La manœuvre
|
||||
|
||||
```bash
|
||||
make cles-recenser # voir ce qui sortirait, sans rien écrire
|
||||
make cles-exporter VERS=/media/…/CLE # sortir, chiffrer, RELIRE
|
||||
```
|
||||
|
||||
**Lance-la toi-même**, dans ton terminal. `gpg` demande une phrase de passe : elle ne doit
|
||||
passer ni par un journal, ni par le contexte d'un assistant. C'est la seule façon de
|
||||
s'en assurer.
|
||||
|
||||
L'outil refuse trois choses, et chacune a été éprouvée en la faisant échouer :
|
||||
|
||||
- une destination **dans** l'infrastructure — ces clés ouvrent les sauvegardes ; les y
|
||||
ranger ferait un coffre dont la clé est à l'intérieur ;
|
||||
- **écraser** une archive existante — elle est peut-être la seule ;
|
||||
- une archive qu'il **n'arrive pas à rouvrir** — elle est alors supprimée. Une sauvegarde
|
||||
de clés qu'on ne sait pas ouvrir est pire que rien : elle donne le sentiment d'être
|
||||
protégé.
|
||||
|
||||
Il ne montre jamais le contenu des clés — seulement leur nom, leur taille et leur
|
||||
empreinte. De quoi vérifier sans divulguer.
|
||||
|
||||
## Les deux gestes qui restent, et qui ne sont pas facultatifs
|
||||
|
||||
**1. La phrase de passe va ailleurs que le support.** Séparés, ils ne valent rien l'un
|
||||
sans l'autre ; ensemble, ils valent l'installation. Un papier dans un autre lieu, ou un
|
||||
gestionnaire de mots de passe qui n'est pas sur ce poste.
|
||||
|
||||
**2. Une seconde copie, dans un autre lieu physique.** Un support unique dans un tiroir
|
||||
unique, c'est le problème qu'on vient de fermer, déplacé de quelques mètres.
|
||||
|
||||
## Les rouvrir — la moitié qui compte
|
||||
|
||||
Le jour où l'on s'en sert, **le poste est mort**. Le dépôt Set-OPS est répliqué trois
|
||||
fois, mais le *cloner* demande la clé SSH… qui est dans l'archive qu'on essaie d'ouvrir.
|
||||
Une procédure rangée dans le dépôt serait donc inaccessible exactement quand elle sert.
|
||||
|
||||
La clé se suffit donc à elle-même. À côté de l'archive :
|
||||
|
||||
```
|
||||
setops-cles-<date>-<poste>.tar.gpg les clés, chiffrées (une archive datée par export)
|
||||
restaurer_cles.py le script, autonome
|
||||
LISEZ-MOI-RESTAURATION.txt le mode d'emploi, et les commandes manuelles
|
||||
```
|
||||
|
||||
> **Une clé faite avant cette version ne porte pas les compagnons.**
|
||||
> `make cles-compagnons VERS=/media/…/CLE` les y dépose **sans refaire l'archive** : ni
|
||||
> phrase de passe redemandée, ni risque d'écraser ce qui est déjà vérifié.
|
||||
|
||||
Sur la machine neuve, il ne faut que `gpg`, `python3` et la phrase de passe :
|
||||
|
||||
```bash
|
||||
A="$(ls -t setops-cles-*.tar* | head -1)" # la plus récente
|
||||
python3 restaurer_cles.py "$A" --lister # voir sans rien écrire
|
||||
python3 restaurer_cles.py "$A" # remettre en place
|
||||
```
|
||||
|
||||
Le script remet chaque fichier à sa place selon son nom, et **repose les droits à 0600**.
|
||||
Ce n'est pas cosmétique : `ssh` refuse une clé privée que d'autres peuvent lire, et son
|
||||
message ne dit pas qu'il s'agit d'un droit — une archive extraite depuis un support FAT
|
||||
arrive systématiquement dans cet état. Il **refuse** aussi d'écraser une clé déjà là :
|
||||
lancé par erreur sur un poste qui fonctionne, il ne détruit rien.
|
||||
|
||||
Si même ce script refuse de tourner, le LISEZ-MOI porte les commandes manuelles — `gpg`,
|
||||
`tar`, `chmod`. **Les deux chemins ont été éprouvés** le 2026-09-05 : export, poste vidé,
|
||||
restauration depuis la clé seule, empreintes comparées — identiques, 4 sur 4, droits
|
||||
700/600.
|
||||
|
||||
## L'éprouver pendant qu'on a encore le choix
|
||||
|
||||
```bash
|
||||
make cles-restaurer ARCHIVE=/media/…/setops-cles-*.tar.gpg
|
||||
```
|
||||
|
||||
Il refusera, puisque les clés sont déjà en place — et ce refus est en soi la preuve que
|
||||
l'archive s'ouvre et que la phrase de passe est la bonne. C'est le test le moins cher
|
||||
qui existe, et il vaut d'être refait après chaque changement de clé.
|
||||
|
||||
## Quand recommencer
|
||||
|
||||
Quand une voûte est créée ou sa clé changée (`voutes.py`), quand une paire SSH de runner
|
||||
est refaite, et à l'arrivée d'un écosystème. `make cles-recenser` dit en une seconde si
|
||||
l'archive rangée est encore complète : compare le nombre de fichiers et les empreintes.
|
||||
|
||||
## Ce que ça ne couvre pas
|
||||
|
||||
La **phrase de passe** elle-même : si elle est perdue, l'archive est du bruit. C'est le
|
||||
prix du chiffrement, et c'est pour ça que le geste 1 n'est pas décoratif.
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
# Supervision dérivée des rôles
|
||||
|
||||
> **Pour qui :** celui qui ajoute un rôle à Set-OPS et se demande comment sa supervision
|
||||
> arrive dans Icinga — et celui qui exploite et veut savoir d'où sortent les services
|
||||
> qu'il voit.
|
||||
|
||||
> **La règle en une phrase.** Un rôle déclare les sondes de sa propre supervision ; le
|
||||
> moteur en dérive les objets Icinga, la permission d'API et les preuves. Comme
|
||||
> `meta/flux.yml` engendre nftables *et* OPNsense.
|
||||
|
||||
## Pourquoi
|
||||
|
||||
Le 2026-09-09, mesure du dépôt sur lui-même :
|
||||
|
||||
```
|
||||
au 2026-09-09 :
|
||||
39 rôles déclarent leurs flux -> nftables + OPNsense, dérivés
|
||||
32 déclarent leur empreinte -> ressources des VM, dérivées
|
||||
32 déclarent leur authentification -> habilitations, dérivées
|
||||
19 groupes déclarent `surveillance:` -> RIEN
|
||||
```
|
||||
|
||||
Au 2026-09-09, les dix-neuf lignes `surveillance:` de `docs/dependances-groupes.yml` sont écrites,
|
||||
versionnées, relues — et **aucune n'est exécutée**. Icinga surveillait deux choses :
|
||||
`sauvegarde` et `sante`.
|
||||
|
||||
C'est la classe d'échec que ce dépôt nomme partout ailleurs, en version documentaire : la
|
||||
carte dit ce qui est surveillé, et personne ne surveille. *Une intention écrite n'est pas
|
||||
une mesure.*
|
||||
|
||||
## Le contrat d'une sonde : c'est celui des greffons Nagios
|
||||
|
||||
Une sonde est un **script local**, déposé par le rôle qui la possède, dans
|
||||
`/usr/local/lib/setops/sondes/<nom>.sh` (0750, root).
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **sortie standard** | `TEXTE lisible` puis, optionnellement, `\| métriques` |
|
||||
| **code de sortie** | `0` OK · `1` AVERTISSEMENT · `2` CRITIQUE · `3` INCONNU |
|
||||
| **réseau** | aucun besoin : c'est le porteur qui pousse le résultat |
|
||||
| **durée** | courte ; le porteur passe toutes les 15 min |
|
||||
|
||||
> **Ce contrat n'est pas de nous.** C'est l'**API des greffons Nagios**, que
|
||||
> `monitoring-plugins` implémente depuis vingt ans et qu'Icinga parle nativement. La
|
||||
> première rédaction de ce document la décrivait sans la nommer — autrement dit la
|
||||
> réinventait. `positionnement.md` dit l'inverse : *adopter aux seuils, ne pas
|
||||
> réimplémenter*.
|
||||
|
||||
**La conséquence pratique est grande.** Un greffon standard **est** une sonde valide, sans
|
||||
la moindre colle : `check_disk`, `check_load`, `check_procs`, `check_ntp_time`,
|
||||
`check_file_age`, `check_smtp`… Une sonde ne s'écrit à la main que lorsque la vérité à
|
||||
mesurer est propre à Set-OPS — et c'était le cas pour `client_pki/certificat`, qui compare
|
||||
l'empreinte *servie* à celle du disque : aucun greffon ne sait ça.
|
||||
|
||||
### Ce que la flotte a vraiment sous la main — et ce qu'elle n'a pas
|
||||
|
||||
`client_sante` installe **`monitoring-plugins-basic`** : **53** greffons, mesurés le
|
||||
2026-09-14 sur la flotte. Ce document a d'abord annoncé « 54 » et cité `check_pgsql` en
|
||||
exemple. **`check_pgsql` n'en fait pas partie**, ni `check_dns`, ni `check_ldap` : ils sont
|
||||
dans `monitoring-plugins-standard`.
|
||||
|
||||
Et ce paquet-là ne s'ajoute pas :
|
||||
|
||||
apt-get install -s monitoring-plugins-standard
|
||||
→ samba, python3-samba, smbclient, snmp, tdb-tools…
|
||||
|
||||
Une pile Samba et une pile SNMP sur **chaque machine** de la flotte, pour obtenir trois
|
||||
greffons. Le durcissement dit le contraire de ça.
|
||||
|
||||
**La conséquence pour qui écrit une sonde :** vérifier que le greffon appelé est dans
|
||||
`-basic` avant de s'appuyer dessus. **Un greffon absent sort en 127** — qui n'est pas un
|
||||
code Nagios (0/1/2/3) : la sonde devient illisible au lieu d'échouer proprement. Quand le
|
||||
greffon manque, prendre l'outil natif du service (`dig` sur un résolveur, `psql` sur une
|
||||
base) ; c'est déjà ce que fait `serveur_resolveur/resolution`.
|
||||
|
||||
Écrire du shell là où un greffon existe, c'est se donner du code à maintenir *et* se priver
|
||||
de vingt ans de cas limites déjà rencontrés par d'autres.
|
||||
|
||||
Le rôle qui possède la sonde la **déploie lui-même** : il connaît ses chemins, ses
|
||||
secrets, sa vérité de terrain. Il la **déclare** dans `meta/supervision.yml`, et c'est
|
||||
cette déclaration que le moteur lit.
|
||||
|
||||
```yaml
|
||||
# roles/<rôle>/meta/supervision.yml
|
||||
sondes:
|
||||
- nom: certificat
|
||||
ttl: 5400
|
||||
raison: "..."
|
||||
```
|
||||
|
||||
## Passif, et à durée de vie — jamais actif
|
||||
|
||||
Un contrôle **actif** ne voit pas la machine **muette** : si elle ne répond plus, la sonde
|
||||
échoue et on met ça sur le compte du réseau. Ici c'est le nœud qui parle, et le `ttl` de
|
||||
son envoi fait la fraîcheur — sans nouvelle, Icinga périme le service tout seul.
|
||||
|
||||
**Le silence alerte autant que l'échec.** C'est exactement ce qui a manqué à
|
||||
`openipmi.service` : une unité en échec à chaque démarrage pendant une semaine, et
|
||||
`systemctl --failed` à zéro partout parce qu'aucune machine n'avait redémarré.
|
||||
|
||||
C'est aussi ce qu'impose *la vérification suit la clé* : la sonde tourne là où vit la
|
||||
vérité, pas sur le superviseur. Un dépôt de sauvegarde chiffré côté client ne peut être
|
||||
jugé que par qui détient la clé.
|
||||
|
||||
## Ce qui n'a rien à faire ici
|
||||
|
||||
Une **métrique à seuil** — durée de collecte, volume de journaux, taux d'occupation —
|
||||
appartient à Prometheus et Grafana. Icinga répond à une seule question : *est-ce cassé ?*
|
||||
Mélanger les deux rendrait les deux moins lisibles.
|
||||
|
||||
## L'autre moitié : `meta/metriques.yml`
|
||||
|
||||
Cette phrase appelle son pendant, et il porte le même patron — **le rôle déclare, le
|
||||
moteur dérive**.
|
||||
|
||||
| fichier | ce qu'il produit | la question |
|
||||
| --- | --- | --- |
|
||||
| `meta/supervision.yml` | une **sonde** → un verdict, avec un TTL | *est-ce cassé ?* |
|
||||
| `meta/metriques.yml` | un **exportateur** → une série ; des **panneaux** → un tableau | *depuis quand, et vers où ?* |
|
||||
|
||||
**Deux fichiers, deux consommateurs.** `serveur_prometheus` lit l'`exportateur` pour en
|
||||
tirer sa cible de scrutation ; `serveur_grafana` lit les `panneaux` pour en assembler un
|
||||
tableau par rôle. Les deux moitiés sont indépendantes : un rôle peut déclarer un
|
||||
exportateur sans panneau (des séries collectées, regardées ailleurs), et des panneaux sans
|
||||
exportateur (`client_metrique` — le job `node` est universel, écrit une fois, et le
|
||||
dériver le ferait exister deux fois).
|
||||
|
||||
**Le flux n'est pas dérivé d'ici, et c'est voulu.** Le port de l'exportateur doit s'ouvrir
|
||||
depuis l'observatoire, et c'est `meta/flux.yml` qui le déclare — là où vivent déjà tous les
|
||||
flux du rôle. Deux fichiers pour un même fait finiraient par diverger.
|
||||
|
||||
### Le critère d'un panneau
|
||||
|
||||
*Une série a sa place si elle **précède** un verdict, ou si elle n'en aura **jamais**.*
|
||||
|
||||
Ce qui bascule d'un coup appartient à Icinga ; ce qui dérive lentement n'a que le graphe
|
||||
pour se faire voir. Une base qui passe de 20 à 60 connexions en trois semaines n'a rien
|
||||
cassé — elle annonce la date où elle cassera.
|
||||
|
||||
### La `raison` devient la description du panneau
|
||||
|
||||
C'est le seul champ qui ne produit aucun pixel de graphe, et c'est le plus important.
|
||||
Grafana l'affiche au survol du titre. Sans elle, celui qui regarde six mois plus tard voit
|
||||
une courbe sans savoir ce qu'elle annonce.
|
||||
|
||||
### Agréger, toujours
|
||||
|
||||
node_exporter publie une série par interface, par point de montage, par cœur : **339
|
||||
séries** pour le seul trafic réseau du site, mesuré le 2026-09-14. Un panneau qui les
|
||||
montre toutes ne montre rien. Et se méfier de `min()` / `max()` : un seul montage
|
||||
pathologique — `/var/lib/lxcfs`, qui rapporte toujours zéro — suffit à rendre un panneau
|
||||
faux **pour toujours**, avec une courbe plate parfaitement lisible. Filtrer par **liste
|
||||
blanche** : un montage inconnu manque au graphe, ce qui se voit, au lieu de l'écraser, ce
|
||||
qui ne se voit pas.
|
||||
|
||||
### Ce que les gardes tiennent
|
||||
|
||||
**P77** exige de chaque panneau un titre, une expression, une unité et une raison — et que
|
||||
l'unité figure dans la table de traduction de `serveur_grafana`. Une unité inventée ne
|
||||
casse rien : le panneau retombe sur `short`, et un graphe d'octets gradué en unités brutes
|
||||
reste parfaitement lisible et parfaitement faux.
|
||||
|
||||
Ce que P77 **ne** dit pas : si l'expression répond. Aucune lecture statique ne le dira. Un
|
||||
panneau se prouve comme une sonde — en le regardant rendre des données, et en le notant au
|
||||
CHANGELOG.
|
||||
|
||||
## Chaque sonde vient avec son contrôle négatif
|
||||
|
||||
Une sonde qu'on n'a jamais vue échouer n'est pas une sonde, c'est une habitude. Dix-neuf
|
||||
voyants verts non éprouvés seraient un recul par rapport à deux voyants prouvés : ils
|
||||
rassureraient.
|
||||
|
||||
Toute sonde ajoutée doit donc être accompagnée de la manière de la faire échouer
|
||||
**pour de vrai**, et cette manière doit être rejouée au moins une fois, sur une machine
|
||||
réelle. Le CHANGELOG en porte la trace.
|
||||
|
||||
**Et contre un état sain, tout autant.** La première version de la sonde du certificat a
|
||||
rendu **14 machines sur 14 en CRITIQUE** — sur une PKI qui se portait très bien. Elle
|
||||
utilisait `openssl verify -CAfile racine`, alors que nos certificats sont signés par un
|
||||
*intermédiaire* que seul `step certificate verify` sait retrouver. Une alarme toujours
|
||||
allumée ne vaut pas mieux qu'une alarme jamais allumée : elle apprend à ne plus regarder.
|
||||
Une sonde se prouve donc **deux fois** — verte sur le sain, rouge sur le cassé.
|
||||
|
||||
## Combien de sondes : une par CAUSE D'ACTION
|
||||
|
||||
Ni une par rôle, ni une par vérification. **Une par geste que le verdict appelle.**
|
||||
|
||||
Les quatorze premières sondes agrègent chacune de deux à six chemins d'échec, et ce choix
|
||||
avait été fait **sans être écrit** — d'où cette section, ajoutée le 2026-09-10 après la
|
||||
question : *« est-ce parce qu'elles agrègent plusieurs vérifications ? »*
|
||||
|
||||
**Ce que coûte une agrégation abusive**, dans le modèle d'Icinga : un service = **un état,
|
||||
un historique, un acquittement**. Fondre deux causes qui appellent des gestes différents,
|
||||
c'est ne plus pouvoir acquitter l'une pendant que l'autre alerte — et lire comme *un
|
||||
service instable* ce qui est en réalité *deux faits distincts qui alternent*.
|
||||
|
||||
**Ce que coûte l'excès inverse** : un voyant de plus est un voyant de moins regardé. Le
|
||||
dépôt le répète assez — *une supervision creuse est pire qu'aucune*.
|
||||
|
||||
Le test pratique, à appliquer sans état d'âme :
|
||||
|
||||
> Les deux échecs appellent-ils **le même geste, de la même personne, dans le même délai** ?
|
||||
> Si oui, une seule sonde, et le message nomme lequel des deux a parlé.
|
||||
> Si non, deux sondes.
|
||||
|
||||
**Exemple d'agrégation légitime** — `moteur` : `icinga2` mort, `icingadb` mort, état figé en
|
||||
base. Trois causes, un seul geste : *va regarder la supervision*. Le message dit laquelle.
|
||||
|
||||
**Exemple d'agrégation abusive** — `cache-apt` : « ne répond pas » arrête tout `apt` de
|
||||
l'écosystème, « volume à 90 % » est un billet pour demain. Même personne, gestes et
|
||||
**délais** différents : deux sondes.
|
||||
|
||||
## Une sonde doit pouvoir être mise en défaut *par paramètre*
|
||||
|
||||
Corollaire des deux règles précédentes, et c'est ce qui rend la discipline tenable à
|
||||
l'échelle : une sonde dont on ne peut prouver le rouge qu'en **cassant un service** ne sera
|
||||
prouvée qu'une fois, puis plus jamais.
|
||||
|
||||
Chaque sonde expose donc sa cible et ses seuils en variables du rôle. On la met en défaut
|
||||
en lui donnant un port fermé, un nom qui n'existe pas, un seuil impossible — sur une
|
||||
machine réelle, sans rien abîmer, et aussi souvent qu'on veut.
|
||||
|
||||
## Où vit la sonde : là où vit la vérité, pas là où vit le symptôme
|
||||
|
||||
*« La vérification suit la clé. »* Un nœud sait qu'il a **lancé** sa sauvegarde ; seul le
|
||||
dépôt sait qu'elle a **abouti**. Un nœud sait qu'il **expose** ses métriques ; seul
|
||||
Prometheus sait qu'il les **collecte**.
|
||||
|
||||
D'où un choix qui peut surprendre : *« ce nœud est-il bien collecté ? »* est une sonde de
|
||||
**`serveur_prometheus`**, pas de `client_metrique`. Une seule sonde y voit les N nœuds à la
|
||||
fois — et surtout, elle voit le cas **silencieux** : celui qui a cessé d'être collecté et
|
||||
qui, par définition, ne peut pas s'en plaindre lui-même.
|
||||
|
||||
## Un contrôle négatif ne doit pas pouvoir abîmer
|
||||
|
||||
Éprouver la sonde du certificat, le 2026-09-09, a consisté à **remplacer le certificat
|
||||
d'hôte** par un auto-signé. La sonde a bien viré au rouge — et le script de synchronisation
|
||||
a propagé ce certificat vers `node_exporter`, dont la clé était restée l'ancienne :
|
||||
|
||||
```
|
||||
failed to load X509KeyPair: tls: private key does not match public key
|
||||
```
|
||||
|
||||
Un service réel est tombé pour éprouver une sonde. Le contrôle avait un **rayon d'action**
|
||||
que je ne lui avais pas donné volontairement.
|
||||
|
||||
La règle qui en découle : un contrôle négatif se fait sur une **copie**, ou sur un chemin
|
||||
que la sonde accepte en paramètre — jamais en substituant l'artefact que d'autres
|
||||
consomment. Quand c'est impossible, on le fait sur la machine la moins critique, on
|
||||
l'annonce, et on vérifie l'état des consommateurs **après**, pas seulement celui de la
|
||||
sonde.
|
||||
|
||||
## Le matériel : un catalogue, deux lecteurs (2026-09-17)
|
||||
|
||||
Les hyperviseurs et la frontière n'ont aucun rôle Set-OPS pour déclarer leurs capteurs, et
|
||||
ils ne peuvent rien pousser. Prometheus les **tire** déjà ; on juge donc ce qu'il a récolté.
|
||||
|
||||
`scripts/materiel.py` est la seule source : familles de températures (processeur, carte
|
||||
mère, NVMe, disques SATA, DDR5, cartes réseau, frontière) avec leurs seuils et leur raison,
|
||||
et contrôles de santé (ventilateur arrêté, SMART en échec, secteurs défaillants ou **en
|
||||
progression**, alerte et usure NVMe, relevés SMART périmés). Deux lecteurs :
|
||||
|
||||
| lecteur | ce qu'il en fait |
|
||||
|---|---|
|
||||
| Grafana — tableau « Set-OPS — Matériel » | tuiles colorées aux seuils, courbes avec seuils tracés, ventilateurs, puissance, usure et âge des disques |
|
||||
| Icinga — hôte par équipement, service `materiel` | `check_materiel.py` lit Prometheus avec les mêmes seuils ; l'hôte est jugé par `up`, pas par un ping qu'aucun flux ne permet |
|
||||
|
||||
Règles apprises en le construisant :
|
||||
|
||||
- **Le matériel ment sur ses limites.** Le Super I/O annonce 125 °C « critique » pour la carte
|
||||
mère : les seuils sont déclarés, avec leur raison.
|
||||
- **Un connecteur vide n'est pas un ventilateur arrêté** : on ne crie que pour un ventilateur
|
||||
qui a tourné dans les 24 h.
|
||||
- **La progression, pas le compte.** Un disque aux secteurs défaillants stables est un
|
||||
avertissement ; c'est un compte qui **monte** qui est critique. Une alarme toujours rouge
|
||||
est une alarme qu'on cesse de lire.
|
||||
- **Le côté droit d'une jointure PromQL est agrégé.** Un réétiquetage laisse cinq minutes
|
||||
deux séries pour une même clé, et la requête est refusée.
|
||||
- **Une requête refusée n'est pas un Prometheus injoignable** : la sonde rapporte l'erreur.
|
||||
|
|
@ -193,14 +193,7 @@ L'hôte est d'abord déclaré dans le **plan** (`instance/plan/serveurs.yml`) et
|
|||
make deployer HOTE=web-frontal-01
|
||||
```
|
||||
|
||||
La cible `deployer` lit les groupes de l'hôte, cherche les playbooks correspondants dans `playbooks/groupes/`, puis les applique **dans l'ordre des couches** — le socle (`serveur_debian`, `serveur_durci`) d'abord, puis le rang de `docs/couches-deploiement.yml`, le même registre que `make site`.
|
||||
|
||||
> **Ce n'est pas « l'ordre de l'inventaire », et la nuance a coûté un déploiement.** Un tri
|
||||
> alphabétique plaçait `client_metrique` avant `serveur_step_ca` : l'intégration réclamait un
|
||||
> certificat que l'autorité, pas encore déployée, ne pouvait pas avoir émis. Constaté le
|
||||
> 2026-08-06 sur les deux premières VM — dont l'hôte de l'AC lui-même. La couche
|
||||
> « intégrations » dit en toutes lettres *déployées en dernier, quand leurs cibles sont
|
||||
> debout* ; `make deployer` l'ignorait.
|
||||
La cible `deployer` lit les groupes de l'hôte, cherche les playbooks correspondants dans `playbooks/groupes/`, puis les applique dans l'ordre de l'inventaire.
|
||||
|
||||
Ensuite, elle lance la vérification post-déploiement :
|
||||
|
||||
|
|
@ -220,9 +213,9 @@ Cette commande limite le playbook au croisement entre le groupe demandé et `hot
|
|||
|
||||
## 9. Variables template et conformité
|
||||
|
||||
Les variables du template servent à construire le golden template dans `instance/inventories/<inventaire>/group_vars/modeles_vm.yml`.
|
||||
Les variables du template servent à construire le golden template dans `instance/inventories/production/group_vars/modeles_vm.yml`.
|
||||
|
||||
Les variables de conformité servent aux VM déployées dans `instance/inventories/<inventaire>/group_vars/serveur_debian.yml`.
|
||||
Les variables de conformité servent aux VM déployées dans `instance/inventories/production/group_vars/serveur_debian.yml`.
|
||||
|
||||
Différence attendue :
|
||||
|
||||
|
|
|
|||
|
|
@ -16,19 +16,4 @@ domaine_interne: "exemple.internal"
|
|||
fuseau_horaire: "America/Toronto"
|
||||
organisation: "Exemple"
|
||||
identite_realm: "exemple"
|
||||
# LE PLAN SUIT L'INVENTAIRE, PAS LE SYMLINK (2026-08-23).
|
||||
#
|
||||
# La valeur precedente etait `{{ playbook_dir }}/../../instance/plan` : le lien
|
||||
# `instance` du moteur, en dur. Tout role lisant le plan lisait donc celui de
|
||||
# l'instance POINTEE PAR LE LIEN, et non celle qu'on deploie.
|
||||
#
|
||||
# CE QUE CA A DONNE. En deployant un autre ecosysteme avec `SETOPS_INSTANCE`, le plancher
|
||||
# /etc/hosts de `ops-01` a recu les FQDN de CHEZLEPRO -- auth.chezlepro.internal,
|
||||
# forge.chezlepro.internal... -- pointes sur l'edge de l'autre. Un ecosysteme
|
||||
# annoncait les noms d'un autre. Neuf roles lisent cette variable ; le plancher est
|
||||
# simplement celui qui l'a rendu visible.
|
||||
#
|
||||
# `inventory_dir` est le dossier de l'inventaire REELLEMENT charge. Le plan qui a
|
||||
# engendre cet inventaire est son voisin : les deux ne peuvent plus se contredire,
|
||||
# et l'expression reste juste qu'on passe par le symlink ou par SETOPS_INSTANCE.
|
||||
setops_plan_dir: "{{ inventory_dir }}/../../plan"
|
||||
setops_plan_dir: "{{ playbook_dir }}/../../instance/plan"
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
# L'EDGE DOIT PORTER LES NOMS QU'IL PUBLIE (2026-08-23).
|
||||
#
|
||||
# Sans ce fichier, `client_pki` n'emet le certificat de l'edge qu'avec ses propres noms
|
||||
# (`infra-edge-01.genese.internal`), et nginx retombe sur le certificat auto-signe de
|
||||
# Debian pour tout FQDN expose. Le service repond, la page s'affiche apres un
|
||||
# avertissement — et rien ne signale la panne. C'est ce qui s'est passe ici : une forge
|
||||
# etait publiee derriere un `ssl-cert-snakeoil.pem`, et `git clone` a ete le
|
||||
# premier a refuser, a juste titre.
|
||||
#
|
||||
# Ce fichier appartient au MODELE parce que trois instances sur quatre le portaient
|
||||
# et que la quatrieme, plus recente, ne l'avait pas : un cablage recopie a la main
|
||||
# finit toujours par manquer quelque part. La preuve P42 le verifie desormais.
|
||||
#
|
||||
# `sans_exposition` est derive du plan (les `expose:` des applications).
|
||||
client_pki_sans: >-
|
||||
{{ ([ansible_fqdn | default(ansible_hostname), ansible_hostname, ansible_host]
|
||||
+ (sans_exposition | default([])))
|
||||
| select | unique | list }}
|
||||
serveur_nginx_certificat: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.crt"
|
||||
serveur_nginx_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key"
|
||||
# Un cert renouvele sur disque reste PERIME en memoire tant que nginx n'a pas recharge.
|
||||
client_pki_reload_services:
|
||||
- nginx
|
||||
|
|
@ -32,9 +32,6 @@ vault_postgresql_keycloak: ""
|
|||
vault_bd_keycloak: ""
|
||||
vault_bd_forgejo: ""
|
||||
vault_bd_icingadb: ""
|
||||
# La base de la console (vigie) dans tous les modes : comptes en mode `db`, préférences
|
||||
# et migrations partout — distincte de celle du moteur.
|
||||
vault_bd_icingaweb2: ""
|
||||
|
||||
# --- Forge (Forgejo) ---
|
||||
vault_forgejo_admin: ""
|
||||
|
|
@ -44,42 +41,3 @@ vault_forgejo_internal_token: ""
|
|||
# --- Observabilité / divers ---
|
||||
vault_grafana_admin: ""
|
||||
vault_redis: ""
|
||||
|
||||
# LA CONSOLE DE SUPERVISION, QUAND ELLE S'AUTHENTIFIE SEULE.
|
||||
#
|
||||
# `serveur_icingaweb2_auth: db` — le mode d'un SITE, qui n'a ni annuaire ni Keycloak.
|
||||
# Icinga Web 2 gère ses comptes nativement ; ce mot de passe est celui du compte
|
||||
# D'AMORÇAGE, celui qui permet d'entrer la première fois pour créer les autres dans
|
||||
# l'interface. Inutile en mode `ldap` ou `external`.
|
||||
vault_icingaweb2_admin: ""
|
||||
|
||||
# LA CONSOLE D'EXPLOITATION, QUAND ELLE S'AUTHENTIFIE SEULE.
|
||||
#
|
||||
# `serveur_ops_gui_auth: locale` — le repli d'un ecosysteme SANS annuaire (un SITE).
|
||||
# Un ecosysteme qui a Keycloak reste en `oidc` et laisse cette cle VIDE : la console
|
||||
# lance des deploiements et peut raser, un mot de passe partage devant ce pouvoir est un
|
||||
# accident qui attend.
|
||||
vault_setops_gui_admin: ""
|
||||
|
||||
# LE SECRET OIDC DE LA CONSOLE D'EXPLOITATION (mode `oidc`).
|
||||
#
|
||||
# Le GUI de Set-OPS n'a aucune authentification a lui : la passerelle est sa seule
|
||||
# serrure, et ce secret est ce qui la lie a Keycloak. Vide chez un ecosysteme sans
|
||||
# annuaire — un SITE — qui emploie alors le vestibule local.
|
||||
vault_setops_console_oidc: ""
|
||||
|
||||
|
||||
|
||||
# LE COMPTE DE METRIQUES DE POSTGRESQL — lecture seule, role `pg_monitor`.
|
||||
#
|
||||
# Il ne lit que les vues de statistiques : pas une ligne de donnee applicative. Faire
|
||||
# tourner l'exportateur en `postgres` serait donner les cles de la base pour lire des
|
||||
# compteurs.
|
||||
#
|
||||
# VIDE = PAS D'EXPORTATEUR DU TOUT. Le role ne le pose pas et ne cree pas le compte —
|
||||
# jamais un mot de passe par defaut. MAIS PROMETHEUS DERIVE QUAND MEME SA CIBLE (`:9187`)
|
||||
# pour tout hote de `serveur_postgresql`, secret ou pas : sans lui, la sonde `collecte`
|
||||
# d'obs-01 passe au rouge (« muettes : ...:9187 »). C'est voulu — une base sans metriques
|
||||
# se voit ; renseigner ce secret est la facon de l'eteindre. (Cette ligne promettait
|
||||
# l'inverse jusqu'au 2026-09-28 ; Chezlepro l'a vecu.)
|
||||
vault_pg_exportateur: ""
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ from inventory_rules import ( # noqa: E402
|
|||
bases_du_groupe,
|
||||
chaine_connexion,
|
||||
expositions_des_applications,
|
||||
zones_inverses,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -32,5 +31,4 @@ class FilterModule:
|
|||
"bases_du_groupe": bases_du_groupe,
|
||||
"chaine_connexion": chaine_connexion,
|
||||
"expositions_des_applications": expositions_des_applications,
|
||||
"zones_inverses": zones_inverses,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
---
|
||||
# LES PAQUETS QUI NE VIENNENT PAS DE DEBIAN — et pourquoi ils sont ici.
|
||||
#
|
||||
# Trois dépôts tiers sont configurés sur la flotte, tous en HTTPS. Or `client_artefacts`
|
||||
# pose `Acquire::https::Proxy "DIRECT"` — obligatoire, parce que le cache refuse les
|
||||
# tunnels HTTPS (« 403 CONNECT denied ») et que sans cette ligne aucun dépôt tiers
|
||||
# n'était joignable. La conséquence n'avait pas été vue : **ces trois dépôts contournent
|
||||
# le cache par conception**, et chaque construction de VM va les chercher sur Internet.
|
||||
#
|
||||
# CE QUE ÇA COÛTAIT. `client_pki` est une intégration UNIVERSELLE : chaque machine de
|
||||
# chaque écosystème installe `step-cli` depuis Smallstep au moment de sa naissance. Sans
|
||||
# Internet, une VM neuve n'obtient pas son client d'AC, donc pas de certificat, donc
|
||||
# n'entre dans aucun flux chiffré. `alloy` (métriques) est dans le même cas.
|
||||
#
|
||||
# Les versions sont ÉPINGLÉES, comme les collections Ansible. Une mise à niveau devient
|
||||
# alors un geste délibéré — `make cacher-paquets` — au lieu d'arriver toute seule le jour
|
||||
# où l'amont publie.
|
||||
depots:
|
||||
smallstep:
|
||||
uri: https://packages.smallstep.com/stable/debian
|
||||
suite: debs
|
||||
composant: main
|
||||
# Sur CHAQUE machine, via `client_pki`. Le paquet le plus critique de la liste.
|
||||
paquets:
|
||||
step-cli: 0.31.0-1
|
||||
|
||||
icinga:
|
||||
uri: https://packages.icinga.com/debian
|
||||
suite: icinga-trixie
|
||||
composant: main
|
||||
# La supervision et sa vue web, avec leurs dépendances PHP propres au dépôt.
|
||||
paquets:
|
||||
icinga2: 2.16.5-1+debian13
|
||||
icinga2-bin: 2.16.5-1+debian13
|
||||
icinga2-common: 2.16.5-1+debian13
|
||||
icinga2-doc: 2.16.5-1+debian13
|
||||
icinga-archive-keyring: 2.0.0-1+debian13
|
||||
icingacli: 2.14.0-2+debian13
|
||||
icingadb: 1.5.1-8+debian13
|
||||
icingadb-redis: 8.2.10-1+debian13
|
||||
icingadb-web: 1.4.0-1+debian13
|
||||
icinga-l10n: 1.4.0-1+debian13
|
||||
icinga-php-legacy: 1.1.0-1+debian13
|
||||
icinga-php-library: 1.0.1-1+debian13
|
||||
icinga-php-thirdparty: 1.0.1-1+debian13
|
||||
icingaweb2: 2.14.0-2+debian13
|
||||
icingaweb2-common: 2.14.0-2+debian13
|
||||
icingaweb2-module-monitoring: 2.12.6-1+debian13
|
||||
php-icinga: 2.14.0-2+debian13
|
||||
|
||||
grafana:
|
||||
uri: https://apt.grafana.com
|
||||
suite: stable
|
||||
composant: main
|
||||
# `alloy` est universel comme `step-cli` : `client_metrique` le pose partout.
|
||||
# RELEVES LE 2026-10-03 sur ce que portent les locataires depuis leur reconstruction du
|
||||
# 2026-10-01 : leurs runners n'ont pas ce cache et avaient tire la derniere version
|
||||
# publiee. Le cache du poste, lui, servait encore les anciennes — un deploiement depuis
|
||||
# le poste aurait tente de les RETROGRADER (`paquets_tiers` installe les .deb du cache).
|
||||
# Le site, encore en 1.19.2 / 13.2.1 / 3.7.7, montera a son prochain passage.
|
||||
paquets:
|
||||
alloy: 1.20.1-1
|
||||
grafana: 13.2.3
|
||||
loki: 3.7.8
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
---
|
||||
# L'ETAT D'UNE INCARNATION PRECEDENTE — voir roles/client_backup/templates/restaurer.sh.j2
|
||||
#
|
||||
# make restauration-etat [HOTE=...]
|
||||
# ce qui attend dans le depot, et ce que chaque jeu est devenu (lecture seule) ;
|
||||
# make sauvegarder-maintenant [HOTE=...]
|
||||
# deposer TOUT DE SUITE, et attendre que ce soit fait. A faire juste avant `raser` :
|
||||
# sinon la reconstruction remet l'etat de la derniere nuit, et perd la journee ;
|
||||
# make temoins-etat [HOTE=...] [SOURCE=candidat]
|
||||
# l'etat VIVANT est-il celui d'avant le rasage ? Compare a l'instantane etiquete
|
||||
# « avant-raser » (lecture seule). Une perte, ou une identite changee (cles de l'AC,
|
||||
# DKIM, instanceid, mot de passe d'une entree de l'annuaire), fait echouer le jeu ;
|
||||
# make restauration-renoncer HOTE=<hote> JEU=<jeu> CONFIRMER=true
|
||||
# ECARTER un etat anterieur sans le remettre. La sauvegarde du noeud, qui refusait
|
||||
# de deposer pour ne pas le chasser de la retention, reprend — et l'etat d'avant
|
||||
# finira par sortir de la retention. C'est une decision, pas une reparation.
|
||||
#
|
||||
# La REMISE, elle, n'a pas de cible : elle est faite par le role proprietaire de l'etat,
|
||||
# au deploiement, au moment ou il le creerait neuf.
|
||||
- name: L'etat d'une incarnation precedente
|
||||
hosts: client_backup
|
||||
become: true
|
||||
gather_facts: false
|
||||
vars:
|
||||
restauration_action: etat
|
||||
restauration_jeu: ""
|
||||
restauration_source: avant-raser
|
||||
tasks:
|
||||
- name: Refuser un renoncement sans jeu nomme
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- restauration_jeu | length > 0
|
||||
fail_msg: "JEU=<jeu> requis (voir `make restauration-etat` pour les noms)."
|
||||
when: restauration_action == 'renoncer'
|
||||
|
||||
- name: Lire ce qui attend, et ce qui a ete tranche
|
||||
ansible.builtin.command:
|
||||
argv: [/usr/local/sbin/setops-restaurer, etat]
|
||||
register: restauration_lue
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: restauration_action == 'etat'
|
||||
|
||||
- name: Etat
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ (restauration_lue.stdout_lines | default([])) + (restauration_lue.stderr_lines | default([])) }}"
|
||||
when: restauration_action == 'etat'
|
||||
|
||||
# L'unite est `oneshot` : `systemctl start` rend la main quand le depot est fait, et
|
||||
# echoue s'il a echoue — y compris quand la garde refuse (etat d'avant non remis).
|
||||
- name: Ce noeud a-t-il quelque chose a deposer ?
|
||||
ansible.builtin.stat:
|
||||
path: /etc/systemd/system/setops-sauvegarde.service
|
||||
register: restauration_unite
|
||||
when: restauration_action == 'sauvegarder'
|
||||
|
||||
# LE DEPOT D'AVANT RASAGE EST ETIQUETE (2026-10-07), et la retention le garde jusqu'a la
|
||||
# reconstruction suivante. Un noeud qui n'a pas encore cette unite deposerait SANS
|
||||
# etiquette, et la premiere sauvegarde d'apres reconstruction le chasserait : c'est
|
||||
# exactement ce qui s'est passe a M4. On refuse plutot que de deposer a moitie.
|
||||
- name: L'unite du depot d'avant rasage est-elle deployee ?
|
||||
ansible.builtin.stat:
|
||||
path: /etc/systemd/system/setops-sauvegarde-avant-raser.service
|
||||
register: restauration_unite_avant_raser
|
||||
when:
|
||||
- restauration_action == 'sauvegarder'
|
||||
- restauration_unite.stat.exists
|
||||
|
||||
- name: Refuser un depot que la retention ne garderait pas
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- restauration_unite_avant_raser.stat.exists
|
||||
fail_msg: >-
|
||||
setops-sauvegarde-avant-raser.service absente : redeployer client_backup
|
||||
(make appliquer GROUPE=client_backup) avant de raser.
|
||||
when:
|
||||
- restauration_action == 'sauvegarder'
|
||||
- restauration_unite.stat.exists
|
||||
|
||||
- name: Deposer maintenant, etiquete avant rasage
|
||||
ansible.builtin.systemd:
|
||||
name: setops-sauvegarde-avant-raser.service
|
||||
state: started
|
||||
when:
|
||||
- restauration_action == 'sauvegarder'
|
||||
- restauration_unite.stat.exists
|
||||
|
||||
# Code 4 = rien a comparer (aucun etat, ou aucun instantane d'avant rasage) : ce n'est
|
||||
# pas un echec. Code 1 = ecart : on affiche d'abord, on echoue ensuite.
|
||||
- name: Temoins — l'etat vivant contre l'instantane d'avant rasage
|
||||
ansible.builtin.command:
|
||||
argv: >-
|
||||
{{ ['/usr/local/sbin/setops-restaurer', 'temoins']
|
||||
+ (['--candidat'] if restauration_source == 'candidat' else []) }}
|
||||
register: restauration_temoins
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: restauration_action == 'temoins'
|
||||
|
||||
- name: Temoins
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ (restauration_temoins.stdout_lines | default([])) + (restauration_temoins.stderr_lines | default([])) }}"
|
||||
when: restauration_action == 'temoins'
|
||||
|
||||
- name: Temoins — verdict
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- restauration_temoins.rc in [0, 4]
|
||||
fail_msg: >-
|
||||
{{ 'ECART : une perte, ou une identite changee (voir ci-dessus).'
|
||||
if restauration_temoins.rc == 1 else
|
||||
'setops-restaurer temoins a echoue (code ' ~ restauration_temoins.rc ~ ') : '
|
||||
~ 'client_backup est-il deploye ?' }}
|
||||
quiet: true
|
||||
when: restauration_action == 'temoins'
|
||||
|
||||
- name: Ecarter l'etat anterieur de ce jeu
|
||||
ansible.builtin.command:
|
||||
argv: [/usr/local/sbin/setops-restaurer, acter, "{{ restauration_jeu }}", abandonne]
|
||||
changed_when: true
|
||||
when: restauration_action == 'renoncer'
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
# client_artefacts — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||
#
|
||||
# Cette integration depend de `serveur_artefacts`, et sa metadonnee le declare
|
||||
# (`roles/client_artefacts/meta/integration.yml`). L'hote qui PORTE ce serveur est donc
|
||||
# configure AVANT ceux qui s'y adressent — sinon les clients s'enrolent aupres d'un
|
||||
# service qui n'est pas encore la, ou qui redemarre au meme instant.
|
||||
#
|
||||
# Ce fichier est le miroir de la declaration ; la preuve P44 refuse tout ecart.
|
||||
#
|
||||
# `any_errors_fatal` sur le premier play : si le serveur n'a pas pu etre configure,
|
||||
# y raccrocher des clients ne produit que des echecs qui accusent le reseau.
|
||||
|
||||
- name: Intégration client_artefacts — le serveur d'abord
|
||||
hosts: client_artefacts:&serveur_artefacts
|
||||
become: true
|
||||
any_errors_fatal: true
|
||||
module_defaults: &defauts_artefacts
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
gather_facts: true
|
||||
pre_tasks: &pre_artefacts
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
roles:
|
||||
- client_artefacts
|
||||
|
||||
- name: Intégration client_artefacts — puis les hôtes qui s'y adressent
|
||||
hosts: client_artefacts:!serveur_artefacts
|
||||
become: true
|
||||
module_defaults: *defauts_artefacts
|
||||
gather_facts: true
|
||||
pre_tasks: *pre_artefacts
|
||||
roles:
|
||||
- client_artefacts
|
||||
|
|
@ -1,38 +1,22 @@
|
|||
---
|
||||
# client_journal — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||
#
|
||||
# Cette integration depend de `serveur_loki`, et sa metadonnee le declare
|
||||
# (`roles/client_journal/meta/integration.yml`). L'hote qui PORTE ce serveur est donc
|
||||
# configure AVANT ceux qui s'y adressent — sinon les clients s'enrolent aupres d'un
|
||||
# service qui n'est pas encore la, ou qui redemarre au meme instant.
|
||||
#
|
||||
# Ce fichier est le miroir de la declaration ; la preuve P44 refuse tout ecart.
|
||||
#
|
||||
# `any_errors_fatal` sur le premier play : si le serveur n'a pas pu etre configure,
|
||||
# y raccrocher des clients ne produit que des echecs qui accusent le reseau.
|
||||
|
||||
- name: Intégration client_journal — le serveur d'abord
|
||||
hosts: client_journal:&serveur_loki
|
||||
- name: Appliquer le groupe client_journal
|
||||
hosts: client_journal
|
||||
become: true
|
||||
any_errors_fatal: true
|
||||
module_defaults: &defauts_journal
|
||||
# Le verrou dpkg est tenu par les maj automatiques de Debian, par vagues, plusieurs
|
||||
# minutes apres le premier demarrage. Pose ICI plutot que dans chaque role : toute
|
||||
# tache apt du play en herite, y compris celles des roles inclus.
|
||||
module_defaults:
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
|
||||
gather_facts: true
|
||||
pre_tasks: &pre_journal
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
roles:
|
||||
- client_journal
|
||||
|
||||
- name: Intégration client_journal — puis les hôtes qui s'y adressent
|
||||
hosts: client_journal:!serveur_loki
|
||||
become: true
|
||||
module_defaults: *defauts_journal
|
||||
gather_facts: true
|
||||
pre_tasks: *pre_journal
|
||||
roles:
|
||||
- client_journal
|
||||
|
|
|
|||
|
|
@ -1,38 +1,22 @@
|
|||
---
|
||||
# client_pki — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||
#
|
||||
# Cette integration depend de `serveur_step_ca`, et sa metadonnee le declare
|
||||
# (`roles/client_pki/meta/integration.yml`). L'hote qui PORTE ce serveur est donc
|
||||
# configure AVANT ceux qui s'y adressent — sinon les clients s'enrolent aupres d'un
|
||||
# service qui n'est pas encore la, ou qui redemarre au meme instant.
|
||||
#
|
||||
# Ce fichier est le miroir de la declaration ; la preuve P44 refuse tout ecart.
|
||||
#
|
||||
# `any_errors_fatal` sur le premier play : si le serveur n'a pas pu etre configure,
|
||||
# y raccrocher des clients ne produit que des echecs qui accusent le reseau.
|
||||
|
||||
- name: Intégration client_pki — le serveur d'abord
|
||||
hosts: client_pki:&serveur_step_ca
|
||||
- name: Appliquer le groupe client_pki
|
||||
hosts: client_pki
|
||||
become: true
|
||||
any_errors_fatal: true
|
||||
module_defaults: &defauts_pki
|
||||
# Le verrou dpkg est tenu par les maj automatiques de Debian, par vagues, plusieurs
|
||||
# minutes apres le premier demarrage. Pose ICI plutot que dans chaque role : toute
|
||||
# tache apt du play en herite, y compris celles des roles inclus.
|
||||
module_defaults:
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
|
||||
gather_facts: true
|
||||
pre_tasks: &pre_pki
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
roles:
|
||||
- client_pki
|
||||
|
||||
- name: Intégration client_pki — puis les hôtes qui s'y adressent
|
||||
hosts: client_pki:!serveur_step_ca
|
||||
become: true
|
||||
module_defaults: *defauts_pki
|
||||
gather_facts: true
|
||||
pre_tasks: *pre_pki
|
||||
roles:
|
||||
- client_pki
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
# client_resolveur — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||
#
|
||||
# Cette integration depend de `serveur_resolveur`, et sa metadonnee le declare
|
||||
# (`roles/client_resolveur/meta/integration.yml`). L'hote qui PORTE ce serveur est donc
|
||||
# configure AVANT ceux qui s'y adressent — sinon les clients s'enrolent aupres d'un
|
||||
# service qui n'est pas encore la, ou qui redemarre au meme instant.
|
||||
#
|
||||
# Ce fichier est le miroir de la declaration ; la preuve P44 refuse tout ecart.
|
||||
#
|
||||
# `any_errors_fatal` sur le premier play : si le serveur n'a pas pu etre configure,
|
||||
# y raccrocher des clients ne produit que des echecs qui accusent le reseau.
|
||||
|
||||
- name: Intégration client_resolveur — le serveur d'abord
|
||||
hosts: client_resolveur:&serveur_resolveur
|
||||
become: true
|
||||
any_errors_fatal: true
|
||||
module_defaults: &defauts_resolveur
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
gather_facts: true
|
||||
pre_tasks: &pre_resolveur
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
roles:
|
||||
- client_resolveur
|
||||
|
||||
- name: Intégration client_resolveur — puis les hôtes qui s'y adressent
|
||||
hosts: client_resolveur:!serveur_resolveur
|
||||
become: true
|
||||
module_defaults: *defauts_resolveur
|
||||
gather_facts: true
|
||||
pre_tasks: *pre_resolveur
|
||||
roles:
|
||||
- client_resolveur
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
---
|
||||
# client_sante — L'ORDRE FAIT PARTIE DE L'INTEGRATION.
|
||||
#
|
||||
# Cette integration depend de `serveur_icinga`, et sa metadonnee le declare
|
||||
# (`roles/client_sante/meta/integration.yml`). L'hote qui PORTE la supervision est donc
|
||||
# configure AVANT ceux qui lui rapportent — sinon les noeuds poussent un resultat passif
|
||||
# vers une API qui n'existe pas encore, et le premier verdict de chaque machine est un
|
||||
# echec qui accuse le reseau.
|
||||
#
|
||||
# Ce fichier est le miroir de la declaration ; la preuve P44 refuse tout ecart.
|
||||
#
|
||||
# `any_errors_fatal` sur le premier play : si la supervision n'a pas pu etre configuree,
|
||||
# y raccrocher des rapporteurs ne produit que du bruit.
|
||||
|
||||
- name: Intégration client_sante — le serveur d'abord
|
||||
hosts: client_sante:&serveur_icinga
|
||||
become: true
|
||||
any_errors_fatal: true
|
||||
module_defaults: &defauts_sante
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
gather_facts: true
|
||||
pre_tasks: &pre_sante
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
roles:
|
||||
- client_sante
|
||||
|
||||
- name: Intégration client_sante — puis les hôtes qui s'y adressent
|
||||
hosts: client_sante:!serveur_icinga
|
||||
become: true
|
||||
module_defaults: *defauts_sante
|
||||
gather_facts: true
|
||||
pre_tasks: *pre_sante
|
||||
roles:
|
||||
- client_sante
|
||||
|
||||
# LE RETRAIT, SUR LES HOTES SORTIS DU GROUPE (2026-10-03). Un role qu'on cesse d'appliquer
|
||||
# laisse son minuteur derriere lui : les hyperviseurs du site ont pousse vers une adresse
|
||||
# perimee pendant des semaines (voir `roles/client_sante/tasks/retirer.yml`).
|
||||
#
|
||||
# LE GROUPE EST NOMME, pas deduit (`all:!client_sante`) : un hyperviseur n'est pas une VM
|
||||
# de la flotte, et un play qui le touche doit le dire. Chez un tenant, `hyperviseurs`
|
||||
# n'existe pas et ce play ne trouve personne.
|
||||
- name: Intégration client_sante — retrait sur les hyperviseurs qui n'y sont plus
|
||||
hosts: hyperviseurs:!client_sante
|
||||
become: true
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: Retirer le porteur de santé
|
||||
ansible.builtin.include_role:
|
||||
name: client_sante
|
||||
tasks_from: retirer.yml
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveur_ops
|
||||
hosts: serveur_ops
|
||||
- name: Appliquer le groupe client_unbound
|
||||
hosts: client_unbound
|
||||
become: true
|
||||
# Le verrou dpkg est tenu par les maj automatiques de Debian, par vagues, plusieurs
|
||||
# minutes apres le premier demarrage. Pose ICI plutot que dans chaque role : toute
|
||||
|
|
@ -19,4 +19,4 @@
|
|||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- serveur_ops
|
||||
- client_unbound
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveur_artefacts
|
||||
hosts: serveur_artefacts
|
||||
become: true
|
||||
module_defaults:
|
||||
ansible.builtin.apt:
|
||||
lock_timeout: 300
|
||||
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- serveur_artefacts
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveur_backup_site
|
||||
hosts: serveur_backup_site
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- serveur_backup_site
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue