From 63aa79e7109ae50e567d7269e340d0dba759fc84 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Tue, 4 Aug 2026 14:46:06 -0400 Subject: [PATCH] =?UTF-8?q?underlay=20:=20l'invariant=20du=20dernier=20oct?= =?UTF-8?q?et=20retrouve=20sa=20port=C3=A9e=20(D-04,=20D-52=20=C3=A0=20D-5?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il valait partout ; il ne vaut que dans l'adressage dérivé des tenants, où passerelle_de(index, zone) produit le même .1 dans les treize sous-réseaux d'un tenant. C'est une propriété de la dérivation, pas une loi universelle. Dans l'underlay il produisait deux effets pervers. Un seuil arbitraire : les sous-réseaux plus étroits qu'un /24 étaient exemptés, donc élargir un /29 changeait la validité du fichier sans que rien d'autre bouge. Et une couture entre propriétaires : l'octet attendu venait de la nomenclature d'un tenant, appliquée à la fabric de l'hébergeur — la validité de l'underlay aurait dépendu du tenant actif. Ce qui reste est plus fort et suffit (D-52) : une passerelle doit être l'adresse d'un hôte déclaré sur ce réseau. Elle attrape les passerelles fantômes, ce que le comptage d'octets ne faisait pas. Vérifié : la garde mord toujours. À noter, parce que l'ordre était mauvais : le ré-adressage de l'OPNsense en .1 a été demandé au nom de cette règle, deux messages avant qu'elle soit recadrée. Pas perdu — .1 est la position conventionnelle d'une passerelle — mais la portée aurait dû être questionnée avant de faire changer une adresse en service. D-53 : le réseau et l'underlay de l'hébergeur méritent leur propre dépôt. underlay.yml décrit une infrastructure, le dépôt de tenant une organisation ; un tenant peut déménager, une fabric non. Consigné, non fait. D-54 : 10.0.0.0/24 est réservé à l'IPAM, la gestion des équipements et l'OOB, accès sysadmin. Aucun hyperviseur, aucune VM, aucun trafic tenant. C'est la raison d'être des VLAN 11 et 40. 30 preuves OK. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++ docs/decisions-architecture.md | 5 ++++- docs/hebergeur-exploitation.md | 28 ++++++++++++++++++++++++ scripts/underlay.py | 38 +++++++++++++++------------------ 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df67eb..0571954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # CHANGELOG — Set-OPS +## 2026-08-04 (suite 2) — l'invariant du dernier octet retrouve sa portée + +Il valait partout ; il ne vaut que là où il a un sens : l'**adressage dérivé des +tenants**, où `passerelle_de(index, zone)` produit le même `.1` dans les treize +sous-réseaux. C'est une propriété de la dérivation, pas une loi universelle. + +Dans l'underlay, il produisait deux effets pervers : + +- **un seuil arbitraire** — les sous-réseaux plus étroits qu'un `/24` étaient exemptés, + donc élargir un `/29` changeait la validité du fichier sans que rien d'autre bouge ; +- **une couture entre propriétaires** — l'octet attendu venait de la nomenclature d'un + *tenant*, appliquée à la fabric de l'*hébergeur*. La validité de l'underlay aurait + dépendu du tenant actif si l'un d'eux réservait autre chose. + +Ce qui reste est plus fort, et suffit : **une passerelle doit être l'adresse d'un hôte +déclaré sur ce réseau** (D-52). Elle attrape les passerelles fantômes, ce que le comptage +d'octets ne faisait pas. + +> **À noter, parce que l'ordre était mauvais.** Le ré-adressage de l'OPNsense en `.1` a +> été demandé au nom de cette règle, deux messages avant qu'elle soit recadrée. Il n'est +> pas perdu — `.1` est la position conventionnelle d'une passerelle, et la frontière la +> porte désormais partout — mais la portée de la règle aurait dû être questionnée avant +> de faire changer une adresse sur un boîtier en service. + +### Deux décisions consignées, non construites + +**D-53** — le **réseau et l'underlay** de l'hébergeur méritent leur propre dépôt. +`underlay.yml` décrit une **infrastructure** ; le dépôt de tenant décrit une +**organisation**. Un tenant peut déménager, une fabric non. Les mêler oblige, à chaque +commit, à trancher ce qu'on touche. Le symlink désignant l'hébergeur pointerait vers ce +dépôt-là — la distinction deviendrait visible dans les chemins. + +**D-54** — `10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB** ; +accès sysadmin. Aucun hyperviseur, aucune VM, aucun trafic tenant — ni encapsulé, ni +décapsulé. C'est la raison d'être des VLAN 11 et 40. Écrit dans `underlay.yml` à côté du +réseau lui-même, pas seulement dans la documentation. + +30 preuves OK. + ## 2026-08-04 (suite) — plus aucun commutateur ne route Question posée à froid : « je ne vois plus de valeur ajoutée au point de routage diff --git a/docs/decisions-architecture.md b/docs/decisions-architecture.md index 975d19a..27812f4 100644 --- a/docs/decisions-architecture.md +++ b/docs/decisions-architecture.md @@ -26,7 +26,8 @@ sont les seules vérifiables. |---|---|---|---|---| | **D-01** | OPNsense est une frontière **nord/sud**, pas la passerelle des zones | le routage inter-zone reste au débit ligne ; la bordure ne voit pas l'est-ouest | `frontiere-opnsense.md` §1 | — | | **D-02** | Le **lien de transit** vit dans l'underlay, pas dans un tenant | la frontière route vers tous les tenants par le même saut : il ne peut dériver d'aucun `index` | `frontiere-opnsense.md` §6 | P23 | -| **D-04** | Un point de routage porte **le même dernier octet** sur tous ses sous-réseaux | on retient une adresse, pas treize | `frontiere-opnsense.md` §4 | P23 | +| **D-04** | Un point de routage porte **le même dernier octet** sur tous ses sous-réseaux — **dans l'adressage dérivé des tenants seulement** | on retient une adresse, pas treize. Dans l'underlay, l'adressage est dicté par les participants d'un lien physique : la règle y imposait un seuil arbitraire et empruntait sa valeur à la nomenclature d'un tenant | `frontiere-opnsense.md` §4 | P20 | +| **D-52** | Dans l'underlay, une passerelle doit être **l'adresse d'un hôte déclaré** sur ce réseau | remplace le comptage d'octets par une règle plus forte : elle attrape les passerelles fantômes | `underlay.py` | P23 | | **D-49** | **Aucun commutateur ne route.** La frontière est le seul équipement L3 | l'EVPN a retiré les VLAN tenants du fil, puis la fusion du lien de sortie a rendu les nœuds de sortie **adjacents** à la frontière : plus un seul flux à router | `underlay.yml` | P23 | | **D-50** | `passerelle` = la passerelle du sous-réseau, **où qu'elle vive** ; le SVI se **dérive** du rôle de son porteur | la clé signifiait « adresse du SVI du switch » — une hypothèse, pas une donnée. Un même moteur sert désormais les deux postures | `devis_reseau.py` | P23 | | **D-51** | **Deux** commutateurs en paire, pas trois en étoile | le centre d'une étoile est sur **tous** les chemins : point de panne unique du plan de données. Deux liens vers deux commutateurs donnent la redondance qu'une étoile ne peut pas donner | `underlay.yml` | — | @@ -60,6 +61,8 @@ sont les seules vérifiables. | **D-46** | Un hébergeur porte **trois** catégories, pas deux : son **tenant**, ses **opérations**, le **plan de contrôle** | Chezlepro est hébergeur ET tenant, ce qui masquait des besoins n'appartenant à aucun tenant | `hebergeur-exploitation.md` §2 | — | | **D-47** | Les **services d'exploitation** de l'hébergeur vivent dans **son dépôt**, et leurs VM se rattachent à un **pont VLAN, jamais un VNet** | un service qui observe la fabric ne peut pas dépendre d'elle : l'EVPN tombe, et la supervision tombe avec la raison de la panne | `hebergeur-exploitation.md` §1, §4 | — | | **D-48** | Les **hyperviseurs** sont gérables par Ansible ; « hors flotte » ne vaut que pour les **commutateurs** et la **frontière** | ce sont des Debian joignables en SSH ; c'est la seule façon d'y poser un exportateur de métriques | `hebergeur-exploitation.md` §5 | — | +| **D-53** | Le **réseau et l'underlay** de l'hébergeur méritent leur **propre dépôt**, séparé de son tenant | `underlay.yml` et le cluster décrivent une infrastructure ; le dépôt de tenant décrit une organisation. Les mêler oblige à trancher qui possède quoi à chaque commit | `hebergeur-exploitation.md` §7 | — | +| **D-54** | `10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB** — accès sysadmin | aucun hyperviseur, aucune VM, aucun trafic tenant ; c'est la raison d'être des VLAN 11 et 40 | `underlay.yml` | — | | **D-18** | Chaque tenant a un **responsable désigné** | sans lui, « qui peut décider de déménager cette organisation ? » se pose au pire moment | `migration-tenant.md` §3 | — | | **D-38** | Toute authentification **web** passe par Keycloak ; LDAP est la **source unique** des comptes | une identité, un mot de passe ; aucun service ne tient son propre répertoire d'humains | `authentification.md` §1-2 | — | diff --git a/docs/hebergeur-exploitation.md b/docs/hebergeur-exploitation.md index 4aae758..141a93d 100644 --- a/docs/hebergeur-exploitation.md +++ b/docs/hebergeur-exploitation.md @@ -84,3 +84,31 @@ Deux obstacles mesurés le 2026-08-04, à lever avant : À noter au passage : la convention héritée était déjà `1000 + numéro de tenant`. La formule de Set-OPS (`1000 + index×10 + zone`) en est un raffinement, pas une invention. + +## 7. Un dépôt à part pour le réseau (décidé, non fait) + +`underlay.yml` et `proxmox-hebergeur.yml` vivent aujourd'hui dans `OPS-Chezlepro`, qui est +aussi le dépôt du **tenant** Chezlepro. C'est ce qui a permis de démarrer, et c'est ce qui +oblige, à chaque commit, à trancher si l'on touche à l'infrastructure ou à l'organisation. + +Ils décrivent des objets différents : l'un une **infrastructure** — des câbles, des VLAN, +un cluster —, l'autre une **organisation** — ses serveurs, ses applications, ses comptes. +Un tenant peut déménager ; une fabric ne déménage pas. + +Le dépôt réseau de l'hébergeur porterait donc `underlay.yml`, `proxmox-hebergeur.yml`, et +plus tard l'inventaire des services d'exploitation (§4). Le symlink qui désigne +l'hébergeur pointerait vers lui plutôt que vers son tenant — ce qui rendrait enfin la +distinction visible dans les chemins eux-mêmes. + +**Rien n'est fait.** La bascule demande de déplacer deux fichiers, de refaire le symlink, +et de vérifier que les trois générateurs qui les lisent suivent. + +## 8. Le VLAN de gestion, et ce qu'il n'est pas + +`10.0.0.0/24` est réservé à l'**IPAM, la gestion des équipements et l'OOB/IPMI**. Accès +sysadmin uniquement. + +Aucun hyperviseur n'y a d'adresse, aucune VM n'y est branchée, aucun trafic tenant ne le +traverse — ni encapsulé, ni décapsulé. C'est précisément la raison d'être des VLAN 11 +(transport VXLAN) et 40 (sortie tenant) : les avoir sortis de ce domaine de diffusion. + diff --git a/scripts/underlay.py b/scripts/underlay.py index b3e0bd4..f09e2f7 100644 --- a/scripts/underlay.py +++ b/scripts/underlay.py @@ -364,27 +364,23 @@ def valider(underlay: dict | None, if p.get("montante") and hn == r_courant: erreurs.append(f"hote '{hn}': une `montante` alors qu'il EST le routeur") - # Invariant du dernier octet : tous les points de routage doivent le partager. - # Exemption des sous-reseaux plus etroits qu'un /24 : sur un /29 de transit, - # l'adressage est dicte par les participants du lien, pas par la convention. - attendu = octet_passerelle(plan_nomenclature) - if attendu is not None: - for r in reseaux(underlay): - gw = r.get("passerelle") - if not gw: - continue - try: - net = ipaddress.ip_network(r["sous_reseau"], strict=False) - except ValueError: - continue - if net.prefixlen > 24: - continue # lien etroit : exempte, cf. le /29 de transit - octet = int(str(gw).rsplit(".", 1)[1]) - if octet != attendu: - erreurs.append( - f"reseau '{r.get('nom','?')}': passerelle {gw} finit par .{octet} alors que " - f"les points de routage portent .{attendu} partout " - f"(nomenclature: reservations.passerelle)") + # L'INVARIANT DU DERNIER OCTET NE S'APPLIQUE PAS ICI (decide le 2026-08-04). + # + # Il vaut pour l'adressage DERIVE des tenants, ou `passerelle_de(index, zone)` + # produit le meme `.1` dans les treize sous-reseaux d'un tenant : on retient une + # adresse, pas treize. C'est une propriete de la derivation, pas une regle + # universelle. + # + # Dans l'underlay, l'adressage est dicte par les PARTICIPANTS d'un lien physique. + # La regle y produisait deux effets pervers : un seuil arbitraire (les sous-reseaux + # plus etroits qu'un /24 etaient exemptes, donc elargir un /29 changeait la + # validite du fichier), et une couture entre proprietaires — l'octet attendu venait + # de la nomenclature d'un TENANT, appliquee a la fabric de l'HEBERGEUR. La validite + # de l'underlay aurait dependu du tenant actif si l'un d'eux reservait autre chose. + # + # Ce qui reste, et qui est plus fort : une passerelle doit etre l'adresse d'un hote + # DECLARE sur ce reseau (verifie plus bas). Elle attrape les passerelles fantomes, + # ce que le comptage d'octets ne faisait pas. v = (underlay or {}).get("acl_inter_tenant") if v is not None and not isinstance(v, bool):