Mesure sur les quatorze : aucune sortie TCP vers une adresse publique, apt par le cache, noms autoritaires en local, unattended-upgrades masked. Et quatre pairs NTP publics par machine. Le role chrony posait le fuseau et installait le demon sans jamais toucher a ses sources : le defaut de Debian tenait depuis le premier jour, herite et jamais choisi. L autorite est la frontiere, par decision de l exploitant. Elle etait deja stratum 2 et ecoutait en 123 ; il ne manquait que le passage. 9 anciennes regles port 123 vers !SETOPS_INTERNES retirees, 9 regles nommees vers SETOPS_FRONTIERE posees : le changement resserre autant qu il centralise. Deux chemins parce que la topologie en a deux. Un tenant n atteint pas la frontiere par sa passerelle de zone — tenue par le SDN — mais par le lien de transit. Une machine du site a la frontiere pour passerelle directe. Les deux valeurs sont derivees de l underlay, via reseau_transit() plutot que d un prefixe d adresse qui aurait menti chez le prochain hebergeur. La patte face aux tenants manquait a opnsense_if_zones, pour la meme raison que grappe-controle la veille. Sonde horloge ecrite en meme temps : synchronisee ET contre la source DECLAREE. Une machine peut etre parfaitement a l heure contre quatre serveurs publics — c est exactement l etat d avant. 14 tenant + 7 site, toutes disciplinees, sources publiques = 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
71 lines
2.9 KiB
Django/Jinja
71 lines
2.9 KiB
Django/Jinja
#!/bin/bash
|
|
# GENERE par Set-OPS (role chrony). Ne pas editer a la main.
|
|
#
|
|
# SONDE « horloge » — la machine est-elle synchronisee, ET contre LA SOURCE DECLAREE ?
|
|
#
|
|
# LES DEUX MOITIES COMPTENT. Une machine peut etre parfaitement a l'heure en interrogeant
|
|
# quatre serveurs publics : c'est le defaut de Debian, et il a tenu quatorze machines
|
|
# jusqu'au 2026-09-11 sans que rien ne le dise. Mesurer la seule derive laisserait ce
|
|
# retour en arriere invisible.
|
|
#
|
|
# POURQUOI L'HEURE MERITE UNE SONDE. step-ca emet des certificats a duree courte ; TLS
|
|
# refuse un certificat pas encore valide ; Loki rejette une ligne trop loin dans le futur ;
|
|
# les silences d'Icinga reposent sur des `ttl`. Une flotte dont les horloges divergent
|
|
# tombe en panne de l'interieur, avec des symptomes qui ressemblent a tout sauf a une
|
|
# horloge.
|
|
#
|
|
# Contrat : docs/supervision-conception.md (API des greffons Nagios) — une ligne, 0/1/2.
|
|
# Mise en defaut PAR PARAMETRE : `chrony_sonde_ecart_max` (le baisser a 0).
|
|
set -uo pipefail
|
|
|
|
ECART_MAX={{ chrony_sonde_ecart_max }}
|
|
ATTENDUS="{{ chrony_serveurs | join(' ') }}"
|
|
|
|
if ! systemctl is-active --quiet chrony; then
|
|
echo "chrony ne tourne pas : l'horloge derive sans que personne ne la corrige." \
|
|
"| ecart=U sources=0 declarees=0"
|
|
exit 2
|
|
fi
|
|
|
|
# `^*` marque la source RETENUE. Sans elle, chrony tourne mais ne suit personne.
|
|
retenue=$(chronyc -n sources 2>/dev/null | awk '/^\^\*/ {print $2; exit}')
|
|
sources=$(chronyc -n sources 2>/dev/null | tail -n +3 | wc -l)
|
|
|
|
if [[ -z "${retenue}" ]]; then
|
|
echo "chrony tourne mais n'a retenu AUCUNE source (${sources} candidate(s)) :" \
|
|
"l'heure n'est plus disciplinee." \
|
|
"| ecart=U sources=${sources} declarees=0"
|
|
exit 2
|
|
fi
|
|
|
|
# L'ecart en millisecondes, en valeur absolue.
|
|
ecart=$(chronyc tracking 2>/dev/null \
|
|
| awk '/^Last offset/ {o=$4+0; if (o<0) o=-o; printf "%.0f", o*1000}')
|
|
# `$4+0` FORCE LE NUMERIQUE. `chronyc` prefixe la valeur d'un `+` ou d'un `-` ; sans la
|
|
# conversion, awk compare une CHAINE a zero et la valeur absolue ne mord pas. Le symptome
|
|
# etait discret — `ecart=-0ms` en perfdata — et c'est precisement ce qu'un grapheur avale
|
|
# sans rien dire.
|
|
ecart=${ecart:-0}
|
|
|
|
declarees=0
|
|
for s in ${ATTENDUS}; do
|
|
[[ "${retenue}" == "${s}" ]] && declarees=1
|
|
done
|
|
|
|
if [[ -n "${ATTENDUS}" && "${declarees}" -eq 0 ]]; then
|
|
echo "L'heure vient de ${retenue}, qui n'est PAS la source declaree (${ATTENDUS}) :" \
|
|
"la machine est sortie de l'ecosysteme pour se synchroniser." \
|
|
"| ecart=${ecart}ms;${ECART_MAX} sources=${sources} declarees=0"
|
|
exit 2
|
|
fi
|
|
|
|
if (( ecart > ECART_MAX )); then
|
|
echo "Synchronisee sur ${retenue}, mais l'ecart atteint ${ecart} ms" \
|
|
"(seuil ${ECART_MAX} ms) : les certificats a duree courte vont souffrir." \
|
|
"| ecart=${ecart}ms;${ECART_MAX} sources=${sources} declarees=1"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Horloge disciplinee par ${retenue}, ecart ${ecart} ms." \
|
|
"| ecart=${ecart}ms;${ECART_MAX} sources=${sources} declarees=1"
|
|
exit 0
|