test_restauration fournit client_backup_attente_verrou et exige --retry-lock ; test_frontiere_refus lit le WAN sous if_wan ; prouver --verifier nomme les preuves en echec ; le Makefile n exporte plus une liste de cles de voute vide (valeur identique sur le poste). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
2.6 KiB
Python
61 lines
2.6 KiB
Python
#!/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())
|