#!/usr/bin/env python3 """La frontiere refuse A VOIX HAUTE a l'interieur, et se tait sur l'Internet. POURQUOI (2026-10-01). Les machines refusaient deja en le disant (nftables, Proxmox) ; la frontiere jetait en silence sur TOUTES ses interfaces. Entre deux zones, un flux mal declare expirait au lieu d'echouer — la sonde `connectivite` l'a mesure au site. Chaque interface interne recoit desormais un `reject` final. Le WAN, lui, reste muet : y repondre servirait les balayages d'Internet (P53). Sur le VRAI devis (il faut voir le site : sans `underlay.yml`, le test se saute) : - aucun `reject` sur le WAN ; - chaque interface interne qui porte des regles porte un `reject` ; - ce `reject` vient APRES toute autre regle de son interface (OPNsense evalue en `quick`). """ from __future__ import annotations import json import subprocess import sys from pathlib import Path RACINE = Path(__file__).resolve().parents[2] def main() -> int: r = subprocess.run([sys.executable, "scripts/devis_opnsense.py", "--json"], capture_output=True, text=True, cwd=RACINE) if r.returncode != 0 or not r.stdout.strip().startswith("{"): print("SAUTE le devis de la frontiere ne se genere pas ici (site invisible)") return 0 devis = json.loads(r.stdout) regles = devis.get("regles") or [] # `if_wan`, la cle que le devis produit. Le test lisait `interfaces.wan`, qui n'existe # pas : il retombait sur « wan », juste au site par chance, faux sur le modele public. wan = str(devis.get("if_wan") or "wan") echecs = [] refus = [x for x in regles if x.get("action") == "reject"] if any(x.get("interface") == wan for x in refus): echecs.append("un `reject` sur le WAN : la bordure repondrait aux balayages") interfaces = {x.get("interface") for x in regles} - {wan} for itf in sorted(i for i in interfaces if i): siens = [x for x in regles if x.get("interface") == itf] rj = [x for x in siens if x.get("action") == "reject"] if not rj: echecs.append(f"{itf} : aucun refus audible") continue haut = max([int(x.get("sequence") or 1) for x in siens if x.get("action") != "reject"] or [0]) if int(rj[0].get("sequence") or 1) <= haut: echecs.append(f"{itf} : le `reject` ({rj[0].get('sequence')}) ne vient pas apres " f"toutes les autres regles ({haut})") for e in echecs: print("ECHEC " + e) if echecs: return 1 print(f"OK {len(refus)} interface(s) interne(s) refusent a voix haute, chacune en dernier ; " f"WAN muet.") return 0 if __name__ == "__main__": sys.exit(main())