From 70fe6de557cd6aad78d5f9a5e09360f27b159d66 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 3 Aug 2026 10:07:30 -0400 Subject: [PATCH] =?UTF-8?q?flux=20:=20l'ICMP=20entre=20au=20registre=20?= =?UTF-8?q?=E2=80=94=20l'overlay=20=C3=A0=201450=20l'exige?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Décision : l'overlay EVPN plafonne à 1450. Conséquence invisible : sous 1500, tout ce qui traverse la frontière dépend de la découverte de MTU de chemin, donc de l'ICMP « fragmentation nécessaire ». Or le registre ne connaissait que TCP et UDP. Ce message ne pouvait pas être déclaré et la bordure en `block in log all` l'aurait jeté : la connexion s'établit, les petites requêtes passent, les grosses réponses restent suspendues — la panne la plus coûteuse à diagnostiquer, et celle qu'on impute d'abord à l'application. `protocole: icmp` est admis ; le champ `port` y porte le type (`frag-needed`). Le socle déclare les deux sens. Vérifié : nftables d'hôte inchangés, le pair `externe` reste sauté. Reconnaissance (lecture seule) : l'EVPN est à moitié construit — contrôleur EVPN0017 (ASN 65000), zones VRF0011 et VRF0017, un VRF par tenant avec le VNI égal à l'index. Aucun VNet, aucun nœud de sortie. Signalé et non corrigé : les pairs BGP sont dans 10.27.19.0/24, le sous-réseau Services-infra de Chezlepro. Le transport du cluster dérive de l'index d'un tenant, et une VM de cette zone partage son sous-réseau avec les VTEP — l'isolation est percée à l'endroit que l'EVPN devait fermer. Preuves : 24 OK, 0 échec. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ docs/audit/preuve-2026-08-03.md | 4 ++-- docs/decisions-architecture.md | 2 ++ docs/sdn-evpn.md | 12 +++++++++++- roles/serveur_debian/meta/flux.yml | 17 +++++++++++++++++ scripts/resoudre_flux.py | 5 ++++- 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f7826e..00d7a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # CHANGELOG — Set-OPS +## 2026-08-03 (suite 5) — l'ICMP entre au registre, parce que l'overlay descend à 1450 + +Décision : l'**overlay EVPN plafonne à 1450**. Elle a une conséquence qui ne se voit pas — +sous 1500, tout ce qui traverse la frontière dépend de la **découverte de MTU de chemin**, donc +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 in log all` l'aurait jeté. Symptôme : la connexion s'établit, +les petites requêtes passent, **les grosses réponses restent suspendues** — la panne la plus +coûteuse à diagnostiquer, et celle qu'on impute d'abord à l'application. + +`protocole: icmp` est admis ; pour lui, le champ `port` porte le **type** (`frag-needed`). Le +socle déclare les **deux sens** : entrant pour qu'un distant puisse nous demander de réduire +nos paquets, sortant pour que nos hôtes signalent l'overlay aux correspondants. + +Vérifié : les nftables d'hôte sont **inchangés** — le pair `externe` reste sauté, ces flux +relèvent de la bordure. Le devis frontière passe à 26 règles. + +### Reconnaissance de l'existant (lecture seule) +L'EVPN est déjà **à moitié construit** sur le cluster : Proxmox 8.4.19, contrôleur `EVPN0017` +(ASN 65000), zones `VRF0011` et `VRF0017` — un VRF par tenant, VNI égal à l'index, conforme à +D-08. Mais **aucun VNet** et **aucun nœud de sortie** : le plan de contrôle existe, le plan de +données non. + +Signalé, non corrigé : les **pairs BGP sont `10.27.19.41/.43/.47`**, dans le sous-réseau +*Services-infra de Chezlepro*. Le transport du cluster dérive donc de l'index d'un tenant — 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. + ## 2026-08-03 (suite 4) — un index des décisions d'architecture `docs/decisions-architecture.md`. Les décisions étaient écrites là où elles s'appliquent, et diff --git a/docs/audit/preuve-2026-08-03.md b/docs/audit/preuve-2026-08-03.md index 16212ee..f0ad28b 100644 --- a/docs/audit/preuve-2026-08-03.md +++ b/docs/audit/preuve-2026-08-03.md @@ -21,7 +21,7 @@ | 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). | | P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 30 groupes classes, aucun cycle, aucune arete en arriere. | -| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 68 flux, schéma + matrice OK. | +| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 70 flux, schéma + matrice OK. | | 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 | | P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. | @@ -36,7 +36,7 @@ | P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 2 instance(s) federee(s), aucun index en collision. | | P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (19 sections). | | P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 5 reseau(x), aucune collision avec la plage tenant. | -| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 22 regles, 2 routes, admin=192.168.254.2/32,192.168.255.0/24,192.168.255.2/32. | +| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 26 regles, 2 routes, admin=192.168.254.2/32,192.168.255.0/24,192.168.255.2/32. | ## Couverture des affirmations ✅ du registre diff --git a/docs/decisions-architecture.md b/docs/decisions-architecture.md index c89d134..55cee7a 100644 --- a/docs/decisions-architecture.md +++ b/docs/decisions-architecture.md @@ -35,6 +35,8 @@ sont les seules vérifiables. | **D-09** | Le **filtrage inter-zone d'un tenant** se fait au même niveau (EVPN) | là où le routage a lieu | `sdn-evpn.md` §3 | — | | **D-10** | L'**inter-tenant passe obligatoirement par la frontière** | il sort du VRF, donc traverse une bordure en `block` par défaut : il ne peut plus être oublié | `sdn-evpn.md` §3 | — | | **D-11** | La **sortie générale est déclarée** dans le registre des flux | `block out` est un vrai default-deny ; un besoin oublié ne se manifeste pas par un refus clair | `frontiere-opnsense.md` §8 | P09 | +| **D-29** | L'**overlay EVPN plafonne à 1450** ; le transport doit donc dépasser 1500 | choix d'exploitation ; l'encapsulation VXLAN coûte 50 octets | `sdn-evpn.md` §5 | P23 (MTU du transport) | +| **D-30** | L'**ICMP « fragmentation nécessaire » est déclaré**, dans les deux sens | à 1450, tout ce qui traverse la frontière dépend de la découverte de MTU de chemin ; une bordure en default-deny la casse en silence | `roles/serveur_debian/meta/flux.yml` | P09 | | **D-12** | Nommage : **`bifrost`** aux frontières, **`sleipnir`** à la fabric | Bifröst est le pont vers l'extérieur ; Sleipnir traverse les mondes sans en sortir | `frontiere-opnsense.md` §6 | — | ## 2. Qui possède quoi diff --git a/docs/sdn-evpn.md b/docs/sdn-evpn.md index ce7ea32..5f42908 100644 --- a/docs/sdn-evpn.md +++ b/docs/sdn-evpn.md @@ -91,7 +91,17 @@ le tenant : c'est le genre de configuration qui s'applique sans erreur et ne fon 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. -**Le MTU est un prérequis vérifié, pas un conseil.** VXLAN ajoute 50 octets ; `make underlay` +**L'overlay est plafonné à 1450** (décision du 2026-08-03), donc le transport doit dépasser +1500. 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. diff --git a/roles/serveur_debian/meta/flux.yml b/roles/serveur_debian/meta/flux.yml index 0e28490..be36a19 100644 --- a/roles/serveur_debian/meta/flux.yml +++ b/roles/serveur_debian/meta/flux.yml @@ -25,6 +25,23 @@ flux: pair: externe chiffrement: clair raison: "Dépôts apt en clair et redirections HTTP des miroirs (l'intégrité vient de la signature des paquets, pas du transport)." + # --- Decouverte de MTU de chemin (PMTUD) --- + # L'overlay EVPN plafonne a 1450 : tout ce qui traverse la frontiere depend de ces + # deux messages. Bloques, la connexion s'etablit, les petites requetes passent et les + # grosses reponses restent suspendues — la panne la plus couteuse a diagnostiquer. + - sens: ingress + port: frag-needed + protocole: icmp + pair: externe + chiffrement: n-a + raison: "ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent." + - sens: egress + port: frag-needed + protocole: icmp + pair: externe + chiffrement: n-a + raison: "ICMP « fragmentation nécessaire » sortant : c'est ainsi que nos hôtes signalent l'overlay à 1450 aux correspondants distants." + - sens: egress port: 123 protocole: udp diff --git a/scripts/resoudre_flux.py b/scripts/resoudre_flux.py index bdb7aa5..daf0002 100644 --- a/scripts/resoudre_flux.py +++ b/scripts/resoudre_flux.py @@ -33,7 +33,10 @@ REGISTRE = RACINE / "docs" / "registre-flux.md" INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) SENS = {"ingress", "egress"} -PROTO = {"tcp", "udp"} +# `icmp` n'a pas de port : le champ `port` porte alors le TYPE (ex. `frag-needed`). +# Sans lui, la decouverte de MTU de chemin ne peut pas etre DECLAREE — et une bordure +# en default-deny la casse en silence des que l'overlay descend sous 1500. +PROTO = {"tcp", "udp", "icmp"} CHIFFREMENT = {"tls-requis", "tls", "starttls", "ssh", "tls-cible", "clair", "n-a"} MOTS_PAIR = {"edge", "flotte", "externe", "localhost", "expositions", "derive"} GROUPE_EDGE = "serveur_nginx"