frontiere : le devis voit le site — fabric, alias et regles

Un mot manquait au vocabulaire des flux. Le runner de SITE declarait son API Proxmox en
`externe`, qui se rend par !SETOPS_INTERNES — or les hyperviseurs SONT en RFC 1918 : la
regle les aurait exclus tout en ayant l'air d'ouvrir le flux. D'ou `fabric`.

Deux flux manquaient :
  - serveur_cache_site n'avait aucun egress : la chaine de caches se terminait sur un
    cache vide, et ca ne se serait vu qu'au premier apt update d'un ecosysteme neuf ;
  - serveur_ops_site n'avait que l'API : le shell des noeuds et l'API de la frontiere
    sont deux autres pouvoirs, declares a part.

Le devis ignorait les machines du site — dans aucun plan de tenant, donc invisibles a sa
boucle, zero regle rendue SANS RIEN SIGNALER. Il emet desormais SETOPS_SITE,
SETOPS_FABRIC, SETOPS_ADMIN_SITE et un alias par role, sur opt2 (arrivee reelle du
trafic) et lan pour le SSH, plus le NAT sortant du site.

Le socle s'applique au site : site_inventaire.py range ses machines dans serveur_debian.

Patient 0 ne declare plus serveur_ops_site ni serveur_cache_site : il est un tenant
comme les autres, c'est meme tout ce qu'il prouve. Inventaire regenere.

42 preuves vertes. Devis : 26 regles a creer, 5 a retirer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-25 00:49:04 -04:00
parent bf35b4f528
commit dc92f01fe4
7 changed files with 236 additions and 7 deletions

View file

@ -1,5 +1,59 @@
# CHANGELOG — Set-OPS # CHANGELOG — Set-OPS
## 2026-08-25 — La frontière voit le site : `fabric`, alias et règles
### Un mot manquait au vocabulaire des flux
Le runner de SITE déclarait son API Proxmox en `pair: externe`. Or « externe » se rend
par `!SETOPS_INTERNES` — *tout sauf les espaces privés* — et les hyperviseurs **sont** en
RFC 1918. La règle sortante les aurait exclus **tout en ayant l'air d'ouvrir le flux**.
Un flux qui a l'air ouvert et qui ne l'est pas est pire qu'un flux fermé : il ne se
cherche pas.
D'où `fabric` : le matériel de l'hébergeur — hyperviseurs, frontière, commutateurs. Ce
n'est ni `flotte` (les machines d'un écosystème) ni `voisins_site` (les tenants d'à côté),
c'est le **socle** sur lequel les uns et les autres reposent. Le seul à s'y adresser est
le runner du site, et c'est tout son objet.
### Deux flux qui n'existaient pas
**Le cache racine ne pouvait rien aller chercher.** `serveur_cache_site` ne déclarait
qu'un `ingress 3142` : la chaîne entière se terminait sur un cache vide. Ça ne se serait
vu qu'au premier `apt update` d'un écosystème neuf — c'est-à-dire au pire moment.
**Le runner de site n'avait que l'API.** Déplacer un disque, poser un pont, lire une
configuration réseau passe par le shell du nœud ; ouvrir les flux du tenant qu'on
matérialise passe par l'API de la frontière. Trois pouvoirs distincts, déclarés à part.
### Ce que le devis ignorait
Les machines du site ne sont dans **aucun** plan de tenant : la boucle du devis ne
pouvait pas les voir, et il rendait zéro règle pour elles **sans rien signaler**. Un
devis muet sur une machine qui existe n'est pas un devis.
Le devis émet désormais `SETOPS_SITE` (le réseau), `SETOPS_FABRIC` (ce que le runner
pilote), `SETOPS_ADMIN_SITE` (seule source du SSH) et un alias par rôle du site. Leur
trafic arrive par `opt2`, **pas** par le lien de transit — une règle posée sur la mauvaise
interface ne correspond jamais, et c'est la panne la plus silencieuse de cette couche. Le
SSH de gestion, lui, arrive par `lan`. Plus le NAT sortant du site : son réseau est
directement attaché, mais le mode automatique d'OPNsense a été quitté en déclarant les
tenants à la main — compter sur un mode qu'on a soi-même quitté se paierait au premier
téléchargement.
**Le socle s'applique aussi au site.** `site_inventaire.py` range ses machines dans
`serveur_debian` : même durcissement SSH, mêmes horloges, mêmes règles que n'importe
quelle machine de la flotte. L'oublier aurait donné à la machine la plus puissante du
site la protection la plus faible.
### Patient 0 rendu à sa condition de tenant
Son plan déclarait encore `serveur_ops_site` et `serveur_cache_site` — il tenait ces
rôles parce que le SITE n'existait pas comme objet à lui. Il existe. Un tenant qui
garderait ces rôles s'arrogerait un pouvoir sur ses voisins, et patient 0 est un tenant
comme les autres : c'est même tout ce qu'il prouve.
Bilan au devis : **26 règles à créer, 5 à retirer** (dont les trois périmées de patient 0).
## 2026-08-24 — Un SITE n'est pas un plan : ses machines vivent dans l'underlay ## 2026-08-24 — Un SITE n'est pas un plan : ses machines vivent dans l'underlay
J'avais d'abord fait entrer le site dans le générateur des tenants, avec une branche J'avais d'abord fait entrer le site dans le générateur des tenants, avec une branche

View file

@ -21,7 +21,7 @@
| 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 : 36 groupes classes, aucun cycle, aucune arete en arriere. | | 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. | | P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 34 rôles, 91 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,8 +36,8 @@
| 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 : 8 reseau(x), aucune collision avec la plage tenant. | | P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 8 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. | | P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 78 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). | | 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 | 15 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (0 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 (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. | | 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. |
@ -46,7 +46,7 @@
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 49 scripts expliques et atteignables, 102 cibles make documentees, 60 roles avec README. | | 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). | | 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). | | 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)). | | 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 | 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. |

View file

@ -20,3 +20,27 @@ flux:
Servir les caches des écosystèmes voisins. Debian n'est ainsi téléchargé qu'une 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 fois pour tout le site, et le cache ne voit que des requêtes AGRÉGÉES — jamais
quelle machine installe quoi. quelle machine installe quoi.
# LA RACINE DOIT POUVOIR REMPLIR. Ce cache n'a pas d'amont interne — c'est LUI l'amont
# de tous les autres. Sans flux sortant, la chaine entiere se termine sur un cache vide :
#
# VM du tenant -> cache du tenant -> cache du SITE -> (rien)
#
# Il manquait, et ca ne se serait vu qu'au premier `apt update` d'un ecosysteme neuf —
# c'est-a-dire au pire moment.
- sens: egress
port: 80
protocole: tcp
pair: externe
chiffrement: clair
raison: >-
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.
- sens: egress
port: 443
protocole: tcp
pair: externe
chiffrement: tls-requis
raison: >-
Les dépôts tiers qui n'existent qu'en HTTPS (smallstep, Grafana, Icinga). Le cache
les relaie pour que la flotte n'ait pas à sortir elle-même.

View file

@ -1,15 +1,40 @@
--- ---
# Flux réseau du runner de SITE. Voir docs/flux-conception.md. # 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 # Les flux du POUVOIR DE MATERIALISER : API de l'hyperviseur, shell des noeuds, API de
# la frontiere. Chacun est un pouvoir distinct, donc declare a part. Ce rôle n'entre JAMAIS
# chez un tenant — c'est `serveur_ops` qui configure les machines, une fois qu'elles # chez un tenant — c'est `serveur_ops` qui configure les machines, une fois qu'elles
# existent. # existent.
flux: flux:
- sens: egress - sens: egress
port: 8006 port: 8006
protocole: tcp protocole: tcp
pair: externe pair: fabric
chiffrement: tls-requis chiffrement: tls-requis
raison: >- raison: >-
API de l'hyperviseur : créer, cloner et détruire les VM de la fabric. Le seul flux 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. par lequel un écosystème peut en matérialiser un autre.
# SSH VERS LES HYPERVISEURS. L'API Proxmox ne fait pas tout : deplacer un disque,
# lire une configuration reseau, poser un pont — cela passe par le shell du nœud.
# Le declarer separement de l'API rend visible que ce sont DEUX pouvoirs distincts.
- sens: egress
port: 22
protocole: tcp
pair: fabric
chiffrement: ssh
raison: >-
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.
# L'API DE LA FRONTIERE. Le runner du site pose les regles et les alias qui laissent
# passer les flux d'un tenant : sans ce flux, il prepare un terrain injoignable.
- sens: egress
port: 443
protocole: tcp
pair: fabric
chiffrement: tls-requis
raison: >-
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.

View file

@ -64,6 +64,10 @@ NOEUD_SORTIE = "<NOEUD-DE-SORTIE-EVPN>"
IF_TRANSIT = "<IF-TRANSIT>" IF_TRANSIT = "<IF-TRANSIT>"
IF_WAN = "<IF-WAN>" IF_WAN = "<IF-WAN>"
IF_GESTION = "<IF-GESTION>" IF_GESTION = "<IF-GESTION>"
# L'interface ou arrive le trafic des MACHINES DU SITE (vlan 30 / vmbr3). Une regle
# posee sur la mauvaise interface ne correspond jamais : le trafic du site ne penetre pas
# le pare-feu par le lien de transit des tenants, mais par sa propre patte.
IF_SITE = "<IF-SITE>"
def depot_hebergeur() -> Path | None: def depot_hebergeur() -> Path | None:
@ -314,6 +318,7 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
if_wan = _ou_marqueur(_intr, "opnsense_if_wan", IF_WAN) if_wan = _ou_marqueur(_intr, "opnsense_if_wan", IF_WAN)
if_transit = _ou_marqueur(_intr, "opnsense_if_transit", IF_TRANSIT) if_transit = _ou_marqueur(_intr, "opnsense_if_transit", IF_TRANSIT)
if_gestion = _ou_marqueur(_intr, "opnsense_if_gestion", IF_GESTION) if_gestion = _ou_marqueur(_intr, "opnsense_if_gestion", IF_GESTION)
if_site = _ou_marqueur(_intr, "opnsense_if_site", IF_SITE)
nom_actif, nomenclature_active = _instance_active() nom_actif, nomenclature_active = _instance_active()
# L'alias d'un tenant vaut ses SOUS-RESEAUX ATTRIBUES, pas son supernet. # L'alias d'un tenant vaut ses SOUS-RESEAUX ATTRIBUES, pas son supernet.
@ -543,6 +548,96 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
}) })
intrants = intrants_frontiere() intrants = intrants_frontiere()
# --- LES MACHINES DU SITE -------------------------------------------------
#
# Elles ne sont dans AUCUN plan de tenant : la boucle ci-dessus ne pouvait donc pas
# les voir, et le devis rendait zero regle pour elles sans rien signaler. Un devis
# muet sur une machine qui existe n'est pas un devis.
#
# Leur trafic arrive par la patte du site (`if_site`), pas par le lien de transit.
# Une regle posee sur la mauvaise interface ne correspond jamais — c'est la panne la
# plus silencieuse de cette couche.
_u = underlay_mod.charger()
_machines_site = underlay_mod.machines(_u)
if _machines_site:
_res_site = {r.get("nom"): r for r in underlay_mod.reseaux(_u)}
_sous_site = sorted({str(_res_site[m["reseau"]]["sous_reseau"])
for m in _machines_site if m.get("reseau") in _res_site})
alias["SETOPS_SITE"] = {
"type": "network",
"contenu": _sous_site,
"description": "Reseau des machines de l'hebergeur (runner de SITE, cache racine)",
}
# LA FABRIC — ce que le runner du site doit joindre pour MATERIALISER : l'API des
# hyperviseurs, leur shell, l'API de la frontiere. C'est du RFC 1918 : le rendre
# par `!SETOPS_INTERNES` (la forme de « vers Internet ») l'aurait exclu tout en
# ayant l'air d'ouvrir le flux. D'ou le mot `fabric`, et cet alias-ci.
_fabric = sorted({str(h["ip"]) for h in underlay_mod.hotes(_u)
if h.get("role") in ("hyperviseur", "frontiere") and h.get("ip")})
alias["SETOPS_FABRIC"] = {
"type": "host",
"contenu": _fabric,
"description": "Materiel de l'hebergeur : hyperviseurs et frontiere — "
"ce que le runner du SITE pilote pour materialiser",
}
# LE PLAN D'ADMINISTRATION, source du SSH vers le site. Sans lui, la regle
# n'aurait pas de source : l'omettre ferme l'acces, l'ouvrir a `any` exposerait
# le SSH du runner — la machine la plus puissante du site — a l'Internet.
_admin_site = [str(r["sous_reseau"]) for r in underlay_mod.reseaux(_u)
if r.get("nom") == "management" and r.get("sous_reseau")]
if _admin_site:
alias["SETOPS_ADMIN_SITE"] = {
"type": "network",
"contenu": _admin_site,
"description": "Plan d'administration — seule source du SSH vers le site",
}
# Le socle vaut aussi pour le site : ses machines sont des Debian de la flotte,
# et c'est lui qui porte leur SSH. `site_inventaire.py` les y range.
_roles_site = sorted({s for m in _machines_site for s in (m.get("services") or [])}
| {"serveur_debian"})
for _role in _roles_site:
_ips = sorted({str(m["ip"]) for m in _machines_site
if _role == "serveur_debian"
or _role in (m.get("services") or [])})
_a = nom_alias(_role, "SITE")
alias[_a] = {
"type": "host",
"contenu": _ips,
"description": f"Machines du site portant {_role}",
}
for _fl in flux_par_role.get(_role, []):
_entrant = _fl.get("sens") == "ingress"
_pairs_fl = _pairs(_fl)
if _entrant and "voisins_site" in _pairs_fl:
# Un tenant qui s'adresse au site : le paquet penetre le pare-feu par
# le lien de TRANSIT, pas par la patte du site. Une regle par voisin,
# parce qu'une regle porte une source.
_portees = [(f"SETOPS_TENANT_{p2}{n2['index']}", if_transit)
for _n2, p2, n2 in tenants]
elif _entrant and "22" in _ports(_fl) and _admin_site:
# Le SSH de gestion arrive par la patte d'ADMINISTRATION de la
# frontiere, pas par celle du site : c'est la que le paquet penetre
# le pare-feu. Une regle posee ailleurs ne correspondrait jamais.
_portees = [("SETOPS_ADMIN_SITE", if_gestion)]
elif _entrant:
continue # rien d'autre n'entre chez le site depuis l'exterieur
else:
_portees = [(_a, if_site)]
for _src, _itf in _portees:
regles.append({
"sens": "in" if _entrant else "out",
"interface": _itf,
"protocole": _fl.get("protocole", "tcp"),
"source": _src if _entrant else _a,
"destination": _a if _entrant else (
"SETOPS_FABRIC" if "fabric" in _pairs_fl else "!SETOPS_INTERNES"),
"ports": _ports(_fl),
"chiffrement": _fl.get("chiffrement"),
"role": _role,
"tenant": "SITE",
"raison": _fl.get("raison", ""),
})
transit = transit_underlay() transit = transit_underlay()
saut = prochain_saut(transit) saut = prochain_saut(transit)
# UNE ROUTE PAR SOUS-RESEAU REELLEMENT ATTRIBUE, et non une par supernet. # UNE ROUTE PAR SOUS-RESEAU REELLEMENT ATTRIBUE, et non une par supernet.
@ -589,6 +684,19 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
} }
for nom, pfx, n in tenants for nom, pfx, n in tenants
] ]
# LE SITE AUSSI SORT. Son reseau est directement attache a la frontiere (OPT2), donc
# le mode automatique d'OPNsense le couvrirait — mais nous ne sommes plus en mode
# automatique : declarer les tenants a la main l'a bascule. Compter sur un mode qu'on
# a soi-meme quitte, c'est le genre de supposition qui se paie au premier `apt update`
# du cache racine, quand plus rien ne se telecharge et que tout a l'air correct.
if _machines_site and "SETOPS_SITE" in alias:
nat.append({
"interface": if_wan,
"source": "SETOPS_SITE",
"destination": "any",
"cible": "wanip",
"tenant": "SITE",
})
# Adresse du noeud de sortie primaire sur le lien de frontiere, calculee une fois : # Adresse du noeud de sortie primaire sur le lien de frontiere, calculee une fois :
# elle sert au prochain saut des routes ET au commentaire qui l'explique. # elle sert au prochain saut des routes ET au commentaire qui l'explique.

View file

@ -53,8 +53,19 @@ CHIFFREMENT = {"tls-requis", "tls", "starttls", "ssh", "tls-cible", "clair", "n-
# #
# Il rend des CIDR (les supernets des voisins), pas des adresses d'hotes : il est donc # Il rend des CIDR (les supernets des voisins), pas des adresses d'hotes : il est donc
# traite a part, comme `admin`. # traite a part, comme `admin`.
# `fabric` — LE MATERIEL DE L'HEBERGEUR : hyperviseurs, frontiere, commutateurs.
#
# Il a fallu ce mot le 2026-08-25. Le runner de SITE declarait son API Proxmox en
# `externe`, et « externe » se rend par « tout sauf les espaces prives ». Or les
# hyperviseurs SONT en RFC 1918 : la regle sortante les aurait exclus, tout en ayant
# l'air d'ouvrir le flux. Un flux qui a l'air ouvert et qui ne l'est pas est pire qu'un
# flux ferme — il ne se cherche pas.
#
# Ce n'est pas non plus `flotte` (les machines d'un ecosysteme) ni `voisins_site` (les
# tenants d'a cote) : c'est le SOCLE sur lequel les uns et les autres reposent. Le seul
# a s'y adresser est le runner du site, et c'est tout son objet — materialiser.
MOTS_PAIR = {"edge", "flotte", "externe", "localhost", "expositions", "derive", "admin", MOTS_PAIR = {"edge", "flotte", "externe", "localhost", "expositions", "derive", "admin",
"voisins_site"} "voisins_site", "fabric"}
GROUPE_EDGE = "serveur_nginx" GROUPE_EDGE = "serveur_nginx"

View file

@ -34,6 +34,12 @@ import underlay as U # noqa: E402
# L'inventaire des tenants passe par ce compte ; le site n'a aucune raison d'en differer. # L'inventaire des tenants passe par ce compte ; le site n'a aucune raison d'en differer.
UTILISATEUR_DEFAUT = "ansible" UTILISATEUR_DEFAUT = "ansible"
# LE SOCLE S'APPLIQUE AUSSI AU SITE. Une machine de l'hebergeur n'est pas d'une autre
# espece : elle veut le meme durcissement SSH, les memes horloges, le meme pare-feu que
# n'importe quelle machine de la flotte. L'oublier lui aurait donne un SSH par defaut et
# aucune regle de frontiere — la machine la plus puissante du site, la moins protegee.
GROUPE_SOCLE = "serveur_debian"
def inventaire() -> dict: def inventaire() -> dict:
u = U.charger() u = U.charger()
@ -68,6 +74,7 @@ def inventaire() -> dict:
# par exemple pour ne PAS chercher un plan la ou il n'y en a pas. # par exemple pour ne PAS chercher un plan la ou il n'y en a pas.
"setops_site": True, "setops_site": True,
} }
groupes.setdefault(GROUPE_SOCLE, []).append(nom)
for service in m.get("services") or []: for service in m.get("services") or []:
groupes.setdefault(service, []).append(nom) groupes.setdefault(service, []).append(nom)