diff --git a/CHANGELOG.md b/CHANGELOG.md index ea70358..e09253b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # CHANGELOG — Set-OPS +## 2026-09-10 (15) — Une source vide n'est pas « tout le monde » + +Les journaux de la fabric, et le defaut de classe que leur mise en place a revele. + + journaux 10 hotes dans Loki (7 VM du site + 3 hyperviseurs) + site 67 services, tous OK (51 avant) + +### Metriques TIREES, journaux POUSSES — et ce n'est pas un caprice + +Un scrape part du collecteur et doit REVENIR : il exige un chemin symetrique, que la route +par defaut gelee (D-57) interdit. Un push part de la source et n'attend qu'un accuse : la +route SPECIFIQUE vers les zones du site suffit — celle qu'on venait justement de completer. + +### Trois trous dans les generateurs, du meme jour + +**Le devis de la frontiere ne connaissait `fabric` qu'en DESTINATION.** Un flux entrant +depuis la fabric tombait dans « rien d'autre n'entre » et n'emettait AUCUNE regle, sans +rien dire. Meme forme que le trou des integrations universelles, trouve le matin meme. + +**La regle etait sur la mauvaise patte.** D-61 fait raisonner le devis en ARRIVEE ; la +regle etait posee sur l'interface de la DESTINATION. `opt7` — la patte face a la fabric, +libellee PROXMOX — manquait a la table des zones, qui ne recensait que le site. Elle y est, +et l'interface se derive desormais du reseau ou vivent les hyperviseurs. + +**Et le plus grave : `fabric` etait un mot RECONNU mais NON RESOLU.** Le generateur de +pare-feu d'hote l'acceptait comme valide, ne trouvait aucune adresse, et une source vide +produit une regle sans `saddr` : + + tcp dport 3100 accept # ouvert a tout le monde + +*Un mot reconnu mais non resolu est pire qu'un mot inconnu* : celui-ci serait refuse a la +validation, celui-la produit une porte grande ouverte qui a l'air d'un flux precis. + +### La classe entiere, refermee + +Le defaut n'etait pas propre a `fabric`. **Toute** paire nommant un ensemble et ne +resolvant rien ouvrait le port. Trouve en lisant les fichiers generes : + + tcp dport 5665 accept # serveur_icinga, sur le mon-01 d'un TENANT + +L'API de supervision ouverte a tous, parce que `pair: serveur_backup` ne resout rien — cet +ecosysteme depose son etat chez le site et n'a pas de depot a lui. + +`expositions` et `externe`, EUX, veulent bien dire « tout le monde » : ce sont des services +publies, et leur ouverture est une INTENTION. Ailleurs : **pas de source, pas de regle** — +et le generateur le DIT, parce qu'un flux tu en silence est une porte qu'on croit fermee. + +Trois regles se sont refermees. Chacune verifiee AVANT d'appliquer : + +| regle | verdict | +|---|---| +| `site-forge-01:3000` | rien n'ecoute — la forge sert 443 | +| `mon-01:5665` (tenant) | deux autres regles couvrent les pousseurs | +| `site-mon-01:3000` | **Grafana ecoute** — a corrige avant de fermer | + +### Grafana : `edge` ET `admin` + +Chez un tenant, le nginx d'edge termine le TLS : `edge` resout, c'est le seul chemin. Le +SITE n'a PAS d'edge — chaque service s'y sert lui-meme. `edge` n'y resolvait donc rien, et +la console etait ouverte a l'Internet PAR ACCIDENT, sous un commentaire qui parlait d'un +edge inexistant. + +Ajouter `admin` rend explicite ce qui etait accidentel. Et la mesure a montre autre chose : +**Grafana etait deja injoignable depuis le poste** — la frontiere ne laisse pas passer le +3000. La regle ouverte ne servait a rien, sauf a rester ouverte si la frontiere s'ouvrait +un jour. + +*Le site a donc une console deployee et sans chemin d'acces.* Ce n'est pas corrige ici, +mais c'est dit. + ## 2026-09-10 (14) — Le site surveille enfin sa fabric La supervision du site voyait ses sept VM et rien d'autre. Mesure de depart, depuis diff --git a/docs/audit/preuve-2026-09-10.md b/docs/audit/preuve-2026-09-10.md index 14aee23..84cdb37 100644 --- a/docs/audit/preuve-2026-09-10.md +++ b/docs/audit/preuve-2026-09-10.md @@ -21,7 +21,7 @@ | P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. | | P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check), 1 nom(s) surveille(s) sans reference orpheline. | | P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. | -| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 105 flux, schéma + matrice OK. | +| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 106 flux, schéma + matrice OK. | | P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). | | P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | serveur_resolveur_site | | P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. | @@ -37,7 +37,7 @@ | P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). | | P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 13 reseau(x), aucune collision avec la plage tenant. | | P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | note : serveur_powerdns declare un port `derive` que le plan du site ne resout pas — aucune regle emise. | -| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 49 groupe(s), 81 regle(s). | +| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 49 groupe(s), 85 regle(s). | | P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). | | P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. | | P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 34 VM placee(s), aucun nom ni VMID en collision. | @@ -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 : 55 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, 153 regle(s) du site. | +| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 154 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 (123 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 215 regles `pass`), tous non consignes et tous motives. | +| 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 (124 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 216 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/docs/registre-flux.md b/docs/registre-flux.md index 1c6cf3f..4743e98 100644 --- a/docs/registre-flux.md +++ b/docs/registre-flux.md @@ -43,7 +43,7 @@ | `serveur_forgejo` | egress | 25 | tcp | serveur_postfix | starttls | Notifications courriel (relais via le MTA Postfix). | | `serveur_forgejo` | egress | 443 | tcp | edge | tls-requis | Découverte OIDC et jetons auprès de Keycloak (via son FQDN publié à l'edge). | | `serveur_forgejo` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base de données Forgejo (verify-full). | -| `serveur_grafana` | ingress | 3000 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). | +| `serveur_grafana` | ingress | 3000 | tcp | edge, admin | clair | Interface web : servie via l'edge la ou il y en a un, joignable depuis le plan d'administration partout — sans quoi un deploiement sans edge n'a plus de console. | | `serveur_grafana` | egress | 443 | tcp | edge | tls-requis | Authentification OIDC auprès de Keycloak (via son FQDN publié à l'edge). | | `serveur_icinga` | ingress | 5665 | tcp | localhost | clair | API Icinga 2 consommée en local par Icinga Web 2 co-localisé. | | `serveur_icinga` | ingress | 5665 | tcp | client_backup | tls-requis | Rapport passif de chaque detenteur d'etat sur SON depot distant : le depot du site heberge du chiffre et ne peut pas le juger. | @@ -60,6 +60,7 @@ | `serveur_keycloak` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Persistance Keycloak dans PostgreSQL (verify-full). | | `serveur_loki` | ingress | 3100 | tcp | client_journal | tls | Réception des journaux poussés par Alloy (client_journal) en HTTPS (http_tls_config, cert step-ca). | | `serveur_loki` | ingress | 3100 | tcp | localhost | clair | Requêtes de Grafana co-localisé (datasource Loki en localhost). | +| `serveur_loki` | ingress | 3100 | tcp | fabric | tls | Journaux des hyperviseurs. La machine qui PORTE les VM ecrivait ses journaux nulle part ailleurs que sur son propre disque — invisibles le jour ou c'est elle qui casse. | | `serveur_nextcloud` | ingress | 80 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). | | `serveur_nextcloud` | egress | 443 | tcp | edge | tls-requis | Découverte OIDC auprès de Keycloak (via son FQDN publié à l'edge). | | `serveur_nextcloud` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base de données Nextcloud (verify-full). | @@ -118,6 +119,6 @@ - **n-a** : 6 flux - **ssh** : 8 flux - **starttls** : 6 flux -- **tls** : 10 flux +- **tls** : 11 flux - **tls-cible** : 2 flux - **tls-requis** : 35 flux diff --git a/roles/serveur_grafana/meta/flux.yml b/roles/serveur_grafana/meta/flux.yml index dedf548..0dd948c 100644 --- a/roles/serveur_grafana/meta/flux.yml +++ b/roles/serveur_grafana/meta/flux.yml @@ -2,12 +2,27 @@ # Flux réseau de Grafana (tableaux de bord). Voir docs/flux-conception.md. # Prometheus et Loki sont co-localisés (datasources en localhost) : aucun flux inter-nœud. flux: + # `edge` ET `admin` — PARCE QUE TOUS LES DEPLOIEMENTS N'ONT PAS D'EDGE (2026-09-10). + # + # Chez un tenant, le nginx d'edge termine le TLS et parle en clair a Grafana : `edge` + # resout, et c'est le seul chemin. Le SITE, lui, n'a PAS d'edge — chaque service s'y + # sert lui-meme. `edge` n'y resolvait donc rien, et une source vide produisait une regle + # SANS `saddr` : la console ouverte a l'Internet, par accident, avec un commentaire qui + # parlait d'un edge inexistant. + # + # Le jour ou le generateur a cesse d'ouvrir sur une source vide, la regle a disparu et + # l'acces avec elle — mesure : `ss` ne montre que `*:3000`, rien sur 443. Ajouter `admin` + # rend explicite ce qui etait accidentel : la console se joint depuis le plan + # d'ADMINISTRATION, qui est d'ou l'exploitant la regarde. Chez un tenant, la ligne ne + # retire rien — elle ajoute l'operateur a l'edge. - sens: ingress port: 3000 protocole: tcp - pair: edge + pair: [edge, admin] chiffrement: clair - raison: "Interface web servie via l'edge (TLS terminé à l'edge)." + raison: >- + Interface web : servie via l'edge la ou il y en a un, joignable depuis le plan + d'administration partout — sans quoi un deploiement sans edge n'a plus de console. - sens: egress port: 443 protocole: tcp diff --git a/roles/serveur_loki/meta/flux.yml b/roles/serveur_loki/meta/flux.yml index f639c3d..d8ea5f8 100644 --- a/roles/serveur_loki/meta/flux.yml +++ b/roles/serveur_loki/meta/flux.yml @@ -13,3 +13,22 @@ flux: pair: localhost chiffrement: clair raison: "Requêtes de Grafana co-localisé (datasource Loki en localhost)." + + # LA FABRIC JOURNALISE AUSSI, ET ELLE, ELLE PEUT POUSSER (2026-09-10). + # + # Les metriques des hyperviseurs sont TIREES : leur route par defaut est gelee (D-57) et + # le porteur de sante y expirait. Les journaux, eux, passent — parce que les routes + # SPECIFIQUES vers les zones du site ont ete completees le meme jour (`10.0.35.0/24` et + # `10.0.36.0/24` manquaient). Alloy pousse donc vers Loki comme n'importe quelle machine. + # + # LA DIFFERENCE AVEC LES METRIQUES N'EST PAS UN CAPRICE : un scrape part du collecteur et + # doit REVENIR, ce qui exige un chemin symetrique ; un push part de la source et n'attend + # qu'un accuse. La route completee suffit au second, pas au premier. + - sens: ingress + port: 3100 + protocole: tcp + pair: fabric + chiffrement: tls + raison: >- + Journaux des hyperviseurs. La machine qui PORTE les VM ecrivait ses journaux nulle + part ailleurs que sur son propre disque — invisibles le jour ou c'est elle qui casse. diff --git a/scripts/devis_opnsense.py b/scripts/devis_opnsense.py index d28867c..27452cb 100644 --- a/scripts/devis_opnsense.py +++ b/scripts/devis_opnsense.py @@ -787,6 +787,38 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: # jamais la zone entiere. _portees = [("SETOPS_SITE", z) for z in sorted({_if_de(m.get("reseau")) for m in _machines_site})] + elif _entrant and "fabric" in _pairs_fl: + # LA FABRIC PARLE AUSSI AU SITE (2026-09-10). + # + # `fabric` n'etait connu qu'en DESTINATION — « le runner du site pilote + # les hyperviseurs ». Un flux ENTRANT depuis la fabric tombait donc dans + # la branche « rien d'autre n'entre » et n'emettait AUCUNE regle, sans + # rien dire. Meme forme que le trou des integrations universelles, + # trouve le matin meme : le generateur ne voyait qu'une moitie du monde. + # + # Le cas concret : les hyperviseurs POUSSENT leurs journaux vers Loki. + # Ils ne peuvent pas etre scrutes (route par defaut gelee, D-57) mais ils + # peuvent emettre — un push n'attend qu'un accuse, la ou un scrape exige + # un chemin symetrique. + # + # Une regle par ZONE du site ou vit le role vise, comme pour `flotte` : + # le paquet penetre le pare-feu par la patte de la zone DESTINATAIRE. + # L'INTERFACE EST CELLE PAR OU LE PAQUET ARRIVE (D-61), donc celle + # qui fait face a la FABRIC — pas celle de la destination. Derivee du + # reseau ou vivent les hyperviseurs, jamais ecrite : le jour ou la + # fabric change de patte, la regle suit. + # SEULEMENT LES RESEAUX DONT ON CONNAIT LA PATTE. Un hyperviseur a + # trois adresses (gestion, transport, transit) ; deux de ces reseaux ne + # touchent pas la frontiere et n'ont pas d'interface declaree. Sans ce + # filtre, `_if_de` retombe sur la patte par DEFAUT et pose une regle sur + # une interface que ce trafic n'emprunte jamais — une regle qui a l'air + # d'ouvrir un flux et ne correspond a rien. + _res_fabric = sorted({str(h.get("reseau")) for h in underlay_mod.hotes(_u) + if h.get("role") == "hyperviseur" + and str(h.get("reseau")) in if_zones}) + _portees = [("SETOPS_FABRIC", _if_de(r)) for r in _res_fabric] + if not _portees: + continue elif _entrant and "22" in _ports(_fl) and _admin_site: # Le SSH de gestion arrive par la patte d'ADMINISTRATION de la # frontiere, pas par celle du site : c'est la que le paquet penetre diff --git a/scripts/resoudre_flux.py b/scripts/resoudre_flux.py index c4f61ef..b00f4a5 100644 --- a/scripts/resoudre_flux.py +++ b/scripts/resoudre_flux.py @@ -223,6 +223,28 @@ def _ip_par_hote(data: dict) -> dict[str, str]: return table +def _adresses_fabric() -> list[str]: + """Les adresses du materiel de l'hebergeur : hyperviseurs et frontiere. + + MEME SOURCE QUE L'ALIAS `SETOPS_FABRIC` du devis de la frontiere — la carte de + l'hebergeur. Ecrire ces adresses ailleurs les ferait mentir le jour ou un noeud + change de patte, ce qui est deja arrive : la frontiere a porte `.1` puis `.17`, et + les hyperviseurs ont continue de renvoyer vers une adresse que plus personne ne + portait, « sans qu'aucun journal ne le dise ». + + Rend [] quand aucun underlay n'est monte : degrader, jamais deviner. + """ + try: + import underlay as _u + carte = _u.charger() + if not carte: + return [] + return sorted({str(h["ip"]) for h in _u.hotes(carte) + if h.get("role") in ("hyperviseur", "frontiere") and h.get("ip")}) + except Exception: + return [] + + def _resoudre_sources(data: dict, pair, actifs_noms: set[str], ip_par_hote: dict[str, str], site: bool = False) -> list[str]: """pair -> IP sources concretes (pour une regle ingress). [] = pas de regle inter-noeud.""" @@ -236,6 +258,22 @@ def _resoudre_sources(data: dict, pair, actifs_noms: set[str], ip_par_hote: dict # `nftables_admin_ssh` ; `voisins_site` des supernets des autres tenants # federes ; `runner_site` du plan du SITE. Traites a part, plus bas. continue + if p == "fabric": + # LA FABRIC N'EST PAS UN GROUPE D'HOTES, ELLE EST DANS LA CARTE (2026-09-10). + # + # `fabric` figurait dans `MOTS_PAIR` — donc accepte comme mot valide — sans + # aucune branche pour le resoudre. Il tombait dans le cas general, ne trouvait + # aucun hote, et une source VIDE produit une regle SANS `saddr` : + # + # tcp dport 3100 accept # ouvert a tout le monde + # + # Un mot reconnu mais non resolu est pire qu'un mot inconnu : celui-ci serait + # refuse a la validation, celui-la produit une porte grande ouverte qui a + # l'air d'un flux precis. + # + # Les adresses viennent de l'underlay, comme celles du devis de la frontiere : + # hyperviseurs et frontiere, le materiel qu'aucun agent n'habite. + continue if p == "flotte": noms |= actifs_noms else: @@ -245,6 +283,8 @@ def _resoudre_sources(data: dict, pair, actifs_noms: set[str], ip_par_hote: dict demandes = pair if isinstance(pair, list) else [pair] if "admin" in demandes: srcs |= set(_sources_admin_ssh_site() if site else _sources_admin_ssh()) + if "fabric" in demandes: + srcs |= set(_adresses_fabric()) if "voisins_site" in demandes: # DU COTE DU SITE, « VOISIN » N'EXCLUT PERSONNE (mesure du 2026-09-02). # @@ -572,7 +612,36 @@ def generer_nftables(flux_par_role: dict[str, list[dict]], site: bool = False) - ports = reels srcs = _resoudre_sources(data, fl.get("pair"), actifs_noms, ip_par_hote, site=site) saddr = "" if not srcs else "ip saddr { " + ", ".join(srcs) + " } " - local = "localhost" in (fl.get("pair") if isinstance(fl.get("pair"), list) else [fl.get("pair")]) + _pairs_fl = fl.get("pair") if isinstance(fl.get("pair"), list) else [fl.get("pair")] + local = "localhost" in _pairs_fl + # UNE SOURCE VIDE N'EST PAS « TOUT LE MONDE » (mesure du 2026-09-10). + # + # Une paire qui NOMME un ensemble — un role, `flotte`, `fabric`, `edge` — + # et qui ne resout rien decrivait jusqu'ici une regle SANS `saddr`, donc + # ouverte a l'Internet. Le silence de la resolution devenait une porte. + # + # CE QUE CA A COUTE, trouve en lisant les fichiers generes : sur le + # `mon-01` d'un tenant, + # + # tcp dport 5665 accept # serveur_icinga + # + # L'API de supervision ouverte a tous, parce que `pair: serveur_backup` ne + # resout rien — cet ecosysteme depose son etat chez le site et n'a pas de + # depot a lui. La regle avait l'air d'un flux precis. + # + # `expositions` et `externe`, EUX, veulent bien dire « tout le monde » : + # ce sont des services publies, et leur ouverture est une intention, pas un + # accident. On ne les touche pas. + # + # Ailleurs : pas de source, pas de regle — et on le DIT, parce qu'un flux + # tu en silence est une porte qu'on croit ouverte (ou fermee). + _public = {"expositions", "externe"} & set(_pairs_fl) + if not srcs and not local and not _public: + print(f"note : {g} declare un flux entrant depuis " + f"`{'/'.join(str(x) for x in _pairs_fl)}` qui ne resout aucune " + f"source — aucune regle emise pour {hote} (une source vide " + f"ouvrirait le port a tous).", file=sys.stderr) + continue for port in ports: if local and not srcs: continue # lo deja accepte diff --git a/scripts/site_inventaire.py b/scripts/site_inventaire.py index de112a0..b4bf3ce 100755 --- a/scripts/site_inventaire.py +++ b/scripts/site_inventaire.py @@ -539,6 +539,12 @@ def inventaire() -> dict: _ip_icinga = str(_s.get("ip") or "") break + _ip_loki = "" + for _n_app, _a_app in applications.items(): + if str(_a_app.get("groupe")) == "serveur_loki": + _ip_loki = str((serveurs.get(_a_app.get("hote")) or {}).get("ip") or "") + break + _hyperviseurs = {} for _h in U.hotes(u): if str(_h.get("role")) != "hyperviseur": @@ -571,6 +577,10 @@ def inventaire() -> dict: # de noeud du cluster. Mesure : `curl: (6) Could not resolve host` sur les trois. **({"client_sante_icinga_url": f"https://{_ip_icinga}:5665"} if _ip_icinga else {}), + # LES JOURNAUX, EUX, PARTENT D'ICI — et par une ADRESSE, meme raison que + # ci-dessus : pas de plancher `/etc/hosts` sur un hyperviseur. + **({"client_journal_loki_url": + f"http://{_ip_loki}:3100/loki/api/v1/push"} if _ip_loki else {}), **communes, } groupes.setdefault("hyperviseurs", []).append(_nom) @@ -596,6 +606,11 @@ def inventaire() -> dict: # Prometheus le remplace par sa propre cible `down`, que la sonde `collecte` # rapporte deja. groupes.setdefault("client_metrique", []).append(_nom) + # LES JOURNAUX SE POUSSENT, EUX. La route specifique vers les zones du site — celle + # qui manquait pour 35 et 36 — suffit a un push : il part de la source et n'attend + # qu'un accuse. Un scrape, lui, exige que la reponse revienne par le meme chemin, + # ce que la route par defaut gelee empeche. D'ou metriques TIREES, journaux POUSSES. + groupes.setdefault("client_journal", []).append(_nom) out: dict = {g: {"hosts": sorted(set(h))} for g, h in groupes.items()} out["site"] = {"hosts": sorted(hostvars)}