ACL de switch : les tenants n'atteignent plus la fabric physique

L'ACL d'isolation bloquait l'autre tenant, puis se terminait par
`permit ip <tenant> any`. Ce `any` autorisait 10.27.x -> 10.0.0.0/24 : le
management des switches, celui de Proxmox et l'OOB/IPMI, plus iSCSI et Ceph.
Une VM compromise atteignait la console physique des hyperviseurs.

Le commentaire du générateur disait « Reste -> passerelle OPNsense », ce qui
est faux pour l'underlay : ce trafic est routé LOCALEMENT par le switch et ne
passe jamais par la frontière, donc elle ne le filtre jamais.

Un `deny` par sous-réseau underlay est désormais émis avant le `permit`
final, dérivé de underlay.yml, dialecte respecté (masque normal ou wildcard).
Vérifié qu'aucun flux du registre ne vise l'underlay : rien de déclaré ne
casse. Sans underlay déclaré, l'ACL retrouve sa forme d'avant.

Consigné en §6 : le registre n'a pas de mot-clé `underlay` (un besoin
légitime, superviser l'hyperviseur, ne pourrait pas être déclaré), et le
devis émet un jeu unique de SVI pour trois switches sans MLAG.

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:59:03 -04:00
parent 0d64d28402
commit 7b2ba07ffc
3 changed files with 45 additions and 0 deletions

View file

@ -1,5 +1,26 @@
# CHANGELOG — Set-OPS
## 2026-08-01 (suite 3) — les tenants n'atteignent plus l'underlay
### Corrigé — ACL de switch : `deny` vers la fabric physique
L'ACL d'isolation bloquait l'autre tenant, puis se terminait par `permit ip <tenant> any`.
Ce `any` autorisait `10.27.x``10.0.0.0/24` : le management des switches, celui de Proxmox
et l'**OOB/IPMI**, plus les réseaux iSCSI et Ceph. Une VM compromise atteignait la console
physique des hyperviseurs.
Le commentaire du générateur disait « Reste → passerelle OPNsense », mais c'est faux pour
l'underlay : ce trafic est routé **localement** par le switch et ne passe jamais par la
frontière, donc il n'est jamais filtré par elle.
`devis_reseau.py` émet désormais un `deny` par sous-réseau underlay avant le `permit` final,
dérivé de `underlay.yml` — dialecte respecté (masque normal ou wildcard). Aucun flux du
registre ne vise l'underlay : le blocage ne casse rien de déclaré.
Deux limites consignées dans `docs/frontiere-opnsense.md` §6 : le registre des flux n'a pas
de mot-clé `underlay`, donc un besoin légitime (superviser l'hyperviseur) ne pourrait pas
être déclaré ; et `devis_reseau.py` émet un jeu unique de SVI pour trois switches sans MLAG,
ce qui reste une décision d'architecture ouverte.
## 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

View file

@ -23,6 +23,13 @@ Conséquence assumée : l'isolation inter-zone repose sur les ACL de switch, moi
que le registre des flux. Le zéro-confiance est-ouest reste porté par les nftables d'hôte et
le TLS mutuel — pas par le pare-feu de bordure.
Corollaire, qui n'est pas anodin : **le trafic d'un tenant vers l'underlay est routé
localement par le switch et ne passe jamais par la frontière.** Il n'est donc jamais filtré
par elle. Sans précaution, le `permit ip <tenant> any` final de l'ACL laisserait une VM
atteindre le management des switches, celui de Proxmox et l'OOB/IPMI. `make devis-reseau`
émet pour cette raison un `deny` par sous-réseau underlay **avant** le `permit` final,
dérivé de `underlay.yml`.
## 2. Ce que la frontière décide (et pourquoi rien n'est saisi à la main)
Le registre des flux distingue les pairs par **mot-clé**. Or `resoudre_flux.py` **saute
@ -181,6 +188,14 @@ ansible-vault edit instance/inventories/principal/group_vars/all/vault.yml
`nftables_admin_ssh`**, sans quoi il sera muet de la même façon.
- **L'application par l'API**`--json` produit la structure ; le client d'application reste
à écrire.
- **Pas de mot-clé `underlay` dans le registre des flux** — l'underlay est aujourd'hui
interdit en bloc aux tenants. Si un besoin légitime apparaît (superviser l'hyperviseur
depuis `obs-01`, par exemple), il ne peut pas être *déclaré* : il faudrait ajouter un pair
`underlay` au registre plutôt qu'une exception manuelle dans l'ACL.
- **Trois switches, un seul devis**`devis_reseau.py` émet un jeu unique de SVI sans
désigner le switch qui les porte. Sans MLAG, il reste à trancher entre « un seul routeur,
les deux autres en L2 pur » et « SVI répliqués en VRRP ». Appliquer le devis tel quel sur
les trois créerait des conflits d'adresses.
- **La sortie générale** (mises à jour apt, ACME) n'est pas déclarée dans le registre : les
hôtes sortent aujourd'hui parce que la politique `output` des nftables est `accept` et que
l'ACL de switch se termine par `permit ip <tenant> any`. À trancher : la déclarer

View file

@ -214,6 +214,15 @@ def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -
a_reseau, a_m = masque_acl(supernet_de(autre["index"]), dialecte)
out += remarque(dialecte, f"Bloquer le tenant {autre_nom}")
out.append(f" deny ip {reseau} {m} {a_reseau} {a_m}")
# L'underlay est la fabric physique : mgmt des switches, mgmt Proxmox, OOB/IPMI,
# iSCSI, Ceph. Le trafic d'un tenant vers ces reseaux est route LOCALEMENT par le
# switch : il ne passe jamais par la frontiere, donc il n'est jamais filtre. Sans
# ce deny, le `permit any` final l'autorise — une VM atteindrait la console
# physique des hyperviseurs. Aucun flux du registre ne vise l'underlay.
for r in underlay_mod.reseaux(underlay):
u_reseau, u_m = masque_acl(r["sous_reseau"], dialecte)
out += remarque(dialecte, f"Bloquer l'underlay {r['nom']} (fabric physique)")
out.append(f" deny ip {reseau} {m} {u_reseau} {u_m}")
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) -----"]