diff --git a/CHANGELOG.md b/CHANGELOG.md index 126be84..b4d8341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,14 +45,36 @@ en ingress) — et le registre a refuse la premiere moitie seule, ce qui est exa raison d'etre. `CODES_ICMP` apprend `echo-request` (un TYPE, pas un code de destination-unreachable). Les regles d'hote sont posees sur les 21 machines. -**Ce qui reste ouvert, et je le dis parce que le probleme n'est pas resolu :** le -generateur de la frontiere ne sait pas traduire un TYPE ICMP pour un pair INTERNE. Il le -note et n'emet rien : +### Le generateur de la frontiere : corrige, et il cachait un second manque - note : serveur_icinga declare un port `echo-request` que le plan du site ne resout pas +**La cause exacte.** `devis_opnsense.py` traitait tout `port` non numerique comme +SYMBOLIQUE — a resoudre par le plan. C'est vrai pour `derive`, le port d'un service que +seul le plan connait. C'est FAUX pour l'ICMP : `echo-request` et `frag-needed` sont des +litteraux, pas des inconnues. Ils tombaient dans la branche « le plan ne resout pas », et +aucune regle n'etait emise. -Le site reste donc a 1/7. Corriger `devis_opnsense.py` pour l'ICMP interne est le prochain -geste — et c'est un geste sur le generateur de la frontiere, pas une retouche. +Une ligne de condition, et le silence de toute une flotte. + +**Ce que la correction a revele en plus.** Huit regles a creer, zero a retirer — et SIX +d'entre elles sont du **PMTUD entre zones du site**, absent lui aussi pour la meme raison. +Le depot dit de ces deux messages ICMP : *« Bloques, la connexion s'etablit, les petites +requetes passent et les grosses reponses restent suspendues — la panne la plus couteuse a +diagnostiquer. »* Elle etait la, silencieuse, sur les sept machines de l'hebergeur. + +**Ce que la regle emise autorise, exactement — et pourquoi on ne pretend pas mieux.** +`appliquer_opnsense` n'envoie `destination_port` que pour TCP et UDP : une regle ICMP ouvre +le PROTOCOLE entre deux pairs, pas le seul type declare. Le type reste porte par la regle +d'HOTE, que `resoudre_flux` emet precisement (`icmp type echo-request accept`). La +frontiere dit qui peut parler a qui ; l'hote dit ce qu'il accepte d'entendre. Emettre un +`icmptype` a la frontiere aurait ete plus fin — et aurait demande de traduire `frag-needed` +(un CODE de `destination-unreachable`) dans le vocabulaire de pf, au risque de casser le +PMTUD pour gagner de la precision sur une barriere qui n'est pas la derniere. + +**Mesure apres application :** + + ping site-mon-01 -> les six autres zones : 6/6 repondent + hotes UP : 1/7 -> 7/7 + ping4 : 1/7 -> 7/7 certificat 7/7, sante 7/7 ## 2026-09-09 (7) — Le contrat des sondes ETAIT celui de Nagios, sans le savoir diff --git a/docs/audit/preuve-2026-09-09.md b/docs/audit/preuve-2026-09-09.md index bd08891..a70213b 100644 --- a/docs/audit/preuve-2026-09-09.md +++ b/docs/audit/preuve-2026-09-09.md @@ -55,14 +55,14 @@ | P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. | | P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 54 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). | | P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 4 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal, OPS-Patient0/product | -| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 110 regle(s) du site. | +| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 118 regle(s) du site. | | P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 integration(s) appliquent leur serveur avant leurs clients. | | P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). | | P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos | | P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). | | P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 89 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. | | P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (121 lignes). | -| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 175 regles `pass`), tous non consignes et tous motives. | +| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 183 regles `pass`), tous non consignes et tous motives. | | P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 | | P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. | | P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 15 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b | diff --git a/scripts/devis_opnsense.py b/scripts/devis_opnsense.py index cf6d648..3e42824 100644 --- a/scripts/devis_opnsense.py +++ b/scripts/devis_opnsense.py @@ -800,7 +800,32 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: if "serveur_ops_tenant" in _pairs_fl and not _runners_tenants: continue _p = _ports(_fl) - if any(not str(x).isdigit() for x in _p): + # L'ICMP N'A PAS DE PORT, ET SON `port` N'EST PAS SYMBOLIQUE (2026-09-09). + # + # Le test ci-dessous dit « non numerique = a resoudre par le plan », ce qui + # est vrai pour `derive` — le port d'un service que seul le plan connait. + # C'est FAUX pour un flux ICMP : `echo-request` et `frag-needed` sont des + # litteraux, pas des inconnues. Ils tombaient donc dans la branche + # « le plan ne resout pas » et AUCUNE regle n'etait emise. + # + # CE QUE CA A COUTE : l'Icinga du site tenait 6 de ses 7 machines pour + # MORTES. `hostalive` est un ping ; le site est decoupe en zones ; la + # frontiere avalait l'ICMP inter-zones faute de regle. Et Icinga SUPPRIME + # les notifications des services d'un hote DOWN — la supervision n'alertait + # plus de rien tout en ayant l'air de fonctionner. Le meme silence valait + # pour PMTUD entre zones du site. + # + # CE QUE LA REGLE EMISE AUTORISE, EXACTEMENT. `appliquer_opnsense` n'envoie + # `destination_port` que pour TCP et UDP : une regle ICMP ouvre le + # PROTOCOLE entre les deux pairs, pas le seul type declare. Le type reste + # porte par la regle d'HOTE, que `resoudre_flux` emet precisement + # (`icmp type echo-request accept`). La frontiere dit qui peut parler a + # qui ; l'hote dit ce qu'il accepte d'entendre. On ne pretend donc pas ici + # une finesse qu'on n'applique pas — c'est le sens de la ligne « (type + # filtre a l'hote) » ajoutee au devis. + if str(_fl.get("protocole", "")).lower() in ("icmp", "icmpv6"): + pass + elif any(not str(x).isdigit() for x in _p): _reel = _ports_site.get(_role) if not _reel: # SUR STDERR, JAMAIS STDOUT : ce script rend du JSON, et une