From f815b069f7ecebc5d17aa87651d2a920d0a1b81e Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 14 Sep 2026 09:34:31 -0400 Subject: [PATCH] supervision : le lot check_http, et les greffons qui manquaient a la flotte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La doctrine dit qu un greffon Nagios EST une sonde valide et compte sur les 54 de monitoring-plugins. Mesure : aucun n etait installe. Le document decrivait une possibilite qui n existait pas, et chaque role ayant besoin d un controle HTTP n avait d autre choix que d ecrire du shell. Le PORTEUR les installe desormais — c est son affaire de pouvoir executer des sondes, et les poser role par role en ferait autant de copies de la decision. Trois sondes, trois enveloppes qui delegue a check_http et rendent son code tel quel : collabora edition /hosting/discovery — l adresse que Nextcloud interroge lui-meme ; sans elle le bouton ouvrir meurt et Nextcloud reste vert rspamd filtrage un filtre muet ne bloque pas le courrier, il le laisse passer oauth2_proxy passerelle ce qui tombe avec elle n est pas elle : les services derriere restent debout et deviennent injoignables Chacune exige une CHAINE que seul le bon service produit — un 200 peut venir d une page d erreur. Cette chaine est aussi la mise en defaut. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q --- docs/audit/preuve-2026-09-14.md | 2 +- roles/client_sante/defaults/main.yml | 8 +++++++ roles/client_sante/tasks/main.yml | 20 ++++++++++++++++-- roles/serveur_collabora/defaults/main.yml | 11 ++++++++++ roles/serveur_collabora/meta/supervision.yml | 21 +++++++++++++++++++ roles/serveur_collabora/tasks/main.yml | 18 ++++++++++++++++ .../templates/sonde-edition.sh.j2 | 21 +++++++++++++++++++ roles/serveur_oauth2_proxy/defaults/main.yml | 11 ++++++++++ .../serveur_oauth2_proxy/meta/supervision.yml | 20 ++++++++++++++++++ roles/serveur_oauth2_proxy/tasks/main.yml | 18 ++++++++++++++++ .../templates/sonde-passerelle.sh.j2 | 20 ++++++++++++++++++ roles/serveur_rspamd/defaults/main.yml | 11 ++++++++++ roles/serveur_rspamd/meta/supervision.yml | 20 ++++++++++++++++++ roles/serveur_rspamd/tasks/main.yml | 18 ++++++++++++++++ .../templates/sonde-filtrage.sh.j2 | 20 ++++++++++++++++++ 15 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 roles/serveur_collabora/meta/supervision.yml create mode 100644 roles/serveur_collabora/templates/sonde-edition.sh.j2 create mode 100644 roles/serveur_oauth2_proxy/meta/supervision.yml create mode 100644 roles/serveur_oauth2_proxy/templates/sonde-passerelle.sh.j2 create mode 100644 roles/serveur_rspamd/meta/supervision.yml create mode 100644 roles/serveur_rspamd/templates/sonde-filtrage.sh.j2 diff --git a/docs/audit/preuve-2026-09-14.md b/docs/audit/preuve-2026-09-14.md index 1cc0988..3148163 100644 --- a/docs/audit/preuve-2026-09-14.md +++ b/docs/audit/preuve-2026-09-14.md @@ -76,7 +76,7 @@ | P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp | | P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). | | P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s | -| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 27 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ | +| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 30 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ | | P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. | | P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). | | P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). | diff --git a/roles/client_sante/defaults/main.yml b/roles/client_sante/defaults/main.yml index cb3d125..d551634 100644 --- a/roles/client_sante/defaults/main.yml +++ b/roles/client_sante/defaults/main.yml @@ -55,3 +55,11 @@ client_sante_ttl_sondes: 5400 # Delai de garde de CHAQUE sonde. Une sonde est cense repondre vite ; celle qui depasse # n'a pas de verdict, et on le dit (INCONNU) plutot que de figer tout le rapport. client_sante_delai_sonde: 20 + +# CE QUE LE PORTEUR INSTALLE. `curl` pousse le resultat vers l'API d'Icinga ; +# `monitoring-plugins-basic` apporte les greffons standard que les roles reutilisent au +# lieu d'ecrire du shell (voir docs/supervision-conception.md). Ils vivent dans +# `/usr/lib/nagios/plugins/`. +client_sante_paquets: + - curl + - monitoring-plugins-basic diff --git a/roles/client_sante/tasks/main.yml b/roles/client_sante/tasks/main.yml index 3116e62..67e8f6c 100644 --- a/roles/client_sante/tasks/main.yml +++ b/roles/client_sante/tasks/main.yml @@ -67,9 +67,25 @@ group: root mode: "0644" -- name: Installer curl pour le rapport passif +# LES GREFFONS STANDARD, POSES PAR LE PORTEUR ET PAS PAR CHAQUE ROLE (2026-09-14). +# +# `docs/supervision-conception.md` dit qu'un greffon Nagios EST une sonde valide, « sans +# la moindre colle », et compte sur les 54 que fournit `monitoring-plugins`. Mesure du +# 2026-09-14 sur la flotte : **aucun n'etait installe**. Le document decrivait une +# possibilite qui n'existait pas — et chaque role qui avait besoin d'un controle HTTP +# n'avait d'autre choix que d'ecrire du shell, exactement ce que la doctrine refuse. +# +# ICI, ET PAS DANS CHAQUE ROLE. C'est l'affaire du PORTEUR de pouvoir executer des +# sondes ; les poser role par role en installerait autant de copies de la decision, et +# laisserait sans greffon les machines dont aucun role n'en reclame — alors qu'elles en +# ont tout autant besoin le jour ou on leur en ajoute un. +# +# 1,2 Mo par machine, pour `check_http`, `check_tcp`, `check_disk`, `check_procs`, +# `check_ntp_time`, `check_file_age`… et vingt ans de cas limites deja rencontres par +# d'autres. +- name: Installer les outils du rapport passif et les greffons standard ansible.builtin.apt: - name: curl + name: "{{ client_sante_paquets }}" state: present # --- UN ROLE QU'ON RETIRE DOIT POUVOIR DEFAIRE CE QU'IL A FAIT (2026-09-10) ----------- diff --git a/roles/serveur_collabora/defaults/main.yml b/roles/serveur_collabora/defaults/main.yml index fbd48be..be0252d 100644 --- a/roles/serveur_collabora/defaults/main.yml +++ b/roles/serveur_collabora/defaults/main.yml @@ -91,3 +91,14 @@ serveur_collabora_admin_mot_de_passe: "" # le chemin, `https` sinon. Voir `serveur_artefacts_remaps` pour la moitie qui relaie. serveur_collabora_depot_schema: >- {{ 'http' if (artefacts_amorcage | default('') | string | length > 0) else 'https' }} + +# --- SONDE DE SUPERVISION ------------------------------------------------------------- +# +# CE QUE LA REPONSE DOIT CONTENIR. Un code 200 ne suffit pas : un serveur peut rendre 200 +# sur une page d'erreur, une redirection de courtoisie ou un cache perime. On exige donc +# une chaine que SEUL le bon service produit. +# +# C'EST AUSSI LA MISE EN DEFAUT DE LA SONDE : y mettre une chaine introuvable la rend +# CRITIQUE sans rien casser, ce qui rend la seconde preuve REJOUABLE. Une sonde se prouve +# deux fois — verte sur le sain, rouge sur le casse. +serveur_collabora_sonde_attendu: "wopi-discovery" diff --git a/roles/serveur_collabora/meta/supervision.yml b/roles/serveur_collabora/meta/supervision.yml new file mode 100644 index 0000000..78b7703 --- /dev/null +++ b/roles/serveur_collabora/meta/supervision.yml @@ -0,0 +1,21 @@ +--- +# Supervision derivee du role. Voir docs/supervision-conception.md. +# +# UNE SEULE SONDE, une seule CAUSE D'ACTION : aller voir collabora. +# +# `/hosting/discovery` n'est pas une page de complaisance : c'est l'adresse que Nextcloud +# interroge LUI-MEME pour savoir quels documents Collabora sait ouvrir. Si elle ne repond +# plus, le bouton « ouvrir » cesse de fonctionner pour tout le monde — et Nextcloud, lui, +# reste parfaitement vert. +# +# LA SONDE EST UN GREFFON STANDARD (`check_http`), pas du shell : la verite a mesurer +# n'a rien de propre a Set-OPS — c'est « ce service HTTP repond-il ce qu'il doit ». +# +# TTL de 5400 s pour un porteur qui passe aux 15 min : trois passages manques avant la +# peremption. Le silence alerte autant que l'echec. +sondes: + - nom: edition + ttl: 5400 + raison: 'L''edition en ligne repond-elle ? `/hosting/discovery` est l''adresse que Nextcloud + interroge lui-meme pour savoir quels documents Collabora sait ouvrir. Sans elle, le + bouton « ouvrir » cesse de fonctionner pour tout le monde — et Nextcloud reste vert.' diff --git a/roles/serveur_collabora/tasks/main.yml b/roles/serveur_collabora/tasks/main.yml index 1662bf7..24caeec 100644 --- a/roles/serveur_collabora/tasks/main.yml +++ b/roles/serveur_collabora/tasks/main.yml @@ -202,3 +202,21 @@ retries: 12 delay: 5 until: serveur_collabora_capacites is succeeded + +# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et +# pousse le resultat ; les greffons standard, eux, sont poses par le porteur. +- name: Assurer le repertoire des sondes de supervision + ansible.builtin.file: + path: /usr/local/lib/setops/sondes + state: directory + owner: root + group: root + mode: "0755" + +- name: Deposer la sonde « edition » + ansible.builtin.template: + src: sonde-edition.sh.j2 + dest: /usr/local/lib/setops/sondes/edition.sh + owner: root + group: root + mode: "0750" diff --git a/roles/serveur_collabora/templates/sonde-edition.sh.j2 b/roles/serveur_collabora/templates/sonde-edition.sh.j2 new file mode 100644 index 0000000..fafbbfa --- /dev/null +++ b/roles/serveur_collabora/templates/sonde-edition.sh.j2 @@ -0,0 +1,21 @@ +#!/bin/bash +# GENERE par Set-OPS (role serveur_collabora). Ne pas editer a la main. +# +# SONDE « edition » — L'edition en ligne repond-elle ? +# +# UNE ENVELOPPE, PAS UNE SONDE ECRITE A LA MAIN. `check_http` vient de +# `monitoring-plugins` : il parle deja l'API des greffons Nagios, gere les delais, les +# redirections et vingt ans de cas limites. On lui passe l'adresse et ce qu'on attend, et +# on rend SON code de sortie tel quel. Ecrire du shell ici serait se donner du code a +# maintenir et se priver de tout ca — voir docs/supervision-conception.md. +# +# `/hosting/discovery` n'est pas une page de complaisance : c'est l'adresse que Nextcloud +# interroge LUI-MEME pour savoir quels documents Collabora sait ouvrir. Si elle ne repond +# plus, le bouton « ouvrir » cesse de fonctionner pour tout le monde — et Nextcloud, lui, +# reste parfaitement vert. +# +# Mise en defaut PAR PARAMETRE : `serveur_collabora_sonde_attendu` (chaine introuvable). +exec /usr/lib/nagios/plugins/check_http \ + -H 127.0.0.1 -p {{ serveur_collabora_port }} -u '/hosting/discovery' \ + -s '{{ serveur_collabora_sonde_attendu }}' \ + -t 8 diff --git a/roles/serveur_oauth2_proxy/defaults/main.yml b/roles/serveur_oauth2_proxy/defaults/main.yml index 3984b88..20f0582 100644 --- a/roles/serveur_oauth2_proxy/defaults/main.yml +++ b/roles/serveur_oauth2_proxy/defaults/main.yml @@ -58,3 +58,14 @@ serveur_oauth2_proxy_ca_bundle: "/etc/ssl/certs/ca-certificates.crt" # puis pousse par SSH : aucun flux nouveau, et l'artefact devient deployable hors ligne. # Surchargeable par `setops_cache_artefacts` pour partager un cache commun. serveur_oauth2_proxy_cache_local: "{{ setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops') }}" + +# --- SONDE DE SUPERVISION ------------------------------------------------------------- +# +# CE QUE LA REPONSE DOIT CONTENIR. Un code 200 ne suffit pas : un serveur peut rendre 200 +# sur une page d'erreur, une redirection de courtoisie ou un cache perime. On exige donc +# une chaine que SEUL le bon service produit. +# +# C'EST AUSSI LA MISE EN DEFAUT DE LA SONDE : y mettre une chaine introuvable la rend +# CRITIQUE sans rien casser, ce qui rend la seconde preuve REJOUABLE. Une sonde se prouve +# deux fois — verte sur le sain, rouge sur le casse. +serveur_oauth2_proxy_sonde_attendu: "OK" diff --git a/roles/serveur_oauth2_proxy/meta/supervision.yml b/roles/serveur_oauth2_proxy/meta/supervision.yml new file mode 100644 index 0000000..0b20c29 --- /dev/null +++ b/roles/serveur_oauth2_proxy/meta/supervision.yml @@ -0,0 +1,20 @@ +--- +# Supervision derivee du role. Voir docs/supervision-conception.md. +# +# UNE SEULE SONDE, une seule CAUSE D'ACTION : aller voir oauth2_proxy. +# +# CE QUI TOMBE AVEC ELLE N'EST PAS ELLE. Cette passerelle porte l'acces aux applications +# qui n'ont pas de SSO natif — la supervision la premiere. Quand elle meurt, les services +# derriere restent debout et deviennent injoignables : on cherche la panne du mauvais cote. +# +# LA SONDE EST UN GREFFON STANDARD (`check_http`), pas du shell : la verite a mesurer +# n'a rien de propre a Set-OPS — c'est « ce service HTTP repond-il ce qu'il doit ». +# +# TTL de 5400 s pour un porteur qui passe aux 15 min : trois passages manques avant la +# peremption. Le silence alerte autant que l'echec. +sondes: + - nom: passerelle + ttl: 5400 + raison: 'La passerelle d''authentification repond-elle ? Elle porte l''acces aux applications sans + SSO natif — la supervision la premiere. Quand elle meurt, les services derriere restent + debout et deviennent injoignables : on cherche la panne du mauvais cote.' diff --git a/roles/serveur_oauth2_proxy/tasks/main.yml b/roles/serveur_oauth2_proxy/tasks/main.yml index 6494e1a..65af08f 100644 --- a/roles/serveur_oauth2_proxy/tasks/main.yml +++ b/roles/serveur_oauth2_proxy/tasks/main.yml @@ -171,3 +171,21 @@ enabled: true state: started daemon_reload: true + +# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et +# pousse le resultat ; les greffons standard, eux, sont poses par le porteur. +- name: Assurer le repertoire des sondes de supervision + ansible.builtin.file: + path: /usr/local/lib/setops/sondes + state: directory + owner: root + group: root + mode: "0755" + +- name: Deposer la sonde « passerelle » + ansible.builtin.template: + src: sonde-passerelle.sh.j2 + dest: /usr/local/lib/setops/sondes/passerelle.sh + owner: root + group: root + mode: "0750" diff --git a/roles/serveur_oauth2_proxy/templates/sonde-passerelle.sh.j2 b/roles/serveur_oauth2_proxy/templates/sonde-passerelle.sh.j2 new file mode 100644 index 0000000..a021521 --- /dev/null +++ b/roles/serveur_oauth2_proxy/templates/sonde-passerelle.sh.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +# GENERE par Set-OPS (role serveur_oauth2_proxy). Ne pas editer a la main. +# +# SONDE « passerelle » — La passerelle d'authentification repond-elle ? +# +# UNE ENVELOPPE, PAS UNE SONDE ECRITE A LA MAIN. `check_http` vient de +# `monitoring-plugins` : il parle deja l'API des greffons Nagios, gere les delais, les +# redirections et vingt ans de cas limites. On lui passe l'adresse et ce qu'on attend, et +# on rend SON code de sortie tel quel. Ecrire du shell ici serait se donner du code a +# maintenir et se priver de tout ca — voir docs/supervision-conception.md. +# +# CE QUI TOMBE AVEC ELLE N'EST PAS ELLE. Cette passerelle porte l'acces aux applications +# qui n'ont pas de SSO natif — la supervision la premiere. Quand elle meurt, les services +# derriere restent debout et deviennent injoignables : on cherche la panne du mauvais cote. +# +# Mise en defaut PAR PARAMETRE : `serveur_oauth2_proxy_sonde_attendu` (chaine introuvable). +exec /usr/lib/nagios/plugins/check_http \ + -H 127.0.0.1 -p 4180 -u '/ping' \ + -s '{{ serveur_oauth2_proxy_sonde_attendu }}' \ + -t 8 diff --git a/roles/serveur_rspamd/defaults/main.yml b/roles/serveur_rspamd/defaults/main.yml index 55be5d7..fbceb6d 100644 --- a/roles/serveur_rspamd/defaults/main.yml +++ b/roles/serveur_rspamd/defaults/main.yml @@ -21,3 +21,14 @@ serveur_rspamd_dkim_actif: true serveur_rspamd_dkim_domaine: "{{ domaine_interne }}" serveur_rspamd_dkim_selecteur: "setops" serveur_rspamd_dkim_repertoire: "/var/lib/rspamd/dkim" + +# --- SONDE DE SUPERVISION ------------------------------------------------------------- +# +# CE QUE LA REPONSE DOIT CONTENIR. Un code 200 ne suffit pas : un serveur peut rendre 200 +# sur une page d'erreur, une redirection de courtoisie ou un cache perime. On exige donc +# une chaine que SEUL le bon service produit. +# +# C'EST AUSSI LA MISE EN DEFAUT DE LA SONDE : y mettre une chaine introuvable la rend +# CRITIQUE sans rien casser, ce qui rend la seconde preuve REJOUABLE. Une sonde se prouve +# deux fois — verte sur le sain, rouge sur le casse. +serveur_rspamd_sonde_attendu: "pong" diff --git a/roles/serveur_rspamd/meta/supervision.yml b/roles/serveur_rspamd/meta/supervision.yml new file mode 100644 index 0000000..69d9714 --- /dev/null +++ b/roles/serveur_rspamd/meta/supervision.yml @@ -0,0 +1,20 @@ +--- +# Supervision derivee du role. Voir docs/supervision-conception.md. +# +# UNE SEULE SONDE, une seule CAUSE D'ACTION : aller voir rspamd. +# +# UN FILTRE MUET NE BLOQUE PAS LE COURRIER — IL LE LAISSE PASSER. Postfix qui n'obtient +# pas de verdict de rspamd delivre sans filtrer, ou differe : dans les deux cas le service +# de courriel a l'air sain pendant que le pourriel entre ou que la file grossit. +# +# LA SONDE EST UN GREFFON STANDARD (`check_http`), pas du shell : la verite a mesurer +# n'a rien de propre a Set-OPS — c'est « ce service HTTP repond-il ce qu'il doit ». +# +# TTL de 5400 s pour un porteur qui passe aux 15 min : trois passages manques avant la +# peremption. Le silence alerte autant que l'echec. +sondes: + - nom: filtrage + ttl: 5400 + raison: 'Le filtrage du courrier repond-il ? Un filtre muet ne bloque pas le courrier, il le + laisse passer : Postfix sans verdict delivre sans filtrer ou differe, et le service de + courriel a l''air sain pendant que le pourriel entre.' diff --git a/roles/serveur_rspamd/tasks/main.yml b/roles/serveur_rspamd/tasks/main.yml index ea2f78b..f139593 100644 --- a/roles/serveur_rspamd/tasks/main.yml +++ b/roles/serveur_rspamd/tasks/main.yml @@ -80,3 +80,21 @@ name: "{{ serveur_rspamd_service }}" enabled: true state: started + +# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et +# pousse le resultat ; les greffons standard, eux, sont poses par le porteur. +- name: Assurer le repertoire des sondes de supervision + ansible.builtin.file: + path: /usr/local/lib/setops/sondes + state: directory + owner: root + group: root + mode: "0755" + +- name: Deposer la sonde « filtrage » + ansible.builtin.template: + src: sonde-filtrage.sh.j2 + dest: /usr/local/lib/setops/sondes/filtrage.sh + owner: root + group: root + mode: "0750" diff --git a/roles/serveur_rspamd/templates/sonde-filtrage.sh.j2 b/roles/serveur_rspamd/templates/sonde-filtrage.sh.j2 new file mode 100644 index 0000000..9f7a3de --- /dev/null +++ b/roles/serveur_rspamd/templates/sonde-filtrage.sh.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +# GENERE par Set-OPS (role serveur_rspamd). Ne pas editer a la main. +# +# SONDE « filtrage » — Le filtrage du courrier repond-il ? +# +# UNE ENVELOPPE, PAS UNE SONDE ECRITE A LA MAIN. `check_http` vient de +# `monitoring-plugins` : il parle deja l'API des greffons Nagios, gere les delais, les +# redirections et vingt ans de cas limites. On lui passe l'adresse et ce qu'on attend, et +# on rend SON code de sortie tel quel. Ecrire du shell ici serait se donner du code a +# maintenir et se priver de tout ca — voir docs/supervision-conception.md. +# +# UN FILTRE MUET NE BLOQUE PAS LE COURRIER — IL LE LAISSE PASSER. Postfix qui n'obtient +# pas de verdict de rspamd delivre sans filtrer, ou differe : dans les deux cas le service +# de courriel a l'air sain pendant que le pourriel entre ou que la file grossit. +# +# Mise en defaut PAR PARAMETRE : `serveur_rspamd_sonde_attendu` (chaine introuvable). +exec /usr/lib/nagios/plugins/check_http \ + -H 127.0.0.1 -p 11334 -u '/ping' \ + -s '{{ serveur_rspamd_sonde_attendu }}' \ + -t 8