Compare commits
453 commits
v2026.08.2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 493a5dcbeb | |||
| 412133bd76 | |||
| ee0e33d452 | |||
| dd4ca09245 | |||
| 162604ad24 | |||
| 34e961fb61 | |||
| 422b68846b | |||
| 55f42add15 | |||
| 4ebca13058 | |||
| 279e583701 | |||
| 3afb756993 | |||
| c7378ed7f7 | |||
| 7a4c55bfc9 | |||
| 0f7c89fbb5 | |||
| 9ca9a5ffe1 | |||
| 2dce6eabd5 | |||
| 479ec3cc7d | |||
| 9a4b63544e | |||
| 3e2846cc5f | |||
| cca22f47ca | |||
| 4660a8892a | |||
| 6141bc4b2f | |||
| e8907285d5 | |||
| e9215a2141 | |||
| 2cbfa2df9b | |||
| f04e790d2c | |||
| 12caff1cff | |||
| 543791e859 | |||
| 11b5bb5733 | |||
| a204ead8ab | |||
| e7f1a74040 | |||
| 8294e5dc71 | |||
| 4532a98707 | |||
| 40af2d1ec6 | |||
| 2e9b31bef7 | |||
| d0a6002b87 | |||
| 013c1b5279 | |||
| 8c1e8c725b | |||
| 44c24e62e4 | |||
| 2beef04dc6 | |||
| d6591cb271 | |||
| 92a6fb0ead | |||
| ce84d0e1ea | |||
| 56033afd00 | |||
| 3c16ef45cd | |||
| 78c2c25761 | |||
| 3d01208a68 | |||
| 077c6c91b4 | |||
| 8251022148 | |||
| c891d7fb26 | |||
| 42e23885cd | |||
| 0d7578b367 | |||
| f42d30b38a | |||
| 5d7d2a8918 | |||
| 1167cc12e0 | |||
| 7976df08d5 | |||
| 6570b757a4 | |||
| 5340220192 | |||
| 055aed665e | |||
| 05ec7a8743 | |||
| b3debd6ca9 | |||
| 49f3d28865 | |||
| 16675037f5 | |||
| 715f9de84d | |||
| badaf753f8 | |||
| 4ee149a0c6 | |||
| f11bb11ad7 | |||
| 2f275d8a7d | |||
| b4ae92c03f | |||
| 8d40c687e1 | |||
| 8b008a8cf4 | |||
| 9414dca750 | |||
| 6a8fc53084 | |||
| a4b6565056 | |||
| 7a2f179f39 | |||
| c1408be9bb | |||
| c2fd6d17ca | |||
| e858fe60d3 | |||
| ff518e18c2 | |||
| 25fd4fc6f1 | |||
| 0318412a9e | |||
| 85932d948c | |||
| 34bfd43e06 | |||
| 209730b62a | |||
| 8603ec4c19 | |||
| d2e2bb8990 | |||
| 49a7e12850 | |||
| b42bc51752 | |||
| b5fa0e8d23 | |||
| 2bf94acb96 | |||
| f425fb6cb3 | |||
| 20a5e44803 | |||
| 249d37d2c4 | |||
| a5a21ee4c5 | |||
| 5a6d0f28f6 | |||
| 14bf977bd6 | |||
| cc906af918 | |||
| 9167f67c7b | |||
| b135b09488 | |||
| 844568af08 | |||
| d9a8905fc7 | |||
| 515aac415a | |||
| 8a9da0c31a | |||
| d3437a6308 | |||
| 502feff6e0 | |||
| bdbf7d8317 | |||
| f3e8883083 | |||
| be06def899 | |||
| cc3a4eec30 | |||
| 5f23201075 | |||
| f818143b49 | |||
| b01de94493 | |||
| 4678ca0223 | |||
| 80bd8e88ac | |||
| 8186389309 | |||
| 6d31842b83 | |||
| cac0b75b3a | |||
| 0cd08663e7 | |||
| 2fde5c7ae7 | |||
| 3fe7e3cea3 | |||
| 2903d75831 | |||
| 5eca6ff4f8 | |||
| 37753984a7 | |||
| e9b03399b9 | |||
| e84b155a5f | |||
| 712a7da1fd | |||
| 81cdb84f66 | |||
| 2695013b32 | |||
| e68f315625 | |||
| 1493059271 | |||
| 1bd211d62b | |||
| efb1b3d334 | |||
| eee6851c3b | |||
| ba2020d486 | |||
| fbd4094d3f | |||
| 2bbe12299f | |||
| 5404c947d8 | |||
| 1653319ef1 | |||
| 8ac7909d49 | |||
| 4e07b52291 | |||
| ca1f1557c4 | |||
| 9e6f33d1cf | |||
| 6b6a56ab3f | |||
| b938897e17 | |||
| 8f699eb3ce | |||
| cf08e51c38 | |||
| 3063bd4bf3 | |||
| 5c638675cb | |||
| 91dc90f22b | |||
| 974fabc40a | |||
| b1c4775afe | |||
| bcf62bafe7 | |||
| 6edb8ac7d2 | |||
| 79fcff3274 | |||
| a401481ae9 | |||
| 22fffdbb6f | |||
| 40b38467e8 | |||
| 5e7534d87f | |||
| d243773b45 | |||
| aac2d35e06 | |||
| 67fd3ab6b7 | |||
| 8804d67dbf | |||
| d673f97ac9 | |||
| 58432fa6e1 | |||
| 35831608d0 | |||
| 4ce296f3d5 | |||
| 3676e69cd9 | |||
| 4f5a0b5696 | |||
| 0156596f01 | |||
| ffc171a2cb | |||
| 5f52dcb2b7 | |||
| 4855cda294 | |||
| 2bd761b85f | |||
| 415189ceff | |||
| 0551c4b4de | |||
| f5adffb0de | |||
| 185b000c60 | |||
| 831c7d5cf4 | |||
| 51b8165221 | |||
| fe9d47c4a6 | |||
| 3a379f6344 | |||
| 478aab3eb8 | |||
| badbcd3971 | |||
| fde604c2a5 | |||
| e1a4dc62bb | |||
| 6a7188290f | |||
| c0f610be33 | |||
| 5f6ffda5e8 | |||
| 1d07a6cf80 | |||
| 47283256d8 | |||
| 4e0b9caa45 | |||
| 3b8559063f | |||
| d461492623 | |||
| cd0f94a50b | |||
| 859ac07a54 | |||
| 7ef7ca196a | |||
| 4188cf5b6f | |||
| d3a8124777 | |||
| f33b5be151 | |||
| bf94a304ff | |||
| f95873915f | |||
| f9a20b0870 | |||
| 6367d851e8 | |||
| 05b85ebd27 | |||
| be8c3917e5 | |||
| 2a276fb6b4 | |||
| d90513ca91 | |||
| ee386dca00 | |||
| 49ceb81a4d | |||
| 8ac9f53f05 | |||
| 0eefc5cf4d | |||
| c1d0d9b1a7 | |||
| 53cfe71c7e | |||
| 712ad9f630 | |||
| 63780645a7 | |||
| 19bbadfb11 | |||
| 788c5073dc | |||
| 8456d298bf | |||
| e46fa198fd | |||
| a5152a5162 | |||
| 0cbb9fdb4a | |||
| 85c7f9710b | |||
| ae212c0252 | |||
| e93e8f4c1d | |||
| 0529951381 | |||
| 504840f378 | |||
| 51dae506fb | |||
| 95dc225037 | |||
| fae3cc8838 | |||
| 4f68182ac6 | |||
| 09a6a49089 | |||
| 627ef00ec7 | |||
| 5ecce4282a | |||
| c308c9c5b0 | |||
| d4bc3e48b7 | |||
| bb88f1f981 | |||
| b02d3c900e | |||
| 392da4d3f5 | |||
| 311f33d6f6 | |||
| baa9d12054 | |||
| aac74f6043 | |||
| 4290b566b4 | |||
| f307a23345 | |||
| 8574b7b540 | |||
| 70193af24d | |||
| d76ce574f0 | |||
| 1b71884871 | |||
| a4a8fc6065 | |||
| 818a100ded | |||
| a431c13025 | |||
| f4d57009ab | |||
| 8eb482e0c6 | |||
| 52b9d31a5d | |||
| 9709817be6 | |||
| b7884217ce | |||
| 63437b5726 | |||
| 7348eb93d1 | |||
| 07216a5d30 | |||
| f815b069f7 | |||
| af93a4129e | |||
| 050b8b267e | |||
| f199888f86 | |||
| 0b848fddc9 | |||
| af3fb1c0f1 | |||
| 220e1e3e28 | |||
| 8a9ac18462 | |||
| db1cb652b7 | |||
| 4b759110bf | |||
| 224ac2fe32 | |||
| 77cf63f1cf | |||
| bd0e40753b | |||
| 94bfa224ff | |||
| ab0e74d843 | |||
| a154893fc1 | |||
| 025776064a | |||
| b4e3520561 | |||
| db4223904d | |||
| 5812e0c6ca | |||
| 56d202a8bb | |||
| b9b0d87db2 | |||
| bef0655112 | |||
| 0c55aafc40 | |||
| 5419ee2d71 | |||
| 904ece3004 | |||
| 6786d15068 | |||
| 989398f3cf | |||
| 5f5af70a9c | |||
| 1b89e83b0c | |||
| 53e2f78148 | |||
| d1ae41cfdf | |||
| f5b5899284 | |||
| 2450c9ea65 | |||
| c733d2df1c | |||
| 0778979b89 | |||
| 5abf20102d | |||
| 009325ee51 | |||
| a60b69f072 | |||
| 1410fe41ec | |||
| c6a7150c60 | |||
| 9685e0bdbe | |||
| 2cf68c2d30 | |||
| 4ef3a1d553 | |||
| 84b5ecc71d | |||
| f2c580235d | |||
| 5880b22d5a | |||
| 6e46ace4de | |||
| a5d9040a11 | |||
| 3e8e052670 | |||
| 6120e6f006 | |||
| 1832530f29 | |||
| a8af541059 | |||
| 5b423cf954 | |||
| 843d39e7aa | |||
| a070c339ee | |||
| 988d35745e | |||
| 90228cb55e | |||
| 69b84f4e2c | |||
| 018d55ec6c | |||
| 6d23121dc2 | |||
| f27ac9762e | |||
| e9b22b0be0 | |||
| f4bb40c4f2 | |||
| 36aba37200 | |||
| 2d9dc866a9 | |||
| 0b0d9ca708 | |||
| f89b097b26 | |||
| a6457a161b | |||
| 53d7b4c4c2 | |||
| 765d97b5c9 | |||
| 2887b57f0c | |||
| c0ea0c8e71 | |||
| 935a64a1bf | |||
| 00a8dc5283 | |||
| 88033f37b8 | |||
| 34b7ec3886 | |||
| 03c628d5e1 | |||
| 0eaceb1048 | |||
| 6077b179af | |||
| 6da1032ceb | |||
| cbdc6c523d | |||
| e2935edd3d | |||
| c9d31e9a60 | |||
| 36b6926e21 | |||
| 5bc3bceac1 | |||
| 00cee67f02 | |||
| 9a823a5ea2 | |||
| dcbb57db69 | |||
| 3eff217bd5 | |||
| bebdb84212 | |||
| 42becd0c02 | |||
| 872b5e91aa | |||
| 6653f49f2e | |||
| cccb4e5b43 | |||
| 4e12c3a802 | |||
| cf9abe74b6 | |||
| 53c5f41a1a | |||
| 0854b2a93c | |||
| 3ea0c95492 | |||
| 4abf875e5f | |||
| 9aef3614ab | |||
| 2f2323bd3e | |||
| 0b653170fa | |||
| 89ca92b132 | |||
| 35f3881218 | |||
| 0dd57ba995 | |||
| e2bc235132 | |||
| fde0903ed8 | |||
| a5c9a88f00 | |||
| fc76a4e252 | |||
| 6f4833e65c | |||
| bb63865a37 | |||
| f2f6cccd3e | |||
| 5c59f19530 | |||
| 732aff9c70 | |||
| 90ea474745 | |||
| 0595bf03c1 | |||
| c918a50257 | |||
| 4beb7e0724 | |||
| 89c911ef05 | |||
| 58ce3dfea4 | |||
| aca63011ea | |||
| ceb832eeac | |||
| 5e4d711b97 | |||
| 45aaf2808d | |||
| 377462b141 | |||
| ef832d11d6 | |||
| d3ba520500 | |||
| c6f2cb1dd9 | |||
| 35853ff5fb | |||
| 6fe62f74e1 | |||
| 5d0f82792a | |||
| 3fa6e4c3e1 | |||
| 3b65384b0f | |||
| 14fa129731 | |||
| 561c034eb4 | |||
| 8195d6b814 | |||
| 8890c997af | |||
| 9e8679215d | |||
| 87716cefc7 | |||
| 6a5a49f924 | |||
| e5841ea684 | |||
| a74e35bf21 | |||
| ac48b7650b | |||
| 1a7c042e5b | |||
| 158c3b314a | |||
| ea67b75a15 | |||
| b60946f19f | |||
| 43666cff6e | |||
| add94f2cbd | |||
| 467b05cbc0 | |||
| a530d5f70e | |||
| fcde343ef0 | |||
| a6713ffdcd | |||
| 0b6c036f8c | |||
| 6715f0d5c5 | |||
| dc92f01fe4 | |||
| bf35b4f528 | |||
| 6e126dd8a9 | |||
| 93dde5b4f2 | |||
| 9960cfd68e | |||
| 85ef6e107e | |||
| 9c173f52ba | |||
| da211bd81b | |||
| bf8e27ef85 | |||
| a1997774be | |||
| fa7e656844 | |||
| c391d1ed30 | |||
| 6547eb2fac | |||
| 12d938cdb0 | |||
| db2d6f6bf0 | |||
| 70e57076e8 | |||
| 1363ebff59 | |||
| 2b55761fba | |||
| f248bb084f | |||
| e6c5f9f539 | |||
| 32e1f6fbcd | |||
| a3f496a496 | |||
| 3ec3768799 | |||
| d94fca490f | |||
| 665b07ba82 | |||
| 8e125b71cb | |||
| 7d71f37528 | |||
| 15cdb7d454 | |||
| 4af1fcd0e2 | |||
| d1db332ed3 | |||
| caee04385b | |||
| 9f36f08db0 | |||
| 5be3fbd5e1 | |||
| 5bb503be45 | |||
| c18debc25e | |||
| bd1b897815 | |||
| 79461fdc38 | |||
| 742bcbf4b0 |
716 changed files with 79285 additions and 2857 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,6 +9,9 @@ facts_cache/
|
||||||
**/vault.yml
|
**/vault.yml
|
||||||
# Underlay = fabric physique de l'operateur (cluster-global) ; gabarit public seul.
|
# Underlay = fabric physique de l'operateur (cluster-global) ; gabarit public seul.
|
||||||
/underlay.yml
|
/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
|
*.secret
|
||||||
*.pem
|
*.pem
|
||||||
*.key
|
*.key
|
||||||
|
|
|
||||||
14876
CHANGELOG.md
14876
CHANGELOG.md
File diff suppressed because it is too large
Load diff
21
CLAUDE.md
21
CLAUDE.md
|
|
@ -4,24 +4,3 @@
|
||||||
|
|
||||||
`AGENTS.md` est la **source d'autorité unique** de ce dépôt. Claude Code doit le lire
|
`AGENTS.md` est la **source d'autorité unique** de ce dépôt. Claude Code doit le lire
|
||||||
et le respecter intégralement avant toute modification.
|
et le respecter intégralement avant toute modification.
|
||||||
|
|
||||||
En cas de contradiction entre `CLAUDE.md` et `AGENTS.md`, **suivre `AGENTS.md`**.
|
|
||||||
|
|
||||||
Ce fichier est volontairement mince : il ne redéclare pas la doctrine (template, SSH,
|
|
||||||
pare-feu, cloud-init, handlers, Makefile, groupes…). Toute cette doctrine vit dans
|
|
||||||
`AGENTS.md`, pour éviter la duplication et les dérives qu'elle provoque.
|
|
||||||
|
|
||||||
## Les cinq règles absolues
|
|
||||||
|
|
||||||
1. **Un seul agent IA** travaille dans ce dépôt à la fois (Codex *ou* Claude, jamais les deux).
|
|
||||||
2. **`instance/inventories/*/hosts.yml` est un artefact GÉNÉRÉ** depuis le plan
|
|
||||||
(`instance/plan/`). On l'édite jamais à la main : on édite le plan, puis
|
|
||||||
`make instancier` → `make instancier-appliquer`.
|
|
||||||
3. **Aucun secret en clair** (mots de passe, clés privées, tokens, certificats) — Vault
|
|
||||||
ou stockage hors dépôt uniquement.
|
|
||||||
4. **Toute action destructive ou risquée exige une confirmation explicite** (ex.
|
|
||||||
`CONFIRMER=true`, `confirm_destructive_action: true`). Sans elle, refuser l'exécution.
|
|
||||||
5. **Rien n'est « prêt » sans validation.** Au minimum `--syntax-check` du playbook
|
|
||||||
touché, `ansible-lint` si disponible, et l'entrée `CHANGELOG.md` correspondante.
|
|
||||||
|
|
||||||
Le détail de chacune de ces règles, et tout le reste, est dans `AGENTS.md`.
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ git clone <url-de-Set-OPS> Set-OPS && cd Set-OPS
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. Choisir un modèle et créer ton instance
|
## 2. Choisir un modèle et créer ton instance
|
||||||
Le dépôt public fournit **un modèle générique : `socle`** — le socle souverain minimal
|
Le dépôt public fournit **un modèle générique : `socle`** — quatre VM, le minimum
|
||||||
(DNS interne, AC/PKI, edge TLS, relais courriel) sur lequel on ajoute des modules. Les
|
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
|
||||||
modèles **assemblés** par offre (`identite`, `observabilite`, `forge`, `collaboration`,
|
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`
|
`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)).
|
(cf. [`exemples/modeles/README.md`](exemples/modeles/README.md)).
|
||||||
|
|
@ -35,6 +37,11 @@ ln -s ../mon-instance instance # le moteur la trouve via ce lien
|
||||||
*(Alternative au symlink : `export SETOPS_INSTANCE=../mon-instance`.)*
|
*(Alternative au symlink : `export SETOPS_INSTANCE=../mon-instance`.)*
|
||||||
|
|
||||||
## 3. Renseigner ton instance (« tes couleurs »)
|
## 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/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/nomenclature.yml` → ton **supernet** (ex. `10.20.0.0/16`) ;
|
||||||
- `instance/plan/domaines.yml` → ton **domaine public** ;
|
- `instance/plan/domaines.yml` → ton **domaine public** ;
|
||||||
|
|
@ -47,14 +54,26 @@ 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...
|
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).
|
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** :
|
Tous tes secrets (token API + `vault_*`) vont dans **une voûte unique par instance** :
|
||||||
`instance/inventories/production/group_vars/all/vault.yml`, à partir du
|
`instance/inventories/production/group_vars/all/vault.yml`, à partir du
|
||||||
gabarit [`exemples/vault.exemple.yml`](exemples/vault.exemple.yml), chiffrée avec
|
gabarit [`exemples/vault.exemple.yml`](exemples/vault.exemple.yml), chiffrée avec
|
||||||
`ansible-vault`. Exporte ton mot de passe Vault, ex. :
|
`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 :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
# 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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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)
|
## 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
|
Une grappe vierge n'a aucun template. Crée une VM **Debian 13 vanille**, rends-la
|
||||||
joignable par Ansible, puis :
|
joignable par Ansible, puis :
|
||||||
|
|
@ -63,8 +82,10 @@ make preparer-modele # socle + durcissement + cloud-init + qemu-guest-age
|
||||||
make verifier-modele
|
make verifier-modele
|
||||||
make nettoyer-modele CONFIRMER=true
|
make nettoyer-modele CONFIRMER=true
|
||||||
```
|
```
|
||||||
Convertis ensuite la VM en **template Proxmox** nommé `modele-debian13` (le nom de
|
Convertis ensuite la VM en **template Proxmox**. Son nom doit être celui que `make config`
|
||||||
clone source par défaut). Détails et procédure : `docs/vm-lifecycle.md` et
|
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
|
||||||
`docs/procedure-template-debian13-proxmox.md`.
|
`docs/procedure-template-debian13-proxmox.md`.
|
||||||
|
|
||||||
## 6. Générer ton inventaire depuis le plan
|
## 6. Générer ton inventaire depuis le plan
|
||||||
|
|
@ -102,8 +123,8 @@ Les déploiements de groupe ne ciblent **que** les hôtes actifs.
|
||||||
make inventaire-verifier # registres + inventaire + garde-fous
|
make inventaire-verifier # registres + inventaire + garde-fous
|
||||||
make verifier # + ansible-lint + --syntax-check
|
make verifier # + ansible-lint + --syntax-check
|
||||||
```
|
```
|
||||||
> Ces cibles chargent l'inventaire complet : exporte d'abord ton mot de passe Vault
|
> Ces cibles chargent l'inventaire complet : pose d'abord la clé de ta voûte
|
||||||
> (étape 4, `ANSIBLE_VAULT_PASSWORD_FILE`), sinon Ansible s'arrête sur
|
> (étape 4), sinon Ansible s'arrête sur
|
||||||
> « Attempting to decrypt but no vault secrets found ».
|
> « 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
|
## 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 cinq :
|
On n'arrive pas avec un *sujet*, on arrive avec une **situation**. Il y en a six :
|
||||||
|
|
||||||
| Ta situation | Ta porte |
|
| Ta situation | Ta porte |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -36,18 +36,18 @@ Piliers de l'écosystème :
|
||||||
- **confiance** — autorité de certification interne (ACME) ;
|
- **confiance** — autorité de certification interne (ACME) ;
|
||||||
- **nommage et adressage** — DNS interne et nomenclature dérivable ;
|
- **nommage et adressage** — DNS interne et nomenclature dérivable ;
|
||||||
- **données** — bases relationnelles et cache, avec registre des connexions ;
|
- **données** — bases relationnelles et cache, avec registre des connexions ;
|
||||||
- **communication** — relais courriel interne ;
|
- **communication** — service de courriel souverain (boîtes LDAP, SMTP, IMAP, antispam, DKIM), et le relais des notifications système ;
|
||||||
- **observabilité et supervision** — métriques, journaux, tableaux de bord, supervision active ;
|
- **observabilité et supervision** — métriques, journaux, tableaux de bord, supervision active ;
|
||||||
- **applicatif** — services internes (forge, etc.) et couche web.
|
- **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). 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é.
|
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é.
|
||||||
|
|
||||||
Cadre et règles d'autorité : voir `AGENTS.md` (section « Mission et identité »).
|
Cadre et règles d'autorité : voir `AGENTS.md` (section « Mission et identité »).
|
||||||
|
|
||||||
## Le plan : on édite, l'inventaire se génère
|
## 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.
|
`Set-OPS` se pilote par un **plan**, pas par l'édition directe de l'inventaire.
|
||||||
`instance/inventories/production/hosts.yml` est **généré** depuis le plan — **ne pas l'éditer à la main**.
|
`instance/inventories/<inventaire>/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
|
éditer le PLAN → make instancier (revoir le diff) → make instancier-appliquer → make deployer
|
||||||
|
|
@ -80,16 +80,18 @@ playbooks/modeles_vm/debian13_proxmox_nettoyer.yml
|
||||||
|
|
||||||
## Principe
|
## Principe
|
||||||
|
|
||||||
Le template contient seulement le socle commun.
|
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).
|
||||||
|
|
||||||
Les services spécialisés seront installés ensuite sur les clones :
|
> **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
|
||||||
- NGINX ;
|
> rôle MariaDB, et **plus aucun rôle n'a besoin de Docker** depuis la réécriture native de
|
||||||
- PostgreSQL ;
|
> `serveur_collabora` — qui en était la dernière exception. Le conteneur n'est pas un
|
||||||
- MariaDB ;
|
> détail d'implémentation ici : c'est un choix fondateur (voir `roles/serveur_collabora/README.md`).
|
||||||
- Docker/Podman ;
|
|
||||||
- monitoring complet ;
|
|
||||||
- applications métier.
|
|
||||||
|
|
||||||
## Exploitation courante
|
## Exploitation courante
|
||||||
|
|
||||||
|
|
@ -135,7 +137,7 @@ Planifier une VM passe désormais par le **plan**, pas par l'édition de l'inven
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make instancier # génère + diff sémantique (que va-t-il changer ?)
|
make instancier # génère + diff sémantique (que va-t-il changer ?)
|
||||||
make instancier-appliquer # régénère instance/inventories/production/hosts.yml
|
make instancier-appliquer # régénère instance/inventories/<inventaire>/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).
|
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,15 +46,19 @@ après avoir vérifié l'accès SSH par clé et les privilèges sudo du compte `
|
||||||
## Vérification
|
## Vérification
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_verifier.yml
|
make verifier-modele
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nettoyage final avant conversion
|
## Nettoyage final avant conversion
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook -i instance/inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
make nettoyer-modele CONFIRMER=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 :
|
Ensuite :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,28 @@
|
||||||
|
|
||||||
> **Pour qui :** l'**agent IA** qui reprend le dépôt — et le mainteneur qui relit ce qu'on lui dit.
|
> **Pour qui :** l'**agent IA** qui reprend le dépôt — et le mainteneur qui relit ce qu'on lui dit.
|
||||||
|
|
||||||
## État du dépôt
|
> ## ⚠️ 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)*
|
||||||
|
|
||||||
`Set-OPS` est le moteur Ansible global d’exploitation d’écosystèmes numériques souverains.
|
`Set-OPS` est le moteur Ansible global d’exploitation d’écosystèmes numériques souverains.
|
||||||
|
|
||||||
|
|
@ -67,7 +88,12 @@ Il ne doit pas contenir :
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Incident récent à corriger
|
## 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.
|
||||||
|
|
||||||
Le playbook :
|
Le playbook :
|
||||||
|
|
||||||
|
|
|
||||||
117
docs/acces-administration.md
Normal file
117
docs/acces-administration.md
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
# 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`.
|
> **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
|
Set-OPS définit et construit l'écosystème numérique souverain. Il se
|
||||||
pilote par un **plan** : l'inventaire Ansible (`instance/inventories/production/hosts.yml`)
|
pilote par un **plan** : l'inventaire Ansible (`instance/inventories/<inventaire>/hosts.yml`)
|
||||||
est **généré** depuis le plan, pas édité à la main.
|
est **généré** depuis le plan, pas édité à la main.
|
||||||
|
|
||||||
- **Par où commencer + catalogue des mécanismes transverses : `docs/carte-set-ops.md`.**
|
- **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 :
|
Les groupes opérationnels de l'inventaire doivent correspondre à un playbook homonyme :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
instance/inventories/production/hosts.yml
|
instance/inventories/<inventaire>/hosts.yml
|
||||||
serveur_debian
|
serveur_debian
|
||||||
|
|
||||||
playbooks/groupes/serveur_debian.yml
|
playbooks/groupes/serveur_debian.yml
|
||||||
|
|
@ -60,9 +60,11 @@ Le cycle de vie des VM est documenté dans :
|
||||||
docs/vm-lifecycle.md
|
docs/vm-lifecycle.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## Intégrations futures
|
## Intégrations transversales
|
||||||
|
|
||||||
Les intégrations transversales des VM sont documentées dans :
|
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 :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
docs/integrations-vm.md
|
docs/integrations-vm.md
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,37 @@ pré-commit). Une preuve **SAUTÉE** (⚪) n'est pas un échec.
|
||||||
|
|
||||||
### Prérequis Vault
|
### Prérequis Vault
|
||||||
|
|
||||||
La preuve `P15` (inventaire Ansible complet, `ansible-inventory --list`) déchiffre le
|
La preuve **`P16`** (inventaire Ansible complet, `ansible-inventory --list`) déchiffre le
|
||||||
`group_vars` de l'instance. Sans `ANSIBLE_VAULT_PASSWORD_FILE`, elle est **automatiquement
|
`group_vars` de l'instance. Sans la clé de la voûte, elle est **automatiquement sautée** (⚪)
|
||||||
sautée** (⚪) avec la mention du prérequis — le reste du harnais reste vert, car les
|
avec la mention du prérequis — le reste du harnais reste vert, car les validateurs Python
|
||||||
validateurs Python lisent le plan et l'inventaire directement, sans secret. Pour l'inclure :
|
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) :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
python3 scripts/voutes.py etat # la clé de cette instance est-elle là ?
|
||||||
make prouver
|
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 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 |
|
| # | Preuve | Ce qu'elle établit |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| P01 | Lint (`ansible-lint`) | 0 violation, profil `production`. |
|
| 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.
|
le côté humain **est** la preuve de l'affirmation « exploitable sans IA » (cf.
|
||||||
`protocole-operateur-independant.md`).
|
`protocole-operateur-independant.md`).
|
||||||
|
|
||||||
**85 gestes** sur **21 unités** · **19** en « casse-répare »
|
**90 gestes** sur **22 unités** · **20** en « casse-répare »
|
||||||
· **5** doublés d'un garde-fou machine (colonne *Preuve auto*).
|
· **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
|
> **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 | — |
|
| 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 | — |
|
| 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](Autorisation-et-RBAC) · § À toi de jouer.*
|
*Source : [Autorisation & RBAC](../../wiki/Autorisation-et-RBAC.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Bases de données
|
## 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 | — |
|
| 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 | — |
|
| 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](Bases-de-données) · § À toi de jouer.*
|
*Source : [Bases de données](../../wiki/Bases-de-donn%C3%A9es.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Cache
|
## 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 | — |
|
| 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 | — |
|
| 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](Cache) · § À toi de jouer.*
|
*Source : [Cache](../../wiki/Cache.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Courriel (SMTP / IMAP)
|
## Courriel (SMTP / IMAP)
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 1 | Envoie via la soumission `:587` | (client authentifié) : « commande » 235 Authentication successful puis 250 queued = ① en action. | 👁 observe | — |
|
| 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 | — |
|
||||||
| 2 | Lis la boîte | (le MDA) : doveadm search -u testmail mailbox INBOX all \| wc -l sur infra-mail-01. | 👁 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 | — |
|
| 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)](Courriel) · § À toi de jouer.*
|
*Source : [Courriel (SMTP / IMAP)](../../wiki/Courriel.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## DNS & résolution de noms
|
## DNS & résolution de noms
|
||||||
|
|
||||||
|
|
@ -68,20 +68,32 @@ 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 | — |
|
| 1 | Le plancher, sans DNS | Sur un nœud : « commande » | 👁 observe | — |
|
||||||
| 2 | Interroge l'autoritatif | Demande à PowerDNS directement : « 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 | — |
|
| 3 | Vois les couches | Compare getent hosts (plancher) et dig (DNS) : deux chemins, même IP. | 👁 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 | — |
|
| 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 | — |
|
||||||
|
|
||||||
*Source : [DNS & résolution de noms](DNS-et-résolution) · § À toi de jouer.*
|
*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.*
|
||||||
|
|
||||||
## Identité & SSO
|
## Identité & SSO
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 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 | — |
|
| 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 | — |
|
||||||
| 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 | — |
|
| 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 | — |
|
| 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 | — |
|
| 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](Identité-et-SSO) · § À toi de jouer.*
|
*Source : [Identité & SSO](../../wiki/Identit%C3%A9-et-SSO.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Infra as Code & idempotence
|
## Infra as Code & idempotence
|
||||||
|
|
||||||
|
|
@ -92,7 +104,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 | — |
|
| 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 | — |
|
| 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](Infra-as-Code-et-idempotence) · § À toi de jouer.*
|
*Source : [Infra as Code & idempotence](../../wiki/Infra-as-Code-et-idempotence.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## La preuve — prouver, pas affirmer
|
## La preuve — prouver, pas affirmer
|
||||||
|
|
||||||
|
|
@ -104,7 +116,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 | — |
|
| 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 | — |
|
| 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](La-preuve) · § À toi de jouer.*
|
*Source : [La preuve — prouver, pas affirmer](../../wiki/La-preuve.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Le GUI (console d'exploitation)
|
## Le GUI (console d'exploitation)
|
||||||
|
|
||||||
|
|
@ -117,7 +129,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 | — |
|
| 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 | — |
|
| 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)](Le-GUI-console-d-exploitation) · § À toi de jouer.*
|
*Source : [Le GUI (console d'exploitation)](../../wiki/Le-GUI-console-d-exploitation.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Le plan & l'adressage dérivé
|
## Le plan & l'adressage dérivé
|
||||||
|
|
||||||
|
|
@ -129,7 +141,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 | — |
|
| 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** |
|
| 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é](Le-plan-et-l-adressage-dérivé) · § À toi de jouer.*
|
*Source : [Le plan & l'adressage dérivé](../../wiki/Le-plan-et-l-adressage-d%C3%A9riv%C3%A9.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Le réseau des tenants — du câble au VRF
|
## Le réseau des tenants — du câble au VRF
|
||||||
|
|
||||||
|
|
@ -138,9 +150,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 | — |
|
| 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 | — |
|
| 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 | — |
|
| 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 (référence technique), Le plan & l'adressage dérivé, Multi-instance & f… | 👁 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 | — |
|
||||||
|
|
||||||
*Source : [Le réseau des tenants — du câble au VRF](Le-réseau-des-tenants) · § À toi de jouer.*
|
*Source : [Le réseau des tenants — du câble au VRF](../../wiki/Le-r%C3%A9seau-des-tenants.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Liaisons (bindings)
|
## Liaisons (bindings)
|
||||||
|
|
||||||
|
|
@ -148,10 +160,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 | — |
|
| 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 | — |
|
| 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 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 | — |
|
| 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 | — |
|
||||||
| 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 | — |
|
| 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)](Liaisons-bindings) · § À toi de jouer.*
|
*Source : [Liaisons (bindings)](../../wiki/Liaisons-bindings.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Multi-instance & fédération
|
## Multi-instance & fédération
|
||||||
|
|
||||||
|
|
@ -164,7 +176,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** |
|
| 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 | — |
|
| 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](Multi-instance-et-fédération) · § À toi de jouer.*
|
*Source : [Multi-instance & fédération](../../wiki/Multi-instance-et-f%C3%A9d%C3%A9ration.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Métriques & journaux
|
## Métriques & journaux
|
||||||
|
|
||||||
|
|
@ -172,10 +184,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 | — |
|
| 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 | — |
|
| 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://grafana.lab.chezlepro.internal) — métriques *et* logs au même endroit. | 👁 observe | — |
|
| 3 | Ouvre Grafana | (https://observatoire.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 | — |
|
| 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](Métriques-et-journaux) · § À toi de jouer.*
|
*Source : [Métriques & journaux](../../wiki/M%C3%A9triques-et-journaux.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## PKI & confiance
|
## PKI & confiance
|
||||||
|
|
||||||
|
|
@ -186,38 +198,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 | — |
|
| 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 | — |
|
| 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](PKI-et-confiance) · § À toi de jouer.*
|
*Source : [PKI & confiance](../../wiki/PKI-et-confiance.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Reverse-proxy & TLS
|
## Reverse-proxy & TLS
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 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 | — |
|
| 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 192.168.15.21:443 -servername grafana.lab… \| openssl x509 -noout -text \| grep -A1 'Subject Alternative'. | 👁 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 | — |
|
||||||
| 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 | — |
|
| 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](Reverse-proxy-et-TLS) · § À toi de jouer.*
|
*Source : [Reverse-proxy & TLS](../../wiki/Reverse-proxy-et-TLS.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Sauvegardes (3-2-1)
|
## Sauvegardes (3-2-1)
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | 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 | — |
|
| 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) : « 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 | — |
|
||||||
| 3 | Restaure — le vrai test | Restaure dans un dossier temporaire et compare : « commande » *(sur infra-pki-01 ; ailleurs, compare le dump correspondant.)* | 👁 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 | — |
|
| 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)](Sauvegardes) · § À toi de jouer.*
|
*Source : [Sauvegardes (3-2-1)](../../wiki/Sauvegardes.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Supervision & impact
|
## Supervision & impact
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 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 | — |
|
| 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 | — |
|
||||||
| 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 | — |
|
| 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 | — |
|
| 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](Supervision-et-impact) · § À toi de jouer.*
|
*Source : [Supervision & impact](../../wiki/Supervision-et-impact.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Sécurité & durcissement
|
## Sécurité & durcissement
|
||||||
|
|
||||||
|
|
@ -227,7 +239,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 | — |
|
| 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 | — |
|
| 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](Sécurité-et-durcissement) · § À toi de jouer.*
|
*Source : [Sécurité & durcissement](../../wiki/S%C3%A9curit%C3%A9-et-durcissement.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Virtualisation & clonage
|
## Virtualisation & clonage
|
||||||
|
|
||||||
|
|
@ -238,14 +250,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 | — |
|
| 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 | — |
|
| 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](Virtualisation-et-clonage) · § À toi de jouer.*
|
*Source : [Virtualisation & clonage](../../wiki/Virtualisation-et-clonage.md) · § À toi de jouer.*
|
||||||
|
|
||||||
## Vérifier le déployé — quand la preuve statique ne suffit plus
|
## Vérifier le déployé — quand la preuve statique ne suffit plus
|
||||||
|
|
||||||
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
| # | Ce qu'on éprouve | Le geste (avec l'attendu) | Type | Preuve auto |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 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 | — |
|
| 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 | — |
|
||||||
| 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 | — |
|
| 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 | — |
|
| 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](Vérifier-le-déployé) · § À toi de jouer.*
|
*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.*
|
||||||
|
|
|
||||||
78
docs/audit/preuve-2026-08-23.md
Normal file
78
docs/audit/preuve-2026-08-23.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# 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._
|
||||||
78
docs/audit/preuve-2026-08-24.md
Normal file
78
docs/audit/preuve-2026-08-24.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# 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._
|
||||||
83
docs/audit/preuve-2026-08-25.md
Normal file
83
docs/audit/preuve-2026-08-25.md
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# 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._
|
||||||
85
docs/audit/preuve-2026-08-26.md
Normal file
85
docs/audit/preuve-2026-08-26.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
# 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._
|
||||||
88
docs/audit/preuve-2026-08-27.md
Normal file
88
docs/audit/preuve-2026-08-27.md
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# 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._
|
||||||
90
docs/audit/preuve-2026-08-28.md
Normal file
90
docs/audit/preuve-2026-08-28.md
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
# 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._
|
||||||
91
docs/audit/preuve-2026-08-30.md
Normal file
91
docs/audit/preuve-2026-08-30.md
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
# 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._
|
||||||
91
docs/audit/preuve-2026-08-31.md
Normal file
91
docs/audit/preuve-2026-08-31.md
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
# 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._
|
||||||
92
docs/audit/preuve-2026-09-01.md
Normal file
92
docs/audit/preuve-2026-09-01.md
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
# 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._
|
||||||
92
docs/audit/preuve-2026-09-02.md
Normal file
92
docs/audit/preuve-2026-09-02.md
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
# 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._
|
||||||
93
docs/audit/preuve-2026-09-03.md
Normal file
93
docs/audit/preuve-2026-09-03.md
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
# 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._
|
||||||
95
docs/audit/preuve-2026-09-05.md
Normal file
95
docs/audit/preuve-2026-09-05.md
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
# 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._
|
||||||
93
docs/audit/preuve-2026-09-06.md
Normal file
93
docs/audit/preuve-2026-09-06.md
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
# 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._
|
||||||
98
docs/audit/preuve-2026-09-08.md
Normal file
98
docs/audit/preuve-2026-09-08.md
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
# 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._
|
||||||
100
docs/audit/preuve-2026-09-09.md
Normal file
100
docs/audit/preuve-2026-09-09.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# 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._
|
||||||
103
docs/audit/preuve-2026-09-10.md
Normal file
103
docs/audit/preuve-2026-09-10.md
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
# 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._
|
||||||
104
docs/audit/preuve-2026-09-11.md
Normal file
104
docs/audit/preuve-2026-09-11.md
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
# 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._
|
||||||
106
docs/audit/preuve-2026-09-12.md
Normal file
106
docs/audit/preuve-2026-09-12.md
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# 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._
|
||||||
111
docs/audit/preuve-2026-09-13.md
Normal file
111
docs/audit/preuve-2026-09-13.md
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
# 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._
|
||||||
114
docs/audit/preuve-2026-09-14.md
Normal file
114
docs/audit/preuve-2026-09-14.md
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
# 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._
|
||||||
115
docs/audit/preuve-2026-09-15.md
Normal file
115
docs/audit/preuve-2026-09-15.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# 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._
|
||||||
118
docs/audit/preuve-2026-09-16.md
Normal file
118
docs/audit/preuve-2026-09-16.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
# 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._
|
||||||
118
docs/audit/preuve-2026-09-17.md
Normal file
118
docs/audit/preuve-2026-09-17.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
# 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._
|
||||||
120
docs/audit/preuve-2026-09-30.md
Normal file
120
docs/audit/preuve-2026-09-30.md
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
# 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._
|
||||||
119
docs/audit/preuve-2026-10-01.md
Normal file
119
docs/audit/preuve-2026-10-01.md
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
# 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._
|
||||||
120
docs/audit/preuve-2026-10-03.md
Normal file
120
docs/audit/preuve-2026-10-03.md
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
# 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._
|
||||||
130
docs/audit/preuve-2026-10-09.md
Normal file
130
docs/audit/preuve-2026-10-09.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Preuve de conformite — Set-OPS — 2026-10-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** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
|
||||||
|
- **Verdict** : ✅ CONFORME (94 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 | 3 instance(s) verifiee(s) — 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, 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 : 35 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 : 4 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 | 78 scripts expliques et atteignables, 151 cibles make documentees, 69 roles avec README. |
|
||||||
|
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (148 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 | 50 document(s) declarent leur lecteur (47 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 (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` (11 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 : 74 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||||
|
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 3 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal). |
|
||||||
|
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 201 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 102 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 304 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 (94 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 `e9215a2` (publie le 2026-10-07). |
|
||||||
|
| 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 (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 | 27 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 ». |
|
||||||
|
| 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 | 189 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 | 4 ecosysteme(s) (OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, SITE-Chezlepro) : pattes, edges, certificats, rechargements, jumeaux d'amorcage, sorties du |
|
||||||
|
| 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, 144 etapes, 150 cibles documentees : chacune portee par un assistant ou exemptee avec son motif. |
|
||||||
|
| P84 | La fiche du site dit exactement ce que chaque locataire porte | — | ✅ OK | 2 fiche(s) de site : chacune dit exactement ce que son locataire porte (copies, racine, inventaire genere). |
|
||||||
|
| P85 | La face reseau du locataire dit exactement ce que le site en tire | — | ✅ OK | 2 face(s) reseau : chacune dit exactement ce que le site en tire (decouverte, devis Proxmox, frontiere, DNS public, sauvegarde). |
|
||||||
|
| P86 | Machine par machine, le locataire et Proxmox admettent les memes entrees | — | ✅ OK | 2 locataire(s) : machine par machine et port par port, nftables et Proxmox admettent les memes sources. |
|
||||||
|
| P87 | La frontiere designe chaque locataire par les identites qu'il publie | — | ✅ OK | 2 locataire(s) : la frontiere les designe par les identites qu'ils publient (supernet, administration, tunnel, groupes, routes, sortie). |
|
||||||
|
| P88 | Ce qui entre depuis l'Internet chez un locataire, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : regles WAN, redirections et tunnel disent exactement ce que leur face reseau ouvre a tous. |
|
||||||
|
| P89 | Ce que l'administration atteint chez un locataire, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : par la gestion, le VPN et le tunnel, l'administration atteint exactement ce que leur face reseau lui ouvre. |
|
||||||
|
| P90 | Ce qui sort d'un locataire vers l'Internet, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : la frontiere laisse sortir exactement ce que leur face reseau publie. |
|
||||||
|
| P91 | La fiche du site est deposee a jour, et l'inventaire se genere sans le site | — | ✅ OK | 2 locataire(s) : fiche du site deposee a jour, et inventaire genere sans le site identique a l'octet pres. |
|
||||||
|
| P92 | Chaque locataire a publie sa face reseau a jour | — | ✅ OK | 2 locataire(s) : face reseau publiee, a jour. |
|
||||||
|
| P93 | La face publie, machine par machine, les parametres de clonage de l'inventaire | — | ✅ OK | 26 machine(s) : la face publie exactement les parametres de clonage de l'inventaire. |
|
||||||
|
| P94 | Creer, raser et placer un locataire par sa face visent les memes machines | — | ✅ OK | 2 locataire(s) : creer, raser et placer visent par la face les memes machines que par l'instance montee. |
|
||||||
|
|
||||||
|
## 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-09._
|
||||||
130
docs/audit/preuve-2026-10-11.md
Normal file
130
docs/audit/preuve-2026-10-11.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Preuve de conformite — Set-OPS — 2026-10-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** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
|
||||||
|
- **Verdict** : ✅ CONFORME (94 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 | 3 instance(s) verifiee(s) — 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, 122 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 : 35 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 : 4 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 | 78 scripts expliques et atteignables, 151 cibles make documentees, 69 roles avec README. |
|
||||||
|
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 37 exigence(s) de role, toutes satisfaites (148 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 | 50 document(s) declarent leur lecteur (48 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 (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` (11 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 : 74 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||||
|
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 3 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal). |
|
||||||
|
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 9 machine(s) du plan retrouvees, 174 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 102 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 (140 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 277 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 (94 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 `e9215a2` (publie le 2026-10-07). |
|
||||||
|
| 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 (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 | 27 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 ». |
|
||||||
|
| 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 | 189 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 | 4 ecosysteme(s) (OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, SITE-Chezlepro) : pattes, edges, certificats, rechargements, jumeaux d'amorcage, sorties du |
|
||||||
|
| 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, 145 etapes, 150 cibles documentees : chacune portee par un assistant ou exemptee avec son motif. |
|
||||||
|
| P84 | La fiche du site dit exactement ce que chaque locataire porte | — | ✅ OK | 2 fiche(s) de site : chacune dit exactement ce que son locataire porte (copies, racine, inventaire genere). |
|
||||||
|
| P85 | La face reseau du locataire dit exactement ce que le site en tire | — | ✅ OK | 2 face(s) reseau : chacune dit exactement ce que le site en tire (decouverte, devis Proxmox, frontiere, DNS public, sauvegarde). |
|
||||||
|
| P86 | Machine par machine, le locataire et Proxmox admettent les memes entrees | — | ✅ OK | 2 locataire(s) : machine par machine et port par port, nftables et Proxmox admettent les memes sources. |
|
||||||
|
| P87 | La frontiere designe chaque locataire par les identites qu'il publie | — | ✅ OK | 2 locataire(s) : la frontiere les designe par les identites qu'ils publient (supernet, administration, tunnel, groupes, routes, sortie). |
|
||||||
|
| P88 | Ce qui entre depuis l'Internet chez un locataire, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : regles WAN, redirections et tunnel disent exactement ce que leur face reseau ouvre a tous. |
|
||||||
|
| P89 | Ce que l'administration atteint chez un locataire, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : par la gestion, le VPN et le tunnel, l'administration atteint exactement ce que leur face reseau lui ouvre. |
|
||||||
|
| P90 | Ce qui sort d'un locataire vers l'Internet, la frontiere le tient de lui | — | ✅ OK | 2 locataire(s) : la frontiere laisse sortir exactement ce que leur face reseau publie. |
|
||||||
|
| P91 | La fiche du site est deposee a jour, et l'inventaire se genere sans le site | — | ✅ OK | 2 locataire(s) : fiche du site deposee a jour, et inventaire genere sans le site identique a l'octet pres. |
|
||||||
|
| P92 | Chaque locataire a publie sa face reseau a jour | — | ✅ OK | 2 locataire(s) : face reseau publiee, a jour. |
|
||||||
|
| P93 | La face publie, machine par machine, les parametres de clonage de l'inventaire | — | ✅ OK | 26 machine(s) : la face publie exactement les parametres de clonage de l'inventaire. |
|
||||||
|
| P94 | Creer, raser et placer un locataire par sa face visent les memes machines | — | ✅ OK | 2 locataire(s) : creer, raser et placer visent par la face les memes machines que par l'instance montee. |
|
||||||
|
|
||||||
|
## 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-11._
|
||||||
|
|
@ -94,7 +94,7 @@ Binaires, vérifiables, définis **avant** le début. On ne les renégocie pas e
|
||||||
| # | Critère | Vérification |
|
| # | Critère | Vérification |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **R1** | Instance créée depuis `socle`, plan renseigné à ses valeurs | `make inventaire-verifier` → rc=0 |
|
| **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 `modele-debian13` visible dans Proxmox |
|
| **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) |
|
||||||
| **R3** | Au moins une VM créée depuis le plan | `make creer-vm HOTE=…` OK ; VM jointe par Ansible (`ansible -m ping`) |
|
| **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 |
|
| **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 |
|
| **R5** | Validation globale du dépôt passée par l'opérateur | `make verifier` → rc=0 |
|
||||||
|
|
|
||||||
546
docs/audit/schema-plan.json
Normal file
546
docs/audit/schema-plan.json
Normal file
|
|
@ -0,0 +1,546 @@
|
||||||
|
{
|
||||||
|
"$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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
docs/audit/wiki-publie.yml
Normal file
5
docs/audit/wiki-publie.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
# 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,13 +98,23 @@ 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 |
|
| `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 |
|
| `ldap-direct` | protocole non-OIDC lié à LDAP | dovecot, postfix |
|
||||||
| `interne-sans-auth` | interface joignable **dans** le tenant sans authentification, non exposée | prometheus, loki |
|
| `interne-sans-auth` | interface joignable **dans** le tenant sans authentification, non exposée | prometheus, loki |
|
||||||
| `sans-auth-humaine` | aucun point d'authentification humaine | 12 |
|
| `sans-auth-humaine` | aucun point d'authentification humaine | 23 |
|
||||||
|
|
||||||
La preuve refuse **l'oubli et le mensonge** : un rôle sans déclaration, une portée
|
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
|
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
|
natif` sans réglage `<rôle>_connexion_locale` **défini dans `defaults`**, et une
|
||||||
déclaration que le code contredit.
|
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
|
> **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`,
|
> « 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
|
> suffisait alors à valider une déclaration `ldap-direct` mensongère. La preuve exige
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ 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
|
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.
|
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
|
## 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**.
|
C'est la décision principale, et elle **contredit délibérément la doctrine du dépôt**.
|
||||||
|
|
@ -170,10 +176,22 @@ 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
|
opération sauf le changement lui-même (§3). C'est le premier geste de la reprise, et il
|
||||||
n'est pas optionnel.
|
n'est pas optionnel.
|
||||||
|
|
||||||
> Le mot de passe de la voûte est lu depuis `ANSIBLE_VAULT_PASSWORD_FILE`
|
> **La clé de la voûte — une voûte, une clé (depuis le 2026-08-28).** Ce paragraphe a
|
||||||
> (`~/.config/setops-vault-pass` par défaut). **Sans ce fichier, rien de ce qui suit n'est
|
> longtemps désigné un `ANSIBLE_VAULT_PASSWORD_FILE` unique, `~/.config/setops-vault-pass`.
|
||||||
> possible** — c'est la clé de voûte au sens propre, et la première chose à sauvegarder
|
> Ce n'est plus le mécanisme : un seul mot de passe ouvrait alors *toutes* les voûtes de la
|
||||||
> hors de la machine.
|
> 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)).
|
||||||
|
|
||||||
### 6.2 Deux consoles Keycloak, et la racine mène à la mauvaise
|
### 6.2 Deux consoles Keycloak, et la racine mène à la mauvaise
|
||||||
|
|
||||||
|
|
@ -375,5 +393,34 @@ 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
|
Set-OPS accorde l'entrée et le niveau ; il ne réimplémente pas le modèle de chaque
|
||||||
application.
|
application.
|
||||||
|
|
||||||
**Rien n'est construit.** Ce document fixe la direction ; le rôle d'amorçage, les
|
**Ce qui est construit, et ce qui ne l'est pas — mesuré le 2026-09-06.** Ce document s'est
|
||||||
`meta/acces.yml` et la preuve restent à écrire.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,14 @@
|
||||||
|
|
||||||
> **Pour qui :** le **mainteneur** qui ajoute une relation service → service.
|
> **Pour qui :** le **mainteneur** qui ajoute une relation service → service.
|
||||||
|
|
||||||
> Note de conception, 2026-07-02. Décision d'architecture à valider avant implémentation.
|
> Note de conception, 2026-07-02. Direction retenue : **liens déclarés côté application (le
|
||||||
> Direction retenue : **liens déclarés côté application (le consommateur déclare ses besoins)**.
|
> 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.
|
||||||
|
|
||||||
## 1. Problème
|
## 1. Problème
|
||||||
|
|
||||||
|
|
@ -19,9 +25,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** —
|
éditer des variables à la main. **Cela échoue à l'épreuve de la portabilité multi-tenant** —
|
||||||
pourtant au cœur de la mission.
|
pourtant au cœur de la mission.
|
||||||
|
|
||||||
## 2. Ce qui existe déjà (partiel)
|
## 2. Ce qui existait déjà, en juillet 2026 (état de départ)
|
||||||
|
|
||||||
Le concept est à moitié né, mais éclaté et non câblé :
|
Le concept était à moitié né, éclaté et non câblé :
|
||||||
|
|
||||||
- **Bases** (`plan/bases-donnees.yml`) : `consommateur` + `portee` (groupe|hote|application) +
|
- **Bases** (`plan/bases-donnees.yml`) : `consommateur` + `portee` (groupe|hote|application) +
|
||||||
`secret` (Vault) + `usage` + `proprietaire`. Un vrai binding base → consommateur, mais vide et
|
`secret` (Vault) + `usage` + `proprietaire`. Un vrai binding base → consommateur, mais vide et
|
||||||
|
|
@ -31,6 +37,12 @@ Le concept est à moitié né, mais éclaté et non câblé :
|
||||||
- **Applications** (`plan/applications.yml`) : seulement `groupe` + `hote`. Aucun lien app → app.
|
- **Applications** (`plan/applications.yml`) : seulement `groupe` + `hote`. Aucun lien app → app.
|
||||||
- **Rôles** : portent déjà une méta auto-descriptive (`meta/empreinte.yml`).
|
- **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é
|
## 3. Modèle proposé
|
||||||
|
|
||||||
**Un binding est une arête typée et dirigée**, d'un **consommateur** (une application) vers une
|
**Un binding est une arête typée et dirigée**, d'un **consommateur** (une application) vers une
|
||||||
|
|
@ -132,7 +144,7 @@ Pour chaque application portant des `liens`, pour chaque lien `{vers, role}` :
|
||||||
applications:
|
applications:
|
||||||
forgejo:
|
forgejo:
|
||||||
groupe: serveur_forgejo
|
groupe: serveur_forgejo
|
||||||
hote: git-01
|
hote: forge-01
|
||||||
liens:
|
liens:
|
||||||
- vers: git.chezlepro.ca # domaine public (domaines.yml)
|
- vers: git.chezlepro.ca # domaine public (domaines.yml)
|
||||||
role: exposition
|
role: exposition
|
||||||
|
|
@ -149,9 +161,12 @@ 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
|
> 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`.
|
> celui des liens app→app. Il **ne faut pas le dupliquer** dans `instancier`.
|
||||||
|
|
||||||
Réalité : quatre rôles (`serveur_postgresql`, `serveur_forgejo`, `serveur_keycloak`,
|
Réalité : les rôles **consommateurs** résolvent leur base **au déploiement, dans le rôle**,
|
||||||
`serveur_icinga`) résolvent leur base **au déploiement, dans le rôle**, depuis le registre
|
depuis le registre `plan/bases-donnees.yml`. Ils sont **cinq** aujourd'hui — `serveur_forgejo`,
|
||||||
`plan/bases-donnees.yml` :
|
`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.* :
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- include_vars: bases-donnees.yml # charge le registre
|
- include_vars: bases-donnees.yml # charge le registre
|
||||||
|
|
@ -175,9 +190,12 @@ 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é
|
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.
|
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
|
~~Reste comme valeur réelle (non bloquant) : **factoriser** le bloc de résolution copié-collé
|
||||||
les 4 rôles en un include partagé (ex. `roles/_resoudre_base/`). À faire **avec l'épreuve de
|
dans les 4 rôles en un include partagé.~~ **Fait.** Le rôle utilitaire
|
||||||
Keycloak** (qui utilise ce mécanisme), pour valider le DRY en le déployant. Cf. §9.
|
[`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.
|
||||||
|
|
||||||
## 6. Règles de validation
|
## 6. Règles de validation
|
||||||
|
|
||||||
|
|
@ -200,27 +218,39 @@ Keycloak** (qui utilise ce mécanisme), pour valider le DRY en le déployant. Cf
|
||||||
d'inventaire), très parlante pour la **démo de portabilité** (déplacer un nœud, voir les
|
d'inventaire), très parlante pour la **démo de portabilité** (déplacer un nœud, voir les
|
||||||
arêtes suivre).
|
arêtes suivre).
|
||||||
|
|
||||||
## 8. Preuve de migration (les 3 liens mail)
|
## 8. Preuve de migration (les liens mail) — faite, mais pas comme prévu
|
||||||
|
|
||||||
Premier cas concret, à faire en régression (les mêmes variables doivent être générées) :
|
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.
|
||||||
|
|
||||||
| Avant (codé en dur) | Après (déclaratif) |
|
| Avant (codé en dur) | Après | Par quel mécanisme |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| `serveur_postfix_mailstore_hote` (group_var) | `postfix.liens: [mailstore → dovecot]` |
|
| `serveur_postfix_mailstore_hote` (group_var) | `postfix.liens: [mailstore → dovecot]` | **lien** ✅ (2026-07-03) |
|
||||||
| `serveur_postfix_rspamd_milter` (group_var) | `postfix.liens: [milter → rspamd]` |
|
| `serveur_postfix_rspamd_milter` (group_var) | `postfix.liens: [milter → rspamd]` | **lien** ✅ (2026-07-03) |
|
||||||
| `id-ldap-01` (defaults) | `postfix.liens: [annuaire → openldap]`, `dovecot.liens: [annuaire → openldap]` |
|
| `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.
|
||||||
|
|
||||||
## 9. Phasage
|
## 9. Phasage
|
||||||
|
|
||||||
- **Phase 0** : cette note + décision. ✅ (direction : liens côté app pour app→app)
|
- **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 ;
|
- **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)
|
migrer les liens mail ; régression DIFF VIDE. ✅ (2026-07-03 : `mailstore` + `milter` migrés)
|
||||||
- **Phase 2** : bases. ✅ **déjà en place** — binding **côté base** (registre + résolution en
|
- **Phase 2** : bases. ✅ **complète** — binding **côté base** (registre + résolution en
|
||||||
rôle, 4 rôles), cf. §5. Ne PAS dupliquer dans instancier. **Reste (reporté à la session
|
rôle), cf. §5. Ne PAS dupliquer dans instancier. La factorisation qui restait est faite :
|
||||||
Keycloak)** : factoriser le bloc de résolution copié-collé en un include partagé (DRY),
|
`roles/resoudre_base/`, inclus par cinq rôles consommateurs.
|
||||||
validé en déployant Keycloak.
|
- **Phase 3** : exposition / domaines. ✅ — **6 applications** déclarent un `expose`
|
||||||
- **Phase 3** : exposition / domaines (vhosts nginx dérivés des liens ; machinerie `expose` /
|
(keycloak, forgejo, grafana, oauth2_proxy, nextcloud, collabora), et `serveur_nginx` en
|
||||||
`expositions_des_applications` déjà présente).
|
dérive vhost, SAN et enregistrement A (`expositions_des_applications`). `make expositions-plan`
|
||||||
|
vérifie ensuite que chacune répond réellement.
|
||||||
- **Phase 4** : vue GUI des liens + graphe. 🟡 **éditeur fait** (2026-07-22, cf. §7) ;
|
- **Phase 4** : vue GUI des liens + graphe. 🟡 **éditeur fait** (2026-07-22, cf. §7) ;
|
||||||
**graphe des liens reste à faire**.
|
**graphe des liens reste à faire**.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,29 +10,46 @@ 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
|
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.
|
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é** (34 docs + 15 pièces d'audit + 23 unités de wiki, et un
|
Le dépôt est **déjà bien documenté**. Le manque n'était pas la doc du *modèle*, mais (a) un
|
||||||
README pour chacun des 54 rôles). Le manque n'était pas la doc du *modèle*,
|
index « par où commencer » et (b) une carte des *mécanismes* (dispersés dans le code + les
|
||||||
mais (a) un index « par où commencer » et (b) une carte des *mécanismes* (dispersés dans le
|
README de rôles). Cette page comble ces deux trous.
|
||||||
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 | 53 | `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 |
|
||||||
|
|
||||||
## 1. À lire d'abord (dans l'ordre)
|
## 1. À lire d'abord (dans l'ordre)
|
||||||
|
|
||||||
| Sujet | Documents |
|
| Sujet | Documents |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Autorité / gouvernance** | `AGENTS.md` (source d'autorité), `CLAUDE.md`, `docs/MISE-A-JOUR-CODEX-CLAUDE.md` |
|
| **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) |
|
| **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) |
|
||||||
| **Services, maturité, dette** | `docs/catalogue-services.md` (**la carte de maturité + la cruft y sont déjà**) |
|
| **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` |
|
| **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` |
|
| **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` |
|
| **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`) |
|
| **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`). 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`) **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é ?* |
|
||||||
| **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` |
|
| **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` — **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 |
|
| **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). **Non éprouvé** : spike avant génération |
|
| **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 8 étapes, machine à états, gardes ; le receveur se construit **avant** tout gel |
|
| **Migration de tenant** | `docs/migration-tenant.md` — recette en **neuf étapes (0 à 8)**, 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` |
|
| **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/` — 21 unités (+ `_Sidebar`) publiées par `make wiki-publier` ; entrer par `wiki/Home.md` |
|
| **Pédagogie (le wiki)** | `wiki/` — les unités (comptées ci-dessus) 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` |
|
| **Vision / positionnement** | `docs/ecosysteme-chezlepro.md`, `docs/positionnement.md`, `docs/pouvoirs-set-ops.md` |
|
||||||
|
|
||||||
## 2. Les mécanismes transverses (et OÙ ils vivent)
|
## 2. Les mécanismes transverses (et OÙ ils vivent)
|
||||||
|
|
@ -42,25 +59,29 @@ 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 |
|
| 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` |
|
| 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` |
|
| 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` |
|
| 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→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 |
|
| **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) | `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 et `amorcage_acces`) | `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` |
|
| 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` | — |
|
| 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`) | — |
|
| 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 | — |
|
| 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 | `ANSIBLE_VAULT_PASSWORD_FILE` / `~/.config/setops-vault-pass` ; déréférencé par `lookup('vars', <nom>)` | — |
|
| 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 |
|
||||||
| 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` |
|
| 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 |
|
| 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** (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` |
|
| 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` |
|
||||||
| 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` |
|
| 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) |
|
| 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) |
|
||||||
| 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 |
|
| **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 |
|
||||||
| 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 |
|
| 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 |
|
| 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` |
|
| **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` |
|
| 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),
|
> ⚠️ **Deux directions de binding, assumées** : `app→app` côté app (instancier),
|
||||||
|
|
@ -75,7 +96,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
|
- ✅ **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`,
|
2026-07-29 : `serveur_debian`, `hosts_statiques`, `resoudre_base`, `resoudre_annuaire`,
|
||||||
`serveur_dovecot`, `serveur_postfix`, `serveur_rspamd`, `client_backup`, `serveur_backup`,
|
`serveur_dovecot`, `serveur_postfix`, `serveur_rspamd`, `client_backup`, `serveur_backup`,
|
||||||
`client_unbound`, `serveur_oauth2_proxy`, `serveur_icingaweb2`).
|
`client_resolveur`, `serveur_oauth2_proxy`, `serveur_icingaweb2`).
|
||||||
- ✅ **soldé** — `expose` **est** consommé au déploiement : `plan/applications.yml` →
|
- ✅ **soldé** — `expose` **est** consommé au déploiement : `plan/applications.yml` →
|
||||||
filtre `expositions_des_applications` → vhosts nginx dérivés
|
filtre `expositions_des_applications` → vhosts nginx dérivés
|
||||||
(`roles/serveur_nginx/tasks/main.yml`, template `expositions.conf.j2`, drapeau
|
(`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
|
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**
|
court séparé. Seule exception, `serveur_durci` : un groupe dont le playbook **compose dix
|
||||||
onze rôles de durcissement (`hardening_packages`, `sysctl_hardening`, `apparmor`,
|
rôles** de durcissement, et dans cet ordre — `hardening_packages`, `sysctl_hardening`,
|
||||||
`auditd`, `fail2ban_ssh`, `ssh_hardening`, `nftables_baseline`…) plutôt qu'un rôle
|
`core_dumps`, `unattended_upgrades`, `apparmor`, `auditd`, `fail2ban_ssh`, `journald`,
|
||||||
homonyme.
|
`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`.
|
La nomenclature des VM et des VMID est documentée dans `docs/nomenclature-vm.md`.
|
||||||
|
|
||||||
|
|
@ -23,21 +23,28 @@ Un service central peut partager un hôte avec d'autres services de la même fon
|
||||||
|
|
||||||
## État d'implémentation des rôles
|
## État d'implémentation des rôles
|
||||||
|
|
||||||
> **Mise à jour (2026-08-18), vérifiée rôle par rôle contre `roles/`.** Les 29 groupes
|
> **Mise à jour (2026-09-06), vérifiée groupe par groupe contre `roles/` et
|
||||||
> `serveur_*` / `client_*` de ce catalogue ont **tous** leur rôle et leur playbook. Aucune
|
> `playbooks/groupes/`.** Les **40 groupes** `serveur_*` / `client_*` du tableau ci-dessous
|
||||||
> capacité annoncée ici n'est un point d'ancrage vide.
|
> 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.)*
|
||||||
|
|
||||||
**Éprouvés sur VM réelles.** Le 2026-08-13, la flotte a été **reconstruite depuis zéro**
|
**É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
|
43 groupes, 0 échec, 37 minutes. L'épreuve a été **rejouée deux fois le 2026-09-02**, sur
|
||||||
« du code validé » : chaque rôle a repris une machine nue et l'a menée à l'état voulu.
|
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.
|
||||||
|
|
||||||
Ce que la reconstruction couvre, par capacité :
|
Ce que la reconstruction couvre, par capacité :
|
||||||
|
|
||||||
| Capacité | Rôles | Ce qui est éprouvé |
|
| Capacité | Rôles | Ce qui est éprouvé |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Socle et durcissement | `serveur_debian`, `serveur_durci` | clone du gabarit doré → machine conforme |
|
| Socle et durcissement | `serveur_debian`, `serveur_durci` (dix rôles composés) | clone du gabarit doré → machine conforme |
|
||||||
| Confiance | `serveur_step_ca`, `client_pki` | mTLS avec SAN dérivés du plan, renouvellement |
|
| Confiance | `serveur_step_ca`, `client_pki` | mTLS avec SAN dérivés du plan, renouvellement |
|
||||||
| Noms | `serveur_powerdns`, `client_unbound` | autoritaire interne + résolveur local |
|
| Noms | `serveur_powerdns`, `client_resolveur` | 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>`) |
|
| 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) |
|
| 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` |
|
| Données | `serveur_postgresql`, `serveur_redis` | bases et comptes dérivés du registre, TLS `verify-full` |
|
||||||
|
|
@ -46,13 +53,23 @@ Ce que la reconstruction couvre, par capacité :
|
||||||
| Observabilité | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` | métriques, journaux, tableaux sous SSO |
|
| 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 |
|
| Supervision | `serveur_icinga`, `serveur_icingaweb2` | Icinga 2 + IcingaDB + Web 2 + BPM, au SSO |
|
||||||
| Forge | `serveur_forgejo` | Git + PostgreSQL + SSO OIDC |
|
| Forge | `serveur_forgejo` | Git + PostgreSQL + SSO OIDC |
|
||||||
| 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 |
|
| 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 |
|
||||||
| Plateforme webapp | `serveur_web_frontal`, `serveur_web_dorsal` | sites statiques et webapps natives (venv + systemd + nginx), **zéro conteneur** |
|
| 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) |
|
| Sauvegardes | `serveur_backup`, `client_backup` | restic hors-nœud, **restauration éprouvée** (2026-08-12 : la donnée revient) |
|
||||||
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte |
|
| 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 |
|
||||||
|
|
||||||
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
||||||
(→ Postfix), `client_dns` (→ plancher `/etc/hosts` + `client_unbound`), `client_ldap`
|
(→ Postfix), `client_dns` (→ plancher `/etc/hosts` + `client_resolveur`), `client_ldap`
|
||||||
(login LDAP au niveau OS, hors design).
|
(login LDAP au niveau OS, hors design).
|
||||||
|
|
||||||
> **Ce que « éprouvé » ne dit pas.** La reconstruction prouve que le moteur mène une
|
> **Ce que « éprouvé » ne dit pas.** La reconstruction prouve que le moteur mène une
|
||||||
|
|
@ -136,9 +153,14 @@ table décrit une répartition éprouvée, pas un minimum requis.
|
||||||
| `mon-01` | Icinga 2, Icinga Web 2, oauth2-proxy |
|
| `mon-01` | Icinga 2, Icinga Web 2, oauth2-proxy |
|
||||||
| `forge-01` | Forgejo |
|
| `forge-01` | Forgejo |
|
||||||
| `collab-01` | Nextcloud, Collabora |
|
| `collab-01` | Nextcloud, Collabora |
|
||||||
| `backup-01` | dépôt restic |
|
|
||||||
| `web-frontal-01` | site statique |
|
| `web-frontal-01` | site statique |
|
||||||
| `web-dorsal-01` | webapp native |
|
| `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`
|
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
|
porte ce qui parle à l'extérieur (Postfix, rspamd), `infra-mail-01` ce qui détient les
|
||||||
|
|
@ -151,7 +173,9 @@ boîtes (Dovecot). La coupure suit l'exposition, pas le logiciel.
|
||||||
| Confiance PKI / ACME | `client_pki` | **tout hôte** (universelle) |
|
| Confiance PKI / ACME | `client_pki` | **tout hôte** (universelle) |
|
||||||
| Métriques Prometheus | `client_metrique` | **tout hôte** (universelle) |
|
| Métriques Prometheus | `client_metrique` | **tout hôte** (universelle) |
|
||||||
| Journaux vers Loki | `client_journal` | **tout hôte** (universelle) |
|
| Journaux vers Loki | `client_journal` | **tout hôte** (universelle) |
|
||||||
| Résolution locale (Unbound) | `client_unbound` | **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) |
|
||||||
| Relais SMTP | `client_smtp` | déclaré par hôte, dans le plan |
|
| 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) |
|
| Sauvegarde restic | `client_backup` | déclaré par hôte — **obligatoire pour tout détenteur d'état** (P36) |
|
||||||
|
|
||||||
|
|
@ -163,14 +187,21 @@ dans le plan.
|
||||||
> **`client_supervision` n'existe pas.** Ce catalogue l'a longtemps annoncé ; il n'a
|
> **`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
|
> 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
|
> 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 — ainsi l'état des
|
> poussés par l'API** par celui qui détient la vérité de terrain. Le nom est retiré plutôt
|
||||||
> sauvegardes est-il rapporté par `backup-01`, seul à pouvoir lire ses dépôts. Le nom est
|
> que réservé : une case vide dans un catalogue se lit comme une promesse.
|
||||||
> 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.
|
||||||
|
|
||||||
## Ordre de déploiement — le raisonnement
|
## Ordre de déploiement — le raisonnement
|
||||||
|
|
||||||
> **L'ordre exécutable n'est pas ici.** Il vit dans `docs/couches-deploiement.yml` et
|
> **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 (30 groupes classés, aucun
|
> `docs/dependances-groupes.yml`, que P08 prouve cohérents (42 groupes classés, aucun
|
||||||
> cycle, aucune arête en arrière) et que `make reconstruire` suit. Ce qui suit en est le
|
> 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
|
> **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
|
> service nouveau. Les phases sont franchies : les « intégrations à prévoir » ci-dessous
|
||||||
|
|
@ -178,10 +209,24 @@ dans le plan.
|
||||||
|
|
||||||
L'ordre ci-dessous privilégie les dépendances structurantes avant les applications.
|
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
|
### Phase 1 - Fondations transversales
|
||||||
|
|
||||||
1. `serveur_powerdns`
|
1. `serveur_powerdns`
|
||||||
- Service central : DNS interne autoritaire et/ou résolution interne selon le design retenu.
|
- 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`).
|
||||||
- Raison : les autres intégrations auront besoin de noms stables plutôt que d'adresses IP.
|
- Raison : les autres intégrations auront besoin de noms stables plutôt que d'adresses IP.
|
||||||
|
|
||||||
2. `serveur_step_ca`
|
2. `serveur_step_ca`
|
||||||
|
|
@ -268,7 +313,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 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 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`.
|
- 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` et `client_unbound` — **par dérivation, sans rien écrire** (intégrations universelles, P26). La supervision, elle, ne pose rien sur l'hôte.
|
- 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.
|
||||||
- Tout hôte qui **détient de l'état** doit porter `client_backup` (P36 le refuse sinon).
|
- 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 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.
|
- Tout rôle serveur doit documenter ses ports, secrets, sauvegardes, dépendances et groupes clients associés.
|
||||||
|
|
|
||||||
277
docs/conception-contextes.md
Normal file
277
docs/conception-contextes.md
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
# 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
|
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
|
n'est déclaré. Sans underlay monté, tout retombe dans le fichier du tenant et
|
||||||
`make config` fonctionne comme avant.
|
`make config` fonctionne comme avant.
|
||||||
- Secrets → **voûte unique** `instance/inventories/production/group_vars/all/vault.yml`
|
- Secrets → **voûte unique** `instance/inventories/<inventaire>/group_vars/all/vault.yml`
|
||||||
(chiffrée par `ansible-vault`), qui contient **tous** les secrets de l'instance
|
(chiffrée par `ansible-vault`), qui contient **tous** les secrets de l'instance
|
||||||
(token Proxmox + `vault_*`). Voir [§4](#4-secrets-de-linstance-).
|
(token Proxmox + `vault_*`). Voir [§4](#4-secrets-de-linstance-).
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ sans tout retaper.
|
||||||
| Invite | Variable | Défaut | Sens / quoi saisir |
|
| 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. |
|
| 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` | `modele-debian13` | Nom de référence du template (lisibilité ; doit correspondre au modèle). |
|
| 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.)* |
|
||||||
|
|
||||||
> Le golden template est l'**actif central** : il est cloné pour chaque VM, jamais
|
> Le golden template est l'**actif central** : il est cloné pour chaque VM, jamais
|
||||||
> jeté ni reconstruit à la légère.
|
> jeté ni reconstruit à la légère.
|
||||||
|
|
@ -68,10 +68,17 @@ Ces valeurs s'appliquent à toute VM clonée, **sauf** si l'hôte les surcharge
|
||||||
## 4. Secrets de l'instance 🔒 *(voûte unique)*
|
## 4. Secrets de l'instance 🔒 *(voûte unique)*
|
||||||
|
|
||||||
Tous les secrets de l'instance vivent dans **une seule voûte chiffrée par
|
Tous les secrets de l'instance vivent dans **une seule voûte chiffrée par
|
||||||
environnement** : `instance/inventories/<env>/group_vars/all/vault.yml`. Un seul
|
instance** : `instance/inventories/<inventaire>/group_vars/all/vault.yml`. Un seul fichier
|
||||||
fichier, un seul mot de passe — fini les voûtes éparpillées. Gabarit committé :
|
par écosystème — fini les voûtes éparpillées.
|
||||||
[`exemples/vault.exemple.yml`](../exemples/vault.exemple.yml) (token Proxmox +
|
|
||||||
17 clés `vault_*` pour PKI, LDAP/SSO, bases, forge, observabilité).
|
> **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é).
|
||||||
|
|
||||||
L'assistant demande « Configurer la voûte de secrets maintenant ». Si `oui` :
|
L'assistant demande « Configurer la voûte de secrets maintenant ». Si `oui` :
|
||||||
|
|
||||||
|
|
@ -96,10 +103,16 @@ 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>` |
|
| **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
|
```bash
|
||||||
ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass \
|
python3 scripts/voute.py saisir vault_opnsense_api_key vault_opnsense_api_secret
|
||||||
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
|
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
|
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
|
sur disque : la voûte est déchiffrée en mémoire, complétée, reparsée et re-déchiffrée
|
||||||
|
|
|
||||||
|
|
@ -33,40 +33,94 @@ couches:
|
||||||
groupes:
|
groupes:
|
||||||
- client_pki
|
- 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
|
- nom: services
|
||||||
raison: "Les services d'infrastructure dont dépendent les applications : bases, annuaire, DNS, cache, métriques, journaux, edge, courriel."
|
raison: "Les services d'infrastructure dont dépendent les applications : bases, annuaire, DNS, cache, métriques, journaux, edge, courriel."
|
||||||
groupes:
|
groupes:
|
||||||
- serveur_postgresql
|
|
||||||
- serveur_openldap
|
- serveur_openldap
|
||||||
- serveur_powerdns
|
- 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_redis
|
||||||
- serveur_prometheus
|
|
||||||
- serveur_loki
|
|
||||||
- serveur_nginx
|
- serveur_nginx
|
||||||
- serveur_rspamd
|
- serveur_rspamd
|
||||||
- serveur_dovecot
|
- serveur_dovecot
|
||||||
- serveur_postfix
|
- serveur_postfix
|
||||||
- serveur_backup
|
- 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
|
- nom: apps
|
||||||
raison: "Les applications métier, qui consomment les services (base, SSO, courriel, edge)."
|
raison: "Les applications métier, qui consomment les services (base, SSO, courriel, edge)."
|
||||||
groupes:
|
groupes:
|
||||||
- serveur_keycloak
|
- serveur_keycloak
|
||||||
- serveur_oauth2_proxy
|
- serveur_oauth2_proxy
|
||||||
- serveur_forgejo
|
- serveur_forgejo
|
||||||
- serveur_icinga
|
|
||||||
- serveur_icingaweb2
|
- serveur_icingaweb2
|
||||||
- serveur_grafana
|
|
||||||
- serveur_collabora
|
- serveur_collabora
|
||||||
- serveur_nextcloud
|
- serveur_nextcloud
|
||||||
- serveur_web_frontal
|
- serveur_web_frontal
|
||||||
- serveur_web_dorsal
|
- 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
|
- 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."
|
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:
|
groupes:
|
||||||
- client_metrique
|
|
||||||
- client_journal
|
|
||||||
- client_smtp
|
- client_smtp
|
||||||
- client_backup
|
- client_backup
|
||||||
- client_unbound
|
- client_resolveur
|
||||||
|
- client_artefacts
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,15 @@
|
||||||
|
|
||||||
> **Pour qui :** le **mainteneur** du service de courriel.
|
> **Pour qui :** le **mainteneur** du service de courriel.
|
||||||
|
|
||||||
> **Statut : CONCEPTION (cadrage).** Aucun rôle n'est encore écrit. Ce document fixe
|
> **Statut, revu le 2026-09-06 — l'Étape A est LIVRÉE, l'Étape B reste du cadrage.**
|
||||||
> les décisions, les prérequis et la topologie avant toute implémentation.
|
> 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**.
|
||||||
|
|
||||||
## 1. Décisions arrêtées
|
## 1. Décisions arrêtées
|
||||||
|
|
||||||
|
|
@ -194,13 +201,16 @@ _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.
|
But : prouver **toute la pile en interne**, en **code de prod**, dans le bac à sable.
|
||||||
Aucune dépendance au public.
|
Aucune dépendance au public.
|
||||||
|
|
||||||
1. **Pilier identité** : `serveur_openldap` (TLS **step_ca**) — ✅ **déployé et prouvé** en bac à sable.
|
1. **Pilier identité** : `serveur_openldap` (TLS **step_ca**) — ✅ **déployé et prouvé**.
|
||||||
2. **`serveur_postfix` + `serveur_dovecot` + `serveur_rspamd`** sur `mail-01` : TLS **step_ca**,
|
2. **`serveur_postfix` + `serveur_rspamd`** sur `edge-mta-01`, **`serveur_dovecot`** sur
|
||||||
annuaire/auth **LDAP**, DKIM interne, nftables mail.
|
`infra-mail-01` : TLS **step_ca**, annuaire/auth **LDAP**, DKIM, nftables mail — ✅ **déployés**.
|
||||||
3. **Prouver** : réception → boîte → accès **IMAP** → envoi **intra-écosystème**, le tout en
|
3. **Prouver** : réception → boîte → accès **IMAP** → envoi **intra-écosystème**, le tout en
|
||||||
TLS interne, auth LDAP.
|
TLS interne, auth LDAP — ✅ **prouvé de bout en bout**, et rejoué à la demande par
|
||||||
|
`make courriel-plan`, file d'attente comprise.
|
||||||
|
|
||||||
*(Étape actuelle : identité OK ; on démarre `serveur_postfix`.)*
|
*(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 B — Fonctionnement EXTERNE (transition prod, plus tard)
|
### Étape B — Fonctionnement EXTERNE (transition prod, plus tard)
|
||||||
|
|
||||||
|
|
@ -216,4 +226,5 @@ 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
|
*Ce document est un cadrage vivant : il évolue à mesure que les décisions ouvertes se
|
||||||
tranchent. Il ne décrit pas encore de code livré.*
|
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.*
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,14 @@ sont les seules vérifiables.
|
||||||
|
|
||||||
| # | Décision | Pourquoi | Détail | Garde |
|
| # | 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-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-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 | — |
|
| **D-15** | Ce symlink **ne suit pas** `make instance-utiliser` | basculer le tenant actif ne change pas la fabric | `frontiere-opnsense.md` §2 | — |
|
||||||
|
|
@ -69,9 +77,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-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-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-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 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-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** | `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-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** | 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-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-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-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-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 | — |
|
| **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,6 +12,12 @@ groupes:
|
||||||
raison: "Les exporters clients doivent etre collectes par Prometheus."
|
raison: "Les exporters clients doivent etre collectes par Prometheus."
|
||||||
surveillance: "Verifier targets Prometheus, scrape duration et erreurs de collecte."
|
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:
|
client_journal:
|
||||||
requiert_groupes_actifs:
|
requiert_groupes_actifs:
|
||||||
- serveur_loki
|
- serveur_loki
|
||||||
|
|
@ -68,10 +74,83 @@ groupes:
|
||||||
requiert_groupes_actifs:
|
requiert_groupes_actifs:
|
||||||
- serveur_postgresql
|
- serveur_postgresql
|
||||||
- serveur_nginx
|
- 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
|
- serveur_postfix
|
||||||
raison: "Forgejo depend d'une base, d'une publication HTTP(S) et d'un relais courriel (MTA Postfix)."
|
raison: "Forgejo depend d'une base (serveur ou fichier) et d'une publication HTTP(S) ; le courriel est un agrement."
|
||||||
surveillance: "Verifier HTTP(S), base, files Git et envoi courriel."
|
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:
|
serveur_nextcloud:
|
||||||
requiert_groupes_actifs:
|
requiert_groupes_actifs:
|
||||||
- serveur_postgresql
|
- serveur_postgresql
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,21 @@ make mtu-mesurer # l'invité porte-t-il le MTU de sa zone SDN ?
|
||||||
make versions-mesurer # de combien nos épinglages ont-ils vieilli ?
|
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
|
## Le trou qu'il comble
|
||||||
|
|
||||||
`scripts/prouver.py` porte 35 preuves. Elles sont toutes **statiques** : elles lisent le
|
`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
|
||||||
dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est
|
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
|
cohérent **avec lui-même** — que les handlers existent, que les intrants ont un
|
||||||
propriétaire, que rien n'est codé en dur.
|
propriétaire, que rien n'est codé en dur.
|
||||||
|
|
@ -215,7 +227,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
|
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.
|
que des écritures ne peut pas le soutenir.
|
||||||
|
|
||||||
**Les cinq devis sont cette relecture**, faite après coup et par une autre main que celle
|
**Ces 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
|
qui a écrit. C'est ce qui les distingue d'un déploiement : `make deployer` réconcilie, les
|
||||||
devis constatent.
|
devis constatent.
|
||||||
|
|
||||||
|
|
@ -242,5 +254,12 @@ La forme correcte, reprise dans les deux devis :
|
||||||
Ils ne corrigent pas — c'est `make deployer` qui réconcilie. Ils répondent à l'autre
|
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.
|
question, et sortent en code 1 s'il y a un écart.
|
||||||
|
|
||||||
Ils couvrent l'identité et les certificats. Le courriel, la base de données et les
|
**Ce paragraphe disait, en dernière ligne du document, que le courriel, la base de données
|
||||||
expositions web attendent le même traitement ; le patron est là pour être repris.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ que recalculer la cible ; aucune dérive silencieuse.
|
||||||
| Donnée | Emplacement | Remarque |
|
| 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). |
|
| 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` | p. ex. `serveur_web_frontal`, `serveur_web_dorsal`. |
|
| 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. |
|
||||||
| Repli ultime | `EMPREINTE_DEFAUT` | groupe inconnu : empreinte minimale. |
|
| Repli ultime | `EMPREINTE_DEFAUT` | groupe inconnu : empreinte minimale. |
|
||||||
| Socle SE | `SOCLE_SE` dans `inventory_rules.py` | coût de base Debian durci. |
|
| 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`). |
|
| Override par hôte | `serveurs.yml` (`coeurs`/`memoire`/`disque`) | déjà supporté par le générateur (`PLACEMENT`). |
|
||||||
|
|
@ -65,24 +65,35 @@ 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`
|
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).
|
(avec `omit` si absent : aucune régression, on garde alors les specs du template).
|
||||||
|
|
||||||
## Empreintes actuelles
|
## Les empreintes : ne pas les recopier, les mesurer
|
||||||
|
|
||||||
| Rôle | cœurs | RAM (Mo) | disque (Go) |
|
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
|
||||||
| serveur_step_ca | 1 | 256 | 2 |
|
une valeur qui vit ailleurs, c'est s'engager à la suivre — cette page ne s'y engage plus :
|
||||||
| serveur_powerdns | 1 | 512 | 2 |
|
|
||||||
| serveur_nginx | 1 | 512 | 3 |
|
```bash
|
||||||
| serveur_openldap | 1 | 512 | 3 |
|
# l'empreinte de chaque logiciel, telle qu'elle est déclarée
|
||||||
| serveur_keycloak | 2 | 1536 | 5 |
|
python3 - <<'EOF'
|
||||||
| serveur_postgresql | 2 | 2048 | 20 |
|
import yaml, pathlib
|
||||||
| serveur_redis | 1 | 512 | 2 |
|
for f in sorted(pathlib.Path('roles').glob('*/meta/empreinte.yml')):
|
||||||
| serveur_forgejo | 1 | 1024 | 20 |
|
e = (yaml.safe_load(f.read_text()) or {}).get('setops_empreinte') or {}
|
||||||
| serveur_prometheus | 1 | 1024 | 20 |
|
print(f"{f.parts[1]:26} {e.get('coeurs')} coeur(s) {e.get('memoire_mo')} Mo {e.get('disque_go')} Go")
|
||||||
| serveur_loki | 1 | 1024 | 20 |
|
EOF
|
||||||
| serveur_grafana | 1 | 512 | 2 |
|
|
||||||
| serveur_icinga | 2 | 1024 | 10 |
|
# ce que ça donne pour un hôte donné, une fois sommé et arrondi
|
||||||
| serveur_web_frontal (repli) | 1 | 512 | 5 |
|
make hote-afficher HOTE=obs-01
|
||||||
| serveur_web_dorsal (repli) | 1 | 1024 | 10 |
|
```
|
||||||
|
|
||||||
|
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é.
|
||||||
|
|
||||||
## Ajuster
|
## Ajuster
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,42 @@ Le service DNS interne est la premiere capacite de plateforme.
|
||||||
> `serveur_debian`) genere `/etc/hosts` sur **chaque** VM depuis l'inventaire : tout
|
> `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
|
> 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,
|
> que PowerDNS ne soit la). PowerDNS devient une **commodite** (zone, externe,
|
||||||
> dynamique), plus un point de defaillance. Le resolveur local `client_unbound` est
|
> dynamique), plus un point de defaillance.
|
||||||
> **optionnel** (opt-in, avec bascule validee) : sans lui, le plancher `/etc/hosts` suffit.
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
## Groupes
|
## Groupes
|
||||||
|
|
||||||
```text
|
```text
|
||||||
serveur_powerdns -> service DNS central PowerDNS Authoritative
|
serveur_powerdns -> autoritatif interne (PowerDNS Authoritative)
|
||||||
client_unbound -> resolveur local optionnel (opt-in, bascule validee)
|
serveur_resolveur -> LE recursif du tenant (Unbound), un seul
|
||||||
|
client_resolveur -> integration universelle : designe ce resolveur dans /etc/resolv.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
`client_unbound` (résolveur local optionnel) peut viser PowerDNS en stub-zone + récursion.
|
|
||||||
|
|
||||||
## Zone initiale
|
## Zone initiale
|
||||||
|
|
||||||
La zone initiale est :
|
La zone initiale est :
|
||||||
|
|
@ -31,7 +55,7 @@ exemple.internal
|
||||||
Elle est definie dans :
|
Elle est definie dans :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
instance/inventories/production/group_vars/serveur_powerdns.yml
|
instance/inventories/<inventaire>/group_vars/serveur_powerdns.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Enregistrement automatique
|
## Enregistrement automatique
|
||||||
|
|
@ -67,22 +91,42 @@ Raison :
|
||||||
|
|
||||||
Quand `serveur_postgresql` sera stable, il sera possible de migrer vers un backend SQL si le besoin operationnel le justifie.
|
Quand `serveur_postgresql` sera stable, il sera possible de migrer vers un backend SQL si le besoin operationnel le justifie.
|
||||||
|
|
||||||
## Résolveur local (optionnel)
|
## La bascule de `/etc/resolv.conf` est protegee
|
||||||
|
|
||||||
Le role `client_unbound` (opt-in) installe un résolveur récursif local qui, en stub-zone,
|
Le role `client_resolveur` ne bascule `/etc/resolv.conf` qu'apres avoir verifie que le
|
||||||
délègue les noms internes à PowerDNS et récurse le reste.
|
resolveur repond **deja** — pour l'interne *et* pour l'Internet :
|
||||||
|
|
||||||
La bascule du resolver local est **protegee** (le role valide qu'Unbound répond AVANT de
|
|
||||||
basculer `/etc/resolv.conf`) :
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
client_unbound_apply: true
|
client_resolveur_apply: true
|
||||||
client_unbound_confirm: true
|
client_resolveur_confirm: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Sans ces deux variables, le role prépare Unbound mais ne modifie pas `/etc/resolv.conf`.
|
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.
|
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.
|
||||||
|
|
||||||
## Surveillance a prevoir
|
## Surveillance a prevoir
|
||||||
|
|
||||||
|
|
@ -95,3 +139,103 @@ Les premiers checks utiles :
|
||||||
- enregistrements des hotes actifs presents ;
|
- enregistrements des hotes actifs presents ;
|
||||||
- serial de zone attendu ;
|
- serial de zone attendu ;
|
||||||
- latence de resolution.
|
- 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,13 +35,17 @@ cohérent.
|
||||||
| **Confiance** | Autorité de certification interne (PKI/ACME) : les serveurs se reconnaissent par certificats émis localement, sans acheter de confiance à l'extérieur. |
|
| **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. |
|
| **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. |
|
| **Données** | Bases relationnelles et cache, avec un registre des connexions applicatives. |
|
||||||
| **Communication** | Relais courriel interne pour les alertes, notifications et réinitialisations. |
|
| **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. |
|
||||||
| **Observabilité** | Métriques, journaux centralisés, tableaux de bord et supervision active. |
|
| **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. |
|
| **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, PostgreSQL,
|
Tous ces services sont **libres** : OpenLDAP, Keycloak, step-ca, PowerDNS, Unbound,
|
||||||
Redis, NGINX, Prometheus, Loki, Grafana, Icinga, Forgejo, Sendmail…). Aucun verrou
|
PostgreSQL, Redis, NGINX, Postfix, Dovecot, rspamd, Prometheus, Loki, Grafana, Icinga,
|
||||||
propriétaire, aucune licence captive.
|
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.)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -60,12 +64,18 @@ plan :
|
||||||
reconstruit à l'identique depuis le plan et le code. L'infrastructure n'est pas un
|
reconstruit à l'identique depuis le plan et le code. L'infrastructure n'est pas un
|
||||||
objet fragile patiemment bricolé — c'est un artefact reproductible.
|
objet fragile patiemment bricolé — c'est un artefact reproductible.
|
||||||
|
|
||||||
> **Honnêteté sur le statut.** Une grande partie de l'écosystème est aujourd'hui
|
> **Honnêteté sur le statut — mise à jour le 2026-09-06.** Ce paragraphe disait, bien après
|
||||||
> *définie, codée et validée* (vérification de syntaxe, analyse statique) mais n'a pas
|
> que ce fut faux, que l'écosystème n'avait « pas encore été éprouvé sur des machines
|
||||||
> encore été éprouvée sur des machines de production réelles. Le socle Debian durci et
|
> réelles ». Il l'a été : la flotte entière a été **rasée et remontée depuis zéro** le
|
||||||
> son modèle sont la fondation établie ; les services applicatifs sont prêts en tant
|
> 2026-08-13, puis **deux fois le 2026-09-02** — 15/15 puis 14/14 machines, **zéro échec**,
|
||||||
> que code et se déploient progressivement. Nous ne présentons jamais un service non
|
> et la validation complète à zéro échec sur treize hôtes. La reconstruction n'est donc pas
|
||||||
> déployé comme « en production ».
|
> 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -100,7 +110,8 @@ Une trentaine de paramètres noyau resserrent le comportement du système et du
|
||||||
|
|
||||||
### 4. Pare-feu prêt, activé au bon moment
|
### 4. Pare-feu prêt, activé au bon moment
|
||||||
|
|
||||||
- **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.
|
- **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é.
|
||||||
|
|
||||||
### 5. Confinement et intégrité
|
### 5. Confinement et intégrité
|
||||||
|
|
||||||
|
|
@ -134,6 +145,8 @@ 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.
|
- **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.
|
- **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.
|
- **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é.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
269
docs/filiation-emancipation.md
Normal file
269
docs/filiation-emancipation.md
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
# 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,9 +38,26 @@ flux:
|
||||||
| `externe` | hors flotte (frontière publique — géré à l'OPNsense, pas dans le nœud) |
|
| `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`) |
|
| `localhost` | boucle locale — aucune règle inter-nœud (nftables autorise `lo`) |
|
||||||
| `expositions` | dérivé des `expose:` des applications (cas de l'edge → backends) |
|
| `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.
|
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
|
## Génération
|
||||||
Un **résolveur** (miroir de `instancier`) agrège, par serveur, les `flux.yml` de tous ses rôles
|
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 :
|
(services + intégrations), résout les `pair`, et produit :
|
||||||
|
|
@ -49,15 +66,52 @@ Un **résolveur** (miroir de `instancier`) agrège, par serveur, les `flux.yml`
|
||||||
- **le registre d'audit** : `docs/registre-flux.md` (généré) + une cible `make flux`.
|
- **le registre d'audit** : `docs/registre-flux.md` (généré) + une cible `make flux`.
|
||||||
|
|
||||||
## Activation prudente
|
## 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). nftables reste *préparé mais
|
|
||||||
non activé* tant que le registre n'est pas complet et validé.
|
|
||||||
|
|
||||||
## Séquence
|
### Descriptions conservées par les pare-feux
|
||||||
|
|
||||||
|
La `raison` non vide du flux et son rôle alimentent les champs natifs : `comment`
|
||||||
|
pour nftables et Proxmox, `description` pour le filtrage OPNsense (`descr` pour ses
|
||||||
|
redirections). Un commentaire de fichier `# ...` ne survit pas au chargement nftables.
|
||||||
|
Les règles communes (administration, connexions établies, refus) portent aussi leur motif.
|
||||||
|
|
||||||
|
Les libellés sont sur une ligne. Le commentaire nftables est borné à 127 octets UTF-8 ;
|
||||||
|
OPNsense garde sa clé `setops:...` complète et borne le texte qui suit pour rester dans
|
||||||
|
255 octets. Une raison abrégée reste disponible en entier dans le registre des flux.
|
||||||
|
Le commentaire Proxmox conserve aussi le pair nommé dans le devis.
|
||||||
|
|
||||||
|
Une correction de description apparaît dans `make proxmox-fw-plan` et
|
||||||
|
`make frontiere-plan`. Son application met à jour le champ sur la règle existante,
|
||||||
|
puis le relit : elle ne recrée pas la règle et ne modifie ni son activation ni sa
|
||||||
|
politique. Les confirmations habituelles restent requises pour appliquer ces devis.
|
||||||
|
`make test` vérifie notamment cette convergence et les limites des champs.
|
||||||
|
|
||||||
|
Références des champs natifs : [nftables](https://netfilter.org/projects/nftables/manpage.html),
|
||||||
|
[API Proxmox](https://github.com/proxmox/pve-firewall/blob/master/src/PVE/API2/Firewall/Rules.pm),
|
||||||
|
[descriptions OPNsense](https://github.com/opnsense/core/blob/master/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/DescriptionField.php).
|
||||||
|
|
||||||
|
### Activer le filtrage
|
||||||
|
|
||||||
|
Activer nftables = **action destructive** (peut couper l'accès) → confirmation explicite +
|
||||||
|
déploiement graduel (garder l'accès SSH/Ansible, tester par nœud).
|
||||||
|
|
||||||
|
> **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
|
||||||
1. ✅ figer le schéma (ce doc) + **piloter** sur postgresql / client_metrique / nginx ;
|
1. ✅ figer le schéma (ce doc) + **piloter** sur postgresql / client_metrique / nginx ;
|
||||||
2. le **résolveur** (agrégation → règles + registre) ;
|
2. ✅ le **résolveur** (`scripts/resoudre_flux.py`) — agrégation → règles + registre ;
|
||||||
3. **remplir** tous les rôles (large transcription du travail zéro-confiance déjà fait) ;
|
3. ✅ **remplir** tous les rôles (transcription du travail zéro-confiance) ;
|
||||||
4. **générer** + registre d'audit ; puis **activer** nftables nœud par nœud.
|
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.
|
||||||
|
|
||||||
## `poste: false` — un service publié qui ne s'adresse pas à un humain
|
## `poste: false` — un service publié qui ne s'adresse pas à un humain
|
||||||
|
|
||||||
|
|
@ -93,11 +147,11 @@ Le registre confondait deux situations :
|
||||||
| le rôle **ouvre** l'écoute | `serveur_dovecot` lie 12345 (SASL réseau) | absent |
|
| 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` |
|
| 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
|
Sans cette distinction, la seule co-location légitime de la flotte — `tcp/22` sur le
|
||||||
`backup-01` — serait signalée à tort. Une preuve qui crie sur un cas sain finit par être
|
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
|
||||||
ignorée, ce qui est pire que de ne pas l'avoir.
|
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
|
**Corollaire à retenir** : un port qu'on **subit** (le défaut amont d'un logiciel) doit être
|
||||||
imposé et déclaré comme les autres. Celui d'Alloy ne l'était pas, et c'est la seule raison
|
imposé et déclaré comme les autres. Celui d'Alloy ne l'était pas, et c'est la seule raison
|
||||||
pour laquelle la collision a pu durer des semaines.
|
pour laquelle la collision a pu durer des semaines.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ mais elle décide de l'emplacement de chaque chose :
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| plan des services, inventaire | le **tenant** | `OPS-<tenant>` |
|
| plan des services, inventaire | le **tenant** | `OPS-<tenant>` |
|
||||||
| `underlay.yml` (fabric physique) | l'**hébergeur** | `OPS-<hébergeur>`, monté par symlink |
|
| `underlay.yml` (fabric physique) | l'**hébergeur** | `OPS-<hébergeur>`, monté par symlink |
|
||||||
| `group_vars/opnsense.yml` (frontière) | l'**hébergeur** | idem — **une** frontière pour tous ses tenants |
|
| `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
|
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
|
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
|
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
|
**chaque tenant a son propre alias d'administration** : `SETOPS_ADMIN_CHEZ17` n'ouvre que
|
||||||
`10.27.0.0/16`. Une union aurait laissé le plan de gestion d'un tenant entrer chez le voisin
|
`10.17.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
|
— ce que les ACL de switch interdisent par ailleurs. La bordure ne doit pas rouvrir ce que
|
||||||
l'isolation inter-tenant ferme.
|
l'isolation inter-tenant ferme.
|
||||||
|
|
||||||
|
|
@ -126,7 +126,9 @@ règle) et un tenant dont `nftables_admin_ssh` est vide (règle SSH omise — l'
|
||||||
exposerait le SSH à Internet).
|
exposerait le SSH à Internet).
|
||||||
|
|
||||||
Le registre des flux distingue les pairs par **mot-clé**. Or `resoudre_flux.py` **saute
|
Le registre des flux distingue les pairs par **mot-clé**. Or `resoudre_flux.py` **saute
|
||||||
volontairement** le pair `externe` (`scripts/resoudre_flux.py:184`) : ces flux-là ne
|
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
|
||||||
concernent pas le pare-feu d'hôte, ils relèvent de la bordure. Plusieurs `raison` le disent
|
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 ».
|
déjà noir sur blanc — « Frontière publique gérée à l'OPNsense ».
|
||||||
|
|
||||||
|
|
@ -165,9 +167,9 @@ alors **deux règles et deux alias**, chacun ne portant que les sources qui peuv
|
||||||
emprunter ce chemin.
|
emprunter ce chemin.
|
||||||
|
|
||||||
```
|
```
|
||||||
SETOPS_ADMIN_CHEZ17_GESTION 10.0.0.0/24 → pass in on lan
|
SETOPS_ADMIN_CHEZ17_GESTION 10.17.0.0/24 → pass in on lan
|
||||||
SETOPS_ADMIN_TECH11_GESTION 10.0.0.0/24 → pass in on lan
|
SETOPS_ADMIN_TECH23_GESTION 10.17.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
|
SETOPS_ADMIN_TECH23_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 à
|
**Le piège de la case à cocher.** Une source **RFC1918** qui arrive par le WAN se heurte à
|
||||||
|
|
@ -179,13 +181,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
|
**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
|
les sous-réseaux où il participe** — on retient une adresse, pas treize. `le commutateur` est
|
||||||
donc `.1` partout : `10.0.0.1`, `10.27.16.1`, `10.27.21.1`… Le chiffre n'est pas codé en dur,
|
donc `.1` partout : `10.17.16.1`, `10.17.21.1`… Le chiffre n'est pas codé en dur,
|
||||||
il vient de `reservations.passerelle` dans la nomenclature, et `make underlay` (**preuve
|
il vient de `reservations.passerelle` dans la nomenclature, et `make underlay` (**preuve
|
||||||
P23**) refuse une passerelle qui s'en écarte.
|
P23**) refuse une passerelle qui s'en écarte.
|
||||||
|
|
||||||
Seule exception, assumée : les liens plus étroits qu'un `/24`. Sur le `/29` de transit,
|
Seule exception, assumée : le **lien de transit**, dont l'adressage est dicté par ses
|
||||||
l'adressage est dicté par les participants du lien — les deux frontières occupent `.1` et
|
participants — les deux frontières occupent `.1` et `.2`, les hyperviseurs `.41`, `.43` et
|
||||||
`.2`, le switch prend `.6`.
|
`.47`.
|
||||||
|
|
||||||
## 5. La garde anti-lockout
|
## 5. La garde anti-lockout
|
||||||
|
|
||||||
|
|
@ -210,7 +212,7 @@ peut dériver d'aucun `index`. Sa place est l'underlay, cluster-global, au même
|
||||||
management, l'iSCSI et Ceph.
|
management, l'iSCSI et Ceph.
|
||||||
|
|
||||||
**Une route par sous-réseau attribué, jamais une par supernet** (2026-08-09). Router
|
**Une route par sous-réseau attribué, jamais une par supernet** (2026-08-09). Router
|
||||||
`10.27.0.0/16` faisait porter à la frontière des destinations qui n'existent nulle part :
|
`10.17.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
|
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
|
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
|
d'administration. Les alias `SETOPS_TENANT_*` énumèrent exactement les mêmes `/24`, et
|
||||||
|
|
@ -228,20 +230,24 @@ sur ce lien :
|
||||||
```yaml
|
```yaml
|
||||||
- nom: transit-frontiere
|
- nom: transit-frontiere
|
||||||
vlan: 40
|
vlan: 40
|
||||||
sous_reseau: 10.0.4.0/29
|
sous_reseau: 10.0.4.0/24
|
||||||
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
|
passerelle_sortie: 10.0.4.1 # bifrost-1 = sortie par défaut de la flotte
|
||||||
```
|
```
|
||||||
|
|
||||||
**Plan du `/29`.** Les frontières occupent le bas de la plage, le SVI du switch le haut :
|
**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.
|
||||||
|
|
||||||
| Adresse | Qui |
|
| Adresse | Qui |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `10.0.4.1` | `bifrost-1` — frontière active, sortie par défaut de la flotte |
|
| `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.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.3` | libre, réservée à une IP virtuelle CARP si les deux passent en HA |
|
||||||
| `10.0.4.4-.5` | libres |
|
| `10.0.4.41 / .43 / .47` | `asgard`, `gandalf`, `vishnu` — les hyperviseurs, via `bond3` |
|
||||||
| `10.0.4.6` | SVI du switch routeur (`le commutateur`) |
|
|
||||||
|
*(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.)*
|
||||||
|
|
||||||
Le jour où les deux OPNsense passent en haute disponibilité, `passerelle_sortie` devra
|
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
|
pointer sur l'**IP virtuelle CARP** et non sur un boîtier nommé — c'est le seul changement
|
||||||
|
|
@ -318,7 +324,7 @@ Même partage que Proxmox — l'anodin en clair, le secret dans la voûte :
|
||||||
|
|
||||||
| Quoi | Où | Réglable |
|
| Quoi | Où | Réglable |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| URL de gestion, interfaces, prochain saut | `group_vars/opnsense.yml` (en clair) | **panneau « Intrants de base » du GUI**, section *Frontière* |
|
| URL de gestion, interfaces, prochain saut | `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` |
|
| 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`,
|
Les valeurs non sensibles sont de **vrais intrants** : `opnsense_api_url`,
|
||||||
|
|
@ -420,7 +426,16 @@ Reste à trancher sur une interface **physique** : `ip ?`, `access-group ?`, `sp
|
||||||
et `<PORT-TRUNK>` ; rien dans le modèle ne peut les deviner.
|
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
|
- ~~**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é
|
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_unbound`
|
par tous les hôtes) déclare 443, 80 et 123/udp — dépôts apt et horloge ; `client_resolveur`
|
||||||
déclare 53 en UDP et TCP, la récursion depuis la racine que le choix souverain implique.
|
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
|
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.
|
l'énonce désormais au lieu de le poser en silence.
|
||||||
|
- **La sortie générale n'existe plus — 2026-10-10.** Le 123 était déjà passé à la frontière
|
||||||
|
(2026-09-11). Le 443 et le 80 du socle sont devenus conditionnels (`seulement_si:
|
||||||
|
artefacts_amorcage` vide) : le site et ses locataires prennent apt par le cache du site,
|
||||||
|
et ne les reçoivent plus. Mesurée à la frontière, cette sortie ne servait plus qu'à ce
|
||||||
|
qu'aucun rôle ne déclarait : télémétrie de Grafana, d'Alloy et de Loki et vérification
|
||||||
|
de version de Collabora (coupées à la source), cartes de rspamd et repère du génome
|
||||||
|
(déclarés dans `serveur_rspamd` et `serveur_ops_site`). Chaque sortie vers Internet porte
|
||||||
|
désormais le nom du rôle qui en a besoin. `test_conditions_site.py` refuse qu'un rôle
|
||||||
|
universel en rouvre une sans condition.
|
||||||
|
|
|
||||||
131
docs/frontiere-physique-virtuel.md
Normal file
131
docs/frontiere-physique-virtuel.md
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
# 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,15 +34,23 @@ poste de l'opérateur et sa forge. Rien à changer.
|
||||||
|
|
||||||
## 3. Ce qui n'a pas de maison aujourd'hui
|
## 3. Ce qui n'a pas de maison aujourd'hui
|
||||||
|
|
||||||
Constaté le 2026-08-04 : **aucun équipement de l'hébergeur n'est dans un inventaire
|
Constaté le 2026-08-04, **révisé le 2026-09-05**. La moitié du constat a été levée
|
||||||
Ansible**, et rien ne sauvegarde leurs configurations.
|
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.
|
||||||
|
|
||||||
| Besoin | État |
|
| Besoin | État |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Supervision des hyperviseurs, commutateurs, frontière | personne |
|
| Supervision des hyperviseurs, commutateurs, frontière | personne — `site-mon-01` ne voit que les VM du site |
|
||||||
| Journaux de ces équipements | personne |
|
| Journaux de ces équipements | personne |
|
||||||
| Sauvegarde de leurs configs (`running-config`, `config.xml`, `/etc/pve`) | 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 |
|
| Résolution des noms d'underlay (`asgard`, `bifrost-3`, `bifrost-1`) | personne — **vérifié : `site-dns-01` ne les résout pas** |
|
||||||
| Certificats pour leurs interfaces web | personne |
|
| Certificats pour leurs interfaces web | personne |
|
||||||
|
|
||||||
Ce n'est pas un oubli de conception : ces besoins tombaient entre les chaises. Ils ne sont
|
Ce n'est pas un oubli de conception : ces besoins tombaient entre les chaises. Ils ne sont
|
||||||
|
|
@ -54,7 +62,9 @@ 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
|
`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.
|
sa fabric ; les loger ailleurs recréerait la confusion qu'on vient de défaire.
|
||||||
|
|
||||||
Ce dépôt n'a pas d'inventaire Ansible aujourd'hui : c'est ce que le chantier ajoutera.
|
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.
|
||||||
|
|
||||||
**Rattachement réseau : un pont VLAN ordinaire, jamais un VNet du SDN.** C'est la
|
**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.
|
contrainte qui découle du §1, et la seule qui distingue ces VM de celles d'un tenant.
|
||||||
|
|
@ -143,30 +153,48 @@ 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
|
`proxmox_noeuds` et `proxmox_stockages` sont déjà chez lui ; il manque la classe, pas le
|
||||||
catalogue.
|
catalogue.
|
||||||
|
|
||||||
## 8. Un dépôt à part pour le réseau (décidé, non fait)
|
## 8. Un dépôt à part pour l'hébergeur — FAIT
|
||||||
|
|
||||||
`underlay.yml` et `proxmox-hebergeur.yml` vivent aujourd'hui dans `OPS-Chezlepro`, qui est
|
> **Cette section disait « Rien n'est fait » jusqu'au 2026-09-06.** Le dépôt existe :
|
||||||
aussi le dépôt du **tenant** Chezlepro. C'est ce qui a permis de démarrer, et c'est ce qui
|
> **`SITE-Chezlepro`**, frère de `OPS-Chezlepro`, et le symlink `underlay.yml` du moteur y
|
||||||
oblige, à chaque commit, à trancher si l'on touche à l'infrastructure ou à l'organisation.
|
> pointe (`../SITE-Chezlepro/underlay.yml`).
|
||||||
|
|
||||||
Ils décrivent des objets différents : l'un une **infrastructure** — des câbles, des VLAN,
|
Le raisonnement qui l'a motivé, et qui vaut pour tout hébergeur : `underlay.yml` et
|
||||||
un cluster —, l'autre une **organisation** — ses serveurs, ses applications, ses comptes.
|
`proxmox-hebergeur.yml` décrivent une **infrastructure** — des câbles, des VLAN, un
|
||||||
Un tenant peut déménager ; une fabric ne déménage pas.
|
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.
|
||||||
|
|
||||||
Le dépôt réseau de l'hébergeur porterait donc `underlay.yml`, `proxmox-hebergeur.yml`, et
|
Ce que le dépôt de l'hébergeur porte aujourd'hui :
|
||||||
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.
|
|
||||||
|
|
||||||
**Rien n'est fait.** La bascule demande de déplacer deux fichiers, de refaire le symlink,
|
| Fichier | Ce qu'il décrit |
|
||||||
et de vérifier que les trois générateurs qui les lisent suivent.
|
|---|---|
|
||||||
|
| `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) |
|
||||||
|
|
||||||
## 9. Le VLAN de gestion, et ce qu'il n'est pas
|
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.
|
||||||
|
|
||||||
`10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB/IPMI**. Accès
|
## 9. Le plan d'administration, et ce qu'il n'est pas
|
||||||
sysadmin uniquement.
|
|
||||||
|
|
||||||
Aucun hyperviseur n'y a d'adresse, aucune VM n'y est branchée, aucun trafic tenant ne le
|
> **Les adresses de cette section ont toutes changé** avec la bascule D-77/D-78, terminée le
|
||||||
traverse — ni encapsulé, ni décapsulé. C'est précisément la raison d'être des VLAN 11
|
> 2026-08-22. Elle désignait `10.0.0.0/24` et les « VLAN 11 (transport VXLAN) et 40 (sortie
|
||||||
(transport VXLAN) et 40 (sortie tenant) : les avoir sortis de ce domaine de diffusion.
|
> tenant) » ; `10.0.0.0/24` n'existe plus, et le transport est passé au VLAN 50.
|
||||||
|
|
||||||
|
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,9 +32,15 @@
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Apps web** (Forgejo, Grafana, Nextcloud…) | **Keycloak OIDC** (SSO) | un seul login, MFA, jetons |
|
| **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 |
|
| **Mail** (Dovecot, Postfix) | **LDAP direct** (bind) | IMAP/SMTP ne parlent pas OIDC ; username + mot de passe |
|
||||||
| **Système / services** (SSSD, etc.) | **LDAP direct** | annuaire standard |
|
| **App web SANS OIDC natif** | **`oauth2-proxy` devant**, lui-même client OIDC | met au SSO ce qui ne sait pas y aller seul |
|
||||||
| Tout | → **même OpenLDAP** | identité unifiée, aucun compte en double |
|
| 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
|
## Sens de provisionnement
|
||||||
|
|
||||||
Les identités se créent et se gèrent dans **OpenLDAP**. Keycloak **fédère** (lit LDAP en
|
Les identités se créent et se gèrent dans **OpenLDAP**. Keycloak **fédère** (lit LDAP en
|
||||||
|
|
@ -54,7 +60,10 @@ Keycloak : c'est LDAP la référence. Le mail bind directement sur LDAP.
|
||||||
## Conséquences pour Set-OPS
|
## Conséquences pour Set-OPS
|
||||||
|
|
||||||
- `serveur_openldap` (déployé) = le pilier annuaire, source de vérité.
|
- `serveur_openldap` (déployé) = le pilier annuaire, source de vérité.
|
||||||
- `serveur_keycloak` = pilier SSO, à **fédérer sur OpenLDAP** (User Federation → LDAP).
|
- `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_dovecot` / `serveur_postfix` = auth **LDAP direct** (cohérent avec ce modèle).
|
- `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.
|
- 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
|
## Phase 0 — au bureau, avant de partir
|
||||||
|
|
||||||
- [ ] **Recevoir la fiche de l'hébergeur** — les huit lignes du §7 de
|
- [ ] **Recevoir la fiche de l'hébergeur** — les dix lignes du §7 de
|
||||||
[`preparer-un-site-hebergeur.md`](preparer-un-site-hebergeur.md). Sans le nom exact
|
[`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.
|
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
|
- [ ] **Fixer l'`index` du site = l'`index` de son tenant.** Il n'y a pas de second
|
||||||
|
|
@ -44,11 +44,13 @@ par une **commande qui interroge le système**, jamais par une conviction.
|
||||||
moteur de pare-feu.
|
moteur de pare-feu.
|
||||||
|
|
||||||
> **Un site neuf se construit d'emblée dans l'adressage cible** — gestion en
|
> **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). Le site historique
|
> `10.<index>.0.0/24`, chemins en `192.168.<vlan>.0/24` (D-77, D-78). Sur un site vierge, la
|
||||||
> est encore en `10.0.x` et migrera par [`runbooks-exploitation.md`](runbooks-exploitation.md) §6.
|
> cible ne coûte rien — et deux sites qui porteraient le **même** plan de gestion rendraient
|
||||||
> 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 réseaux identiques ne peuvent pas s'atteindre.
|
||||||
> la **reprise mutuelle impossible** : deux plans de gestion 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` ».)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -203,17 +205,17 @@ make frontiere-plan make sdn-plan make placement-plan
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
underlay:
|
underlay:
|
||||||
# LE SEED DU SITE — le même que celui de son tenant, et la clé qu'on oublie.
|
# UN SITE N'A PAS D'INDEX (depuis le 2026-08-25). Il en portait un ; c'était un vestige.
|
||||||
# C'est elle qui dit au validateur que 10.<index>.0.0/16 est SON supernet, donc que
|
# Un site ne dérive AUCUN adressage — ses machines vivent sur des réseaux de fabric.
|
||||||
# la bande basse lui appartient. Sans elle, `make underlay` refuse le réseau de
|
# Cette valeur ne disait qu'une chose : quel supernet de tenant est le sien. Et elle le
|
||||||
# gestion en le prenant pour celui d'un AUTRE site — message déroutant, cause triviale.
|
# disait pour le site ENTIER alors qu'UN SEUL réseau est concerné.
|
||||||
index: <index>
|
#
|
||||||
# LES TENANTS QUE CE SITE PORTE — noms de dossier, pas de fantaisie. Les trois devis
|
# LES TENANTS QUE CE SITE PORTE — nom de dossier -> index. Les trois devis d'équipement
|
||||||
# d'équipement (frontière, commutateur, SDN) découvrent TOUTE la fédération : sans
|
# (frontière, commutateur, SDN) découvrent TOUTE la fédération : sans cette clé, le
|
||||||
# cette clé, le second site se voit proposer les règles, les VLAN et les zones du
|
# second site se voit proposer les règles, les VLAN et les zones du premier. Le matériel
|
||||||
# premier. Le matériel les accepte, aucune ne correspond jamais à un paquet, et rien
|
# les accepte, aucune ne correspond jamais à un paquet, et rien ne le signale.
|
||||||
# ne le signale. Un site UNIQUE n'a rien à déclarer ; c'est le second qui se nomme.
|
tenants:
|
||||||
tenants: [OPS-<tenant>]
|
OPS-<tenant>: <index>
|
||||||
routeur: <nom-du-commutateur> # racine du spanning-tree, pas un routeur
|
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.
|
# `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
|
routage_tenants: sdn # le routage inter-zone vit sur l'hyperviseur
|
||||||
|
|
@ -222,7 +224,12 @@ underlay:
|
||||||
dialecte: cisco # cisco | binardat — propriété du MATÉRIEL
|
dialecte: cisco # cisco | binardat — propriété du MATÉRIEL
|
||||||
stp: { mode: mstp, topologie: etoile }
|
stp: { mode: mstp, topologie: etoile }
|
||||||
reseaux:
|
reseaux:
|
||||||
- { nom: management, vlan: 10, sous_reseau: 10.<index>.0.0/24, passerelle: 10.<index>.0.1, mtu: 1500 }
|
# `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: transit-frontiere, vlan: 40, sous_reseau: 192.168.40.0/24, passerelle_sortie: 192.168.40.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 }
|
- { 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).
|
# Stockage : uniquement les réseaux réellement câblés (fabric: stockage, mtu 9000).
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@
|
||||||
|
|
||||||
Une intégration est de l'un des deux genres, et ils ne se déclarent pas au même endroit.
|
Une intégration est de l'un des deux genres, et ils ne se déclarent pas au même endroit.
|
||||||
|
|
||||||
**Universelle** — supervision, journaux, PKI. Il n'y a aucun choix de cible : un seul
|
**Universelle** — métriques, journaux, PKI, **résolution** et **source d'artefacts**
|
||||||
Prometheus, un seul Loki, une seule AC. Elle est déclarée **une fois, par le rôle**, dans
|
(les cinq qui portent `universelle: true`). Il n'y a aucun choix de cible : un seul
|
||||||
`roles/<role>/meta/integration.yml`, et tout hôte la reçoit :
|
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 :
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
integration:
|
integration:
|
||||||
|
|
@ -18,8 +20,16 @@ integration:
|
||||||
sauf_role: serveur_step_ca # facultatif — voir « exemptions »
|
sauf_role: serveur_step_ca # facultatif — voir « exemptions »
|
||||||
```
|
```
|
||||||
|
|
||||||
**Facultative** — `client_backup`, `client_smtp`, `client_unbound`. Là il y a un vrai choix,
|
**Facultative** — `client_backup` et `client_smtp`. Là il y a un vrai choix, et il se
|
||||||
et il se déclare par serveur, dans `plan/serveurs.yml : integrations`.
|
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.
|
||||||
|
|
||||||
**Pourquoi cette inversion.** Le plan portait 57 lignes d'intégration écrites à la main. 28
|
**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
|
d'entre elles disaient oui à quelque chose de vrai pour tous les hôtes — elles n'existaient
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,34 @@ 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
|
> au même titre que le [dimensionnement](dimensionnement-ressources.md). Décidée le
|
||||||
> 2026-06-26.
|
> 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. Décisions cadre (validées)
|
||||||
|
|
||||||
1. **Secrets : hors périmètre.** Le GUI n'affiche ni ne stocke aucun secret. Les
|
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.
|
`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
|
Le panneau peut, au plus, afficher une **liste de rappel en lecture seule** des
|
||||||
secrets attendus (sans valeur).
|
secrets attendus (sans valeur).
|
||||||
2. **Nomenclature : lecture seule** dans le panneau. L'édition de `supernet`/VLAN/
|
2. **Nomenclature : lecture seule** dans le panneau — à une exception près, l'**`index`**,
|
||||||
catégories reste dans `plan/nomenclature.yml` (autorité unique du plan réseau).
|
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.)*
|
||||||
3. **Conception avant code** (cette note).
|
3. **Conception avant code** (cette note).
|
||||||
|
|
||||||
## 2. Modèle : constante vs défaut surchargeable
|
## 2. Modèle : constante vs défaut surchargeable
|
||||||
|
|
@ -88,9 +108,10 @@ supporté) ; par groupe, dans le `group_vars/<groupe>/` correspondant.
|
||||||
- **Suite** : politiques de durcissement (défauts par groupe), DNS internes, relais
|
- **Suite** : politiques de durcissement (défauts par groupe), DNS internes, relais
|
||||||
SMTP, endpoints services centraux.
|
SMTP, endpoints services centraux.
|
||||||
|
|
||||||
## 8. Points ouverts à confirmer
|
## 8. Points ouverts — tranchés par ce qui a été construit
|
||||||
- OK pour la **migration `group_vars/*.yml` → répertoires** (§3) ? (alternative :
|
|
||||||
réécrire les fichiers existants en bloc, au prix des commentaires).
|
| Question de juin | Réponse, telle que le code la donne |
|
||||||
- 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 ?
|
| 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. |
|
||||||
- Périmètre MVP (§7) suffisant pour une première itération ?
|
| 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. |
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,22 @@ fois**, depuis un endroit unique, puis les laisser se **dériver** ou se **propa
|
||||||
- `setops_plan_dir` — chemin du plan.
|
- `setops_plan_dir` — chemin du plan.
|
||||||
|
|
||||||
### B. Réseau & nomenclature — `plan/nomenclature.yml`
|
### B. Réseau & nomenclature — `plan/nomenclature.yml`
|
||||||
- `supernet`, `cidr_hote`, `reservations`, `categories` (VLAN/sous-réseau/passerelle),
|
- **`index`** — le **seed**, et le seul champ d'adressage. Supernet, sous-réseaux,
|
||||||
`fonctions` (catégorie + service).
|
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.
|
||||||
|
|
||||||
### C. Hyperviseur Proxmox — `group_vars/proxmox.yml`
|
### C. Hyperviseur Proxmox — `group_vars/proxmox.yml`
|
||||||
- `proxmox_api_host`, `proxmox_api_user`, `proxmox_api_port`, `proxmox_validate_certs`.
|
- `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
|
- 🔒 `proxmox_api_token_id`, `proxmox_api_token_secret` — dans la **voûte unique** de
|
||||||
l'instance, `group_vars/all/vault.yml`. (L'ancienne `proxmox.vault.yml` reste lue en
|
l'instance, `group_vars/all/vault.yml`. **`proxmox.vault.yml` n'est plus lue** (retirée le
|
||||||
compatibilité si elle existe encore ; cf. `docs/config-proxmox.md`.)
|
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`.
|
||||||
- Golden template : `proxmox_clone_vmid_modele`, `proxmox_clone_source_nom`.
|
- Golden template : `proxmox_clone_vmid_modele`, `proxmox_clone_source_nom`.
|
||||||
- Placement par défaut : `proxmox_clone_noeud`, `proxmox_clone_stockage`,
|
- Placement par défaut : `proxmox_clone_noeud`, `proxmox_clone_stockage`,
|
||||||
`proxmox_clone_pont`, format, complet, timeout, disque, interface, démarrer.
|
`proxmox_clone_pont`, format, complet, timeout, disque, interface, démarrer.
|
||||||
|
|
@ -40,7 +48,7 @@ nftables baseline · fail2ban SSH · auditd · AppArmor · sysctl · unattended-
|
||||||
journald (rétention) · core_dumps · systemd_ssh_auto.
|
journald (rétention) · core_dumps · systemd_ssh_auto.
|
||||||
|
|
||||||
### F. Endpoints des services centraux (les rôles `client_*` en dérivent)
|
### F. Endpoints des services centraux (les rôles `client_*` en dérivent)
|
||||||
- DNS interne : plancher `/etc/hosts` (`hosts_statiques`) + PowerDNS + `client_unbound` (opt-in).
|
- 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.
|
||||||
- AC/PKI (`client_pki_ca_url` → infra-pki, provisioner).
|
- 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).
|
- 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).
|
- Relais courriel (`client_smtp_relais` → edge-mta, MTA Postfix, port 25).
|
||||||
|
|
@ -84,13 +92,13 @@ domaines publics, `edge`, autorité DNS, FQDN exposés.
|
||||||
| Intrant | Classe | Surcharge où ? |
|
| Intrant | Classe | Surcharge où ? |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `domaine_interne` | **Constante** | — |
|
| `domaine_interne` | **Constante** | — |
|
||||||
| Nomenclature (supernet, CIDR, catégories, fonctions) | **Constante** | — (le plan réseau est la loi) |
|
| Nomenclature (**`index`**, CIDR d'hôte, catégories, fonctions) | **Constante** | — (le seed est la loi ; l'adressage en dérive) |
|
||||||
| Accès Proxmox (API host/user/port/token) | **Constante** | — (un seul cluster) |
|
| Accès Proxmox (API host/user/port/token) | **Constante** | — (un seul cluster) |
|
||||||
| Golden template (vmid_modele, source_nom) | **Constante** | — |
|
| Golden template (vmid_modele, source_nom) | **Constante** | — |
|
||||||
| Secrets Vault | **Constante** 🔒 | — (gérés à part, jamais en clair) |
|
| Secrets Vault | **Constante** 🔒 | — (gérés à part, jamais en clair) |
|
||||||
| `fuseau_horaire` | Défaut | par hôte (rare) |
|
| `fuseau_horaire` | Défaut | par hôte (rare) |
|
||||||
| `proxmox_clone_noeud` / `stockage` / `pont` | Défaut | par hôte (`serveurs.yml`) |
|
| `proxmox_clone_noeud` / `stockage` / `pont` | Défaut | par hôte (`serveurs.yml`) |
|
||||||
| DNS internes (plancher `/etc/hosts` + PowerDNS + `client_unbound`) | Défaut | par hôte / groupe |
|
| DNS internes (plancher `/etc/hosts` + PowerDNS) | Défaut | par hôte / groupe |
|
||||||
| Politiques durcissement (SSH, nftables, fail2ban, journald…) | 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 |
|
| Relais SMTP, TLS internes | Défaut | par hôte / groupe |
|
||||||
| `ciuser`, compte `ansible` | Défaut | rarement surchargé |
|
| `ciuser`, compte `ansible` | Défaut | rarement surchargé |
|
||||||
|
|
@ -109,7 +117,17 @@ 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
|
`modeles_vm.yml`, `plan/nomenclature.yml`). À détailler dans la note de conception de la
|
||||||
fonctionnalité GUI.
|
fonctionnalité GUI.
|
||||||
|
|
||||||
## 4. Incohérences repérées (à corriger)
|
## 4. Incohérences repérées — soldées
|
||||||
- `fuseau_horaire` défini en **lab** seulement, absent de **production**.
|
|
||||||
- `group_vars/serveur_debian.yml` **référencé** (commentaire de prod `all.yml`) mais
|
Les deux écarts que cette section signalait n'existent plus (vérifié le 2026-09-06), et le
|
||||||
**absent** des deux environnements.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ flowchart TB
|
||||||
subgraph INST["③ INSTANCES — la flotte (inventory hosts.yml)"]
|
subgraph INST["③ INSTANCES — la flotte (inventory hosts.yml)"]
|
||||||
direction LR
|
direction LR
|
||||||
INV[["hosts.yml"]]
|
INV[["hosts.yml"]]
|
||||||
VMS(("11 VM<br/>infra-pki · dns · mail · edge<br/>idm · data · obs · mon · forge · web"))
|
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"))
|
||||||
end
|
end
|
||||||
|
|
||||||
ECO["④ ÉCOSYSTÈME souverain en service<br/>PKI · DNS · IdM/SSO · Données · Observabilité · Forge · Web"]
|
ECO["④ ÉCOSYSTÈME souverain en service<br/>PKI · DNS · IdM/SSO · Données · Observabilité · Forge · Web"]
|
||||||
|
|
@ -60,8 +60,9 @@ flowchart TB
|
||||||
réseau (VMID·VLAN·IP·gw) · ressources (cœurs·RAM·disque) · groupes · DSN+DNS
|
réseau (VMID·VLAN·IP·gw) · ressources (cœurs·RAM·disque) · groupes · DSN+DNS
|
||||||
│ = instanciation
|
│ = instanciation
|
||||||
▼
|
▼
|
||||||
③ INSTANCES — la flotte (inventory hosts.yml : 11 VM cohérentes)
|
③ INSTANCES — la flotte (inventory hosts.yml : 14 VM cohérentes)
|
||||||
infra-pki·dns·mail·edge · idm · data · obs · mon · forge · web
|
infra-pki · infra-dns · infra-mail · infra-edge · edge-mta · idm · data-sql
|
||||||
|
obs · mon · forge · collab · web-frontal · web-dorsal · ops
|
||||||
▲ clone du golden template + identité cloud-init
|
▲ clone du golden template + identité cloud-init
|
||||||
│ make deployer (rôles Ansible par groupe)
|
│ make deployer (rôles Ansible par groupe)
|
||||||
▼
|
▼
|
||||||
|
|
|
||||||
147
docs/metriques-conception.md
Normal file
147
docs/metriques-conception.md
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
# 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,12 +82,15 @@ VM destinée à devenir un template, pas un serveur de production
|
||||||
Vérifications utiles depuis le poste Ansible :
|
Vérifications utiles depuis le poste Ansible :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh ansible@10.0.2.99
|
ssh ansible@<ip-de-la-VM-modele> # l'adresse est celle que tu lui as donnee, pas une derivee du plan
|
||||||
ansible -i instance/inventories/lab/hosts.yml modeles_vm -m ping
|
ansible -i "$SETOPS_INVENTAIRE" modeles_vm -m ping
|
||||||
ansible -i instance/inventories/lab/hosts.yml modeles_vm -m setup
|
ansible -i "$SETOPS_INVENTAIRE" modeles_vm -m setup
|
||||||
```
|
```
|
||||||
|
|
||||||
Adapter l'utilisateur et l'adresse IP selon `instance/inventories/lab/hosts.yml`.
|
`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.
|
||||||
|
|
||||||
### Prérequis côté dépôt
|
### Prérequis côté dépôt
|
||||||
|
|
||||||
|
|
@ -107,7 +110,7 @@ modeles_vm
|
||||||
Les variables du template sont dans :
|
Les variables du template sont dans :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
instance/inventories/production/group_vars/modeles_vm.yml
|
instance/inventories/<inventaire>/group_vars/modeles_vm.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commande de préparation
|
### Commande de préparation
|
||||||
|
|
@ -153,7 +156,7 @@ Un résultat `changed=0` à la relance est le signal que le playbook est idempot
|
||||||
|
|
||||||
| Symptôme | Cause probable | Action |
|
| Symptôme | Cause probable | Action |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `UNREACHABLE` | IP, SSH, utilisateur ou clé SSH incorrecte. | Vérifier `instance/inventories/lab/hosts.yml`, cloud-init et tester `ssh`. |
|
| `UNREACHABLE` | IP, SSH, utilisateur ou clé SSH incorrecte. | Vérifier l'inventaire résolu (`$SETOPS_INVENTAIRE`), 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 `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. |
|
| é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. |
|
| erreur handler SSH | Handler manquant ou nom `notify` incohérent. | Vérifier les handlers du rôle SSH avant de relancer. |
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,17 @@ statut fédéré/local et production ; signale toute **collision d'index** :
|
||||||
make instances
|
make instances
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
★ OPS-Chezlepro 13 1131-1136 fédérée prod
|
INSTANCE INDEX VLAN FEDERE PROD
|
||||||
OPS-Technolibre 2 1021-1026 fédérée
|
OPS-Chezlepro-lab 13 1131-1136 LOCAL non
|
||||||
OPS-Chezlepro-lab 1 1011-1016 local
|
* OPS-Chezlepro 17 1171-1176 oui oui
|
||||||
|
OPS-Technolibre 23 1231-1236 oui non
|
||||||
|
* = instance active (symlink 'instance'). Basculer : make instance-utiliser NOM=<depot>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*(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
|
**Basculer l'active** — le symlink, avec garde-fous (le dossier existe, `instance` est
|
||||||
bien un symlink) :
|
bien un symlink) :
|
||||||
|
|
||||||
|
|
@ -64,8 +70,9 @@ 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.
|
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 ; la bascule reste au
|
Le GUI (onglet **Réseau**) montre la même flotte et les collisions — **et sait basculer**,
|
||||||
CLI (chirurgie de symlink, mal placée dans une interface web).
|
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.)*
|
||||||
|
|
||||||
**Deux réflexes.** (1) Avant tout déploiement, `make instance-courante` : la seule vraie
|
**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
|
façon de se tromper est de déployer sur la mauvaise flotte (la colonne `prod` est là pour
|
||||||
|
|
@ -97,7 +104,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`,
|
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`**.
|
`../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
|
De là : **active** = ce que résout le symlink `instance` ; **fédérée** = `index` présent
|
||||||
*et* `federe ≠ false`. Les **modèles** (`Set-OPS-Modeles/integral/…`) sont un cran plus
|
*et* `federe ≠ false`. Les **modèles** (`exemples/modeles/…` ici, `Set-OPS-modeles/…` pour les modèles privés) sont un cran plus
|
||||||
profond — le glob ne les attrape pas, volontairement.
|
profond — le glob ne les attrape pas, volontairement.
|
||||||
|
|
||||||
Conséquence : « inscrire » une instance = la déposer à côté des autres. Rien à éditer,
|
Conséquence : « inscrire » une instance = la déposer à côté des autres. Rien à éditer,
|
||||||
|
|
@ -124,6 +131,9 @@ sable met `false` et affiche « bac à sable ».
|
||||||
et la cible Proxmox (`proxmox.yml`).
|
et la cible Proxmox (`proxmox.yml`).
|
||||||
3. Créer la voûte unique depuis le gabarit (cf. [`config-proxmox.md`](config-proxmox.md)) :
|
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`.
|
`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`,
|
4. `make instance-utiliser NOM=OPS-ClientX` puis `make instancier-appliquer`,
|
||||||
`make inventaire-ui`.
|
`make inventaire-ui`.
|
||||||
|
|
||||||
|
|
@ -144,7 +154,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` :
|
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
|
`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,
|
*modèle* (libellés de zones + placement des fonctions) ; supernet, sous-réseaux,
|
||||||
passerelles, VLAN et VMID se **dérivent** (`scripts/inventory_rules` : `supernet_de`,
|
passerelles, VLAN et VMID se **dérivent** (`scripts/inventory_rules.py` : `supernet_de`,
|
||||||
`base3_de`, `passerelle_de`, `vlan_de`). Changer `index` rederive tout le réseau — et la
|
`base3_de`, `passerelle_de`, `vlan_de`). Changer `index` rederive tout le réseau — et la
|
||||||
preuve **P20** interdit tout adressage stocké.
|
preuve **P20** interdit tout adressage stocké.
|
||||||
|
|
||||||
|
|
@ -166,10 +176,13 @@ 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
|
`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.
|
montre « local »). Il garde son adressage dérivé et reste déployable sur *son* infra.
|
||||||
|
|
||||||
**Plafond théorique : 255 écosystèmes fédérés** (index 1 à 255), borné par l'IPv4
|
**Plafond : le 2ᵉ octet IPv4.** `valider_index` borne l'index à **0–255**
|
||||||
`10.<index>` (2ᵉ octet 1→255). Le décalage de +10, retiré le 2026-08-12, en confisquait
|
(`INDEX_MIN`/`INDEX_MAX`, `scripts/inventory_rules.py`) — la garde est posée **à la source
|
||||||
dix — et surtout, il empêchait de lire l'index directement dans l'adresse. Le VLAN (≤ 4094) autorise jusqu'à 308, le VMID bien
|
de la dérivation**, donc aucune fonction ne peut fabriquer une adresse hors bornes, d'où
|
||||||
plus — c'est donc l'adressage IP qui plafonne. Au-delà d'une poignée d'instances
|
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
|
||||||
co-localisées, confier l'allocation à un **IPAM** (NetBox) plutôt qu'au moteur — cf.
|
co-localisées, confier l'allocation à un **IPAM** (NetBox) plutôt qu'au moteur — cf.
|
||||||
[`positionnement.md`](positionnement.md).
|
[`positionnement.md`](positionnement.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,16 @@ infra-pki-01
|
||||||
infra-edge-01
|
infra-edge-01
|
||||||
infra-mail-01
|
infra-mail-01
|
||||||
infra-dns-01
|
infra-dns-01
|
||||||
|
edge-mta-01
|
||||||
idm-01
|
idm-01
|
||||||
data-01
|
data-sql-01
|
||||||
obs-01
|
obs-01
|
||||||
mon-01
|
mon-01
|
||||||
forge-01
|
forge-01
|
||||||
collab-01
|
collab-01
|
||||||
web-frontal-01
|
web-frontal-01
|
||||||
web-dorsal-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 :
|
La couche applicative web suit le même format. Le tier est porté par la fonction, au singulier puisqu'il nomme une instance :
|
||||||
|
|
@ -48,11 +50,13 @@ Les anciens noms de test `web-01` et `web-02` sont retirés. Ils ne doivent pas
|
||||||
| `infra-pki-01` | `serveur_step_ca` |
|
| `infra-pki-01` | `serveur_step_ca` |
|
||||||
| `infra-edge-01` | `serveur_nginx` |
|
| `infra-edge-01` | `serveur_nginx` |
|
||||||
| `infra-mail-01` | `serveur_dovecot` (mail-store) |
|
| `infra-mail-01` | `serveur_dovecot` (mail-store) |
|
||||||
| `infra-dns-01` | `serveur_powerdns` |
|
| `edge-mta-01` | `serveur_postfix`, `serveur_rspamd` (ce qui parle à l'extérieur) |
|
||||||
|
| `infra-dns-01` | `serveur_powerdns`, `serveur_resolveur` |
|
||||||
| `idm-01` | `serveur_openldap`, `serveur_keycloak` |
|
| `idm-01` | `serveur_openldap`, `serveur_keycloak` |
|
||||||
| `data-01` | `serveur_postgresql`, `serveur_redis` |
|
| `data-sql-01` | `serveur_postgresql`, `serveur_redis` |
|
||||||
| `obs-01` | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` |
|
| `obs-01` | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` |
|
||||||
| `mon-01` | `serveur_icinga` |
|
| `mon-01` | `serveur_icinga`, `serveur_icingaweb2`, `serveur_oauth2_proxy` |
|
||||||
|
| `ops-01` | `serveur_ops`, `serveur_ops_tenant` (le runner de l'écosystème) |
|
||||||
| `forge-01` | `serveur_forgejo` |
|
| `forge-01` | `serveur_forgejo` |
|
||||||
| `collab-01` | `serveur_nextcloud`, `serveur_collabora` |
|
| `collab-01` | `serveur_nextcloud`, `serveur_collabora` |
|
||||||
| `web-frontal-01`, `web-frontal-02` | `serveur_web_frontal` |
|
| `web-frontal-01`, `web-frontal-02` | `serveur_web_frontal` |
|
||||||
|
|
@ -60,45 +64,47 @@ 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.
|
Les groupes restent fins et composables. La cohabitation se fait en associant plusieurs groupes au même hôte.
|
||||||
|
|
||||||
## Plages VMID
|
## VMID et adressage : tout dérive du seed `index`
|
||||||
|
|
||||||
| Plage | Usage |
|
> **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
|
||||||
| `91xxx` | fondations transversales : PKI, reverse proxy, SMTP |
|
> chiffres (`91xxx`…`99xxx`). Il n'y a plus de plages à réserver, et il n'y a plus *un*
|
||||||
| `92xxx` | identité : LDAP, SSO |
|
> réseau : chaque écosystème dérive le sien.
|
||||||
| `93xxx` | données et cache : PostgreSQL, Redis |
|
|
||||||
| `94xxx` | observabilité et supervision |
|
|
||||||
| `95xxx` | applications internes |
|
|
||||||
| `99xxx` | modèles, essais initiaux ou exceptions documentées |
|
|
||||||
|
|
||||||
## Plan d'adressage interne
|
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
|
||||||
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).
|
de stocker un adressage quelconque (`supernet`, `sous_reseau`, `passerelle`, `vlan`).
|
||||||
|
|
||||||
| 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
|
```text
|
||||||
hostname = <fonction>-<NN>
|
supernet = 10.<index>.0.0/16
|
||||||
VMID = 9 · catégorie · service · NN
|
zone (3 oct.) = 10.<index>.(15 + catégorie)
|
||||||
VLAN = catégorie.vlan
|
sous-réseau = 10.<index>.(15 + catégorie).0/24
|
||||||
IP = 10.0.<vlan>.(service × 10 + NN)
|
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>
|
||||||
```
|
```
|
||||||
|
|
||||||
`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.
|
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.
|
||||||
|
|
||||||
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`.
|
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`.
|
||||||
|
|
||||||
La segmentation `10.0.0.0/16` remplace l'ancienne plage d'essais `192.168.12.x`.
|
*(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).
|
||||||
|
|
||||||
## Variables de provisioning d'hôte
|
## Variables de provisioning d'hôte
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,18 @@ 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
|
est une instance. Ce document décrit le modèle, les registres, les commandes et
|
||||||
le flux de travail.
|
le flux de travail.
|
||||||
|
|
||||||
> Règle d'or : **`instance/inventories/production/hosts.yml` est GÉNÉRÉ. Ne jamais l'éditer
|
> Règle d'or : **`instance/inventories/<inventaire>/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`).
|
> à 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
|
## 1. Le modèle : deux ancres, cinq liens
|
||||||
|
|
@ -34,11 +43,14 @@ liaison — seulement une **capacité** qu'une VM fournit (le rôle appliqué).
|
||||||
|
|
||||||
## 2. Les registres (source unique de vérité)
|
## 2. Les registres (source unique de vérité)
|
||||||
|
|
||||||
Tous sous `docs/`, machine-lisibles, validés, consommés par le GUI, le CLI et Ansible.
|
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.
|
||||||
|
|
||||||
| Registre | Décrit | Champs clés |
|
| Registre | Décrit | Champs clés |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `nomenclature.yml` | nommage & adressage | `fonctions` (catégorie/service), `categories` (VLAN/sous-réseau/passerelle), `supernet` |
|
| `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. |
|
||||||
| `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`) |
|
| `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) |
|
| `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` |
|
| `bases-donnees.yml` | serveurs de BD + bases | `serveurs_bd` ; `bases_donnees` : `serveur`/`base`/`proprietaire`/`secret`(Vault), `consommateur` + `portee` (`application`/`groupe`/`hote`), `usage` |
|
||||||
|
|
@ -46,9 +58,12 @@ Tous sous `docs/`, machine-lisibles, validés, consommés par le GUI, le CLI et
|
||||||
| `dependances-groupes.yml` | prérequis entre groupes | `requiert_groupes_actifs` |
|
| `dependances-groupes.yml` | prérequis entre groupes | `requiert_groupes_actifs` |
|
||||||
|
|
||||||
### Dérivations clés
|
### Dérivations clés
|
||||||
- **Nommage/adressage** : tout part de la `fonction` de l'hôte (`web-frontal-03`).
|
- **Nommage/adressage** : tout part du seed `index` et de la `fonction` de l'hôte.
|
||||||
`VMID = 9·catégorie·service·NN`, `VLAN = catégorie.vlan`,
|
`supernet = 10.<index>.0.0/16`, `zone = 10.<index>.(15+catégorie)`,
|
||||||
`IP = 10.0.<vlan>.(service×10 + NN)`. Voir `docs/nomenclature-vm.md`.
|
`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.)*
|
||||||
- **DSN** (lien application↔base) : `<type>://<proprietaire>:<secret>@<hôte>:<port>/<base>`.
|
- **DSN** (lien application↔base) : `<type>://<proprietaire>:<secret>@<hôte>:<port>/<base>`.
|
||||||
Une application reçoit les bases où `(portee=application ET consommateur=elle)`
|
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)`.
|
OU `(portee=groupe ET consommateur=son groupe)` OU `(portee=hote ET consommateur=son hôte)`.
|
||||||
|
|
@ -83,19 +98,33 @@ c'est le feu vert pour appliquer.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Via le GUI — `make inventaire-ui`
|
### Via le GUI — `make inventaire-ui`
|
||||||
Cinq vues :
|
|
||||||
|
**Douze vues**, éditables ou dérivées :
|
||||||
|
|
||||||
| Vue | Rôle |
|
| Vue | Rôle |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Inventaire** | **lecture seule** (inventaire généré) — vue d'ensemble des hôtes |
|
| **Serveurs** *(éditable)* | les VM du plan : fonction/état/placement/intégrations (VMID·IP·VLAN dérivés en direct) ; bouton **« Appliquer le plan »** |
|
||||||
| **Serveurs** | éditer 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 |
|
||||||
| **Chaîne** | vue holistique par hôte : groupes → rôles, et par application ses `expose` / `requiert` / bases (DSN) |
|
| **Bases** *(éditable)* | serveurs de BD et bases (portée + consommateur), DSN affiché (secret masqué) |
|
||||||
| **Applications** | éditer les applications : groupe/hôte/port/requiert/expose |
|
| **Domaines** *(éditable)* | zones publiques vs internes, autorité · edge · DNSSEC, expositions |
|
||||||
| **Bases** | éditer serveurs de BD et bases (portée + consommateur), DSN affiché |
|
| **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 »** |
|
||||||
|
|
||||||
Édition → **Sauvegarder** (écrit le registre) → **Appliquer le plan** (régénère
|
Édition → **Sauvegarder** (écrit le registre) → **Appliquer le plan** (régénère
|
||||||
`hosts.yml`). L'écriture directe de l'inventaire est refusée (409).
|
`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`
|
### Via le CLI / `make`
|
||||||
Chaque registre a son script miroir et ses cibles `make` :
|
Chaque registre a son script miroir et ses cibles `make` :
|
||||||
|
|
||||||
|
|
@ -137,7 +166,7 @@ registres + **`node --check` du JS du GUI**).
|
||||||
La règle de résolution vit **une seule fois**, en Python (`scripts/inventory_rules.py`),
|
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`) :
|
et est exposée à Ansible par un *filter plugin* (`filter_plugins/registres.py`) :
|
||||||
`bases_de_application`, `applications_de_hote`, `expositions_des_applications`,
|
`bases_de_application`, `applications_de_hote`, `expositions_des_applications`,
|
||||||
`chaine_connexion`. Les playbooks par application (`serveur_web_dorsal`/`_frontaux`)
|
`chaine_connexion`. Les playbooks par application (`serveur_web_dorsal`, `serveur_web_frontal`)
|
||||||
itèrent ainsi sur les applications de l'hôte et résolvent leurs DSN.
|
itèrent ainsi sur les applications de l'hôte et résolvent leurs DSN.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -159,7 +188,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
|
## 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
|
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'un loup) vit
|
aucune donnée d'instance. Une **instance** (le plan + l'inventaire d'une organisation) vit
|
||||||
dans son **propre dépôt** (ex. `OPS-monatelier`).
|
dans son **propre dépôt** (ex. `OPS-monatelier`).
|
||||||
|
|
||||||
Le moteur localise l'instance via **`SETOPS_INSTANCE`** (défaut : `instance`). Deux
|
Le moteur localise l'instance via **`SETOPS_INSTANCE`** (défaut : `instance`). Deux
|
||||||
|
|
@ -168,7 +197,7 @@ modèles :
|
||||||
- **Modèle A — dépôts frères** (en cours) : moteur et instance côte à côte ; un
|
- **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
|
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.
|
l'instance sans configuration. Idéal quand on développe le moteur *et* l'instance.
|
||||||
- **Modèle B — moteur en sous-module** (futur, pour la meute) : l'instance épingle
|
- **Modèle B — moteur en sous-module** (futur, quand plusieurs écosystèmes vivront chez des exploitants distincts) : l'instance épingle
|
||||||
une version du moteur ; `SETOPS_INSTANCE` pointe la racine de l'instance.
|
une version du moteur ; `SETOPS_INSTANCE` pointe la racine de l'instance.
|
||||||
|
|
||||||
Pour brancher une instance (modèle A) :
|
Pour brancher une instance (modèle A) :
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,13 @@ Raisons assumées :
|
||||||
1. **Souveraineté** — mission explicite du dépôt. NetBox + AWX + Backstage = trois
|
1. **Souveraineté** — mission explicite du dépôt. NetBox + AWX + Backstage = trois
|
||||||
applications lourdes à héberger et maintenir (Django + PostgreSQL, etc.). Set-OPS
|
applications lourdes à héberger et maintenir (Django + PostgreSQL, etc.). Set-OPS
|
||||||
reste possédé en entier, sans dépendance.
|
reste possédé en entier, sans dépendance.
|
||||||
2. **Bon dimensionnement** — ~13 VM. NetBox est de la machinerie d'échelle entreprise.
|
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.
|
||||||
3. **Modèle sur-mesure** — NetBox exprimerait nos DSN / expositions à coups de
|
3. **Modèle sur-mesure** — NetBox exprimerait nos DSN / expositions à coups de
|
||||||
*custom fields* et de plugins, moins naturellement que nos registres.
|
*custom fields* et de plugins, moins naturellement que nos registres.
|
||||||
4. **Maîtrise** — chaque ligne est comprise et auditable.
|
4. **Maîtrise** — chaque ligne est comprise et auditable.
|
||||||
|
|
@ -53,13 +59,29 @@ Raisons assumées :
|
||||||
|
|
||||||
## 3. Ce que le maison NE fait PAS (et que les outils mûrs ont)
|
## 3. Ce que le maison NE fait PAS (et que les outils mûrs ont)
|
||||||
|
|
||||||
À garder en tête honnêtement — ce sont des fonctions qu'on n'a pas, pas des bugs :
|
> **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.
|
||||||
|
|
||||||
- historique/audit des changements (au-delà de `git`) ;
|
Ce qui manque vraiment :
|
||||||
- RBAC multi-utilisateurs ;
|
|
||||||
- détection de conflits IPAM, réservations, gestion d'adresses à grande échelle ;
|
- **historique/audit des changements** au-delà de `git` — un journal applicatif, avec ses
|
||||||
- webhooks / intégrations tierces, API riche (REST/GraphQL) ;
|
auteurs et ses motifs, que `git log` ne rend qu'imparfaitement ;
|
||||||
- écosystème de plugins, communauté, correctifs de sécurité maintenus par d'autres.
|
- **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*. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -75,16 +97,38 @@ ne lui ajoute pas de fonctionnalités de type NetBox/AWX.
|
||||||
Adopter l'outil du marché — **sans réécrire**, en branchant Ansible/notre GUI
|
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 :
|
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 |
|
| Besoin qui apparaît | Adopter |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| RBAC, plusieurs opérateurs, historique d'audit, secrets/credentials centralisés | **AWX** (exécution) |
|
| **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 |
|
||||||
| IPAM sérieux, détection de conflits, source de vérité partagée, API/webhooks | **NetBox** (et `nb_inventory` remplace notre générateur) |
|
| 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) |
|
||||||
| Catalogue de services / portail développeur, ownership, scaffolding | **Backstage** |
|
| Catalogue de services / portail développeur, ownership, scaffolding | **Backstage** |
|
||||||
| Provisionnement de VM déclaratif et reproductible à plus grande échelle | **Terraform** (Proxmox) ou **NixOS + Colmena** |
|
| 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
|
**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
|
dans Set-OPS, c'est le signal d'adopter l'outil correspondant plutôt que de
|
||||||
prolonger le maison.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -92,6 +136,15 @@ prolonger le maison.
|
||||||
|
|
||||||
- On **garde** le plan de contrôle maison : il fonctionne, il est souverain et
|
- 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 ».
|
bien dimensionné pour aujourd'hui. Pas de réécriture sur NetBox « par principe ».
|
||||||
- On **gèle** son périmètre fonctionnel (voir §4).
|
- 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 **réévalue** à l'échéance d'un seuil ci-dessus, ou si la charge de
|
- 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.
|
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,8 +2,12 @@
|
||||||
|
|
||||||
> **Pour qui :** qui **évalue le moteur** — ce qu'il sait faire, et ce qu'il ne sait pas encore.
|
> **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, au 2026-07-02.
|
> Bilan des capacités du moteur. Établi le 2026-07-02, **revu le 2026-09-06** : la
|
||||||
> Fondé sur l'état réel du dépôt (≈50 rôles, ≈30 playbooks de groupe, le moteur de plan, la GUI).
|
> 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.
|
||||||
>
|
>
|
||||||
> Deux niveaux de maturité sont distingués honnêtement :
|
> Deux niveaux de maturité sont distingués honnêtement :
|
||||||
> - **⭐ Prouvé** — déployé et vérifié de bout en bout sur cluster Proxmox réel.
|
> - **⭐ Prouvé** — déployé et vérifié de bout en bout sur cluster Proxmox réel.
|
||||||
|
|
@ -19,8 +23,9 @@
|
||||||
complet — PKI, identité, DNS, web, courriel — cloné depuis un golden template durci, piloté par
|
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.**
|
une GUI utilisable sans IA, en multi-tenant, tout en logiciel libre.**
|
||||||
|
|
||||||
Le cœur d'infrastructure est **prouvé de bout en bout** ; la couche des services applicatifs
|
Le cœur d'infrastructure **et** la couche des services applicatifs (SSO, données, forge,
|
||||||
(SSO, données, forge, observabilité) est **outillée et prête à éprouver**.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -31,8 +36,11 @@ Pouvoir central : **plan déclaratif → écosystème vivant**. On décrit *quoi
|
||||||
|
|
||||||
- **`scripts/instancier.py`** — lit `nomenclature / serveurs / applications / bases-donnees.yml`
|
- **`scripts/instancier.py`** — lit `nomenclature / serveurs / applications / bases-donnees.yml`
|
||||||
et **dérive** tout : VMID, IP, groupes d'inventaire, dimensionnement. ⭐
|
et **dérive** tout : VMID, IP, groupes d'inventaire, dimensionnement. ⭐
|
||||||
- **Nomenclature fédérée** — un `index` + catégorie + service produit un **VMID
|
- **Nomenclature fédérée** — le seul champ saisi est l'**`index`** ; supernet, VLAN, IP,
|
||||||
`{index}{cat}{svc}{seq}`** et une IP déterministes, sans collision entre instances. ⭐
|
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. ⭐
|
||||||
- **Dimensionnement automatique** — chaque rôle porte une `meta/empreinte.yml`
|
- **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. ⭐
|
(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,
|
- **Multi-instance = multi-tenant** — le symlink `instance/` pointe vers un dépôt par écosystème,
|
||||||
|
|
@ -47,8 +55,10 @@ 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
|
- **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),
|
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. ⭐
|
**sonde de vivacité**, passage auto en « actif », jeton d'authentification. ⭐
|
||||||
- **Voûte au déploiement** — secrets chiffrés par Ansible Vault, saisis au déploiement, **jamais
|
- **Voûte au déploiement** — secrets chiffrés par Ansible Vault, **jamais en clair** dans la
|
||||||
en clair** dans la GUI ; usage via le fichier de mot de passe, non interactif. ⭐
|
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. ⭐
|
||||||
- **Validation intégrée** — `syntax-check`, `ansible-lint` (profil *production*), `verifier`. ⭐
|
- **Validation intégrée** — `syntax-check`, `ansible-lint` (profil *production*), `verifier`. ⭐
|
||||||
|
|
||||||
## 3. Le socle — un golden template durci
|
## 3. Le socle — un golden template durci
|
||||||
|
|
@ -60,9 +70,13 @@ Impératif fondateur : un sysadmin exploite l'outil **sans IA** ; l'IA n'assiste
|
||||||
`template_cleanup`.
|
`template_cleanup`.
|
||||||
- **SSH** : `ssh_baseline` puis `ssh_hardening` (bascule mot de passe → clé, seulement après
|
- **SSH** : `ssh_baseline` puis `ssh_hardening` (bascule mot de passe → clé, seulement après
|
||||||
validation de l'accès par clé).
|
validation de l'accès par clé).
|
||||||
- **Durcissement** : `apparmor`, `auditd`, `fail2ban_ssh`, `sysctl_hardening`,
|
- **Durcissement** — les **dix** rôles que compose le groupe `serveur_durci` :
|
||||||
`hardening_packages`, `unattended_upgrades`, `journald`, `core_dumps`,
|
`hardening_packages`, `sysctl_hardening`, `core_dumps`, `unattended_upgrades`, `apparmor`,
|
||||||
`nftables_baseline` (installé et préparé, **non activé par défaut**).
|
`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`).
|
||||||
- **Proxmox** : clonage depuis le golden template + redimensionnement disque (grow-only).
|
- **Proxmox** : clonage depuis le golden template + redimensionnement disque (grow-only).
|
||||||
|
|
||||||
> Séparation nette : **Proxmox + cloud-init** donnent l'identité initiale de la VM ;
|
> Séparation nette : **Proxmox + cloud-init** donnent l'identité initiale de la VM ;
|
||||||
|
|
@ -83,20 +97,42 @@ 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 /
|
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).
|
boîtes à l'intérieur** (défense en profondeur).
|
||||||
|
|
||||||
## 5. Les services outillés — rôles présents 🔧
|
## 5. Les services applicatifs — prouvés depuis ⭐
|
||||||
|
|
||||||
Construits et câblables par le plan ; **à éprouver** en déploiement réel avant de les déclarer
|
Ce paragraphe annonçait, jusqu'au 2026-09-06, des rôles « construits mais à éprouver ».
|
||||||
prouvés.
|
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).
|
||||||
|
|
||||||
- **SSO web** : `serveur_keycloak` (architecture décidée : OpenLDAP source de vérité, Keycloak
|
- **SSO web** : `serveur_keycloak` — OpenLDAP source de vérité, fédération LDAP automatisée,
|
||||||
fédéré en OIDC, courriel en bind LDAP direct).
|
courriel en bind LDAP direct. ⭐
|
||||||
- **Données** : `serveur_postgresql`, `serveur_redis`.
|
- **Passerelle SSO** : `serveur_oauth2_proxy` — met au SSO une application sans OIDC natif
|
||||||
- **Forge logicielle** : `serveur_forgejo`.
|
(éprouvé devant Icinga Web 2). ⭐
|
||||||
- **Observabilité** : `serveur_prometheus`, `serveur_grafana`, `serveur_loki`, `serveur_icinga`,
|
- **Données** : `serveur_postgresql`, `serveur_redis` — bases et comptes dérivés du registre,
|
||||||
avec les clients `client_metrique`, `client_journal`, `client_supervision`.
|
TLS `verify-full` de bout en bout. ⭐
|
||||||
- **Intégrations transverses** : `client_pki`, `client_backup`, `client_smtp`, `client_metrique`, `client_journal`, `client_unbound`.
|
- **Forge logicielle** : `serveur_forgejo` — Git + PostgreSQL + SSO OIDC. ⭐
|
||||||
- **Méta-rôles d'agrégation** : `identity`, `applications`, `database`, `web`, `monitoring`,
|
- **Observabilité** : `serveur_prometheus`, `serveur_grafana`, `serveur_loki`, avec les clients
|
||||||
`backup`, `storage`.
|
`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é.
|
||||||
|
|
||||||
## 6. Les patrons d'ingénierie — la valeur invisible ⭐
|
## 6. Les patrons d'ingénierie — la valeur invisible ⭐
|
||||||
|
|
||||||
|
|
@ -119,9 +155,13 @@ prouvés.
|
||||||
|
|
||||||
## Prochaines frontières
|
## Prochaines frontières
|
||||||
|
|
||||||
- **Éprouver** les services outillés (Keycloak fédéré, observabilité, PostgreSQL, Forgejo).
|
- **Consigner l'usage** de la collaboration (dépôt de fichier, édition partagée) — le seul
|
||||||
|
🔧 qui reste.
|
||||||
- **Courriel Étape B** (public) : DNS public (MX, SPF, **DKIM** — clé `setops._domainkey` déjà
|
- **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.
|
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
|
- **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é.
|
contrôle maison, qui reste volontairement gelé.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,26 @@ 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
|
## 2. Le plan d'adressage — la seule chose à respecter à la lettre
|
||||||
|
|
||||||
**Un site dérive du même `index` que son tenant.** Il n'y a pas de second registre à
|
**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 l'underlay occupe la **bande
|
tenir : le seul seed de tout l'adressage reste l'`index`, et **sites et tenants se
|
||||||
basse** du supernet — celle que la dérivation des tenants n'alloue jamais.
|
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.
|
||||||
|
|
||||||
| Rôle | VLAN | Sous-réseau | MTU | Nature |
|
| Rôle | VLAN | Sous-réseau | MTU | Nature |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| **Gestion** | 10 | `10.<index>.0.0/24` | 1500 | **destination** — unique par site |
|
| **Gestion** | 10 *(ou aucun — voir ci-dessous)* | `10.<index>.0.0/24` | 1500 | **destination** — unique par site |
|
||||||
| Sortie des tenants | 40 | `192.168.40.0/24` | 1500 | chemin — identique partout |
|
| 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 |
|
| Transport VXLAN | 50 | `192.168.50.0/24` | 1500 | chemin — identique partout |
|
||||||
| Stockage iSCSI | 20 | `192.168.20.0/24` | 9000 | chemin — identique partout |
|
| Stockage iSCSI | 20 | `192.168.20.0/24` | 9000 | chemin — identique partout |
|
||||||
|
|
@ -41,6 +54,12 @@ basse** du supernet — celle que la dérivation des tenants n'alloue jamais.
|
||||||
Index 17 → gestion en `10.17.0.0/24` ; index 11 → `10.11.0.0/24`. Deux sites ne peuvent
|
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.
|
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
|
**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
|
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.
|
numéro de VLAN — `192.168.20.x` est sur le VLAN 20.
|
||||||
|
|
@ -73,7 +92,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
|
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
|
à 1500, les VM tournent à **1450** — automatiquement, mais à condition que 1500 passe
|
||||||
réellement de bout en bout sur les VLAN 11 et 40. Un MTU rogné en chemin donne le pire des
|
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
|
||||||
symptômes : les petites requêtes passent, les grosses meurent, et rien n'est signalé.
|
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
|
Le jumbo (9000) sur le stockage est facultatif. **À moitié configuré, il ne fonctionne
|
||||||
|
|
@ -216,8 +235,15 @@ réseaux **défait en silence l'isolation inter-tenant**. WireGuard s'y prête b
|
||||||
|
|
||||||
## 9. Ce qu'il ne faut pas faire
|
## 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 ;
|
- **Ne pas créer de VM à la main** pour le tenant. Elles sont toutes dérivées du plan ; une
|
||||||
une VM créée à côté est invisible pour l'outil, qui la détruira sans le savoir.
|
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 configurer le SDN** : l'outil le fait entièrement.
|
- **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 é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
|
- **Ne pas contourner un blocage en silence.** Un point resté ouvert et *signalé* se règle
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,55 @@ Créer une nouvelle VM dans Proxmox avec des paramètres sobres.
|
||||||
Exemple :
|
Exemple :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Nom de la VM : debian13-template
|
Nom de la VM : modeleSetOPS ← voir l'encadré : ce nom N'EST PAS libre
|
||||||
VMID : 9000 ou autre ID réservé aux modèles
|
VMID : 9000 ou autre ID réservé aux modèles
|
||||||
OS : Debian 13
|
OS : Debian 13
|
||||||
BIOS : OVMF / UEFI
|
BIOS : OVMF / UEFI
|
||||||
Machine : q35
|
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
|
### Disque EFI Proxmox
|
||||||
|
|
||||||
Avec OVMF/UEFI, Proxmox crée un petit disque EFI, par exemple :
|
Avec OVMF/UEFI, Proxmox crée un petit disque EFI, par exemple :
|
||||||
|
|
@ -729,7 +771,13 @@ Exemple :
|
||||||
qm template 9000
|
qm template 9000
|
||||||
```
|
```
|
||||||
|
|
||||||
À partir de là, le modèle peut être cloné.
|
À 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
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -740,14 +788,14 @@ Après conversion du modèle, le flux normal passe par `make` et l'API Proxmox.
|
||||||
Les paramètres communs Proxmox sont dans :
|
Les paramètres communs Proxmox sont dans :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
instance/inventories/production/group_vars/proxmox.yml
|
instance/inventories/<inventaire>/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
|
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` :
|
des autres `vault_*`), semée par `make config` :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
instance/inventories/production/group_vars/all/vault.yml
|
instance/inventories/<inventaire>/group_vars/all/vault.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
Créer le clone et l'ajouter à l'inventaire :
|
Créer le clone et l'ajouter à l'inventaire :
|
||||||
|
|
@ -771,7 +819,9 @@ 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 :
|
Après le premier démarrage, tester l'accès :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh ansible@10.0.15.31
|
# 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)
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensuite appliquer la conformité Ansible :
|
Ensuite appliquer la conformité Ansible :
|
||||||
|
|
|
||||||
|
|
@ -11,48 +11,70 @@
|
||||||
| `client_journal` | egress | 3100 | tcp | serveur_loki | tls | Expédition des journaux par Alloy vers le collecteur central Loki (HTTPS, cert step-ca). |
|
| `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_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_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. |
|
| `client_smtp` | egress | 25 | tcp | serveur_postfix | starttls | Relais des notifications locales vers le MTA central (Postfix), STARTTLS. |
|
||||||
| `client_unbound` | ingress | 53 | udp | localhost | clair | Résolveur local sur boucle locale (les processus du nœud interrogent 127.0.0.1). |
|
| `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. |
|
||||||
| `client_unbound` | egress | 53 | tcp | serveur_powerdns | clair | Transfert des requêtes de la zone souveraine vers le DNS autoritatif interne (PowerDNS). |
|
| `serveur_artefacts` | egress | 80 | tcp | externe | clair | Remplir le cache depuis les dépôts Debian amont (deb.debian.org, security). |
|
||||||
| `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. |
|
| `serveur_artefacts` | egress | 3142 | tcp | voisins_site | clair | Prendre le cache du site comme amont, plutot que d'aller chez Debian. |
|
||||||
| `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` | 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` | 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), et le dépôt de binaires (roues PyPI, collections Galaxy, versions GitHub, Nextcloud, Forgejo). Le site 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 | 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_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 | 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` | 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 | 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 | 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 | 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 | 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 | 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 | 80 | tcp | externe | clair | Dépôts apt en clair et redirections des miroirs, pour un écosystème SANS amont d'artefacts. 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 | 443 | tcp | externe | tls-requis | Dépôts apt en direct, pour un écosystème SANS aucune source d'artefacts. Le site et ses locataires passent par le cache du site et ne la reçoivent pas. |
|
||||||
| `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_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 | 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 | 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` | 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_dovecot` | egress | 636 | tcp | serveur_openldap | tls-requis | userdb/passdb : Dovecot résout et authentifie les comptes sur l'annuaire (LDAPS). |
|
||||||
| `serveur_forgejo` | ingress | 3000 | tcp | edge | clair | Interface web + Git HTTP servis via l'edge (TLS terminé à l'edge). |
|
| `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` | egress | 25 | tcp | serveur_postfix | starttls | Notifications courriel (relais via le MTA Postfix). |
|
| `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 | 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_forgejo` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base de données Forgejo (verify-full). |
|
||||||
| `serveur_grafana` | ingress | 3000 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). |
|
| `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` | egress | 443 | tcp | edge | tls-requis | Authentification OIDC auprès de Keycloak (via son FQDN publié à 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 | 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_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 | 5432 | tcp | serveur_postgresql | tls-requis | Base relationnelle du moteur Icinga (verify-full). |
|
||||||
| `serveur_icingaweb2` | ingress | 8080 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge ; SSO possible via oauth2-proxy). |
|
| `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` | egress | 636 | tcp | serveur_openldap | tls-requis | Authentification des utilisateurs sur l'annuaire (LDAPS). |
|
| `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_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` | 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 | 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_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 | 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 | 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` | 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 | 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 | 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_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 | externe | clair | HTTP entrant — redirection permanente vers HTTPS. |
|
| `serveur_nginx` | ingress | 80 | tcp | admin | clair | HTTP depuis le reseau d'administration — 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 | 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` | 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). |
|
| `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). |
|
||||||
|
|
@ -61,35 +83,58 @@
|
||||||
| `serveur_oauth2_proxy` | egress | 8080 | tcp | localhost | clair | Relais vers l'application co-localisée protégée (upstream en localhost). |
|
| `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 | 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_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 | 443 | tcp | externe | tls-requis | Repere du genome : `git ls-remote` anonyme sur eregion (forge.alliance-boreale.ca) pour dire si la forge du site a pris du retard. Lecture seule, sans identifiant. |
|
||||||
|
| `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 | 25 | tcp | externe, client_smtp | starttls | SMTP entrant : courrier externe (MX) et notifications internes (client_smtp). |
|
||||||
| `serveur_postfix` | ingress | 587 | tcp | flotte | starttls | Soumission authentifiée (submission) pour les agents internes qui envoient du courrier. |
|
| `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` | egress | 24 | tcp | serveur_dovecot | tls-requis | Remise finale par LMTP au mailstore (Dovecot), en TLS vérifié. |
|
| `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 | 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 | 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 | 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_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 | 5432 | tcp | serveur_keycloak, serveur_forgejo, serveur_icinga, serveur_nextcloud | tls-requis | Connexions applicatives à PostgreSQL (verify-full ; pg_hba hostssl). |
|
||||||
| `serveur_powerdns` | ingress | 53 | udp | flotte | clair | Résolution DNS interne (zone souveraine). DoT/DoH = feuille de route (chiffrement DNS). |
|
| `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 | 53 | tcp | flotte | clair | Résolution DNS interne en TCP (réponses volumineuses, AXFR restreint par allow_axfr_ips). |
|
| `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` | 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 | 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_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` | 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 | 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_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 | 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_rspamd` | ingress | 11334 | tcp | localhost | clair | Interface de contrôle rspamd (statistiques, apprentissage) en local. |
|
||||||
|
| `serveur_rspamd` | egress | 80 | tcp | externe | clair | Carte de hachages SURBL (sa-update.surbl.org), publiee en clair par l'amont. Une carte alteree ne peut que mal classer un courriel ; elle n'execute rien. |
|
||||||
|
| `serveur_rspamd` | egress | 443 | tcp | externe | tls-requis | Cartes de rspamd (maps.rspamd.com) : messageries gratuites et jetables, redirecteurs, listes blanches DMARC/SPF/DKIM, hameconnage. Sans elles l'antispam garde les copies du paquet, qui vieillissent. |
|
||||||
| `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_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 | clair | Front nginx local des webapps, proxie par l'edge (TLS termine a l'edge). |
|
| `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` | egress | 443 | tcp | flotte | tls | git clone/pull du depot de chaque app (Forgejo souverain) au deploiement. |
|
| `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 | edge | clair | Contenu statique servi au navigateur via l'edge (TLS termine a l'edge). |
|
| `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` | egress | 443 | tcp | flotte | tls | git clone/pull du depot du site (Forgejo souverain) au deploiement. |
|
| `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. |
|
||||||
|
|
||||||
## Synthèse chiffrement
|
## Synthèse chiffrement
|
||||||
|
|
||||||
- **clair** : 29 flux
|
- **clair** : 52 flux
|
||||||
- **n-a** : 3 flux
|
- **n-a** : 8 flux
|
||||||
- **ssh** : 3 flux
|
- **ssh** : 8 flux
|
||||||
- **starttls** : 6 flux
|
- **starttls** : 6 flux
|
||||||
- **tls** : 8 flux
|
- **tls** : 10 flux
|
||||||
- **tls-cible** : 2 flux
|
- **tls-cible** : 2 flux
|
||||||
- **tls-requis** : 26 flux
|
- **tls-requis** : 36 flux
|
||||||
|
|
|
||||||
133
docs/remise-au-client.md
Normal file
133
docs/remise-au-client.md
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
> **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**.
|
||||||
118
docs/responsabilites-locataire-hebergeur.md
Normal file
118
docs/responsabilites-locataire-hebergeur.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
> **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.*
|
||||||
1003
docs/runbooks-construction.yml
Normal file
1003
docs/runbooks-construction.yml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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* :
|
**Diagnostic-réflexe** — comparer le cert *servi* au cert *fichier* :
|
||||||
```bash
|
```bash
|
||||||
# SERVI (en mémoire par nginx)
|
# SERVI (en mémoire par nginx)
|
||||||
echo | openssl s_client -connect infra-edge-01…:443 -servername keycloak.lab… 2>/dev/null \
|
echo | openssl s_client -connect infra-edge-01.chezlepro.internal:443 -servername keycloak.chezlepro.internal 2>/dev/null \
|
||||||
| openssl x509 -noout -enddate
|
| openssl x509 -noout -enddate
|
||||||
# FICHIER (sur disque)
|
# FICHIER (sur disque)
|
||||||
openssl x509 -in /etc/step/certs/infra-edge-01….crt -noout -enddate
|
openssl x509 -in /etc/step/certs/infra-edge-01.chezlepro.internal.crt -noout -enddate
|
||||||
```
|
```
|
||||||
Dates différentes (servi < fichier) ⇒ nginx sert un cert périmé.
|
Dates différentes (servi < fichier) ⇒ nginx sert un cert périmé.
|
||||||
|
|
||||||
|
|
@ -50,8 +50,15 @@ 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).
|
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
|
Mapping (défaut du rôle grafana) : `grafana-admin`→Admin, `grafana-editor`→Editor, sinon Viewer
|
||||||
(`serveur_grafana_oidc_role_path`). Idéal souverain : piloter par un **groupe d'annuaire** plutôt
|
(`serveur_grafana_oidc_role_path`).
|
||||||
qu'un utilisateur explicite. Voir l'unité wiki *Autorisation & RBAC*.
|
|
||||||
|
> **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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -105,6 +112,64 @@ Note : ne s'applique qu'aux Forgejo **gérées par Set-OPS**.
|
||||||
|
|
||||||
## 5. Restaurer — et d'abord : prouver qu'on peut
|
## 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 ;
|
> É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.
|
> la restauration d'une base reste manuelle, et porte un piège décrit plus bas.
|
||||||
|
|
||||||
|
|
@ -136,10 +201,11 @@ la base badger de step-ca, qui avance à **chaque** émission de certificat.
|
||||||
|
|
||||||
`pg_dumpall` écrit `CREATE DATABASE <suivante>` **avant** le `\connect` correspondant.
|
`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
|
Découper « du `\connect X` au `\connect` suivant » emporte donc un ordre qui vise une
|
||||||
**autre** base. Couper aussi sur `CREATE DATABASE`, et **vérifier avant de rejouer** :
|
**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** :
|
||||||
|
|
||||||
```
|
```
|
||||||
awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /){exit} f' \
|
awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /||/^DROP DATABASE /){exit} f' \
|
||||||
toutes-bases.sql > section.sql
|
toutes-bases.sql > section.sql
|
||||||
|
|
||||||
grep -qE '^(DROP|CREATE|ALTER) DATABASE|^\\connect' section.sql \
|
grep -qE '^(DROP|CREATE|ALTER) DATABASE|^\\connect' section.sql \
|
||||||
|
|
@ -157,49 +223,286 @@ 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
|
**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.
|
`DROP DATABASE` de toutes les bases. Une restauration réelle se fait sur un cluster neuf.
|
||||||
|
|
||||||
## 6. La frontière porte deux adresses de gestion (transition D-77)
|
## 6. La bascule d'adressage de la fabric (D-77) — FAITE
|
||||||
|
|
||||||
> Posée le 2026-08-12 par l'API (`interfaces/vip_settings`, mode `ipalias`). **Ce n'est
|
> **Cette section décrivait une transition en cours jusqu'au 2026-09-06.** Elle est
|
||||||
> pas une anomalie** : c'est une bascule d'adressage en cours.
|
> 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 :
|
||||||
|
|
||||||
```
|
```
|
||||||
10.0.0.1/24 adresse historique — TOUJOURS ACTIVE, rien ne l'a quittée
|
/etc/pve/nodes/vishnu/qemu-server/9006.conf
|
||||||
10.17.0.1/24 adresse cible (D-77 : underlay dans la bande basse du /16 du site)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Ajouter avant de retirer**, jamais l'inverse. Un point de routage qui change d'adresse
|
Le clonage appelle `nodes/vishnu/qemu/9006/clone` : c'est l'API de **ce nœud-là** qui doit
|
||||||
d'un coup coupe simultanément l'exploitant, les commutateurs qui l'ont en passerelle par
|
répondre. Nœud éteint, API muette, aucune naissance.
|
||||||
défaut, et l'outil qui devait faire la bascule.
|
|
||||||
|
|
||||||
### Ce qui reste à déplacer, et dans quel ordre
|
**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 :
|
||||||
|
|
||||||
| # | À déplacer | Vers | Nature (D-78) |
|
```
|
||||||
|---|---|---|---|
|
pool CephNVMe size=3 min_size=2
|
||||||
| 1 | le poste de l'exploitant | `10.17.0.x/24` (seconde adresse) | — |
|
OSD sur les trois hôtes : asgard, gandalf, vishnu
|
||||||
| 2 | les commutateurs `10.0.0.3/.4` **et leur `ip default-gateway`** | `10.17.0.3/.4`, passerelle `10.17.0.1` | destination |
|
base-9006-disk-0, base-9006-disk-1 présentes dans le pool
|
||||||
| 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` | — | — |
|
|
||||||
|
|
||||||
Les étapes 4 à 6 sortent définitivement de l'espace dérivé (D-78) : ces réseaux ne sont
|
L'image est répliquée trois fois et reste lisible avec un nœud en moins. Et les quatorze VM
|
||||||
jamais des destinations, seulement des chemins. Une fois faites, **seule la gestion** doit
|
d'un écosystème tournent ailleurs, sur leurs propres disques Ceph : elles ne s'aperçoivent
|
||||||
rester unique d'un site à l'autre.
|
de rien.
|
||||||
|
|
||||||
> **L'étape 5 change aussi le numéro de VLAN**, côté commutateur (trunk) et côté
|
> **`vishnu` n'est pas un point unique de défaillance pour l'exploitation. Il l'est pour la
|
||||||
> hyperviseurs (`bond3.11` → `bond3.50`). Le 11 est écarté parce que `192.168.11.0/24` est
|
> reproduction** — et la reproduction est ce que ce dépôt existe pour garantir.
|
||||||
> 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.
|
|
||||||
|
|
||||||
### Ce qui ne dépend PAS de cette bascule
|
**La manœuvre.** Deux gestes, quelques minutes, aucun mouvement de données :
|
||||||
|
|
||||||
**Le renumérotage du tenant** (`10.27` → `10.17`) est **indépendant**. La frontière route
|
```bash
|
||||||
le supernet du tenant vers le même prochain saut, quelle que soit sa propre adresse de
|
# 1. re-héberger la configuration sur un nœud debout
|
||||||
gestion : il suffit que la route et les alias suivent. Les deux chantiers peuvent donc
|
mv /etc/pve/nodes/vishnu/qemu-server/9006.conf \
|
||||||
être menés séparément — et c'est préférable.
|
/etc/pve/nodes/asgard/qemu-server/9006.conf
|
||||||
|
|
||||||
> **Longueur de préfixe.** Une fois les deux faits, la gestion (`10.17.0.0/24`) vit
|
# 2. le déclarer, sinon la garde refusera
|
||||||
> *à l'intérieur* du supernet du tenant (`10.17.0.0/16`). Aucun conflit : la route
|
# SITE-Chezlepro/plan/10-intrants.yml : gabarit.noeud: asgard
|
||||||
> 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.
|
|
||||||
|
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`.
|
||||||
|
|
|
||||||
96
docs/schemas-flux.md
Normal file
96
docs/schemas-flux.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# 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,26 +27,38 @@ C'est le VRF qu'on regrettait de ne pas avoir dans le matériel, obtenu en logic
|
||||||
|
|
||||||
## 2. La projection du modèle
|
## 2. La projection du modèle
|
||||||
|
|
||||||
Vérifiée sur les deux tenants fédérés, elle ne demande **aucun changement de dérivation** :
|
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** :
|
||||||
|
|
||||||
| Objet Proxmox SDN | Vient de | Exemple (Chezlepro, zone Services-infra) |
|
| Objet Proxmox SDN | Vient de | Exemple (Chezlepro, zone Services-infra) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **zone** (un VRF) | le tenant | `CHEZ17` |
|
| **zone** (un VRF) | `zone_de(index)` → `t<index>` | `t17` |
|
||||||
| **VNet** | la zone de sécurité | `chez174` |
|
| **VNet** | `vnet_de(index, libellé)` → `t<index><zone abrégée>` | `t17serv` |
|
||||||
| **tag** (VNI) | `vlan_de(index, zone)` | `1174` |
|
| **tag** (VNI) | `vlan_de(index, zone)` | `1174` |
|
||||||
| **subnet** | `sous_reseau_de(index, zone)` | `10.17.19.0/24` |
|
| **subnet** | `sous_reseau_de(index, zone)` | `10.17.19.0/24` |
|
||||||
| **gateway** | `passerelle_de(index, zone)` | `10.17.19.1` |
|
| **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
|
> **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**
|
> 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
|
> 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 »*.
|
> amont : *« zone ID … can't be more length than 8 characters »*.
|
||||||
>
|
>
|
||||||
> Le nommage dérive du tenant, comme tout le reste : `<PRÉFIXE><index>` pour la zone,
|
> **Le nommage a changé une seconde fois, et ce tableau ne l'avait pas suivi.** Il annonçait
|
||||||
> `<préfixe><index><zone>` pour le VNet. Le préfixe vient de `devis_reseau.prefixe()` —
|
> `CHEZ17` / `chez174`, dérivés de `devis_reseau.prefixe()` — le nom court du *dossier* du
|
||||||
> la même fonction que le devis des commutateurs, donc un seul endroit fabrique le nom
|
> tenant. La forme en vigueur est plus simple et ne dépend que du seed :
|
||||||
> court d'un tenant. Éprouvé jusqu'au pire cas de la fédération : `COOP245` = 7,
|
> **`t<index>`** pour la zone, **`t<index><zone abrégée>`** pour le VNet
|
||||||
> `coop2459` = 8. **P30** refuse tout dépassement, sur les deux objets.
|
> (`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.
|
||||||
>
|
>
|
||||||
> **Les zones créées à la main (`VRF0011`, `VRF0017`) sont remplacées.** Ce nommage ne
|
> **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
|
> disait ni de quel tenant il s'agissait, ni rien qu'on puisse relier au plan : il
|
||||||
|
|
@ -65,8 +77,9 @@ 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.
|
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
|
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 — le
|
du nombre de tenants n'est plus l'espace de VLAN mais le **second octet IPv4** du supernet :
|
||||||
plafond de 245 tenants reste, sa cause change.
|
`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.
|
||||||
|
|
||||||
## 3. Le partage des responsabilités
|
## 3. Le partage des responsabilités
|
||||||
|
|
||||||
|
|
@ -81,8 +94,10 @@ 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
|
**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
|
sortir du VRF, donc traverser la bordure, qui est en `block` par défaut. Un flux inter-tenant
|
||||||
légitime devra être **déclaré** pour exister — le registre des flux n'a pas encore de mot-clé
|
légitime doit donc être **déclaré** pour exister — et le registre des flux a désormais le
|
||||||
pour ça, c'est un point ouvert.
|
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é.
|
||||||
|
|
||||||
**La défense est en profondeur, sans coût de maintenance.** Le filtrage est-ouest est appliqué
|
**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
|
deux fois : par l'hyperviseur, puis par l'hôte destinataire. Une VM compromise doit franchir
|
||||||
|
|
@ -168,29 +183,31 @@ Lecture seule par l'API Proxmox. **Le plan de contrôle existe, le plan de donn
|
||||||
| VNets | **aucun** |
|
| VNets | **aucun** |
|
||||||
| Nœuds de sortie | **aucun** — un VRF sans sortie n'a aucun chemin vers la frontière |
|
| Nœuds de sortie | **aucun** — un VRF sans sortie n'a aucun chemin vers la frontière |
|
||||||
|
|
||||||
### Deux blocages à lever avant d'aller plus loin
|
### Deux blocages — levés
|
||||||
|
|
||||||
**Les VTEP sont adressés dans un tenant.** `vmbr3` porte `10.27.19.{41,43,47}` — le
|
> **Cette section décrivait l'état du 2026-08-02.** Les deux sont levés ; on la garde parce
|
||||||
sous-réseau *Services-infra de Chezlepro*. Le transport du cluster dérive donc de l'index d'un
|
> que le *raisonnement* explique le plan d'adressage actuel, qui paraîtrait arbitraire sans
|
||||||
tenant : un changement d'index le casse, une migration l'emporte. Et une VM de cette zone
|
> lui.
|
||||||
partage son sous-réseau avec les trois VTEP, ce qui perce 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
|
**Les VTEP étaient adressés dans un tenant.** `vmbr3` portait `10.27.19.{41,43,47}` — le
|
||||||
d'underlay ferait échouer **P23**, qui interdit tout chevauchement avec un supernet tenant. La
|
sous-réseau *Services-infra de Chezlepro*. Le transport du cluster dérivait donc de l'index
|
||||||
garde détecte la faute avant qu'on ne la documente.
|
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.
|
||||||
|
|
||||||
`underlay.yml` déclare donc les trois hyperviseurs à leur adresse **cible** — `10.0.0.{41,43,47}`,
|
Le modèle **refusait d'ailleurs d'exprimer cet état** : déclarer `10.27.19.0/24` comme réseau
|
||||||
dernier octet conservé comme sur `vmbr0`. Le déplacement réel de l'adresse sur les nœuds reste
|
d'underlay faisait échouer **P23**, qui interdit tout chevauchement accidentel avec un
|
||||||
à faire : c'est une modification du réseau d'un hyperviseur en service.
|
supernet tenant. La garde a détecté la faute avant qu'on ne la documente.
|
||||||
|
|
||||||
**Le pont `vmbr3` n'est pas *VLAN-aware*** (pas de `bridge_vlan_aware`, contrairement à
|
**Aujourd'hui** : les VTEP vivent sur `underlay-vxlan` — `192.168.50.{41,43,47}`, VLAN 50,
|
||||||
`vmbr2`). L'adresse du VTEP y est donc **non étiquetée** : elle vit dans le VLAN natif du port
|
sur une interface étiquetée dédiée (`bond3.50`). Le transport ne dérive plus d'aucun index.
|
||||||
de commutateur. Déplacer le VTEP vers l'underlay suppose soit un VLAN natif 10, soit une
|
*Pourquoi 50 et pas 11 : sous la règle `192.168.<vlan>`, le VLAN 11 aurait produit
|
||||||
interface étiquetée dédiée (`bond3.10`) — ce n'est pas qu'un changement d'adresse.
|
`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.*
|
||||||
|
|
||||||
**`vishnu` n'est pas câblé.** Son `vmbr3` n'a **aucun port physique** : le pont existe, porte
|
**Le pont `vmbr3` n'était pas *VLAN-aware***, et l'adresse du VTEP y vivait donc dans le VLAN
|
||||||
une adresse, et ne mène nulle part. Un pair VXLAN pointé sur lui ne fonctionnera jamais.
|
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.
|
||||||
|
|
||||||
## 8. Ce qui reste à trancher
|
## 8. Ce qui reste à trancher
|
||||||
|
|
||||||
|
|
@ -199,9 +216,8 @@ une adresse, et ne mène nulle part. Un pair VXLAN pointé sur lui ne fonctionne
|
||||||
- **Le contrôleur EVPN** : ASN, voisins, et si l'on fait du BGP avec la bordure ou des routes
|
- **Le contrôleur EVPN** : ASN, voisins, et si l'on fait du BGP avec la bordure ou des routes
|
||||||
statiques comme aujourd'hui.
|
statiques comme aujourd'hui.
|
||||||
- **Le nombre de nœuds de sortie** et leur redondance.
|
- **Le nombre de nœuds de sortie** et leur redondance.
|
||||||
- **Le mot-clé d'un flux inter-tenant** dans le registre. Aujourd'hui aucun ne l'exprime, donc
|
- ~~**Le mot-clé d'un flux inter-tenant** dans le registre.~~ **Tranché** : c'est
|
||||||
tout inter-tenant tombe dans le `block` de la bordure — un défaut sûr, mais qui rend
|
`voisins_site` (2026-08-24), né du besoin de chaîner les caches d'artefacts. Cf. §3.
|
||||||
impossible de *déclarer* une exception légitime.
|
|
||||||
- **La migration depuis l'existant** : le tenant Chezlepro tourne déjà sur des VLAN. Passer à
|
- **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
|
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 ?
|
`docs/migration-tenant.md` s'applique-t-elle, ou faut-il un chemin plus court ?
|
||||||
|
|
|
||||||
130
docs/sortir-les-cles-du-poste.md
Normal file
130
docs/sortir-les-cles-du-poste.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
> **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.
|
||||||
279
docs/supervision-conception.md
Normal file
279
docs/supervision-conception.md
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
# 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,7 +193,14 @@ L'hôte est d'abord déclaré dans le **plan** (`instance/plan/serveurs.yml`) et
|
||||||
make deployer HOTE=web-frontal-01
|
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 de l'inventaire.
|
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.
|
||||||
|
|
||||||
Ensuite, elle lance la vérification post-déploiement :
|
Ensuite, elle lance la vérification post-déploiement :
|
||||||
|
|
||||||
|
|
@ -213,9 +220,9 @@ Cette commande limite le playbook au croisement entre le groupe demandé et `hot
|
||||||
|
|
||||||
## 9. Variables template et conformité
|
## 9. Variables template et conformité
|
||||||
|
|
||||||
Les variables du template servent à construire le golden template dans `instance/inventories/production/group_vars/modeles_vm.yml`.
|
Les variables du template servent à construire le golden template dans `instance/inventories/<inventaire>/group_vars/modeles_vm.yml`.
|
||||||
|
|
||||||
Les variables de conformité servent aux VM déployées dans `instance/inventories/production/group_vars/serveur_debian.yml`.
|
Les variables de conformité servent aux VM déployées dans `instance/inventories/<inventaire>/group_vars/serveur_debian.yml`.
|
||||||
|
|
||||||
Différence attendue :
|
Différence attendue :
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,19 @@ domaine_interne: "exemple.internal"
|
||||||
fuseau_horaire: "America/Toronto"
|
fuseau_horaire: "America/Toronto"
|
||||||
organisation: "Exemple"
|
organisation: "Exemple"
|
||||||
identite_realm: "exemple"
|
identite_realm: "exemple"
|
||||||
setops_plan_dir: "{{ playbook_dir }}/../../instance/plan"
|
# 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"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
# 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,6 +32,9 @@ vault_postgresql_keycloak: ""
|
||||||
vault_bd_keycloak: ""
|
vault_bd_keycloak: ""
|
||||||
vault_bd_forgejo: ""
|
vault_bd_forgejo: ""
|
||||||
vault_bd_icingadb: ""
|
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) ---
|
# --- Forge (Forgejo) ---
|
||||||
vault_forgejo_admin: ""
|
vault_forgejo_admin: ""
|
||||||
|
|
@ -41,3 +44,42 @@ vault_forgejo_internal_token: ""
|
||||||
# --- Observabilité / divers ---
|
# --- Observabilité / divers ---
|
||||||
vault_grafana_admin: ""
|
vault_grafana_admin: ""
|
||||||
vault_redis: ""
|
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,6 +18,7 @@ from inventory_rules import ( # noqa: E402
|
||||||
bases_du_groupe,
|
bases_du_groupe,
|
||||||
chaine_connexion,
|
chaine_connexion,
|
||||||
expositions_des_applications,
|
expositions_des_applications,
|
||||||
|
zones_inverses,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,4 +32,5 @@ class FilterModule:
|
||||||
"bases_du_groupe": bases_du_groupe,
|
"bases_du_groupe": bases_du_groupe,
|
||||||
"chaine_connexion": chaine_connexion,
|
"chaine_connexion": chaine_connexion,
|
||||||
"expositions_des_applications": expositions_des_applications,
|
"expositions_des_applications": expositions_des_applications,
|
||||||
|
"zones_inverses": zones_inverses,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
paquets-tiers.yml
Normal file
64
paquets-tiers.yml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
# 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
|
||||||
121
playbooks/backup/restauration.yml
Normal file
121
playbooks/backup/restauration.yml
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
---
|
||||||
|
# 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'
|
||||||
38
playbooks/groupes/client_artefacts.yml
Normal file
38
playbooks/groupes/client_artefacts.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
# 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,22 +1,38 @@
|
||||||
---
|
---
|
||||||
- name: Appliquer le groupe client_journal
|
# client_journal — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||||
hosts: client_journal
|
#
|
||||||
|
# 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
|
||||||
become: true
|
become: true
|
||||||
# Le verrou dpkg est tenu par les maj automatiques de Debian, par vagues, plusieurs
|
any_errors_fatal: true
|
||||||
# minutes apres le premier demarrage. Pose ICI plutot que dans chaque role : toute
|
module_defaults: &defauts_journal
|
||||||
# tache apt du play en herite, y compris celles des roles inclus.
|
|
||||||
module_defaults:
|
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
lock_timeout: 300
|
lock_timeout: 300
|
||||||
|
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
|
pre_tasks: &pre_journal
|
||||||
pre_tasks:
|
|
||||||
- name: Vérifier que la cible est Debian
|
- name: Vérifier que la cible est Debian
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- ansible_facts.distribution == "Debian"
|
- ansible_facts.distribution == "Debian"
|
||||||
fail_msg: "Ce playbook est prévu pour 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:
|
roles:
|
||||||
- client_journal
|
- client_journal
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,38 @@
|
||||||
---
|
---
|
||||||
- name: Appliquer le groupe client_pki
|
# client_pki — L'ORDRE FAIT PARTIE DE L'INTEGRATION (2026-08-25).
|
||||||
hosts: client_pki
|
#
|
||||||
|
# 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
|
||||||
become: true
|
become: true
|
||||||
# Le verrou dpkg est tenu par les maj automatiques de Debian, par vagues, plusieurs
|
any_errors_fatal: true
|
||||||
# minutes apres le premier demarrage. Pose ICI plutot que dans chaque role : toute
|
module_defaults: &defauts_pki
|
||||||
# tache apt du play en herite, y compris celles des roles inclus.
|
|
||||||
module_defaults:
|
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
lock_timeout: 300
|
lock_timeout: 300
|
||||||
|
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
|
pre_tasks: &pre_pki
|
||||||
pre_tasks:
|
|
||||||
- name: Vérifier que la cible est Debian
|
- name: Vérifier que la cible est Debian
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- ansible_facts.distribution == "Debian"
|
- ansible_facts.distribution == "Debian"
|
||||||
fail_msg: "Ce playbook est prévu pour 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:
|
roles:
|
||||||
- client_pki
|
- client_pki
|
||||||
|
|
|
||||||
38
playbooks/groupes/client_resolveur.yml
Normal file
38
playbooks/groupes/client_resolveur.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
# 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
|
||||||
56
playbooks/groupes/client_sante.yml
Normal file
56
playbooks/groupes/client_sante.yml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
---
|
||||||
|
# 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
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue