runner : separer le pouvoir de configurer de celui de materialiser
La ligne de partage est celle des voutes. serveur_ops calcule et configure -- voute du tenant, SSH chez lui. serveur_ops_site materialise -- voute du SITE, API de l'hyperviseur, jamais de SSH chez un tenant. Un runner par tenant qui materialiserait mettrait la voute du SITE en N exemplaires. Un runner unique qui ferait tout traverserait le default-deny inter-tenant et rendrait l'emancipation impossible. Le role depose la voute CHIFFREE et relit l'en-tete apres avoir ecrit : sans `decrypt: false`, Ansible dechiffre la source quand il detient le mot de passe -- constate le jour meme, 776 octets en clair au lieu de 3465. Controle negatif fait, la garde mord. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f248bb084f
commit
2b55761fba
12 changed files with 279 additions and 6 deletions
42
CHANGELOG.md
42
CHANGELOG.md
|
|
@ -1,5 +1,47 @@
|
||||||
# CHANGELOG — Set-OPS
|
# CHANGELOG — Set-OPS
|
||||||
|
|
||||||
|
## 2026-08-24 — Deux runners, deux pouvoirs, aucun omnipotent
|
||||||
|
|
||||||
|
Le travail d'un runner se divise en trois, et la ligne de partage est **celle des voûtes** :
|
||||||
|
|
||||||
|
```
|
||||||
|
calculer plan -> inventaire aucune voûte portée TENANT
|
||||||
|
configurer rôles sur ses machines voûte du TENANT portée TENANT
|
||||||
|
matérialiser créer/détruire des VM voûte du SITE portée FABRIC
|
||||||
|
```
|
||||||
|
|
||||||
|
Un runner **par tenant** qui matérialiserait mettrait la voûte du SITE en N exemplaires —
|
||||||
|
le secret le plus dangereux du système, recopié autant de fois qu'il y a de locataires.
|
||||||
|
|
||||||
|
Un runner **unique** qui ferait tout devrait entrer en SSH chez tous les tenants, donc
|
||||||
|
traverser le default-deny inter-tenant — et rendrait l'**émancipation impossible** : un
|
||||||
|
écosystème dont le runner appartient à l'hébergeur ne peut plus se rebâtir sans lui.
|
||||||
|
|
||||||
|
D'où `serveur_ops_site`, additif : il crée des VM vides et n'entre **jamais** chez un
|
||||||
|
tenant ; `serveur_ops` habille des machines et ne touche **jamais** la fabric. Réservé à
|
||||||
|
l'écosystème de l'hébergeur — un tenant ordinaire qui le déclarerait s'arrogerait un
|
||||||
|
pouvoir sur ses voisins.
|
||||||
|
|
||||||
|
### La voûte, de droit plutôt que par emprunt
|
||||||
|
|
||||||
|
La cérémonie du prêt que j'avais bricolée en ligne de commande masquait une pièce
|
||||||
|
manquante. Le rôle dépose maintenant la voûte du SITE, **chiffrée**, en `0600` — et le mot
|
||||||
|
de passe n'est toujours pas stocké : le Makefile ajoute `--ask-vault-pass` quand aucun
|
||||||
|
fichier n'est défini.
|
||||||
|
|
||||||
|
### Une garde née d'une faute
|
||||||
|
|
||||||
|
`decrypt: false` est obligatoire sur la copie : sans lui, Ansible **déchiffre** la source
|
||||||
|
quand il détient le mot de passe. Mesuré le jour même, en la déposant à la main — 776
|
||||||
|
octets en clair au lieu de 3465 chiffrés, sur une machine où ils n'avaient rien à faire.
|
||||||
|
|
||||||
|
Le rôle **relit l'en-tête après avoir écrit** et refuse si le fichier n'est pas chiffré.
|
||||||
|
Contrôle négatif fait : pointé sur un fichier en clair, il échoue ; rétabli ensuite, la
|
||||||
|
voûte déposée est bien `$ANSIBLE_VAULT`, 3465 octets, `0600`.
|
||||||
|
|
||||||
|
Une fuite silencieuse aurait été le pire des cas : le fichier existe, le rôle se dit
|
||||||
|
satisfait, et les clés du cluster dorment en clair.
|
||||||
|
|
||||||
## 2026-08-24 — Filiation, mutualisation, émancipation : nommer un motif que le moteur avait déjà
|
## 2026-08-24 — Filiation, mutualisation, émancipation : nommer un motif que le moteur avait déjà
|
||||||
|
|
||||||
Un écosystème ne naît pas autoportant. Il lui faut une forge pour lire son génome, une
|
Un écosystème ne naît pas autoportant. Il lui faut une forge pour lire son génome, une
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@
|
||||||
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ 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. |
|
| 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). |
|
| 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. |
|
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 34 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. |
|
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 32 rôles, 83 flux, schéma + matrice OK. |
|
||||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
| 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 |
|
| 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. |
|
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||||
|
|
@ -36,21 +36,21 @@
|
||||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |
|
| 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). |
|
| 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. |
|
| 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, 54 regles, 15 routes, admin=10.0.0.0/24,10.17.0.0/24,192.168.254.2/32,192.168.255.2/32. |
|
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 55 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). |
|
| 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 | 15 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (1 exemption(s) derivee(s) du service rendu). |
|
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 15 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. |
|
| 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. |
|
| 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 | 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 |
|
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 26 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 15, 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. |
|
| 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. |
|
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 47 scripts expliques et atteignables, 98 cibles make documentees, 58 roles avec README. |
|
||||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 34 exigence(s) de role, toutes satisfaites (121 cle(s) declaree(s) par l'instance). |
|
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 34 exigence(s) de role, toutes satisfaites (121 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 (34 groupes). |
|
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 33 revendication(s) de port, aucune collision entre roles co-localises (34 groupes). |
|
||||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (22 genere(s) exempte(s)). |
|
| 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). |
|
| 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). |
|
| 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. |
|
| 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. |
|
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 33 role(s) serveur/client tous nommes, 34 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. |
|
| 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. |
|
| 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). |
|
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 43 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ Ce que la reconstruction couvre, par capacité :
|
||||||
| 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) |
|
||||||
| 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 |
|
| 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` | cache apt de l'ecosysteme (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 |
|
| Source d'artefacts | `serveur_artefacts`, `client_artefacts` | cache apt de l'ecosysteme (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 |
|
||||||
|
| 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 |
|
||||||
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte |
|
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte |
|
||||||
|
|
||||||
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ couches:
|
||||||
# Le poste d'exploitation vient APRES la forge : il clone le genome depuis
|
# Le poste d'exploitation vient APRES la forge : il clone le genome depuis
|
||||||
# elle. Le placer plus tot le laisserait sans source.
|
# elle. Le placer plus tot le laisserait sans source.
|
||||||
- serveur_ops
|
- serveur_ops
|
||||||
|
# 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."
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,15 @@ groupes:
|
||||||
raison: "Un cache apt ne depend d'aucun service de l'ecosysteme : il ne fait que relayer et retenir."
|
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."
|
surveillance: "Verifier l'ecoute sur 3142, le taux de service depuis le journal, et l'espace du cache."
|
||||||
|
|
||||||
|
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_nextcloud:
|
serveur_nextcloud:
|
||||||
requiert_groupes_actifs:
|
requiert_groupes_actifs:
|
||||||
- serveur_postgresql
|
- serveur_postgresql
|
||||||
|
|
|
||||||
15
playbooks/groupes/serveur_ops_site.yml
Normal file
15
playbooks/groupes/serveur_ops_site.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
- name: Appliquer le groupe serveur_ops_site
|
||||||
|
hosts: serveur_ops_site
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Vérifier que la cible est Debian
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_facts.distribution == "Debian"
|
||||||
|
fail_msg: "Ce playbook est prévu pour Debian."
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- serveur_ops_site
|
||||||
62
roles/serveur_ops_site/README.md
Normal file
62
roles/serveur_ops_site/README.md
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# serveur_ops_site
|
||||||
|
|
||||||
|
**Le runner de SITE** : celui qui matérialise. Il crée des VM sur la fabric, et rien d'autre.
|
||||||
|
|
||||||
|
## Pourquoi ce rôle existe séparément
|
||||||
|
|
||||||
|
Le travail d'un runner se divise en trois, et la ligne de partage est celle des voûtes :
|
||||||
|
|
||||||
|
| | voûte requise | portée | rôle |
|
||||||
|
|---|---|---|---|
|
||||||
|
| calculer — plan → inventaire | aucune | tenant | `serveur_ops` |
|
||||||
|
| configurer — rôles sur ses machines | **tenant** | tenant | `serveur_ops` |
|
||||||
|
| matérialiser — créer/détruire des VM | **SITE** | fabric | **ce rôle** |
|
||||||
|
|
||||||
|
**Un runner par tenant qui matérialiserait** mettrait la voûte du SITE en N exemplaires —
|
||||||
|
le secret le plus dangereux du système, recopié autant de fois qu'il y a de locataires.
|
||||||
|
|
||||||
|
**Un runner unique qui ferait tout** devrait entrer en SSH chez tous les tenants, donc
|
||||||
|
traverser le default-deny inter-tenant. Et il rendrait l'**émancipation impossible** : un
|
||||||
|
écosystème dont le runner appartient à l'hébergeur ne peut pas se rebâtir sans lui.
|
||||||
|
|
||||||
|
Deux pouvoirs, deux rôles, **aucun omnipotent**. Ce rôle crée des VM vides et n'entre
|
||||||
|
jamais chez un tenant ; `serveur_ops` habille des machines et ne touche jamais la fabric.
|
||||||
|
|
||||||
|
## Qui a le droit de le déclarer
|
||||||
|
|
||||||
|
**L'écosystème de l'hébergeur**, celui qui exploite la fabric. Un tenant ordinaire qui le
|
||||||
|
déclarerait s'arrogerait un pouvoir sur ses voisins.
|
||||||
|
|
||||||
|
## Ce qu'il dépose, et comment
|
||||||
|
|
||||||
|
La voûte du SITE, **chiffrée**, en `0600`. Le mot de passe n'est jamais stocké : le
|
||||||
|
Makefile ajoute `--ask-vault-pass` quand aucun fichier de mot de passe n'est défini, et
|
||||||
|
l'exploitant le tape au moment d'agir.
|
||||||
|
|
||||||
|
`decrypt: false` est **obligatoire** sur la copie. Sans lui, Ansible déchiffre la source
|
||||||
|
quand il détient le mot de passe — constaté le 2026-08-24 : 776 octets en clair au lieu de
|
||||||
|
3465 chiffrés, sur une machine où ils n'avaient rien à faire. Le rôle **relit l'en-tête**
|
||||||
|
après avoir écrit et refuse si le fichier n'est pas chiffré.
|
||||||
|
|
||||||
|
## Colocalisation
|
||||||
|
|
||||||
|
Rien n'oblige à lui donner sa propre machine : chez l'hébergeur, les deux pouvoirs
|
||||||
|
résident légitimement au même endroit, et une machine de plus dans un écosystème
|
||||||
|
volontairement maigre se paie. Le déclarer séparément rend le pouvoir **visible**, ce qui
|
||||||
|
est l'essentiel.
|
||||||
|
|
||||||
|
Chez un tenant qui n'est pas l'hébergeur, la question ne se pose pas : le rôle n'a pas à
|
||||||
|
y être.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Variable | Rôle |
|
||||||
|
|---|---|
|
||||||
|
| `serveur_ops_site_depot` | dossier du dépôt SITE chez le runner (cloné par `serveur_ops`) |
|
||||||
|
| `serveur_ops_site_voute_source` | chemin de `underlay.vault.yml` sur le contrôleur |
|
||||||
|
| `serveur_ops_site_voute_deposer` | `false` pour un runner qui lit la carte sans détenir les clés |
|
||||||
|
|
||||||
|
## Ce que ce rôle ne fait pas
|
||||||
|
|
||||||
|
Il n'installe rien, n'ouvre aucun port, ne sauvegarde rien. Il ne fait qu'**attribuer un
|
||||||
|
pouvoir** — et le rendre lisible dans le plan.
|
||||||
43
roles/serveur_ops_site/defaults/main.yml
Normal file
43
roles/serveur_ops_site/defaults/main.yml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
# LE RUNNER DE SITE — celui qui MATÉRIALISE.
|
||||||
|
#
|
||||||
|
# Le travail d'un runner se divise en trois, et les deux premières portées appartiennent
|
||||||
|
# au TENANT tandis que la troisième appartient à la FABRIC :
|
||||||
|
#
|
||||||
|
# calculer plan -> inventaire aucune voûte portée OPS (serveur_ops)
|
||||||
|
# configurer rôles sur ses machines voûte du TENANT portée OPS (serveur_ops)
|
||||||
|
# matérialiser créer/détruire des VM voûte du SITE portée SITE (ce rôle)
|
||||||
|
#
|
||||||
|
# POURQUOI LA SÉPARATION. Si chaque écosystème portait le pouvoir de matérialiser, la
|
||||||
|
# voûte du SITE se retrouverait en N exemplaires — le secret le plus dangereux du système,
|
||||||
|
# recopié autant de fois qu'il y a de locataires. C'est précisément ce que la séparation
|
||||||
|
# des voûtes (2026-08-22) cherchait à éviter.
|
||||||
|
#
|
||||||
|
# Et l'inverse serait pire : un runner UNIQUE qui ferait tout devrait entrer en SSH chez
|
||||||
|
# tous les tenants, donc traverser le default-deny inter-tenant — et rendrait toute
|
||||||
|
# émancipation impossible, puisqu'un écosystème ne pourrait plus se rebâtir sans son hôte.
|
||||||
|
#
|
||||||
|
# Deux pouvoirs, deux rôles, aucun omnipotent : ce rôle crée des VM vides et n'entre
|
||||||
|
# jamais chez un tenant ; `serveur_ops` habille des machines et ne touche jamais la fabric.
|
||||||
|
#
|
||||||
|
# QUI PEUT LE DÉCLARER : l'écosystème de l'HÉBERGEUR, celui qui exploite la fabric. Un
|
||||||
|
# tenant ordinaire qui le déclarerait s'arrogerait un pouvoir sur ses voisins.
|
||||||
|
|
||||||
|
serveur_ops_site_utilisateur: "setops"
|
||||||
|
serveur_ops_site_racine: "/opt/setops"
|
||||||
|
|
||||||
|
# Le dépôt du SITE, tel qu'il atterrit chez le runner. Il doit avoir été cloné par
|
||||||
|
# `serveur_ops` (entrée `role: hebergeur` de `serveur_ops_depots`).
|
||||||
|
serveur_ops_site_depot: ""
|
||||||
|
|
||||||
|
# --- LA VOÛTE DU SITE --------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Déposée CHIFFRÉE, jamais en clair. Le mot de passe n'est pas stocké : le Makefile
|
||||||
|
# ajoute `--ask-vault-pass` quand aucun fichier de mot de passe n'est défini, et
|
||||||
|
# l'exploitant le tape au moment d'agir.
|
||||||
|
#
|
||||||
|
# `decrypt: no` EST OBLIGATOIRE sur la copie. Sans lui, Ansible DÉCHIFFRE la source quand
|
||||||
|
# il détient le mot de passe — mesuré le 2026-08-24, 776 octets en clair au lieu de 3465
|
||||||
|
# chiffrés, sur une machine où ils n'avaient rien à faire.
|
||||||
|
serveur_ops_site_voute_source: ""
|
||||||
|
serveur_ops_site_voute_deposer: true
|
||||||
12
roles/serveur_ops_site/meta/authentification.yml
Normal file
12
roles/serveur_ops_site/meta/authentification.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
# Position de ce role dans la directive d'authentification (D-38..D-41).
|
||||||
|
# Voir docs/authentification.md. Gardee par la preuve P29.
|
||||||
|
authentification:
|
||||||
|
portee: sans-auth-humaine
|
||||||
|
mecanisme: aucun
|
||||||
|
formulaire_local: sans-objet
|
||||||
|
secours: "Acces SSH a l'hote, puis `sudo -iu setops`"
|
||||||
|
raison: >-
|
||||||
|
Ce role n'expose aucune interface : il DETIENT un pouvoir au lieu d'en offrir un.
|
||||||
|
Ce qui le protege n'est pas une authentification mais le fait que la voute deposee
|
||||||
|
reste CHIFFREE et que son mot de passe soit tape a l'execution, jamais stocke.
|
||||||
7
roles/serveur_ops_site/meta/empreinte.yml
Normal file
7
roles/serveur_ops_site/meta/empreinte.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
# Empreinte ressources — ce rôle n'ajoute qu'un fichier et des appels d'API. Il se
|
||||||
|
# colocalise avec `serveur_ops`, dont il partage la racine et l'utilisateur.
|
||||||
|
setops_empreinte:
|
||||||
|
coeurs: 0
|
||||||
|
memoire_mo: 0
|
||||||
|
disque_go: 0
|
||||||
15
roles/serveur_ops_site/meta/flux.yml
Normal file
15
roles/serveur_ops_site/meta/flux.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
# Flux réseau du runner de SITE. Voir docs/flux-conception.md.
|
||||||
|
#
|
||||||
|
# Un seul flux, et c'est tout son sens : l'API de l'hyperviseur. Ce rôle n'entre JAMAIS
|
||||||
|
# chez un tenant — c'est `serveur_ops` qui configure les machines, une fois qu'elles
|
||||||
|
# existent.
|
||||||
|
flux:
|
||||||
|
- sens: egress
|
||||||
|
port: 8006
|
||||||
|
protocole: tcp
|
||||||
|
pair: externe
|
||||||
|
chiffrement: tls-requis
|
||||||
|
raison: >-
|
||||||
|
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.
|
||||||
64
roles/serveur_ops_site/tasks/main.yml
Normal file
64
roles/serveur_ops_site/tasks/main.yml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
- name: Exiger les intrants du runner de site
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- serveur_ops_site_depot | length > 0
|
||||||
|
- (not (serveur_ops_site_voute_deposer | bool)) or (serveur_ops_site_voute_source | length > 0)
|
||||||
|
fail_msg: >-
|
||||||
|
serveur_ops_site exige `serveur_ops_site_depot` (le dossier du dépôt SITE chez le
|
||||||
|
runner, cloné par serveur_ops) et, si le dépôt de la voûte est demandé,
|
||||||
|
`serveur_ops_site_voute_source` (le chemin de `underlay.vault.yml` sur le contrôleur).
|
||||||
|
|
||||||
|
- name: Le dépôt du SITE est-il bien là ?
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ serveur_ops_site_racine }}/{{ serveur_ops_site_depot }}/underlay.yml"
|
||||||
|
register: serveur_ops_site_carte
|
||||||
|
|
||||||
|
- name: Refuser si la carte de la fabric manque
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- serveur_ops_site_carte.stat.exists
|
||||||
|
fail_msg: >-
|
||||||
|
{{ serveur_ops_site_racine }}/{{ serveur_ops_site_depot }}/underlay.yml est absent.
|
||||||
|
Déclarer ce dépôt dans `serveur_ops_depots` (role: hebergeur) et rejouer serveur_ops :
|
||||||
|
un runner de site sans carte ne sait pas sur quoi il matérialise.
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
# LES CLÉS DU MONDE PHYSIQUE, DE DROIT ET NON PAR EMPRUNT.
|
||||||
|
#
|
||||||
|
# `decrypt: no` : voir defaults/main.yml. Le fichier reste chiffré ; seul le mot de passe,
|
||||||
|
# tapé à l'exécution, l'ouvre.
|
||||||
|
- name: Déposer la voûte du SITE (chiffrée)
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ serveur_ops_site_voute_source }}"
|
||||||
|
dest: "{{ serveur_ops_site_racine }}/{{ serveur_ops_site_depot }}/underlay.vault.yml"
|
||||||
|
owner: "{{ serveur_ops_site_utilisateur }}"
|
||||||
|
group: "{{ serveur_ops_site_utilisateur }}"
|
||||||
|
mode: "0600"
|
||||||
|
decrypt: false
|
||||||
|
when:
|
||||||
|
- serveur_ops_site_voute_deposer | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
# ÉCRIRE, PUIS RELIRE (D-68). Une voûte déchiffrée par accident est une fuite silencieuse :
|
||||||
|
# le fichier existe, le rôle se dit satisfait, et les clés du cluster dorment en clair.
|
||||||
|
- name: Relire l'en-tête de la voûte déposée
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "head -c 21 {{ serveur_ops_site_racine }}/{{ serveur_ops_site_depot }}/underlay.vault.yml"
|
||||||
|
register: serveur_ops_site_entete
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- serveur_ops_site_voute_deposer | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Exiger que la voûte déposée soit CHIFFRÉE
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'$ANSIBLE_VAULT' in serveur_ops_site_entete.stdout"
|
||||||
|
fail_msg: >-
|
||||||
|
La voûte déposée n'est PAS chiffrée — elle a été déchiffrée en transit.
|
||||||
|
Vérifier `decrypt: false` sur la copie. Détruire le fichier au shred et
|
||||||
|
considérer les justificatifs du cluster comme exposés.
|
||||||
|
when:
|
||||||
|
- serveur_ops_site_voute_deposer | bool
|
||||||
|
- not ansible_check_mode
|
||||||
Loading…
Reference in a new issue