underlay : un SITE n'a pas d'index — le vestige est retire

`index: 17` ne disait pas « voici mon adressage » : un site ne derive rien, ses machines
vivent sur un reseau de fabric hors de toute derivation. Il disait UNE seule chose — quel
supernet de tenant est le mien — pour autoriser un reseau du site a en occuper la bande
basse (D-77). Un reste de la coincidence hebergeur<->tenant chez Chezlepro, qui est les
deux a la fois ; ailleurs il aurait fallu inventer un index a un hebergeur qui n'heberge
pas son propre ecosysteme.

L'exception se declare desormais sur le RESEAU qui en a besoin, et elle NOMME le tenant
dont elle occupe la bande basse (`bande_basse_de: OPS-Chezlepro`). Plus vrai : un seul
reseau est concerne. Plus verifiable : le nom se resout dans le registre `tenants:`, donc
une faute de frappe est refusee au lieu de passer.

Quatre controles negatifs, tous refuses — tenant inconnu, exception absente, debordement
sur la bande des zones, et le mauvais tenant nomme.

42 preuves vertes, les quatre devis du panneau OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-25 12:11:20 -04:00
parent 467b05cbc0
commit add94f2cbd
2 changed files with 58 additions and 5 deletions

View file

@ -1,5 +1,35 @@
# CHANGELOG — Set-OPS
## 2026-08-25 — Un SITE n'a pas d'index : le vestige est retiré
Le site portait `index: 17`. Ça n'a jamais voulu dire « voici mon adressage » — un site
ne dérive rien, ses machines vivent sur un réseau de fabric (`10.0.3.0/24`), hors de toute
dérivation. Ça voulait dire **une seule chose** : *quel supernet de tenant est le mien*,
pour autoriser un réseau du site à en occuper la bande basse (D-77).
C'était un reste de la coïncidence hébergeur↔tenant chez Chezlepro, qui est les deux à la
fois. Ailleurs, ça aurait induit en erreur : un hébergeur qui n'héberge pas son propre
écosystème n'a aucun supernet à lui, et devait quand même inventer un index.
**L'exception se déclare désormais sur le réseau qui en a besoin**, et elle **nomme** le
tenant dont elle occupe la bande basse :
```yaml
- nom: management
segment_physique: true
bande_basse_de: OPS-Chezlepro
sous_reseau: 10.17.0.0/24
```
Trois gains. C'est **plus vrai** — un seul réseau est concerné, pas le site entier. C'est
**plus lisible** — le lecteur voit pourquoi ce `/24` habite un `/16` qui n'est pas le sien.
Et c'est **vérifiable** : le nom se résout dans le registre `tenants:`, donc une faute de
frappe est refusée au lieu de passer silencieusement.
Quatre contrôles négatifs, tous refusés : un tenant inconnu, l'exception absente alors que
le chevauchement existe, un débordement sur la bande des zones, et — le cas subtil — le
**mauvais** tenant nommé.
## 2026-08-25 — Le SITE devient un écosystème complet : PKI, DNS, forge
Le site portait **trois services sur les sept** que le modèle `origine` appelle le plus

View file

@ -479,9 +479,31 @@ def valider(underlay: dict | None,
if tenants_vlans is None or tenants_supernets is None:
tenants_vlans, tenants_supernets = _tenants_reperes()
t_supernets = [ipaddress.ip_network(s, strict=False) for s in tenants_supernets]
# Sans `index` declare, on ignore quel supernet est le SIEN : on retombe alors sur la
# regle stricte d'avant D-77 (aucun chevauchement), qui reste sure.
idx_site = index_site(underlay)
# UN SITE N'A PAS D'INDEX (2026-08-25). Il en portait un, et c'etait un vestige.
#
# Cette valeur ne disait pas « voici mon adressage » — le site ne derive rien, ses
# machines vivent sur un reseau de fabric. Elle disait une seule chose : « QUEL
# SUPERNET DE TENANT EST LE MIEN », pour autoriser un reseau du site a occuper sa
# bande basse. C'etait un reste de la coincidence hebergeur↔tenant chez Chezlepro,
# qui est a la fois les deux — et qui aurait induit en erreur partout ailleurs.
#
# L'exception se declare desormais SUR LE RESEAU qui en a besoin, et elle NOMME le
# tenant dont elle occupe la bande basse. C'est plus vrai (un seul reseau est
# concerne, pas le site entier) et plus verifiable : le nom se resout dans le registre
# d'allocation, donc une faute de frappe est refusee au lieu de passer.
alloc = allocations(underlay)
def _supernet_hote(r: dict) -> tuple[ipaddress.IPv4Network | None, str | None]:
"""Supernet dont ce reseau occupe la bande basse, et l'erreur s'il n'existe pas."""
nom_t = str(r.get("bande_basse_de") or "").strip()
if not nom_t:
return None, None
if nom_t not in alloc:
return None, (f"reseau '{r.get('nom', '?')}': `bande_basse_de: {nom_t}` — ce "
f"nom n'est pas dans le registre `tenants:` du site. Sans "
f"allocation connue, on ne peut pas savoir quel supernet il "
f"occupe, ni verifier qu'il reste dans la bande basse.")
return ipaddress.ip_network(supernet_de(alloc[nom_t]), strict=False), None
erreurs: list[str] = []
if mode_site:
@ -528,8 +550,9 @@ def valider(underlay: dict | None,
# Chevaucher celui d'un AUTRE tenant reste une erreur — c'est ce qui empeche deux
# sites de se joindre. Et deborder sur la bande haute de son propre supernet en
# est une aussi : on entrerait en collision avec ses propres zones.
mien = ipaddress.ip_network(supernet_de(idx_site), strict=False) \
if idx_site is not None else None
mien, err_bande = _supernet_hote(r)
if err_bande:
erreurs.append(err_bande)
for ts in t_supernets:
if not net.overlaps(ts):
continue