From 1653319ef175c371f2689bdb735ba2dff6266119 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 28 Sep 2026 23:55:58 -0400 Subject: [PATCH] sonde vigie : elle eprouve ce que la vigie lit, pas seulement qu elle repond Apres check_http, la sonde lit la configuration deployee d Icinga Web 2 et eprouve chaque ressource utilisee avec ses identifiants : base du moteur (hotes), annuaire ou base des comptes, Redis. Au vert sur les trois ; quatre mises en defaut critiques. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 26 ++++ roles/serveur_icingaweb2/defaults/main.yml | 5 + roles/serveur_icingaweb2/meta/supervision.yml | 9 +- .../templates/sonde-vigie.sh.j2 | 115 +++++++++++++++--- wiki/Rôle-serveur_icingaweb2.md | 2 +- 5 files changed, 136 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19dad2f..25ecb2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # CHANGELOG — Set-OPS +## 2026-09-28 (28) — `vigie` vérifie ce que la vigie lit, pas seulement qu'elle répond + +Quatrième des cinq sondes « répond » de la revue (25). + +**Avant.** `check_http` sur la boucle locale : un 302 vers le formulaire de connexion +suffisait. Une vigie dont la base est injoignable (mot de passe tourné d'un seul côté, +`pg_hba`, certificat), dont l'annuaire refuse la liaison, ou dont le Redis est tombé, sert +très bien ce formulaire — puis une erreur, des tableaux vides, ou un compte connecté sans +aucun droit parce que ses groupes ne se lisent plus. + +**Maintenant.** Après `check_http`, la sonde lit la configuration DÉPLOYÉE de la vigie +(`resources.ini`, `authentication.ini`, `groups.ini`, `modules/icingadb/`) et éprouve chaque +ressource qu'elle utilise, avec SES identifiants et le même pilote PHP : la base du moteur +(compte des hôtes, zéro = critique), l'annuaire (liaison + lecture de la racine) ou la base +des comptes, et Redis (`PING`). Un tenant (annuaire) et le site (base locale) passent par +le même code. Les secrets restent dans les fichiers (0640), rien n'est affiché. + +**Éprouvé.** Au vert sur les trois : Chezlepro et Technolibre (13 hôtes, soit ceux du plan ; +annuaire ; Redis), site (23 hôtes, base des comptes et groupes, Redis) ; 51 ms. Mises en +défaut sur Technolibre, sur une copie de la configuration : table absente +(`serveur_icingaweb2_sonde_table`), Redis sur un port fermé, liaison à l'annuaire refusée, +mot de passe de la base faux — les quatre critiques, chacune nommant sa cause. Au site, +codes HTTP attendus changés : critique, « ne sert plus ses pages ». + +Reste de la revue : `passerelle` (oauth2-proxy atteint-il Keycloak ?). + ## 2026-09-28 (27) — La sonde d'Icinga Web 2 s'appelle `vigie`, plus `console` Le vocabulaire des plans dit **vigie** pour Icinga Web 2 (`vigie.`) et **console** diff --git a/roles/serveur_icingaweb2/defaults/main.yml b/roles/serveur_icingaweb2/defaults/main.yml index bfaf546..30d2744 100644 --- a/roles/serveur_icingaweb2/defaults/main.yml +++ b/roles/serveur_icingaweb2/defaults/main.yml @@ -113,6 +113,11 @@ serveur_icingaweb2_bpm_processes: {} # bloque — et l'absence de reponse, qui est le nginx local tombe. serveur_icingaweb2_sonde_codes: "HTTP/1.1 200,HTTP/1.1 302,HTTP/1.1 401,HTTP/1.1 403" +# CE QUE LA VIGIE LIT, EPROUVE AVEC SES PROPRES IDENTIFIANTS (2026-09-28). La table que la +# sonde interroge dans la base du moteur, au travers de la ressource que la vigie utilise. +# Mise en defaut PAR PARAMETRE : une table qui n'existe pas. +serveur_icingaweb2_sonde_table: "host" + # --- MODE `db` : le compte d'amorcage de la console ------------------------------------ # # UN SEUL COMPTE EST POSE PAR LE MOTEUR, et c'est voulu : celui qui permet d'entrer la diff --git a/roles/serveur_icingaweb2/meta/supervision.yml b/roles/serveur_icingaweb2/meta/supervision.yml index 82d3aee..e36ab03 100644 --- a/roles/serveur_icingaweb2/meta/supervision.yml +++ b/roles/serveur_icingaweb2/meta/supervision.yml @@ -28,7 +28,8 @@ sondes: - nom: vigie ttl: 5400 - raison: 'La vigie (Icinga Web 2) repond-elle encore ? Si elle meurt, le moteur - continue de collecter et tous les verdicts restent verts : le systeme est en - parfaite sante et l''exploitant est aveugle. C''est une panne sans symptome, qu''on - decouvre en cherchant a diagnostiquer autre chose.' + raison: 'La vigie (Icinga Web 2) repond-elle, et lit-elle ce qu''elle doit montrer + (base du moteur, annuaire ou base des comptes, Redis) ? Si elle meurt, ou si elle + sert des pages vides, le moteur continue de collecter et tous les verdicts restent + verts : le systeme est en parfaite sante et l''exploitant est aveugle. C''est une + panne sans symptome, qu''on decouvre en cherchant a diagnostiquer autre chose.' diff --git a/roles/serveur_icingaweb2/templates/sonde-vigie.sh.j2 b/roles/serveur_icingaweb2/templates/sonde-vigie.sh.j2 index 564101c..e51a311 100644 --- a/roles/serveur_icingaweb2/templates/sonde-vigie.sh.j2 +++ b/roles/serveur_icingaweb2/templates/sonde-vigie.sh.j2 @@ -1,29 +1,112 @@ #!/bin/bash # GENERE par Set-OPS (role serveur_icingaweb2). Ne pas editer a la main. # -# SONDE « vigie » — la vigie (Icinga Web 2) repond-elle encore ? +# SONDE « vigie » — la vigie (Icinga Web 2) repond-elle, et PEUT-ELLE MONTRER quelque chose ? # # « vigie », PAS « console » (2026-09-28). Elle s'appelait `console`, alors que le # vocabulaire des plans reserve « console » a la console d'exploitation Set-OPS # (`serveur_ops`, sonde `console-ops`) et nomme ce service-ci `vigie`. Dans Icinga, # `mon-01!console` parlait donc de la vigie : le nom dit ce qu'on vient y faire. # -# UNE ENVELOPPE, PAS UNE SONDE ECRITE A LA MAIN. `check_http` vient de -# `monitoring-plugins-basic` : il parle deja l'API des greffons Nagios, gere les delais et -# les redirections. On lui passe l'adresse et on rend SON code de sortie tel quel. -# -# SUR LA BOUCLE LOCALE, ET SANS S'AUTHENTIFIER. En mode `external`, l'ecoute est restreinte -# a 127.0.0.1 et une passerelle SSO se tient devant : la sonde frappe donc la ou -# l'application ecoute vraiment, et se contente de la reponse que rend une requete non -# authentifiee. On mesure « la pile PHP sert encore », pas « l'annuaire accepte encore ». +# Mise en defaut PAR PARAMETRE : `serveur_icingaweb2_sonde_codes` pour la premiere etape, +# `serveur_icingaweb2_sonde_table` (table inexistante) pour la seconde. +set -uo pipefail + +# PREMIERE ETAPE — LA PILE WEB SERT. `check_http` vient de `monitoring-plugins-basic` : il +# parle deja l'API des greffons Nagios. Sur la boucle locale et sans s'authentifier : en +# mode `external`, l'ecoute est restreinte a 127.0.0.1 et une passerelle SSO se tient +# devant ; la sonde frappe la ou l'application ecoute vraiment. # # `-e` PLUTOT QU'UN 200 IMPLICITE : Icinga Web 2 renvoie une REDIRECTION vers son # formulaire de connexion quand personne n'est authentifie. Exiger un 200 ferait crier la -# sonde sur le comportement NORMAL d'une vigie saine — la pire sorte de fausse alerte, -# celle qu'on finit par ignorer. +# sonde sur le comportement NORMAL d'une vigie saine. +http=$(/usr/lib/nagios/plugins/check_http -H 127.0.0.1 -p {{ serveur_icingaweb2_http_port }} -u '/' \ + -e '{{ serveur_icingaweb2_sonde_codes }}' -t 10 2>&1) +rc=$? +if (( rc != 0 )); then + echo "La vigie ne sert plus ses pages : ${http:0:160}" + exit "${rc}" +fi + +# SECONDE ETAPE — CE QU'ELLE LIT (2026-09-28). « La pile PHP repond » n'etait pas la +# question : une vigie dont la base est injoignable (mot de passe tourne d'un seul cote, +# `pg_hba`, certificat), dont l'annuaire refuse sa liaison, ou dont le Redis est tombe, +# sert tres bien son formulaire de connexion — puis une page d'erreur, ou des tableaux +# vides, ou un compte connecte SANS AUCUN DROIT parce que ses groupes ne se lisent plus. +# Meme lecon que `tableaux` chez Grafana. # -# Mise en defaut PAR PARAMETRE : `serveur_icingaweb2_sonde_codes`. -exec /usr/lib/nagios/plugins/check_http \ - -H 127.0.0.1 -p {{ serveur_icingaweb2_http_port }} -u '/' \ - -e '{{ serveur_icingaweb2_sonde_codes }}' \ - -t 10 +# ON LIT LA CONFIGURATION DEPLOYEE, PAS CELLE QU'ON CROIT AVOIR POSEE : chaque ressource +# que la vigie UTILISE (module icingadb, authentification, groupes) est eprouvee avec SES +# identifiants, par le MEME pilote PHP (PDO pgsql, ext-ldap). Un tenant (annuaire) et le +# site (base locale) passent par le meme code sans qu'on ait a le lui dire. +# Les secrets restent dans les fichiers (0640 root:icingaweb2) ; rien n'est affiche. +TABLE='{{ serveur_icingaweb2_sonde_table }}' php -d display_errors=stderr -- '{{ serveur_icingaweb2_config_dir }}' <<'PHP' + a quoi elle sert (moteur, comptes, groupes) +foreach (['modules/icingadb/config.ini' => 'moteur', 'authentication.ini' => 'comptes', 'groups.ini' => 'groupes'] as $f => $usage) { + foreach ($lire($f) as $s) { if (!empty($s['resource'])) $utilisees[$s['resource']][$usage] = $usage; } +} +if (!array_filter($utilisees, fn($u) => isset($u['moteur']))) { + echo "La vigie sert ses pages, mais aucune base du moteur n'est configuree (modules/icingadb/config.ini).\n"; exit(2); +} +$ko = []; $vu = []; $perf = ''; +foreach ($utilisees as $nom => $usages) { + $role = implode(' et ', $usages); + $r = $res[$nom] ?? null; + if ($r === null) { $ko[] = "$nom ($role) : ressource absente de resources.ini"; continue; } + if (($r['type'] ?? '') === 'db') { + $dsn = sprintf('pgsql:host=%s;port=%s;dbname=%s', $r['host'], $r['port'] ?? 5432, $r['dbname']); + if (!empty($r['ssl_mode'])) $dsn .= ';sslmode=' . $r['ssl_mode']; + if (!empty($r['ssl_ca'])) $dsn .= ';sslrootcert=' . $r['ssl_ca']; + try { + $db = new PDO($dsn, $r['username'], $r['password'] ?? '', [PDO::ATTR_TIMEOUT => 5, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + if (isset($usages['moteur'])) { + $n = (int) $db->query('SELECT count(*) FROM ' . preg_replace('/[^a-z_]/', '', getenv('TABLE')))->fetchColumn(); + if ($n === 0) { $ko[] = "$nom : la base du moteur ne contient AUCUN hote"; continue; } + $vu[] = "base du moteur ($n hotes)"; $perf = " | hotes=$n"; + } else { + $db->query('SELECT 1')->fetchColumn(); + $vu[] = "base des $role"; + } + } catch (Throwable $e) { + $ko[] = "$nom ($role, {$r['host']}) : " . substr(preg_replace('/\s+/', ' ', $e->getMessage()), 0, 110); + } + } elseif (($r['type'] ?? '') === 'ldap') { + $enc = $r['encryption'] ?? ''; + $url = ($enc === 'ldaps' ? 'ldaps://' : 'ldap://') . $r['hostname'] . ':' . ($r['port'] ?? 389); + $l = @ldap_connect($url); + ldap_set_option($l, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($l, LDAP_OPT_NETWORK_TIMEOUT, (int) ($r['timeout'] ?? 5)); + if ($enc === 'starttls' && !@ldap_start_tls($l)) { $ko[] = "$nom ($role) : STARTTLS refuse — " . ldap_error($l); continue; } + if (!@ldap_bind($l, $r['bind_dn'] ?? null, $r['bind_pw'] ?? null)) { + $ko[] = "$nom ($role, {$r['hostname']}) : liaison refusee — " . ldap_error($l); continue; + } + if (!@ldap_read($l, $r['root_dn'], '(objectClass=*)', ['dn'])) { + $ko[] = "$nom ($role) : liee, mais {$r['root_dn']} illisible — " . ldap_error($l); continue; + } + $vu[] = "annuaire ($role)"; + } +} +// L'ETAT EN DIRECT : le module icingadb lit les etats volatils dans Redis. Sans lui, la +// vigie affiche une banniere d'erreur et des resultats de controle perimes. +foreach ($lire('modules/icingadb/redis.ini') as $sec => $r) { + if (empty($r['host'])) continue; + $s = @fsockopen($r['host'], (int) ($r['port'] ?? 6380), $errno, $errstr, 3); + if (!$s) { $ko[] = "Redis $sec ({$r['host']}:{$r['port']}) : $errstr"; continue; } + stream_set_timeout($s, 3); + fwrite($s, "PING\r\n"); + $rep = trim((string) fgets($s)); + fclose($s); + if ($rep !== '+PONG') { $ko[] = "Redis $sec : rend « " . substr($rep, 0, 40) . " » au lieu de PONG"; continue; } + $vu[] = 'Redis'; +} +if ($ko) { + echo "La vigie sert ses pages, mais NE PEUT PLUS MONTRER ce qu'elle doit : " . implode(' ; ', $ko) . $perf . "\n"; + exit(2); +} +echo "Vigie : pages servies ; lit " . implode(', ', $vu) . '.' . $perf . "\n"; +exit(0); +PHP diff --git a/wiki/Rôle-serveur_icingaweb2.md b/wiki/Rôle-serveur_icingaweb2.md index 3f1752a..3480bac 100644 --- a/wiki/Rôle-serveur_icingaweb2.md +++ b/wiki/Rôle-serveur_icingaweb2.md @@ -23,7 +23,7 @@ graph LR | sonde | TTL | ce qu'elle voit | |---|---|---| -| `vigie` | 5400 s | La vigie (Icinga Web 2) repond-elle encore ? Si elle meurt, le moteur continue de collecter et tous les verdicts restent verts : le systeme est en parfaite sante et l'exploitant est aveugle. C'est une panne sans symptome, qu'on decouvre en cherchant a diagnostiquer autre chose. | +| `vigie` | 5400 s | La vigie (Icinga Web 2) repond-elle, et lit-elle ce qu'elle doit montrer (base du moteur, annuaire ou base des comptes, Redis) ? Si elle meurt, ou si elle sert des pages vides, le moteur continue de collecter et tous les verdicts restent verts : le systeme est en parfaite sante et l'exploitant est aveugle. C'est une panne sans symptome, qu'on decouvre en cherchant a diagnostiquer autre chose. | ## Ce qu'il expose en séries