Sondes Nagios

Cette section fournit des exemples de sondes (plugins) compatibles Nagios, Icinga, Centreon, Shinken, Naemon, et plus généralement tout outil qui comprend les codes retour Nagios :

Code

État

Signification

0

OK

Tout va bien

1

WARNING

Dégradé, à traiter

2

CRITICAL

Service KO, intervention requise

3

UNKNOWN

La sonde n’a pas pu s’exécuter

Ces scripts sont des exemples : adaptez les noms (reemo_ = INSTANCE_NAME), les ports et les seuils à votre plateforme, puis validez-les en recette avant la mise en production.

Prérequis et installation

Les sondes qui interrogent Docker (Swarm, services, Vault, base de données) s’exécutent localement sur un manager de chaque cluster, via NRPE (ou via l’agent de votre outil). Les sondes réseau (certificats, HTTP, TURN, port de l’API) s’exécutent depuis le serveur de supervision.

  1. Copier les scripts dans /usr/local/lib/nagios/plugins/ (droits 0755, propriétaire root).

  2. Autoriser l’utilisateur nagios à lancer uniquement ces scripts avec sudo, sans mot de passe. Évitez d’ajouter nagios au groupe docker : cela revient à lui donner les droits root.

    # /etc/sudoers.d/nagios-reemo  (éditer avec visudo -f)
    nagios ALL=(root) NOPASSWD: /usr/local/lib/nagios/plugins/check_reemo_*
    
  3. Dépendances : bash, openssl, curl, awk, timeout (coreutils), et turnutils_uclient (paquet coturn-utils ou coturn) pour la sonde TURN.

check_reemo_swarm_nodes

À exécuter sur un manager de chaque cluster. Vérifie que tous les nœuds sont Ready / Active, qu’aucun manager n’est injoignable et qu’un leader existe.

#!/bin/bash
# check_reemo_swarm_nodes - etat des noeuds Docker Swarm
out=$(docker node ls --format '{{.Hostname}} {{.Status}} {{.Availability}} {{.ManagerStatus}}' 2>&1)
if [ $? -ne 0 ]; then
  echo "UNKNOWN - docker node ls : $out"; exit 3
fi
total=$(echo "$out" | wc -l)
bad=$(echo "$out" | awk '$2 != "Ready" || $3 != "Active" || $4 == "Unreachable"')
if [ -n "$bad" ]; then
  echo "CRITICAL - noeud(s) en defaut : $(echo "$bad" | paste -sd ';') | nodes=$total"
  exit 2
fi
if ! echo "$out" | grep -q 'Leader'; then
  echo "CRITICAL - aucun leader Swarm | nodes=$total"; exit 2
fi
echo "OK - $total noeud(s) Ready/Active | nodes=$total"
exit 0

check_reemo_swarm_services

À exécuter sur un manager de chaque cluster. Vérifie pour chaque service que le nombre de réplicas en cours correspond au nombre attendu, et signale les mises à jour en échec (paused ou rollback).

#!/bin/bash
# check_reemo_swarm_services [PREFIXE] [REGEX_EXCLUSION]
#   PREFIXE          : ne controle que les services commencant par ce prefixe
#                      (ex. reemo_). Le service traefik est toujours controle.
#   REGEX_EXCLUSION  : services a ignorer. Par defaut : l'ancien systeme de
#                      sauvegarde (_mysqlbackup).
# Les taches uniques (restart_config: condition none, ex. _db et _logapidb) ne
# sont pas controlees sur leurs replicas, mais sur l'etat de leur derniere execution.
PREFIX="${1:-}"
EXCLUDE="${2:-_mysqlbackup$}"

list=$(docker service ls --format '{{.Name}} {{.Replicas}}' 2>&1)
if [ $? -ne 0 ]; then
  echo "UNKNOWN - docker service ls : $list"; exit 3
fi

crit=(); warn=(); n=0
while read -r name rep _; do
  [ -z "$name" ] && continue
  if [ -n "$PREFIX" ] && [[ "$name" != "$PREFIX"* ]] && [ "$name" != "traefik" ]; then
    continue
  fi
  [[ "$name" =~ $EXCLUDE ]] && continue
  n=$((n + 1))
  cond=$(docker service inspect "$name" --format '{{.Spec.TaskTemplate.RestartPolicy.Condition}}' 2>/dev/null)
  if [ "$cond" = "none" ]; then
    last=$(docker service ps "$name" --format '{{.CurrentState}}' 2>/dev/null | head -1)
    case "$last" in
      Complete*|Running*|Starting*|Preparing*|Assigned*|Pending*) ;;
      *) warn+=("$name tache unique : ${last:-inconnue}") ;;
    esac
    continue
  fi
  cur=${rep%%/*}; want=${rep#*/}
  [ "$cur" != "$want" ] && crit+=("$name $cur/$want")
  st=$(docker service inspect "$name" --format '{{if .UpdateStatus}}{{.UpdateStatus.State}}{{end}}' 2>/dev/null)
  case "$st" in
    paused|rollback_started|rollback_paused|rollback_completed) warn+=("$name update=$st") ;;
  esac
done <<< "$list"

if [ ${#crit[@]} -gt 0 ]; then
  echo "CRITICAL - replicas manquants : $(IFS=';'; echo "${crit[*]}") $( [ ${#warn[@]} -gt 0 ] && IFS=';' && echo "/ ${warn[*]}") | services=$n"
  exit 2
fi
if [ ${#warn[@]} -gt 0 ]; then
  echo "WARNING - $(IFS=';'; echo "${warn[*]}") | services=$n"
  exit 1
fi
echo "OK - $n service(s) avec tous leurs replicas | services=$n"
exit 0

check_reemo_cert

À exécuter depuis le serveur de supervision. Contrôle l’expiration, la chaîne de certification et le nom d’un certificat TLS. Fonctionne aussi sur les ports en TCP passthrough (Workstation, Appliance portal…) et sur les ports mTLS : le certificat serveur est lu avant que le certificat client ne soit demandé.

#!/bin/bash
# check_reemo_cert HOTE PORT [SNI] [WARN_JOURS] [CRIT_JOURS] [VERIF_CHAINE 1|0]
H="$1"; P="$2"; SNI="${3:-$1}"; W="${4:-30}"; C="${5:-7}"; VERIFY="${6:-1}"
if [ -z "$H" ] || [ -z "$P" ]; then
  echo "UNKNOWN - usage : $0 HOTE PORT [SNI] [WARN] [CRIT] [VERIF_CHAINE]"; exit 3
fi

out=$(echo | timeout 15 openssl s_client -connect "$H:$P" -servername "$SNI" \
            -verify_hostname "$SNI" 2>&1)
end=$(echo "$out" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$end" ]; then
  echo "CRITICAL - impossible de lire le certificat sur $H:$P"; exit 2
fi
subject=$(echo "$out" | openssl x509 -noout -subject 2>/dev/null | sed 's/^subject=//')
days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
vcode=$(echo "$out" | grep -o 'Verify return code: [0-9]*' | head -1 | awk '{print $4}')
vmsg=$(echo "$out" | grep 'Verify return code' | head -1 | sed 's/.*(\(.*\))/\1/')

if [ "$days" -lt 0 ]; then
  echo "CRITICAL - certificat EXPIRE depuis $(( -days )) j ($subject) | days=$days"; exit 2
fi
if [ "$VERIFY" = "1" ] && [ "$vcode" != "0" ]; then
  echo "CRITICAL - chaine/nom invalide : $vmsg ($subject), expire dans $days j | days=$days"; exit 2
fi
if [ "$days" -le "$C" ]; then
  echo "CRITICAL - expire dans $days j le $end ($subject) | days=$days"; exit 2
fi
if [ "$days" -le "$W" ]; then
  echo "WARNING - expire dans $days j le $end ($subject) | days=$days"; exit 1
fi
echo "OK - expire dans $days j le $end ($subject) | days=$days"
exit 0

Astuce

Pour les certificats émis par la PKI interne (API, relayws, provisionnement), passez VERIF_CHAINE=0 : la CA interne n’est pas connue du serveur de supervision. Seule l’expiration est alors contrôlée.

Healthchecks HTTP (plugin standard check_http)

Le plugin check_http (ou check_curl) du paquet monitoring-plugins suffit pour le portail d’administration et signal. Pour le portail, le script reemo_healthcheck (voir Santé et monitoring) contrôle en plus le statut de chaque sous-service :

# Portail : utilisez de préférence le script reemo_healthcheck, qui analyse le JSON
reemo_healthcheck https://portail.example.com

# Portail d'administration
check_http -H admin.example.com -S --sni -u /api/healthcheck -e 200 -w 3 -c 10

# Signal (port signal, 8443 par defaut)
# (reponse attendue : HTTP 426 avec le texte "Upgrade Required")
check_http -H signal.example.com -p 8443 -S --sni -u / -e 426 -s 'Upgrade Required' -w 3 -c 10

# Redirection HTTP -> HTTPS du portail
check_http -H portail.example.com -p 80 -e 301,302,307,308

check_reemo_api_port

À exécuter depuis le serveur de supervision, sans certificat. Réservé à l’architecture séparée api_manager / portal_manager. Vérifie que le port 443 de l’API répond et que l’API exige un certificat client : la connexion sans certificat doit être refusée au niveau TLS.

#!/bin/bash
# check_reemo_api_port API_IP [PORT]
IP="$1"; PORT="${2:-443}"
[ -z "$IP" ] && { echo "UNKNOWN - usage : $0 API_IP [PORT]"; exit 3; }

err=$(curl -sSk -o /dev/null -w '%{http_code}' --max-time 15 "https://$IP:$PORT/" 2>&1)
rc=$?

case $rc in
  0)
    echo "CRITICAL - API $IP:$PORT repond HTTP ${err: -3} SANS certificat client : mTLS non exige"
    exit 2 ;;
  6|7)
    echo "CRITICAL - API $IP:$PORT : connexion impossible (port ferme ou filtre)"; exit 2 ;;
  28)
    echo "CRITICAL - API $IP:$PORT : timeout"; exit 2 ;;
esac
if echo "$err" | grep -qi -E 'certificate required|bad certificate|certificate_required'; then
  echo "OK - API $IP:$PORT ouvert, certificat client exige (mTLS)"; exit 0
fi
echo "WARNING - API $IP:$PORT : reponse TLS inattendue (curl $rc) : $(echo "$err" | head -1)"
exit 1

Note

Le message renvoyé par curl dépend de la version de TLS et de la bibliothèque TLS utilisée (tlsv13 alert certificate required en TLS 1.3 avec OpenSSL, alert bad certificate en TLS 1.2). Validez la sonde sur votre plateforme et adaptez l’expression recherchée si besoin.

check_reemo_turn

À exécuter depuis le serveur de supervision (idéalement depuis l’extérieur, comme un client). Fait une allocation TURN réelle avec des identifiants éphémères (TURN_AUTH_MODE=secret).

#!/bin/bash
# check_reemo_turn HOTE [PORT] [FICHIER_SECRET]
#   FICHIER_SECRET : fichier contenant TURN_SECRET (droits 0400, proprietaire nagios)
H="$1"; P="${2:-58200}"; F="${3:-/etc/nagios/reemo_turn_secret}"
if [ -z "$H" ] || [ ! -r "$F" ]; then
  echo "UNKNOWN - usage : $0 HOTE [PORT] [FICHIER_SECRET] (secret lisible requis)"; exit 3
fi
command -v turnutils_uclient >/dev/null || { echo "UNKNOWN - turnutils_uclient absent"; exit 3; }

u="$(( $(date +%s) + 600 )):nagios"
p=$(printf '%s' "$u" | openssl dgst -sha1 -hmac "$(tr -d '\n' < "$F")" -binary | base64)

out=$(timeout 30 turnutils_uclient -y -n 5 -m 1 -u "$u" -w "$p" -p "$P" "$H" 2>&1)
recv=$(echo "$out" | grep -o 'tot_recv_msgs=[0-9]*' | tail -1 | cut -d= -f2)

if echo "$out" | grep -qi '401\|unauthorized\|wrong credentials'; then
  echo "CRITICAL - TURN $H:$P : authentification refusee (secret different ?)"; exit 2
fi
if [ -n "$recv" ] && [ "$recv" -gt 0 ]; then
  echo "OK - TURN $H:$P allocation et relais OK ($recv msg recus) | recv=$recv"; exit 0
fi
echo "CRITICAL - TURN $H:$P : aucune allocation/relais ($(echo "$out" | tail -1))"
exit 2

Note

Le format de sortie de turnutils_uclient peut varier selon la version de coturn : validez l’analyse (tot_recv_msgs) sur votre version. En mode TURN_AUTH_MODE=static, remplacez -u/-w par TURN_USERNAME / TURN_PASSWORD.

Avertissement

TURN_SECRET permet de générer des identifiants TURN valides. Stockez-le avec des droits restreints (chmod 0400, propriétaire nagios).

check_reemo_vault

À exécuter sur chaque nœud du cluster qui héberge Vault. La sonde contrôle les réplicas présents sur le nœud local. Codes retour de bao status : 0 = déverrouillé, 2 = verrouillé (sealed), 1 = erreur.

#!/bin/bash
# check_reemo_vault [PREFIXE_SERVICE]
PREFIX="${1:-reemo_vault}"
cids=$(docker ps --format '{{.ID}} {{.Names}}' | awk -v p="^${PREFIX}[0-9]+\\\\." '$2 ~ p {print $1":"$2}')
if [ -z "$cids" ]; then
  echo "OK - aucune replica Vault sur ce noeud"; exit 0
fi
sealed=(); err=(); ok=0
for c in $cids; do
  id=${c%%:*}; name=${c#*:}; name=${name%%.*}
  docker exec "$id" bao status >/dev/null 2>&1
  case $? in
    0) ok=$((ok + 1));;
    2) sealed+=("$name");;
    *) err+=("$name");;
  esac
done
if [ ${#sealed[@]} -gt 0 ]; then
  echo "CRITICAL - Vault SEALED : ${sealed[*]}"; exit 2
fi
if [ ${#err[@]} -gt 0 ]; then
  echo "CRITICAL - Vault en erreur : ${err[*]}"; exit 2
fi
echo "OK - $ok replica(s) Vault deverrouillee(s)"
exit 0

check_reemo_mariadb

À exécuter sur les nœuds infra_manager / api_manager. Seul le nœud qui héberge la base répond : sur les autres nœuds, la sonde renvoie OK. L’état global du service est couvert par check_reemo_swarm_services.

#!/bin/bash
# check_reemo_mariadb [NOM_SERVICE]
SVC="${1:-reemo_mysql}"
id=$(docker ps -q --filter "name=^${SVC}\." | head -1)
if [ -z "$id" ]; then
  echo "OK - $SVC non heberge sur ce noeud"; exit 0
fi
out=$(docker exec "$id" sh -c 'MYSQL_PWD=$(cat "$MYSQL_ROOT_PASSWORD_FILE") mysqladmin -u root status' 2>&1)
if [ $? -ne 0 ]; then
  echo "CRITICAL - $SVC : $out"; exit 2
fi
threads=$(echo "$out" | grep -o 'Threads: [0-9]*' | awk '{print $2}')
echo "OK - $SVC : $out | threads=${threads:-0}"
exit 0

check_reemo_ndb

À exécuter sur un nœud qui héberge un mgmd (DB_DIALECT=NDBCLUSTER). Vérifie que tous les nœuds NDB sont connectés et contrôle l’occupation mémoire.

#!/bin/bash
# check_reemo_ndb [WARN_%] [CRIT_%]
W="${1:-80}"; C="${2:-90}"
id=$(docker ps -q --filter "name=reemo_mysql-mgmd" | head -1)
[ -z "$id" ] && { echo "UNKNOWN - aucun mgmd sur ce noeud"; exit 3; }

show=$(docker exec "$id" ndb_mgm -e show 2>&1) || { echo "CRITICAL - ndb_mgm : $show"; exit 2; }
nc=$(echo "$show" | grep -c 'not connected')
if [ "$nc" -gt 0 ]; then
  echo "CRITICAL - $nc noeud(s) NDB non connecte(s) : $(echo "$show" | grep 'not connected' | awk '{print $1}' | paste -sd ' ')"
  exit 2
fi

mem=$(docker exec "$id" ndb_mgm -e "all report memory" 2>/dev/null \
      | grep -o '[A-Za-z]* usage is [0-9]*%' | awk '{gsub("%","",$4); print $1"="$4}')
max=$(echo "$mem" | cut -d= -f2 | sort -n | tail -1)
perf=$(echo "$mem" | sort -u | paste -sd ' ')
if [ -n "$max" ] && [ "$max" -ge "$C" ]; then
  echo "CRITICAL - memoire NDB a ${max}% | $perf"; exit 2
fi
if [ -n "$max" ] && [ "$max" -ge "$W" ]; then
  echo "WARNING - memoire NDB a ${max}% | $perf"; exit 1
fi
echo "OK - tous les noeuds NDB connectes, memoire max ${max:-?}% | $perf"
exit 0

check_reemo_pki

À exécuter sur le poste d’administration Ansible (là où se trouve la PKI : LOCAL_PATH ou clone du dépôt git de la PKI). Contrôle l’expiration de tous les certificats de la PKI interne.

#!/bin/bash
# check_reemo_pki REPERTOIRE [WARN_JOURS] [CRIT_JOURS]
D="$1"; W="${2:-30}"; C="${3:-7}"
[ -d "$D" ] || { echo "UNKNOWN - repertoire $D introuvable"; exit 3; }
crit=(); warn=(); n=0
for f in "$D"/*.crt; do
  [ -e "$f" ] || continue
  n=$((n + 1))
  end=$(openssl x509 -in "$f" -noout -enddate 2>/dev/null | cut -d= -f2) || continue
  days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
  if   [ "$days" -le "$C" ]; then crit+=("$(basename "$f")=${days}j")
  elif [ "$days" -le "$W" ]; then warn+=("$(basename "$f")=${days}j")
  fi
done
[ ${#crit[@]} -gt 0 ] && { echo "CRITICAL - ${crit[*]} ${warn[*]}"; exit 2; }
[ ${#warn[@]} -gt 0 ] && { echo "WARNING - ${warn[*]}"; exit 1; }
echo "OK - $n certificat(s) valides plus de $W jours"
exit 0

check_reemo_backup_age

La sauvegarde (service reemo_backup) envoie ses archives vers le serveur de sauvegarde. Le contrôle le plus fiable consiste à vérifier sur le serveur de sauvegarde l’âge du fichier le plus récent :

#!/bin/bash
# check_reemo_backup_age REPERTOIRE_BACKUP [WARN_SEC] [CRIT_SEC]
D="$1"; W="${2:-90000}"; C="${3:-172800}"      # 25 h / 48 h
last=$(ls -1t "$D" 2>/dev/null | head -1)
[ -z "$last" ] && { echo "CRITICAL - aucune sauvegarde dans $D"; exit 2; }
exec /usr/lib/nagios/plugins/check_file_age -w "$W" -c "$C" -f "$D/$last"

En complément, activez les notifications natives du conteneur de sauvegarde en cas d’échec : BACKUP_MAIL_RECEIVER ou BACKUP_WEBHOOK_ENABLED / BACKUP_WEBHOOK_URL, avec BACKUP_NOTIFY_ON.

Configuration NRPE

Exemple de /etc/nagios/nrpe.d/reemo.cfg sur un manager portal_manager :

command[check_reemo_swarm_nodes]=/usr/bin/sudo /usr/local/lib/nagios/plugins/check_reemo_swarm_nodes
command[check_reemo_swarm_services]=/usr/bin/sudo /usr/local/lib/nagios/plugins/check_reemo_swarm_services reemo_
command[check_docker]=/usr/lib/nagios/plugins/check_procs -c 1: -C dockerd
command[check_disk_docker]=/usr/lib/nagios/plugins/check_disk -w 20% -c 10% -p /var/lib/docker
command[check_disk_opt]=/usr/lib/nagios/plugins/check_disk -w 20% -c 10% -p /opt

Sur un manager api_manager / infra_manager, ajoutez :

command[check_reemo_vault]=/usr/bin/sudo /usr/local/lib/nagios/plugins/check_reemo_vault
command[check_reemo_mariadb]=/usr/bin/sudo /usr/local/lib/nagios/plugins/check_reemo_mariadb
# ou, en NDB Cluster :
command[check_reemo_ndb]=/usr/bin/sudo /usr/local/lib/nagios/plugins/check_reemo_ndb 80 90

Définitions Nagios

Exemple de commandes et de services côté serveur Nagios :

define command {
    command_name  check_reemo_cert
    command_line  $USER1$/check_reemo_cert $ARG1$ $ARG2$ $ARG3$ 30 7 $ARG4$
}
define command {
    command_name  check_reemo_https
    command_line  $USER1$/check_http -H $ARG1$ -p $ARG2$ -S --sni -u $ARG3$ -e 200 -w 3 -c 10
}
define command {
    command_name  check_reemo_tcp
    command_line  $USER1$/check_tcp -H $ARG1$ -p $ARG2$ -w 2 -c 5
}
define command {
    command_name  check_reemo_signal
    command_line  $USER1$/check_http -H $ARG1$ -p $ARG2$ -S --sni -u / -e 426 -s 'Upgrade Required' -w 3 -c 10
}
define command {
    command_name  check_reemo_api_port
    command_line  $USER1$/check_reemo_api_port $ARG1$ 443
}
define command {
    command_name  check_reemo_turn
    command_line  $USER1$/check_reemo_turn $ARG1$ $ARG2$ /etc/nagios/reemo_turn_secret
}

# --- Portail -------------------------------------------------------------
# La commande reemo_healthcheck est définie dans la page « Santé et monitoring »
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - certificat portail
    check_command        check_reemo_cert!portail.example.com!443!portail.example.com!1
    check_interval       720
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - healthcheck portail
    check_command        reemo_healthcheck!https://portail.example.com
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - signal
    check_command        check_reemo_signal!signal.example.com!8443
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - certificat workstation
    check_command        check_reemo_cert!portail.example.com!8445!portail.example.com!0
    check_interval       720
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - services swarm
    check_command        check_nrpe!check_reemo_swarm_services
}

# --- Appliances (APPLIANCE_ENABLED) --------------------------------------
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - port appliance portal 8444
    check_command        check_reemo_tcp!portail.example.com!8444
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - certificat appliance portal
    check_command        check_reemo_cert!portail.example.com!8444!portail.example.com!0
    check_interval       720
}

# --- Credential portal (CREDENTIAL_ENABLED + CREDENTIALPORTAL_ENABLED) ---
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - port credential portal 8446
    check_command        check_reemo_tcp!portail.example.com!8446
}
define service {
    use                  generic-service
    host_name            portail
    service_description  Reemo - certificat credential portal
    check_command        check_reemo_cert!portail.example.com!8446!portail.example.com!0
    check_interval       720
}

# --- API (architecture separee) ------------------------------------------
define service {
    use                  generic-service
    host_name            api
    service_description  Reemo - API port 443 / mTLS exige
    check_command        check_reemo_api_port!10.0.0.10
}
define service {
    use                  generic-service
    host_name            api
    service_description  Reemo - certificat API
    check_command        check_reemo_cert!10.0.0.10!443!reemo_api!0
    check_interval       720
}
define service {
    use                  generic-service
    host_name            api
    service_description  Reemo - Vault sealed
    check_command        check_nrpe!check_reemo_vault
}

# --- TURN ----------------------------------------------------------------
define service {
    use                  generic-service
    host_name            turn
    service_description  Reemo - TURN allocation
    check_command        check_reemo_turn!203.0.113.10!58200
}

Fréquences conseillées :

  • healthchecks, services Swarm, TURN, Vault, API : toutes les 1 à 5 minutes ;

  • certificats et PKI : 2 fois par jour (check_interval 720) ;

  • sauvegardes : toutes les heures.