Vérification finale et tests d’acceptation#

Pourquoi#

Chaque chapitre précédent a sa propre section “Vérifier”, ciblée sur un composant isolé. Mais un système fait de vingt pièces qui fonctionnent individuellement ne garantit pas qu’elles fonctionnent bien ensemble, et surtout, tester manuellement vingt points à chaque changement de configuration est long et sujet à l’oubli. La solution : un script d’acceptation unique, exécutable en une commande, qui audite l’ensemble du système et catégorise chaque vérification en OK / WARN / CRITICAL.

Comment#

Principe de structure#

#!/bin/bash
OK_COUNT=0; WARN_COUNT=0; CRIT_COUNT=0

ok()       { printf "  [ OK ]       %s\n" "$1"; OK_COUNT=$((OK_COUNT+1)); }
warn()     { printf "  [ WARN ]     %s (%s)\n" "$1" "$2"; WARN_COUNT=$((WARN_COUNT+1)); }
critical() { printf "  [ CRITICAL ] %s (%s)\n" "$1" "$2"; CRIT_COUNT=$((CRIT_COUNT+1)); }

Chaque vérification suit ce schéma : mesurer une valeur, la comparer à un seuil, et appeler ok/warn/critical selon le résultat. Par exemple, pour le RMS offset (chapitre Chrony) :

RMS=$(chronyc tracking 2>/dev/null | awk '/^RMS offset/ {print $4}')
if [ "$(echo "$RMS < 0.000001" | bc -l)" = "1" ]; then
    ok "RMS offset = ${RMS}s (< 1µs)"
elif [ "$(echo "$RMS < 0.00001" | bc -l)" = "1" ]; then
    warn "RMS offset = ${RMS}s" "cible < 1µs"
else
    critical "RMS offset = ${RMS}s" "dérive importante"
fi

Catégories de vérification couvertes#

Un script d’acceptation complet pour ce type de serveur couvre typiquement :

CatégorieExemples de vérifications
Services critiqueschrony/nginx/fail2ban/auditd actifs, aucun service en échec
Synchronisation horaireStratum, Reference ID, RMS offset, skew, leap status
PPS/GNSS/dev/pps0 présent, impulsions actives, fix 3D, nombre de satellites
Réseauadressage IPv4/IPv6, routes par défaut, résolution DNS
Webconfig nginx valide, accessibilité HTTPS, fraîcheur de la télémétrie
TLSvalidité du certificat, type de clé, version TLS négociée
Sécuritéjails fail2ban actives, tables nftables provisionnées, règles auditd
Systèmeusage disque/RAM, absence de swap, fraîcheur des graphes Munin
Journauxerreurs récentes, absence d’OOM killer, anomalies d’audit

Assembler un script minimal#

Les fragments ci-dessus se combinent en un script réel, court mais déjà utile, à étendre au fil des chapitres avec ses propres seuils :

cat > acceptance.sh <<'EOF'
#!/bin/bash
OK_COUNT=0; WARN_COUNT=0; CRIT_COUNT=0

ok()       { printf "  [ OK ]       %s\n" "$1"; OK_COUNT=$((OK_COUNT+1)); }
warn()     { printf "  [ WARN ]     %s (%s)\n" "$1" "$2"; WARN_COUNT=$((WARN_COUNT+1)); }
critical() { printf "  [ CRITICAL ] %s (%s)\n" "$1" "$2"; CRIT_COUNT=$((CRIT_COUNT+1)); }

# Services critiques
for svc in chrony nginx fail2ban auditd; do
    if systemctl is-active --quiet "$svc"; then
        ok "$svc actif"
    else
        critical "$svc inactif" "systemctl status $svc"
    fi
done

# RMS offset (chapitre Chrony)
RMS=$(chronyc tracking 2>/dev/null | awk '/^RMS offset/ {print $4}')
if [ -z "$RMS" ]; then
    critical "RMS offset illisible" "chronyc injoignable ?"
elif [ "$(echo "$RMS < 0.000001" | bc -l)" = "1" ]; then
    ok "RMS offset = ${RMS}s (< 1µs)"
elif [ "$(echo "$RMS < 0.00001" | bc -l)" = "1" ]; then
    warn "RMS offset = ${RMS}s" "cible < 1µs"
else
    critical "RMS offset = ${RMS}s" "dérive importante"
fi

# PPS/GNSS
if [ -e /dev/pps0 ]; then
    ok "/dev/pps0 présent"
else
    critical "/dev/pps0 absent" "module pps-gpio chargé ?"
fi

# Système : pas de swap actif
if [ "$(swapon --show | wc -l)" -eq 0 ]; then
    ok "aucun swap actif"
else
    warn "swap actif détecté" "cf. chapitre Système de fichiers"
fi

echo
echo "RÉSUMÉ ACCEPTANCE"
printf "  OK       : %3d\n" "$OK_COUNT"
printf "  WARN     : %3d\n" "$WARN_COUNT"
printf "  CRITICAL : %3d\n" "$CRIT_COUNT"
echo
if [ "$CRIT_COUNT" -gt 0 ]; then
    echo ">>> ÉCHEC - $CRIT_COUNT problème(s) critique(s)"
elif [ "$WARN_COUNT" -gt 0 ]; then
    echo ">>> SUCCÈS PARTIEL - $WARN_COUNT avertissement(s)"
    echo ">>> Surveillance recommandée"
else
    echo ">>> SUCCÈS COMPLET"
fi
EOF
chmod +x acceptance.sh

Cette version tient en quelques exemples de vérifications pour rester lisible dans ce guide. Le script réel de ce projet en compte près d’une centaine, une par ligne du tableau ci-dessus, ajoutée progressivement à chaque chapitre au fil de l’installation.

Erreurs à éviter#

Un test qui échoue systématiquement, quel que soit l’état réel du système, est pire qu’aucun test. Il crée une fausse habitude d’ignorer les avertissements (“ce WARN-là, c’est normal, il est toujours là”). C’est arrivé concrètement sur ce projet avec un test TLS : la vérification cherchait la chaîne exacte Protocol : TLSv1.3 dans la sortie d’openssl s_client pour confirmer la version négociée. Un changement mineur de version d’OpenSSL a suffi à faire disparaître cet espacement précis (Protocol : TLSv1.3, deux espaces au lieu d’un) : le test échouait en permanence, alors que TLS 1.3 était bel et bien négocié à chaque connexion. Quand un test échoue de façon constante sur un système qui semble par ailleurs sain, la première question à se poser n’est pas “qu’est-ce qui est cassé côté serveur ?” mais “le test lui-même mesure-t-il ce qu’il prétend mesurer ?”

  • Seuils WARN/CRITICAL copiés sans adaptation au contexte réel. Un seuil de température de 60 °C peut être parfaitement normal sous un climat tempéré et systématiquement dépassé sous un climat plus chaud, sans que cela reflète un vrai problème. Calibrer les seuils sur l’observation réelle du système, pas sur une valeur générique trouvée ailleurs.
  • Laisser des WARN permanents s’installer comme bruit de fond. Sur un système avec plusieurs dizaines de vérifications, certains constats relèvent d’une limitation réelle, connue et acceptée (bug matériel documenté, faux positif d’un outil tiers). Les laisser en WARN à chaque exécution contredirait le principe ci-dessus : c’est exactement ainsi que naît l’habitude d’ignorer les avertissements. Le bon traitement est d’en faire une exception explicite dans le test lui-même : le cas connu passe en OK, accompagné de sa justification dans le message, comme le fait la batterie de ce projet pour l’horodatage matériel TX (« bug driver macb Pi 5, attendu », chapitre PHC). Un WARN reste alors réservé aux états transitoires appelés à disparaître (fonctionnalité pas encore déployée), et la sortie normale tend vers zéro avertissement : tout WARN qui apparaît est un signal neuf qui mérite attention, jamais un bruit ambiant.

Vérifier#

./acceptance.sh

Sortie attendue en fin de script :

RÉSUMÉ ACCEPTANCE
  OK       :  83
  WARN     :   0
  CRITICAL :   0

>>> SUCCÈS COMPLET

Ce script doit tourner après chaque modification de configuration, pas seulement à l’installation initiale : c’est le meilleur filet de sécurité contre les régressions silencieuses. Un 0 CRITICAL est le seuil minimal de mise en production ; les WARN doivent tous être expliqués (voir ci-dessus), pas simplement ignorés.