frontiere : ne router que les sous-reseaux REELLEMENT attribues

Router 10.27.0.0/16 entier faisait porter a la frontiere des destinations qui
n'existent nulle part. Ces paquets atteignaient le noeud de sortie, y
arrivaient dans la table PRINCIPALE — pas dans le VRF, qui n'est atteint que
par les /24 annonces en BGP — et repartaient vers la passerelle du reseau
d'ADMINISTRATION. Mesure : ip route get 10.27.99.99 rendait via 192.168.11.254.

C'est aussi ce qui faisait reussir tout connect() depuis le VLAN
d'administration, y compris vers des adresses inexistantes — symptome attribue
pendant deux jours a une fonction d'anti-usurpation de la frontiere, alors que
c'etait un routage trop large.

Le devis emet desormais une route par sous-reseau attribue (12 au lieu de 2).
Le NAT reste sur le supernet : il porte sur la SOURCE, qui contient tous les
sous-reseaux — la garde P24 itere sur les alias et reste satisfaite.

Verifie avant de livrer : les 14 hotes du plan sont tous dans les six /24,
aucun ne serait coupe. L'applicateur ne gere pas les routes (0 mention) : le
devis prescrit, l'exploitant applique — D-23/D-24, et ce chemin est celui de
son administration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-09 16:07:10 -04:00
parent 78f2f7fd6f
commit df05d90059
2 changed files with 24 additions and 7 deletions

View file

@ -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 (20 sections). |
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 6 reseau(x), aucune collision avec la plage tenant. |
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 31 regles, 2 routes, admin=10.0.0.0/24,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, 31 regles, 12 routes, admin=10.0.0.0/24,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, 2 tenant(s), 38 groupe(s), 64 regle(s). |
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 4 integration(s) universelle(s) : aucune lacune, aucune recopie (1 exemption(s) derivee(s) du service rendu). |
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 0 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |

View file

@ -35,7 +35,7 @@ import yaml
RACINE = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(RACINE / "scripts"))
from inventory_rules import supernet_de # noqa: E402
from inventory_rules import sous_reseau_de, supernet_de # noqa: E402
# Helpers internes du resolveur de flux : reutilises VOLONTAIREMENT plutot que
# redupliques ici — le registre des flux et l'inventaire doivent avoir une seule
# lecture, sinon le devis et les nftables divergeraient en silence.
@ -392,16 +392,33 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
intrants = intrants_frontiere()
transit = transit_underlay()
saut = prochain_saut(transit)
# UNE ROUTE PAR SOUS-RESEAU REELLEMENT ATTRIBUE, et non une par supernet.
#
# Router le /16 entier faisait porter a la frontiere des destinations qui n'existent
# nulle part. Ces paquets atteignaient le noeud de sortie, y arrivaient dans la table
# PRINCIPALE — pas dans le VRF, qui n'est atteint que par les /24 annonces en BGP — et
# repartaient vers la passerelle du reseau d'ADMINISTRATION. Mesure du 2026-08-09 :
# `ip route get 10.27.99.99` rendait `via 192.168.11.254`.
#
# C'est aussi ce qui faisait reussir tout `connect()` depuis le VLAN d'administration,
# y compris vers des adresses inexistantes — symptome que nous avons attribue pendant
# deux jours a une fonction d'anti-usurpation de la frontiere, alors que c'etait un
# routage trop large.
#
# La frontiere ne route desormais QUE ce qui existe. Le NAT, lui, reste sur le supernet
# (alias `SETOPS_TENANT_*`) : il porte sur la SOURCE, et le supernet contient tous les
# sous-reseaux — la garde P24 reste donc satisfaite.
_sdn = underlay_mod.routage_tenants(underlay_mod.charger()) == "sdn"
routes = [
{
"reseau": supernet_de(n["index"]),
"reseau": sous_reseau_de(n["index"], int(z)),
"prochain_saut": saut,
"description": f"{nom} (index {n['index']}) — routage inter-zone "
+ ("dans la zone EVPN du tenant"
if underlay_mod.routage_tenants(underlay_mod.charger()) == "sdn"
else "sur les switches L3"),
"description": f"{nom}{cat.get('libelle', f'zone{z}')} (zone {z}, index "
f"{n['index']}) — " + ("zone EVPN du tenant" if _sdn
else "switches L3"),
}
for nom, _pfx, n in tenants
for z, cat in sorted((n.get("categories") or {}).items(), key=lambda x: int(x[0]))
]
# NAT sortant : une regle par tenant, source = son alias de supernet, cible = l'adresse