supervision : la sonde se declare dans le role, comme le flux
LE CONSTAT. 39 roles declarent leurs flux, 32 leur empreinte, 32 leur
authentification — tous derives. Et 19 groupes sur 19 declaraient une
surveillance en prose que RIEN n executait ; Icinga en surveillait deux.
La carte disait ce qui etait surveille, et personne ne surveillait.
LE MECANISME. Un role declare ses sondes dans meta/supervision.yml et
depose lui-meme son script dans /usr/local/lib/setops/sondes/. Le porteur
client_sante les fait toutes tourner et pousse un resultat passif par
sonde, sans savoir ce qu elles mesurent. serveur_icinga derive les objets
Service ET le filtre de permission d API des memes declarations. Ajouter
une sonde ne demande de toucher ni au porteur ni a Icinga.
PREMIERE SONDE : client_pki/certificat. Heures restantes sur le certificat
reellement pose, chaine verifiee, et empreinte SERVIE comparee au disque
quand un service le consomme. 14/14 au tenant, 7/7 au site.
QUATRE OBSTACLES, ET TROIS SONT LA MEME LECON.
La sonde a rendu 14/14 en CRITIQUE sur une PKI saine : openssl verify
-CAfile racine ne trouve pas l intermediaire qui signe nos certificats.
step certificate verify, lui, repond VALIDE.
Deployee au site, elle a rendu 5/7 : le seuil d avertissement (12 h)
etait AU-DESSUS du point de renouvellement (8 h, le tiers restant). Elle
criait avant que le mecanisme ne soit cense agir. Seuils ramenes a 6 h et
3 h. Un seuil se DERIVE du moment ou le mecanisme surveille agit.
Une alarme toujours allumee ne vaut pas mieux qu une alarme jamais
allumee : elle apprend a ne plus regarder. Une sonde se prouve DEUX FOIS,
verte sur le sain et rouge sur le casse.
Le filtre d API etait ecrit avant la lecture des declarations : les
services auraient existe et Icinga aurait refuse leurs resultats.
Et mon controle negatif a casse un service reel : substituer le certificat
d hote a fait propager un cert sans sa clef vers node_exporter. Un controle
negatif se fait sur une COPIE.
P64 tient les deux bouts : declaree sans etre deposee, ou deposee sans
etre declaree. Trois controles negatifs rejoues.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-09 21:45:49 -04:00
|
|
|
#!/bin/bash
|
|
|
|
|
# GENERE par Set-OPS (role client_pki). Ne pas editer a la main.
|
|
|
|
|
#
|
|
|
|
|
# SONDE : le certificat d'hote tiendra-t-il jusqu'au prochain renouvellement ?
|
|
|
|
|
#
|
doc : rangee par lecteur, trois portes d'entree (architecture, setup, usage) et un index
README reduit au role du projet et au demarrage rapide. docs/architecture.md (schemas
Mermaid), docs/setup.md, docs/usage.md (154 cibles en 13 familles), docs/README.md (index).
Documents ranges en conception/, exploitation/, hebergement/, projet/ ; 598 chemins suivis.
QUICKSTART.md, SOLUTION.md, architecture-set-ops.md et la carte fondus puis retires.
Preuves : P48 lit l'index et conception/mecanismes.md ; les gardes parcourent docs/ sous-
dossiers compris (_documents) ; usage.md suit le Makefile dans les deux sens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:01:34 -04:00
|
|
|
# Contrat (docs/conception/supervision-conception.md) : une ligne sur la sortie standard,
|
supervision : la sonde se declare dans le role, comme le flux
LE CONSTAT. 39 roles declarent leurs flux, 32 leur empreinte, 32 leur
authentification — tous derives. Et 19 groupes sur 19 declaraient une
surveillance en prose que RIEN n executait ; Icinga en surveillait deux.
La carte disait ce qui etait surveille, et personne ne surveillait.
LE MECANISME. Un role declare ses sondes dans meta/supervision.yml et
depose lui-meme son script dans /usr/local/lib/setops/sondes/. Le porteur
client_sante les fait toutes tourner et pousse un resultat passif par
sonde, sans savoir ce qu elles mesurent. serveur_icinga derive les objets
Service ET le filtre de permission d API des memes declarations. Ajouter
une sonde ne demande de toucher ni au porteur ni a Icinga.
PREMIERE SONDE : client_pki/certificat. Heures restantes sur le certificat
reellement pose, chaine verifiee, et empreinte SERVIE comparee au disque
quand un service le consomme. 14/14 au tenant, 7/7 au site.
QUATRE OBSTACLES, ET TROIS SONT LA MEME LECON.
La sonde a rendu 14/14 en CRITIQUE sur une PKI saine : openssl verify
-CAfile racine ne trouve pas l intermediaire qui signe nos certificats.
step certificate verify, lui, repond VALIDE.
Deployee au site, elle a rendu 5/7 : le seuil d avertissement (12 h)
etait AU-DESSUS du point de renouvellement (8 h, le tiers restant). Elle
criait avant que le mecanisme ne soit cense agir. Seuils ramenes a 6 h et
3 h. Un seuil se DERIVE du moment ou le mecanisme surveille agit.
Une alarme toujours allumee ne vaut pas mieux qu une alarme jamais
allumee : elle apprend a ne plus regarder. Une sonde se prouve DEUX FOIS,
verte sur le sain et rouge sur le casse.
Le filtre d API etait ecrit avant la lecture des declarations : les
services auraient existe et Icinga aurait refuse leurs resultats.
Et mon controle negatif a casse un service reel : substituer le certificat
d hote a fait propager un cert sans sa clef vers node_exporter. Un controle
negatif se fait sur une COPIE.
P64 tient les deux bouts : declaree sans etre deposee, ou deposee sans
etre declaree. Trois controles negatifs rejoues.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-09 21:45:49 -04:00
|
|
|
# 0 = OK, 1 = AVERTISSEMENT, 2 = CRITIQUE. Aucun acces reseau requis — c'est le porteur
|
|
|
|
|
# (`client_sante`) qui pousse le resultat a Icinga.
|
|
|
|
|
set -uo pipefail
|
|
|
|
|
|
|
|
|
|
CERT="{{ client_pki_cert }}"
|
|
|
|
|
RACINE="{{ client_pki_steppath }}/certs/root_ca.crt"
|
|
|
|
|
AVERT={{ client_pki_sonde_avert_h }}
|
|
|
|
|
CRIT={{ client_pki_sonde_crit_h }}
|
|
|
|
|
|
|
|
|
|
[[ -r "${CERT}" ]] || { echo "Aucun certificat d'hote lisible en ${CERT}."; exit 2; }
|
|
|
|
|
|
|
|
|
|
fin=$(openssl x509 -in "${CERT}" -noout -enddate 2>/dev/null | cut -d= -f2)
|
|
|
|
|
[[ -n "${fin}" ]] || { echo "Certificat illisible : ${CERT}."; exit 2; }
|
|
|
|
|
reste=$(( ( $(date -d "${fin}" +%s) - $(date +%s) ) / 3600 ))
|
|
|
|
|
|
|
|
|
|
# LA CHAINE, PAS SEULEMENT LA DATE. Un certificat non expire mais signe par une autre
|
|
|
|
|
# racine que la notre est une panne aussi complete qu'un certificat perime — et elle
|
|
|
|
|
# arrive : une AC reconstruite emet des certificats parfaitement valides que plus
|
|
|
|
|
# personne ne verifie.
|
|
|
|
|
#
|
|
|
|
|
# `step certificate verify`, PAS `openssl verify` — et la premiere version de cette sonde
|
|
|
|
|
# a fait la faute (2026-09-09). Nos certificats d'hote sont signes par un INTERMEDIAIRE,
|
|
|
|
|
# et seule la racine vit sur le disque : `openssl verify -CAfile racine` ne trouve pas le
|
|
|
|
|
# maillon manquant et refuse. Resultat, 14 machines sur 14 en CRITIQUE — sur une PKI qui
|
|
|
|
|
# se portait tres bien. `step` sait lire la chaine que step-ca ecrit dans le fichier.
|
|
|
|
|
#
|
|
|
|
|
# LA LECON N'EST PAS « prendre le bon outil » : c'est qu'une sonde doit etre eprouvee
|
|
|
|
|
# contre un etat SAIN autant que contre un etat casse. Une alarme toujours allumee ne vaut
|
|
|
|
|
# pas mieux qu'une alarme jamais allumee — elle apprend a ne plus regarder.
|
|
|
|
|
if ! step certificate verify "${CERT}" --roots "${RACINE}" >/dev/null 2>&1; then
|
|
|
|
|
echo "Le certificat d'hote ne se verifie PAS contre ${RACINE} (${reste} h restantes)."
|
|
|
|
|
exit 2
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
{% if client_pki_reload_services %}
|
|
|
|
|
# LE CERTIFICAT SERVI, PAS CELUI DU DISQUE (mesure du 2026-08).
|
|
|
|
|
#
|
|
|
|
|
# Un cert renouvele sur disque restait servi PERIME en memoire tant que le consommateur
|
|
|
|
|
# n'etait pas recharge. Le fichier disait « tout va bien », le port disait le contraire —
|
|
|
|
|
# et c'est le port que voient les autres machines. On compare donc les empreintes.
|
|
|
|
|
disque=$(openssl x509 -in "${CERT}" -noout -fingerprint -sha256 2>/dev/null | cut -d= -f2)
|
|
|
|
|
{% for service in client_pki_reload_services %}
|
|
|
|
|
{% if client_pki_sonde_port %}
|
|
|
|
|
servi=$(echo | timeout 6 openssl s_client -connect 127.0.0.1:{{ client_pki_sonde_port }} 2>/dev/null \
|
|
|
|
|
| openssl x509 -noout -fingerprint -sha256 2>/dev/null | cut -d= -f2)
|
|
|
|
|
if [[ -n "${servi}" && "${servi}" != "${disque}" ]]; then
|
|
|
|
|
echo "{{ service }} sert un certificat DIFFERENT de celui du disque : recharger le service (${reste} h restantes)."
|
|
|
|
|
exit 2
|
|
|
|
|
fi
|
|
|
|
|
{% endif %}
|
|
|
|
|
{% endfor %}
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
if (( reste <= CRIT )); then
|
|
|
|
|
echo "Certificat d'hote : ${reste} h avant expiration — le renouvellement ne passe plus."
|
|
|
|
|
exit 2
|
|
|
|
|
fi
|
|
|
|
|
if (( reste <= AVERT )); then
|
|
|
|
|
echo "Certificat d'hote : ${reste} h avant expiration (seuil ${AVERT} h)."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo "Certificat d'hote valide ${reste} h, chaine verifiee."
|