66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
|
|
#!/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/<machine>.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:]))
|