runner : serveur_ops_tenant — le runner d'un tenant recoit enfin sa voute
Some checks are pending
verifier / verifier (push) Waiting to run
Some checks are pending
verifier / verifier (push) Waiting to run
La doctrine des runners decrit trois portees depuis le 2026-08-22 : calculer plan -> inventaire aucune voute serveur_ops configurer roles sur ses machines voute du TENANT <- revendiquee, jamais recue materialiser creer/detruire des VM voute du SITE serveur_ops_site La deuxieme ligne etait un trou. RIEN ne deposait jamais la voute d'un tenant sur son runner. Un runner pouvait deriver son inventaire et ne rien pouvoir en faire : chaque role qui demande un secret echouait sur son assertion, et l'echec ne disait pas qu'il manquait un FICHIER, seulement que les valeurs etaient vides. Decouvert en preparant la reconstruction de Chezlepro. Le runner du SITE peut materialiser ses quinze machines ; il ne peut pas les configurer, parce que Chezlepro a sa PROPRE autorite de certification — donc `client_pki` y reclame un secret de Chezlepro. Le runner du site ne l'a pas, et NE DOIT PAS l'avoir : c'est la ligne qui rend l'hebergement mutualise defendable. `serveur_ops_tenant` est le symetrique exact de `serveur_ops_site` : un MARQUEUR, pas un installateur. Il depose la voute `decrypt: false`, puis RELIT l'en-tete du fichier depose — une voute dechiffree par accident est une fuite silencieuse. Le dossier d'inventaire (`principal` ou `production`) se DECOUVRE sur la machine plutot que d'etre ecrit. Pourquoi un role a part et non une option de `serveur_ops` : donner sa voute a un runner est un POUVOIR, pas un reglage. Le declarer au plan force a repondre a « cette machine a-t-elle le droit de configurer cet ecosysteme ? » — une option activee par defaut y repondrait a notre place. CHEZLEPRO LISAIT ENCORE SON GENOME CHEZ PATIENT 0. Trouve au passage, et bloquant pour la reconstruction : `serveur_ops_forge_amont` pointait sur `10.29.16.11` — la forge de patient 0, l'amont d'avant que le site ait la sienne. D-81 a tranche depuis. Laisse tel quel, Chezlepro se serait reconstruit depuis un moteur perime, sur un reseau que le decoupage en zones a de toute facon deplace. Corrige vers la forge du site, PAR SON IP — `forge.genese.internal` appartient a la zone souveraine du site, et un tenant ne resout que la sienne ; le certificat SERVI porte bien `IP Address:10.0.33.11`. La racine de l'AC du site est desormais versionnee a cote de la carte de la fabric a laquelle elle appartient, et son chemin se DERIVE du symlink `underlay.yml` : il vaut depuis le poste comme depuis un runner. Le role est inscrit dans les quatre registres qui l'exigeaient — couches de deploiement, graphe des dependances, catalogue des services, carte d'orientation. Ce sont les preuves P08, P38 et P48 qui l'ont reclame, chacune a son tour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9e8679215d
commit
8890c997af
13 changed files with 329 additions and 7 deletions
43
CHANGELOG.md
43
CHANGELOG.md
|
|
@ -1,5 +1,48 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-08-26 — `serveur_ops_tenant` : le runner d'un tenant reçoit enfin sa voûte
|
||||
|
||||
**47 preuves.** La doctrine des runners décrit trois portées depuis le 2026-08-22 :
|
||||
|
||||
```
|
||||
calculer plan → inventaire aucune voûte serveur_ops
|
||||
configurer rôles sur ses machines voûte du TENANT ← revendiquée, jamais reçue
|
||||
matérialiser créer/détruire des VM voûte du SITE serveur_ops_site
|
||||
```
|
||||
|
||||
La deuxième ligne était un trou. **Rien ne déposait jamais la voûte d'un tenant sur son
|
||||
runner.** Un runner pouvait dériver son inventaire et ne rien pouvoir en faire : chaque
|
||||
rôle qui demande un secret échouait sur son assertion, et l'échec ne disait pas qu'il
|
||||
manquait un *fichier*, seulement que les valeurs étaient vides.
|
||||
|
||||
Découvert en préparant la reconstruction de Chezlepro. Le runner du site peut matérialiser
|
||||
ses quinze machines ; il ne peut pas les configurer, parce que Chezlepro a sa **propre**
|
||||
autorité de certification — donc `client_pki` y réclame un secret de Chezlepro. Le runner
|
||||
du site ne l'a pas, et **ne doit pas l'avoir** : c'est la ligne qui rend l'hébergement
|
||||
mutualisé défendable.
|
||||
|
||||
`serveur_ops_tenant` est le symétrique exact de `serveur_ops_site` : un **marqueur**, pas
|
||||
un installateur. Il dépose la voûte `decrypt: false`, puis **relit l'en-tête** du fichier
|
||||
déposé — une voûte déchiffrée par accident est une fuite silencieuse. Le dossier
|
||||
d'inventaire (`principal` ou `production`) se **découvre** sur la machine.
|
||||
|
||||
*Pourquoi un rôle à part et non une option : donner sa voûte à un runner est un pouvoir,
|
||||
pas un réglage. Le déclarer au plan force à répondre à « cette machine a-t-elle le droit de
|
||||
configurer cet écosystème ? » — une option activée par défaut y répondrait à notre place.*
|
||||
|
||||
### Chezlepro lisait encore son génome chez patient 0
|
||||
|
||||
Trouvé au passage, et bloquant pour la reconstruction : `serveur_ops_forge_amont` pointait
|
||||
sur `10.29.16.11` — la forge de patient 0, l'amont d'avant que le site ait la sienne.
|
||||
**D-81** a tranché depuis. Laissé tel quel, Chezlepro se serait reconstruit depuis un
|
||||
moteur périmé, sur un réseau que le découpage en zones a de toute façon déplacé.
|
||||
|
||||
Corrigé vers la forge du site, **par son IP** — `forge.genese.internal` appartient à la
|
||||
zone souveraine du site, et un tenant ne résout que la sienne ; le certificat servi porte
|
||||
bien `IP Address:10.0.33.11`. La racine de l'AC du site est désormais versionnée à côté de
|
||||
la carte de la fabric à laquelle elle appartient, et son chemin se **dérive** du symlink
|
||||
`underlay.yml` — il vaut donc depuis le poste comme depuis un runner.
|
||||
|
||||
## 2026-08-26 — Le runner du site travaille, et le génome remonte chez lui
|
||||
|
||||
**46 preuves.** `serveur_ops` et `serveur_ops_site` sont déployés sur `site-ops-01` : le
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
| 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. |
|
||||
| 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. |
|
||||
|
|
@ -41,16 +41,16 @@
|
|||
| 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 |
|
||||
| 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, 60 roles avec README. |
|
||||
| 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 : 35 role(s) serveur/client tous nommes, 36 groupe(s) cite(s) en table existent tous. |
|
||||
| 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). |
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ README de rôles). Cette page comble ces deux trous.
|
|||
|
||||
| Ce qu'on compte | Combien | Comment on le mesure |
|
||||
|---|---|---|
|
||||
| rôles | 60 | `roles/*/` |
|
||||
| README de rôles | 60 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| rôles | 61 | `roles/*/` |
|
||||
| README de rôles | 61 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| documents | 38 | `docs/*.md` |
|
||||
| pièces d'audit | 27 | `docs/audit/*` |
|
||||
| unités de wiki | 27 | `wiki/*.md` |
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ couches:
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -118,6 +118,16 @@ groupes:
|
|||
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
|
||||
|
|
|
|||
15
playbooks/groupes/serveur_ops_tenant.yml
Normal file
15
playbooks/groupes/serveur_ops_tenant.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveur_ops_tenant
|
||||
hosts: serveur_ops_tenant
|
||||
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_tenant
|
||||
48
roles/serveur_ops_tenant/README.md
Normal file
48
roles/serveur_ops_tenant/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# serveur_ops_tenant — donner à un runner la voûte de SON écosystème
|
||||
|
||||
> **Pour qui :** l'exploitant qui monte le runner d'un tenant.
|
||||
|
||||
Marqueur, pas installateur. `serveur_ops` installe le poste — Ansible, les collections, les
|
||||
dépôts du génome, les symlinks. Ce rôle-ci y ajoute **un pouvoir** : la voûte de
|
||||
l'écosystème, déposée **chiffrée**, sans laquelle le runner peut tout calculer et ne rien
|
||||
configurer.
|
||||
|
||||
## Les trois portées d'un runner
|
||||
|
||||
```
|
||||
calculer plan → inventaire aucune voûte serveur_ops
|
||||
configurer rôles sur ses machines voûte du TENANT serveur_ops_tenant
|
||||
matérialiser créer/détruire des VM voûte du SITE serveur_ops_site
|
||||
```
|
||||
|
||||
Aucun runner n'est omnipotent. Le runner d'un **site** matérialise le terrain et n'entre
|
||||
jamais chez un tenant ; le runner d'un **tenant** configure son écosystème et ne touche
|
||||
jamais la fabric.
|
||||
|
||||
## Pourquoi un rôle à part
|
||||
|
||||
Donner sa voûte à un runner est un **pouvoir**, pas un réglage. Le déclarer au plan force à
|
||||
répondre à « cette machine a-t-elle le droit de configurer cet écosystème ? » — une option
|
||||
activée par défaut y répondrait à notre place.
|
||||
|
||||
## Ce qu'il exige
|
||||
|
||||
| Variable | Ce que c'est |
|
||||
|---|---|
|
||||
| `serveur_ops_tenant_depot` | le dossier de cet écosystème chez le runner ; vaut `serveur_ops_instance` par défaut |
|
||||
| `serveur_ops_tenant_voute_source` | le chemin du `vault.yml` **sur le contrôleur** |
|
||||
|
||||
Le dossier d'inventaire (`principal` ou `production`) se **découvre** sur la machine : il
|
||||
n'est écrit nulle part.
|
||||
|
||||
## Ce qu'il garantit
|
||||
|
||||
`decrypt: false` sur la copie, puis **relecture de l'en-tête** du fichier déposé. Une voûte
|
||||
déchiffrée par accident est une fuite silencieuse — le fichier existe, le rôle se dirait
|
||||
satisfait, et les secrets dormiraient en clair sur une machine. Le rôle refuse plutôt.
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- `roles/serveur_ops/README.md` — l'installateur
|
||||
- `roles/serveur_ops_site/README.md` — le pouvoir symétrique, côté fabric
|
||||
- `docs/implanter-un-tenant-sur-un-site.md` — la séquence complète
|
||||
58
roles/serveur_ops_tenant/defaults/main.yml
Normal file
58
roles/serveur_ops_tenant/defaults/main.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
# LE RUNNER D'UN TENANT — celui qui CONFIGURE.
|
||||
#
|
||||
# Le travail d'un runner se divise en trois portées, et chacune a son rôle :
|
||||
#
|
||||
# calculer plan -> inventaire aucune voûte `serveur_ops`
|
||||
# configurer rôles sur ses machines voûte du TENANT CE RÔLE
|
||||
# matérialiser créer/détruire des VM voûte du SITE `serveur_ops_site`
|
||||
#
|
||||
# CE QUI MANQUAIT (2026-08-26). La portée « configurer » était attribuée à `serveur_ops`
|
||||
# dans la doctrine — mais rien ne déposait jamais la voûte du tenant sur son runner. Un
|
||||
# runner pouvait donc dériver son inventaire et ne rien pouvoir en faire : chaque rôle qui
|
||||
# demande un secret échouait sur son assertion, et l'échec ne disait pas qu'il manquait un
|
||||
# FICHIER, seulement que les valeurs étaient vides.
|
||||
#
|
||||
# Découvert en préparant la reconstruction de Chezlepro : le runner du SITE peut
|
||||
# matérialiser ses quinze machines, mais ne peut pas les configurer — Chezlepro a sa
|
||||
# PROPRE autorité de certification, donc `client_pki` y réclame un secret de Chezlepro.
|
||||
# Le runner du site ne l'a pas, et ne doit pas l'avoir : c'est la ligne qui rend
|
||||
# l'hébergement mutualisé défendable.
|
||||
#
|
||||
# POURQUOI UN RÔLE À PART, et non une option de `serveur_ops`. Donner sa voûte à un runner
|
||||
# est un POUVOIR, pas un réglage. Le déclarer explicitement au plan force à répondre à la
|
||||
# question « cette machine a-t-elle le droit de configurer cet écosystème ? » — alors
|
||||
# qu'une option activée par défaut y répondrait à notre place. C'est le même patron que
|
||||
# `serveur_artefacts` + `serveur_cache_site` : un installateur, puis un marqueur qui
|
||||
# ajoute un pouvoir.
|
||||
#
|
||||
# QUI PEUT LE DÉCLARER : l'écosystème lui-même, pour SA machine. Un runner de SITE ne le
|
||||
# déclare jamais — il porte les plans des tenants en `role: tenant` précisément pour dire
|
||||
# qu'il prépare leur terrain sans les piloter.
|
||||
|
||||
serveur_ops_tenant_utilisateur: "setops"
|
||||
serveur_ops_tenant_racine: "/opt/setops"
|
||||
|
||||
# Le dossier du dépôt de CET écosystème chez le runner, cloné par `serveur_ops`
|
||||
# (entrée `role: instance` de `serveur_ops_depots`). Par défaut : celui que le lien
|
||||
# `instance` désigne déjà — le pilote et sa voûte ne peuvent pas viser deux écosystèmes.
|
||||
serveur_ops_tenant_depot: "{{ serveur_ops_instance | default('') }}"
|
||||
|
||||
# --- LA VOÛTE DU TENANT ------------------------------------------------------
|
||||
#
|
||||
# Déposée CHIFFRÉE, jamais en clair. Le mot de passe n'est pas stocké : 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 — mesuré le 2026-08-24 sur la voûte du site, 776 octets
|
||||
# en clair au lieu de 3465 chiffrés, sur une machine où ils n'avaient rien à faire.
|
||||
serveur_ops_tenant_voute_source: ""
|
||||
serveur_ops_tenant_voute_deposer: true
|
||||
|
||||
# LE DOSSIER D'INVENTAIRE SE DÉCOUVRE, il ne s'écrit pas. Le dépôt en connaît deux noms
|
||||
# — `principal` et `production` — et l'ordre de préférence est celui de
|
||||
# `inventory_rules.dossier_inventaire`. Écrire le nom en dur ici le ferait mentir pour
|
||||
# l'écosystème qui emploie l'autre.
|
||||
serveur_ops_tenant_inventaires_connus:
|
||||
- principal
|
||||
- production
|
||||
12
roles/serveur_ops_tenant/meta/authentification.yml
Normal file
12
roles/serveur_ops_tenant/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_tenant/meta/empreinte.yml
Normal file
7
roles/serveur_ops_tenant/meta/empreinte.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
# Empreinte ressources — ce rôle n'ajoute qu'un fichier. Il se colocalise avec
|
||||
# `serveur_ops`, dont il partage la racine et l'utilisateur.
|
||||
setops_empreinte:
|
||||
coeurs: 0
|
||||
memoire_mo: 0
|
||||
disque_go: 0
|
||||
11
roles/serveur_ops_tenant/meta/flux.yml
Normal file
11
roles/serveur_ops_tenant/meta/flux.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
# Flux réseau du runner de TENANT. Voir docs/flux-conception.md.
|
||||
#
|
||||
# AUCUN FLUX PROPRE, ET C'EST LE POINT. Ce rôle ne fait que déposer un fichier ; ce qui
|
||||
# consomme la voûte, c'est `serveur_ops` quand il configure les machines de l'écosystème,
|
||||
# et ses flux sont déclarés chez lui. Ce rôle n'atteint NI la fabric NI la frontière —
|
||||
# c'est exactement ce qui le sépare de `serveur_ops_site`.
|
||||
#
|
||||
# Le dire explicitement plutôt que d'omettre le fichier : une absence de déclaration se
|
||||
# lit comme un oubli, une déclaration vide se lit comme une décision.
|
||||
flux: []
|
||||
113
roles/serveur_ops_tenant/tasks/main.yml
Normal file
113
roles/serveur_ops_tenant/tasks/main.yml
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
- name: Exiger les intrants du runner de tenant
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_ops_tenant_depot | length > 0
|
||||
- (not (serveur_ops_tenant_voute_deposer | bool))
|
||||
or (serveur_ops_tenant_voute_source | length > 0)
|
||||
fail_msg: >-
|
||||
serveur_ops_tenant exige `serveur_ops_tenant_depot` (le dossier de CET écosystème
|
||||
chez le runner, cloné par serveur_ops — il vaut `serveur_ops_instance` par défaut)
|
||||
et, si le dépôt de la voûte est demandé, `serveur_ops_tenant_voute_source`
|
||||
(le chemin de son `vault.yml` sur le contrôleur).
|
||||
|
||||
- name: Le plan de l'écosystème est-il bien là ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/plan/serveurs.yml"
|
||||
register: serveur_ops_tenant_plan
|
||||
|
||||
- name: Refuser si le plan de l'écosystème manque
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_ops_tenant_plan.stat.exists
|
||||
fail_msg: >-
|
||||
{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/plan/serveurs.yml est
|
||||
absent. Déclarer ce dépôt dans `serveur_ops_depots` (role: instance) et rejouer
|
||||
serveur_ops : donner sa voûte à un runner qui n'a pas le plan qu'elle ouvre ne
|
||||
configure rien — ça ne fait que déposer un secret.
|
||||
when: not ansible_check_mode
|
||||
|
||||
# LE DOSSIER D'INVENTAIRE SE DÉCOUVRE. Deux noms existent dans le dépôt (`principal`,
|
||||
# `production`) et l'écrire en dur le ferait mentir pour l'écosystème qui emploie l'autre.
|
||||
- name: Chercher le dossier d'inventaire de cet écosystème
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/inventories/{{ item }}"
|
||||
register: serveur_ops_tenant_candidats
|
||||
loop: "{{ serveur_ops_tenant_inventaires_connus }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
|
||||
- name: Retenir le premier qui existe
|
||||
ansible.builtin.set_fact:
|
||||
serveur_ops_tenant_inventaire: >-
|
||||
{{ (serveur_ops_tenant_candidats.results | selectattr('stat.exists')
|
||||
| map(attribute='item') | list | first) | default('') }}
|
||||
|
||||
- name: Refuser si aucun inventaire n'est reconnu
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_ops_tenant_inventaire | length > 0
|
||||
fail_msg: >-
|
||||
Aucun dossier d'inventaire trouvé sous
|
||||
{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/inventories/ parmi
|
||||
{{ serveur_ops_tenant_inventaires_connus | join(', ') }}. Sans lui, on ne saurait pas
|
||||
où déposer la voûte — et la déposer au mauvais endroit revient à ne pas la déposer,
|
||||
en laissant croire le contraire.
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Préparer le dossier des variables communes
|
||||
ansible.builtin.file:
|
||||
path: >-
|
||||
{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/inventories/{{
|
||||
serveur_ops_tenant_inventaire }}/group_vars/all
|
||||
state: directory
|
||||
owner: "{{ serveur_ops_tenant_utilisateur }}"
|
||||
group: "{{ serveur_ops_tenant_utilisateur }}"
|
||||
mode: "0700"
|
||||
when:
|
||||
- serveur_ops_tenant_voute_deposer | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
# LES SECRETS DE L'ÉCOSYSTÈME, DE DROIT ET NON PAR EMPRUNT.
|
||||
#
|
||||
# `decrypt: false` : 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 de l'écosystème (chiffrée)
|
||||
ansible.builtin.copy:
|
||||
src: "{{ serveur_ops_tenant_voute_source }}"
|
||||
dest: >-
|
||||
{{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot }}/inventories/{{
|
||||
serveur_ops_tenant_inventaire }}/group_vars/all/vault.yml
|
||||
owner: "{{ serveur_ops_tenant_utilisateur }}"
|
||||
group: "{{ serveur_ops_tenant_utilisateur }}"
|
||||
mode: "0600"
|
||||
decrypt: false
|
||||
when:
|
||||
- serveur_ops_tenant_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 secrets de l'écosystème dorment en
|
||||
# clair sur une machine.
|
||||
- name: Relire l'en-tête de la voûte déposée
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
head -c 21 {{ serveur_ops_tenant_racine }}/{{ serveur_ops_tenant_depot
|
||||
}}/inventories/{{ serveur_ops_tenant_inventaire }}/group_vars/all/vault.yml
|
||||
register: serveur_ops_tenant_entete
|
||||
changed_when: false
|
||||
when:
|
||||
- serveur_ops_tenant_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_tenant_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 secrets de cet écosystème comme exposés.
|
||||
when:
|
||||
- serveur_ops_tenant_voute_deposer | bool
|
||||
- not ansible_check_mode
|
||||
Loading…
Reference in a new issue