#!/usr/bin/env python3 """Chaque machine du site a-t-elle ses regles nftables generees, AVANT de deployer ? POURQUOI (2026-10-13, premier site construit depuis zero). `nftables_baseline` cherche le jeu de regles DERIVE des flux (`flux-genere/.nft`, a cote de la carte du site) ; absent, il pose en silence son GABARIT GENERIQUE, qui ne connait aucun flux reel. Au site de TechnoLibre, `make flux` n'avait jamais tourne : les neuf machines seraient nees avec un pare- feu qui refuse leurs propres services, et rien ne l'aurait dit avant le premier refus. Lit l'inventaire du site (donc `SETOPS_UNDERLAY` s'il est pose) et refuse si une machine active qui arme son pare-feu n'a pas son fichier. `--inventaire FICHIER` lit un inventaire deja rendu (JSON de `site_inventaire.py --list`) — c'est ainsi que le test l'eprouve. """ from __future__ import annotations import argparse import json import subprocess import sys from pathlib import Path RACINE = Path(__file__).resolve().parents[1] def manquants(inv: dict) -> list[str]: hv = (inv.get("_meta") or {}).get("hostvars") or {} actifs = ((inv.get("hotes_actifs") or {}).get("hosts")) or [] out = [] for h in sorted(actifs): v = hv.get(h) or {} if not v.get("nftables_baseline_enabled", False): continue f = str(v.get("nftables_baseline_ruleset_genere") or "") if not f or not Path(f).is_file(): out.append(f"{h} : {f or '(aucun chemin)'}") return out def main(argv: list[str]) -> int: ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument("--inventaire", default="") a = ap.parse_args(argv) if a.inventaire: inv = json.loads(Path(a.inventaire).read_text(encoding="utf-8")) else: r = subprocess.run([sys.executable, str(RACINE / "scripts" / "site_inventaire.py"), "--list"], capture_output=True, text=True, cwd=RACINE) if r.returncode != 0: print("Inventaire du site illisible :\n" + r.stderr.strip()[:400], file=sys.stderr) return 2 inv = json.loads(r.stdout) m = manquants(inv) if m: print("Refus : ces machines du site n'ont pas leurs regles nftables generees — leur " "pare-feu retomberait sur le gabarit generique :", file=sys.stderr) for x in m: print(f" - {x}", file=sys.stderr) print("Lancer `make flux` (avec le meme SETOPS_UNDERLAY), puis relancer.", file=sys.stderr) return 2 print("OK : chaque machine active du site a ses regles nftables generees.") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))