La face reseau porte ses zones ; verifier_frontiere confronte supernet, administration, tunnel, alias de groupe, routes et traduction sortante a la face reseau et a la fiche du site. Aucun ecart ; cinq alterations vues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
269 lines
16 KiB
Markdown
269 lines
16 KiB
Markdown
# 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) sont faites ; restent les entrées publiques, puis les sorties et l'administration.
|
|
|
|
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.
|