pare-feu Proxmox : une règle par rôle source, plus aucune adresse en dur
Un flux dont le pair nomme quatre rôles donne maintenant quatre règles, chacune renvoyant à l'IPSet de son rôle. 52 règles, toutes par IPSet, zéro littérale. Le gain n'est pas cosmétique : une règle porte qui elle autorise. `-source +t17-srv-keycloak` se lit ; une liste de quatre adresses demande de retrouver à qui chacune appartient. La raison appartient au flux, pas à chacune de ses règles : elle est écrite une fois au-dessus du paquet qu'elle explique plutôt que répétée quatre fois. Vérifié : aucun renvoi orphelin, aucun IPSet inutilisé. Preuves : 25 OK, 0 échec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
dbb4465602
commit
7b2c9272d7
3 changed files with 43 additions and 24 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -46,10 +46,17 @@ Et le devis listait **28 à 30 IPSets par tenant dont la moitié n'était réfé
|
|||
part** : un opérateur en aurait créé 58 pour n'en utiliser qu'une douzaine. Seuls les IPSets
|
||||
réellement référencés sont émis — **6 par tenant**. Un devis crée ce qu'il liste.
|
||||
|
||||
Restent quatre règles en liste explicite : celles dont la source est **plusieurs rôles à la
|
||||
fois** (LDAPS ouvert à Keycloak, Dovecot, Icinga Web et Postfix). Aucun IPSet unique ne les
|
||||
couvre et Proxmox n'accepte qu'une référence par règle ; les éclater gonflerait le devis pour
|
||||
un gain discutable.
|
||||
### Puis : une règle par rôle source
|
||||
Les quatre règles restées en liste explicite sont éclatées — un flux dont le pair nomme quatre
|
||||
rôles donne quatre règles, chacune renvoyant à l'IPSet de son rôle. **Plus une seule adresse
|
||||
en dur : 52 règles, toutes par IPSet.**
|
||||
|
||||
Le gain n'est pas cosmétique : une règle porte désormais **qui** elle autorise. `-source
|
||||
+t17-srv-keycloak` se lit ; `-source 10.27.16.21,10.27.17.11,10.27.19.31,10.27.20.21` demande
|
||||
de retrouver à qui appartient chaque adresse.
|
||||
|
||||
La raison, elle, appartient au **flux** et non à chacune de ses règles : elle est écrite une
|
||||
fois au-dessus du paquet qu'elle explique, au lieu d'être répétée quatre fois.
|
||||
|
||||
### Corrigé — l'affectation variait selon l'état du tenant
|
||||
Elle partait de `hotes_actifs`, avec un repli sur « tous » quand il n'y en avait aucun. Deux
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
| 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, 26 regles, 2 routes, admin=192.168.254.2/32,192.168.255.0/24,192.168.255.2/32. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 2 tenant(s), 34 groupe(s), 40 regle(s). |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 2 tenant(s), 34 groupe(s), 52 regle(s). |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
|
|
|
|||
|
|
@ -145,29 +145,35 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
|
|||
continue # la frontiere s'en charge
|
||||
if pairs == ["localhost"]:
|
||||
continue # boucle locale : aucune regle inter-noeud
|
||||
srcs = _resoudre_sources(data, fl.get("pair"), tous, ips)
|
||||
if not srcs:
|
||||
if not _resoudre_sources(data, fl.get("pair"), tous, ips):
|
||||
sans_source.append(f"{groupe}:{','.join(_ports(fl))}")
|
||||
continue
|
||||
# Source par IPSet des que possible : une seule liste a tenir, et une
|
||||
# regle qui se lit. Les mots-cles `flotte` et `edge` y ont droit comme
|
||||
# les roles — `edge` designe le role nginx.
|
||||
source = ",".join(srcs)
|
||||
if len(pairs) == 1:
|
||||
# UNE REGLE PAR ROLE SOURCE. Un flux dont le pair liste quatre roles
|
||||
# donne quatre regles, chacune renvoyant a l'IPSet de son role : la
|
||||
# regle porte alors QUI elle autorise, au lieu d'aligner des adresses.
|
||||
# `flotte` et `edge` sont des mots-cles, mais ont leur IPSet eux aussi.
|
||||
for pair in pairs:
|
||||
if pair in ("localhost", "expositions", "derive"):
|
||||
continue
|
||||
cle = {"flotte": f"t{n['index']}-flotte",
|
||||
"edge": nom_ipset(GROUPE_EDGE, n["index"])}.get(
|
||||
pairs[0], nom_ipset(pairs[0], n["index"]))
|
||||
pair, nom_ipset(pair, n["index"]))
|
||||
srcs_pair = _resoudre_sources(data, pair, tous, ips)
|
||||
if not srcs_pair:
|
||||
continue # ce role n'est pas porte dans ce tenant
|
||||
if cle in ipsets:
|
||||
source = f"+{cle}"
|
||||
source, via = f"+{cle}", pair
|
||||
utilises.add(cle)
|
||||
for port in _ports(fl):
|
||||
regles.append({
|
||||
"action": "ACCEPT", "sens": "IN",
|
||||
"source": source,
|
||||
"proto": fl.get("protocole", "tcp"),
|
||||
"dport": port,
|
||||
"raison": fl.get("raison", ""),
|
||||
})
|
||||
else:
|
||||
source, via = ",".join(srcs_pair), pair
|
||||
for port in _ports(fl):
|
||||
regles.append({
|
||||
"action": "ACCEPT", "sens": "IN",
|
||||
"source": source, "via": via,
|
||||
"proto": fl.get("protocole", "tcp"),
|
||||
"dport": port,
|
||||
"raison": fl.get("raison", ""),
|
||||
})
|
||||
if regles:
|
||||
groupes.append({
|
||||
"nom": nom_groupe(groupe, n["index"]), "role": groupe, "regles": regles,
|
||||
|
|
@ -230,11 +236,17 @@ def rendre(devis: dict) -> str:
|
|||
out += ["", f"### Groupes de securite — {len(b['groupes'])} (les flux entrants d'un role)"]
|
||||
for g in b["groupes"]:
|
||||
out.append(f"securitygroup {g['nom']} # {g['role']}")
|
||||
# La raison est portee par le FLUX, pas par chacune de ses regles : quatre
|
||||
# roles autorises sur un meme port la repeteraient quatre fois. On la sort
|
||||
# une seule fois, au-dessus du paquet qu'elle explique.
|
||||
derniere = None
|
||||
for r in g["regles"]:
|
||||
cle = (r["dport"], r["proto"], r["raison"])
|
||||
if cle != derniere and r["raison"]:
|
||||
out.append(f" # {r['raison']}")
|
||||
derniere = cle
|
||||
out.append(f" {r['sens']} {r['action']} -source {r['source']} "
|
||||
f"-p {r['proto']} -dport {r['dport']}")
|
||||
if r["raison"]:
|
||||
out.append(f" # {r['raison']}")
|
||||
out += ["", f"### Affectation aux VM — {len(b['affectations'])}"]
|
||||
for a in b["affectations"]:
|
||||
out.append(f" vmid {a['vmid'] or '(non materialise)'} {a['hote']:<18} "
|
||||
|
|
|
|||
Loading…
Reference in a new issue