La refonte de ce matin posait une convention. Une convention qu'on n'outille pas tient tant que quelqu'un y pense : c'est le raisonnement de D-70, applique au corpus documentaire. Etat de depart mesure : 2 documents sur 34 declaraient leur lecteur. Les 32 autres disaient leur SUJET — ce qui avait enfoui le runbook de reprise le plus utile du depot au §6 de autorisation.md. Les 38 le declarent desormais, lecteur determine document par document et non colle au gabarit : l'exploitant (devis, migration de tenant, cycle de vie, gabarit d'or), le mainteneur (conceptions, registres, carte), le lecteur externe (ecosysteme-chezlepro), l'agent IA (MISE-A-JOUR-CODEX-CLAUDE). Deux exemptions DERIVEES, pas listees — un chemin en dur aurait vieilli a la premiere page ajoutee : un document qui s'annonce genere, et un fragment sans titre. Les 13 exemptes verifies un par un ; aucun document ecrit a la main n'est exempte par accident. La preuve ne lit que l'EN-TETE, ce qui empeche frontiere-opnsense.md et plan-et-generation.md — qui parlent de generation dans leur corps — d'etre exemptes a tort. Eprouvee dans les deux sens. Elle a echoue seule des sa premiere execution en nommant deux documents que mon inventaire avait manques (docs/audit/). Puis test negatif delibere : declaration retiree de meta-classe.md -> ECHEC la nommant ; restauree -> OK. Ce qu'elle ne teste pas : que le lecteur declare soit le BON. Ca se juge en revue ; elle garantit qu'on a du y penser. P01–P34. Comptes perimes corriges au passage (AGENTS.md et devis-services.md annoncaient encore 30 preuves). Verifie : prouver.py 0 (34 OK), plan-recette inchange. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
207 lines
12 KiB
Markdown
207 lines
12 KiB
Markdown
# SDN EVPN : le routage passe aux hyperviseurs
|
|
|
|
> **Pour qui :** le **mainteneur** du réseau overlay.
|
|
|
|
> **Décision d'architecture du 2026-08-02.** Elle remplace le routage inter-zone sur les
|
|
> commutateurs L3 par des zones EVPN de Proxmox SDN. Complète `docs/frontiere-opnsense.md`
|
|
> (la bordure, inchangée) et `underlay.yml.example` (la fabric, très allégée).
|
|
>
|
|
> **Décidé le 2026-08-03** : une zone EVPN **par tenant** ; le routage **et le filtrage**
|
|
> entre les VLAN d'un même tenant se font à ce niveau ; le trafic **inter-tenant passe
|
|
> obligatoirement par l'OPNsense**.
|
|
|
|
## 1. Ce que la décision résout
|
|
|
|
Les commutateurs Binardat ne savent pas lier une ACL à une interface de routage : l'isolation
|
|
inter-tenant ne pouvait pas vivre sur la fabric. On l'a d'abord assumé — tout sur les nftables
|
|
d'hôte — en notant ce qu'on perdait : **le plan de gestion n'avait plus de protection réseau
|
|
contre les tenants**, une VM émettant vers `10.0.0.x` étant routée localement vers le mgmt des
|
|
commutateurs, celui de Proxmox et l'OOB/IPMI.
|
|
|
|
**Une zone EVPN est un VRF.** Le tenant n'a plus de route vers l'underlay — celui-ci n'est pas
|
|
dans sa table de routage. Son seul chemin vers l'extérieur passe par le nœud de sortie, donc
|
|
par l'OPNsense, qui filtre. Le plan de gestion redevient protégé **par construction**, pas par
|
|
une règle qu'on pourrait oublier.
|
|
|
|
C'est le VRF qu'on regrettait de ne pas avoir dans le matériel, obtenu en logiciel.
|
|
|
|
## 2. La projection du modèle
|
|
|
|
Vérifiée sur les deux tenants fédérés, elle ne demande **aucun changement de dérivation** :
|
|
|
|
| Objet Proxmox SDN | Vient de | Exemple (Chezlepro, zone Services-infra) |
|
|
|---|---|---|
|
|
| **zone** (un VRF) | le tenant | `CHEZ17` |
|
|
| **VNet** | la zone de sécurité | `chez174` |
|
|
| **tag** (VNI) | `vlan_de(index, zone)` | `1174` |
|
|
| **subnet** | `sous_reseau_de(index, zone)` | `10.27.19.0/24` |
|
|
| **gateway** | `passerelle_de(index, zone)` | `10.27.19.1` |
|
|
|
|
> **Rectification du 2026-08-03.** Ce tableau annonçait `chez17-services-infra`, qui
|
|
> aurait été **refusé à l'application** : zones et VNets sont limités à **8 caractères**
|
|
> par Proxmox — l'identifiant sert de base aux noms de bridge, veth et tap. Message
|
|
> amont : *« zone ID … can't be more length than 8 characters »*.
|
|
>
|
|
> Le nommage dérive du tenant, comme tout le reste : `<PRÉFIXE><index>` pour la zone,
|
|
> `<préfixe><index><zone>` pour le VNet. Le préfixe vient de `devis_reseau.prefixe()` —
|
|
> la même fonction que le devis des commutateurs, donc un seul endroit fabrique le nom
|
|
> court d'un tenant. Éprouvé jusqu'au pire cas de la fédération : `COOP245` = 7,
|
|
> `coop2459` = 8. **P30** refuse tout dépassement, sur les deux objets.
|
|
>
|
|
> **Les zones créées à la main (`VRF0011`, `VRF0017`) sont remplacées.** Ce nommage ne
|
|
> disait ni de quel tenant il s'agissait, ni rien qu'on puisse relier au plan : il
|
|
> fallait une table de correspondance pour le lire. Le remplacement se fait **pendant
|
|
> que les zones sont vides** — les supprimer ne débranche rien. Avec des VM attachées,
|
|
> ce serait une migration ; le devis émet donc une §0 qui les retire d'abord.
|
|
|
|
**`make devis-sdn`** émet ces objets, tenant par tenant : la zone, ses six VNets, ses six
|
|
sous-réseaux, puis `pvesh set /cluster/sdn` pour pousser. Non destructif, à relire.
|
|
Vérification la plus forte disponible : la dérivation **reproduit à l'identique** les deux
|
|
zones déjà présentes sur le cluster — même nom, même VNI de VRF, même MTU, même
|
|
contrôleur.
|
|
|
|
Le seed `index` reste la source unique. Le `.1` ne change pas d'adresse, il **change de
|
|
porteur** : du SVI d'un commutateur vers la passerelle **anycast** du VNet, présente sur
|
|
chaque hyperviseur — donc plus proche de la VM, et sans point unique de défaillance.
|
|
|
|
Effet de bord favorable : un VNI est codé sur 24 bits là où un VLAN plafonne à 4094. La limite
|
|
du nombre de tenants n'est plus l'espace de VLAN mais le second octet IPv4 du supernet — le
|
|
plafond de 245 tenants reste, sa cause change.
|
|
|
|
## 3. Le partage des responsabilités
|
|
|
|
| Trafic | Où il est routé | Où il est filtré |
|
|
|---|---|---|
|
|
| **entre zones d'un même tenant** | zone EVPN du tenant (VRF, sur les hyperviseurs) | **au même endroit** |
|
|
| **entre tenants** | sort du VRF → nœud de sortie → **OPNsense** | la frontière, et elle seule |
|
|
| **vers l'extérieur** | idem | la frontière |
|
|
| **de service à service, sur un hôte** | — | **deux barrières** : pare-feu Proxmox (`make devis-proxmox-fw`) **puis** nftables d'hôte (`make flux`) |
|
|
|
|
Deux conséquences qui méritent d'être dites.
|
|
|
|
**L'inter-tenant ne peut plus être « oublié ».** Il ne circule pas latéralement : il doit
|
|
sortir du VRF, donc traverser la bordure, qui est en `block` par défaut. Un flux inter-tenant
|
|
légitime devra être **déclaré** pour exister — le registre des flux n'a pas encore de mot-clé
|
|
pour ça, c'est un point ouvert.
|
|
|
|
**La défense est en profondeur, sans coût de maintenance.** Le filtrage est-ouest est appliqué
|
|
deux fois : par l'hyperviseur, puis par l'hôte destinataire. Une VM compromise doit franchir
|
|
les deux. Et comme les deux **dérivent du même registre par les mêmes fonctions**, elles ne
|
|
peuvent pas se contredire — la duplication est dans l'application, jamais dans la décision.
|
|
|
|
**Le commutateur ne voit plus rien du trafic tenant.** Il transporte du VXLAN qu'il ne lit
|
|
pas. Y chercher une trace d'un problème applicatif serait perdre son temps : le miroir utile
|
|
est sur l'hyperviseur.
|
|
|
|
## 4. Ce que le devis switch devient
|
|
|
|
C'est fait, et piloté par `underlay.routage_tenants: sdn`. Disparaissent : les VLAN tenants, les SVI de zone, les ACL d'isolation,
|
|
et les VLAN tenants dans les trunks. En EVPN, **aucun VLAN de tenant ne circule sur le fil** —
|
|
seulement du VXLAN encapsulé dans de l'IP.
|
|
|
|
Restent : les VLAN d'underlay, le SVI de management, les trunks qui ne portent plus qu'eux,
|
|
les routes, le spanning-tree. La fabric redevient ce qu'elle aurait dû être — un **transport
|
|
IP**.
|
|
|
|
`underlay.routeur` garde son sens, mais son rôle se réduit : il route l'underlay, plus les
|
|
tenants.
|
|
|
|
## 5. Ce que ça change ailleurs
|
|
|
|
**Le nœud de sortie remplace le prochain saut.** En EVPN, le trafic quitte le VRF par un ou
|
|
plusieurs *exit nodes* désignés. Ce sont eux, et non plus le commutateur, que l'OPNsense voit
|
|
comme voisins.
|
|
|
|
C'est **appliqué** : en mode `sdn`, le devis frontière n'émet plus le SVI du commutateur comme
|
|
prochain saut des routes tenants — il émet le marqueur `<NOEUD-DE-SORTIE-EVPN>` et dit
|
|
pourquoi. Une route pointée vers le SVI arriverait sur un équipement qui n'a aucun chemin vers
|
|
le tenant : c'est le genre de configuration qui s'applique sans erreur et ne fonctionne pas.
|
|
|
|
À noter : `underlay.passerelle_sortie` **garde** son sens — c'est l'adresse du pare-feu sur le
|
|
lien de transit, donc la sortie de l'**underlay**, indépendante du routage tenant. Ce sont
|
|
deux choses distinctes qu'il ne faut pas confondre.
|
|
|
|
**L'overlay est plafonné à 1450** (décision du 2026-08-03) parce que le transport réel — le
|
|
pont `vmbr3` des hyperviseurs — est à **1500** : 1450 + 50 de VXLAN y tient exactement. Conséquence à ne pas manquer : sous 1500, **tout ce qui traverse la frontière dépend de
|
|
la découverte de MTU de chemin**, qui a besoin de l'ICMP « fragmentation nécessaire ».
|
|
|
|
Or le registre des flux ne connaissait que TCP et UDP : ce message ne pouvait pas être
|
|
*déclaré*, et la bordure en `block` l'aurait jeté. Symptôme : la connexion s'établit, les
|
|
petites requêtes passent, les grosses réponses restent suspendues. `protocole: icmp` est
|
|
désormais admis — le champ `port` porte alors le **type** (`frag-needed`) — et le socle déclare
|
|
les deux sens.
|
|
|
|
**Le MTU du transport est un prérequis vérifié, pas un conseil.** VXLAN ajoute 50 octets ; `make underlay`
|
|
**refuse** un réseau de transport sous 1550 dès que `routage_tenants: sdn`. C'est le premier
|
|
mur, et le plus déroutant : il ne casse pas franchement, il casse **partiellement** — le ping
|
|
passe, les transferts échouent.
|
|
|
|
**Ce qui ne change pas** : le plan, le registre des flux, les nftables d'hôte, la frontière
|
|
OPNsense et ses règles dérivées, la voûte, les preuves. La cible d'émission change ; le modèle
|
|
non.
|
|
|
|
## 6. Ce qui reste à éprouver — avant toute génération
|
|
|
|
Le principe du dépôt s'applique : **éprouver l'outil avant d'écrire le rôle.** L'EVPN est la
|
|
partie la plus jeune de Proxmox SDN, et c'est là que vivent les bogues.
|
|
|
|
Un tenant de labo, une zone EVPN, deux VNet, une VM dans chacun — et vérifier dans l'ordre :
|
|
|
|
1. le MTU de bout en bout, avec des paquets pleins et le bit *don't fragment* ;
|
|
2. le routage **entre VNet d'une même zone** (c'est ce qui remplace le SVI du commutateur) ;
|
|
3. l'**absence** de route vers l'underlay depuis le tenant — c'est le gain principal, il se
|
|
vérifie par une tentative qui doit échouer ;
|
|
4. la sortie par le nœud de sortie, jusqu'à la bordure ;
|
|
5. le comportement à la **perte d'un nœud** : la passerelle anycast est censée survivre.
|
|
|
|
Ce n'est qu'après que la question « comment générer cette configuration » se pose.
|
|
|
|
## 7. L'état réel du cluster (reconnaissance du 2026-08-03)
|
|
|
|
Lecture seule par l'API Proxmox. **Le plan de contrôle existe, le plan de données non.**
|
|
|
|
| | |
|
|
|---|---|
|
|
| Proxmox | 8.4.19 — `asgard`, `gandalf`, `vishnu` |
|
|
| Contrôleur | `EVPN0017`, ASN 65000 |
|
|
| Zones | `VRF0011`, `VRF0017` — un VRF par tenant, VNI = index, MTU 1450 |
|
|
| VNets | **aucun** |
|
|
| Nœuds de sortie | **aucun** — un VRF sans sortie n'a aucun chemin vers la frontière |
|
|
|
|
### Deux blocages à lever avant d'aller plus loin
|
|
|
|
**Les VTEP sont adressés dans un tenant.** `vmbr3` porte `10.27.19.{41,43,47}` — le
|
|
sous-réseau *Services-infra de Chezlepro*. Le transport du cluster dérive donc de l'index d'un
|
|
tenant : un changement d'index le casse, une migration l'emporte. Et une VM de cette zone
|
|
partage son sous-réseau avec les trois VTEP, ce qui perce l'isolation à l'endroit même que
|
|
l'EVPN devait fermer.
|
|
|
|
Le modèle **refuse d'ailleurs d'exprimer cet état** : déclarer `10.27.19.0/24` comme réseau
|
|
d'underlay ferait échouer **P23**, qui interdit tout chevauchement avec un supernet tenant. La
|
|
garde détecte la faute avant qu'on ne la documente.
|
|
|
|
`underlay.yml` déclare donc les trois hyperviseurs à leur adresse **cible** — `10.0.0.{41,43,47}`,
|
|
dernier octet conservé comme sur `vmbr0`. Le déplacement réel de l'adresse sur les nœuds reste
|
|
à faire : c'est une modification du réseau d'un hyperviseur en service.
|
|
|
|
**Le pont `vmbr3` n'est pas *VLAN-aware*** (pas de `bridge_vlan_aware`, contrairement à
|
|
`vmbr2`). L'adresse du VTEP y est donc **non étiquetée** : elle vit dans le VLAN natif du port
|
|
de commutateur. Déplacer le VTEP vers l'underlay suppose soit un VLAN natif 10, soit une
|
|
interface étiquetée dédiée (`bond3.10`) — ce n'est pas qu'un changement d'adresse.
|
|
|
|
**`vishnu` n'est pas câblé.** Son `vmbr3` n'a **aucun port physique** : le pont existe, porte
|
|
une adresse, et ne mène nulle part. Un pair VXLAN pointé sur lui ne fonctionnera jamais.
|
|
|
|
## 8. Ce qui reste à trancher
|
|
|
|
- **Où vit la configuration SDN.** Elle est *par cluster*, donc propriété de l'hébergeur —
|
|
comme `underlay.yml`, et par le même raisonnement.
|
|
- **Le contrôleur EVPN** : ASN, voisins, et si l'on fait du BGP avec la bordure ou des routes
|
|
statiques comme aujourd'hui.
|
|
- **Le nombre de nœuds de sortie** et leur redondance.
|
|
- **Le mot-clé d'un flux inter-tenant** dans le registre. Aujourd'hui aucun ne l'exprime, donc
|
|
tout inter-tenant tombe dans le `block` de la bordure — un défaut sûr, mais qui rend
|
|
impossible de *déclarer* une exception légitime.
|
|
- **La migration depuis l'existant** : le tenant Chezlepro tourne déjà sur des VLAN. Passer à
|
|
EVPN est un changement de plan de transport pour des VM en service — la recette de
|
|
`docs/migration-tenant.md` s'applique-t-elle, ou faut-il un chemin plus court ?
|