devis switch : le port vers la frontière, et l'ordre d'application

Deux omissions que le devis faisait porter à l'opérateur.

Le VLAN de transit était étiqueté sur le trunk `<PORT-VERS-PROXMOX>`, et
aucune interface vers le pare-feu n'était émise. Les hyperviseurs n'ont pas
d'interface sur le transit, et le pare-feu n'a rien à faire des VLAN
tenants — il route vers eux, il ne les étiquette pas. Le transit sort du
trunk Proxmox et prend son propre port (section 4b), dérivé de l'underlay.

Les routes de la section 5 déplacent la sortie du switch, y compris celle de
ses propres réponses. Tant que la frontière ne répond pas, elles coupent
l'accès d'administration AU SWITCH LUI-MÊME — le mécanisme qui a rendu une
VM muette le 2026-07-29, appliqué à l'équipement depuis lequel on travaille.
Le devis les crachait à la suite des autres comme si elles étaient
équivalentes ; il énonce maintenant les préalables et l'ordre.

Sans transit déclaré, les deux sections s'affichent en clair comme
manquantes plutôt que de disparaître.

Preuves : 24 OK, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-01 19:52:50 -04:00
parent 069e549160
commit 0d64d28402
3 changed files with 65 additions and 1 deletions

View file

@ -1,5 +1,25 @@
# CHANGELOG — Set-OPS
## 2026-08-01 (suite 2) — le port vers la frontière, et l'ordre d'application
### Ajouté — section 4b : le port du switch vers la frontière
Le devis étiquetait le VLAN de transit sur le trunk `<PORT-VERS-PROXMOX>` et n'émettait
aucune interface vers le pare-feu. Deux erreurs en une : les hyperviseurs n'ont pas
d'interface sur le transit, et le pare-feu n'a rien à faire des VLAN tenants — il route vers
eux, il ne les étiquette pas. Le VLAN de transit sort donc du trunk Proxmox et prend son
propre port, dérivé du transit déclaré.
### Ajouté — avertissement d'ordre en tête de la section 5
Les routes de la section 5 déplacent la sortie du switch, **y compris celle de ses propres
réponses**. Tant que l'adresse de la frontière ne répond pas, elles coupent l'accès
d'administration au switch lui-même — le mécanisme qui a rendu une VM muette le 2026-07-29,
appliqué cette fois à l'équipement depuis lequel on travaille.
Le devis crachait ces lignes à la suite des autres comme si elles étaient équivalentes.
Il énonce désormais les préalables (boîtier câblé, adressé, joignable depuis le switch,
session console ouverte) — inacceptable autrement pour un outil censé être exploitable sans
IA.
## 2026-08-01 (suite) — le prochain saut dérive du transit
`opnsense_prochain_saut` n'est plus un intrant du panneau : il **dérive** du réseau de

View file

@ -95,6 +95,17 @@ ip route 192.168.255.0 255.255.255.0 10.0.4.2 # retour (nftables_admin_ssh)
**L'aller** est la sortie générale : sans elle, aucun hôte de la flotte n'atteint quoi que ce
soit hors de sa zone.
### L'ordre d'application, qui n'est pas indifférent
Les sections 0 à 4b du devis switch sont sûres et posables à tout moment. **La section 5 ne
l'est pas.** Ces routes déplacent la sortie du switch — y compris celle de ses propres
réponses — vers l'adresse de la frontière. Tant que cette adresse ne répond pas, elles
coupent l'accès d'administration **au switch lui-même**.
C'est le même mécanisme que le piège ci-dessous, mais appliqué à l'équipement depuis lequel
on travaille. Préalables, rappelés dans le devis : boîtier câblé, adressé sur le lien de
transit, joignable depuis le switch, et une session console ouverte pendant l'opération.
**Le retour** est le piège proprement dit. Un paquet d'administration entre par la frontière
et atteint la VM ; la réponse part de la VM vers sa passerelle — un SVI **de switch**. Si le
switch n'a pas de route vers le réseau d'administration, ou s'il l'atteint par un autre chemin

View file

@ -82,6 +82,26 @@ def section_underlay(underlay: dict | None) -> list[str]:
return out
def section_frontiere(transit: dict | None) -> list[str]:
"""Port du switch vers le pare-feu de bordure. Porte le VLAN de transit, et lui seul.
Distinct du trunk vers Proxmox : les hyperviseurs n'ont aucune interface sur le
transit, et le pare-feu n'a rien a faire des VLAN tenants — il route vers eux, il
ne les etiquette pas.
"""
if not transit:
return ["! ----- 4b. Port vers la frontiere -----",
"! Aucun reseau de transit declare dans underlay.yml (cle 'passerelle_sortie')."]
return [
"! ----- 4b. Port vers la frontiere nord/sud -----",
f"! Porte le seul VLAN {transit['vlan']} ({transit['nom']}). En lien dedie,",
f"! remplacer par : switchport access vlan {transit['vlan']}.",
"interface <PORT-VERS-FRONTIERE>",
" switchport mode trunk",
f" switchport trunk allowed vlan add {transit['vlan']}",
]
def section_routes(underlay: dict | None) -> list[str]:
"""Routes du switch vers la frontiere nord/sud. Vide si aucun transit declare.
@ -104,6 +124,14 @@ def section_routes(underlay: dict | None) -> list[str]:
"!"]
sortie = transit["passerelle_sortie"]
out = ["! ----- 5. Routes vers la frontiere nord/sud -----",
"! /!\\ A APPLIQUER EN DERNIER, ET SEULEMENT UNE FOIS LA FRONTIERE VIVANTE.",
f"! Ces routes deplacent la sortie du switch — y compris celle de ses propres",
f"! reponses — vers {sortie}. Tant que cette adresse ne repond pas, elles",
"! coupent l'acces d'administration AU SWITCH LUI-MEME.",
"! Prealables : boitier cable, adresse sur le lien de transit, et joignable",
f"! depuis le switch (ping {sortie} depuis Vlan{transit['vlan']}).",
"! Garder une session console ouverte pendant l'operation.",
"!",
f"! Transit '{transit['nom']}' (VLAN {transit['vlan']}) : SVI {transit['passerelle']}"
f" <-> frontiere {sortie}",
"! Aller : sortie generale de la flotte.",
@ -189,7 +217,11 @@ def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -
out += remarque(dialecte, "Reste (Internet / inter-tenant controle) -> passerelle OPNsense")
out.append(f" permit ip {reseau} {m} any")
out += ["!", "! ----- 4. Trunk vers Proxmox + inter-switch (a adapter) -----"]
vlans_underlay = [str(r["vlan"]) for r in underlay_mod.reseaux(underlay)]
transit = underlay_mod.reseau_transit(underlay)
# Le VLAN de transit ne va PAS vers Proxmox : aucun hyperviseur n'a d'interface
# dessus. Il sort par le port de la frontiere (ci-dessous), et lui seul.
vlans_underlay = [str(r["vlan"]) for r in underlay_mod.reseaux(underlay)
if not (transit and r["vlan"] == transit["vlan"])]
vlans_tenants = [
str(vlan_de(n["index"], zone))
for _, _, n in tenants
@ -199,6 +231,7 @@ def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -
out.append("interface <PORT-VERS-PROXMOX>")
out.append(" switchport mode trunk")
out.append(f" switchport trunk allowed vlan add {vlans}")
out += ["!"] + section_frontiere(transit)
out += ["!"] + section_routes(underlay)
out += ["end", "write memory"]
return "\n".join(out)