#!/bin/bash # GENERE par Set-OPS (role client_sante). Ne pas editer a la main. # # SONDE « plancher » — le plancher /etc/hosts de cette machine dit-il la meme chose que # l'autoritatif du domaine ? # # POURQUOI (2026-09-29). Le plancher et la zone PowerDNS derivent du meme plan, mais # chacun ne le suit que quand son role est rejoue. La console a ete ajoutee au plan : la # zone l'a recue, les planchers non — sauf celui d'`ops-01`, redeploye depuis. Depuis # l'edge, `console` ne se resolvait pas, et rien ne le signalait. Sur ces machines, le # plancher est la SEULE resolution des noms internes : un nom qui y manque n'existe pas. # # COMMENT, SANS TRANSFERT DE ZONE. La zone publie `_plancher. TXT "n=… sha256=…"` # : le nombre et l'empreinte des paires « nom adresse » que le plancher doit porter pour ce # domaine. On calcule la meme chose ici sur /etc/hosts. Si elles different, on nomme ce # qu'on peut : chaque nom du plancher est demande a l'autoritatif (en trop, readresse), et # l'ecart de nombre dit combien de noms publies manquent ici. # # L'autoritatif est interroge DIRECTEMENT : le resolveur qu'utilisent ces machines peut # ignorer ce domaine (c'est le cas chez les locataires, qui passent par celui du site). # # Mise en defaut PAR PARAMETRE : `client_sante_plancher_fichier` (un plancher vide). set -uo pipefail DOMAINE='{{ domaine_interne | default('') }}' AUTORITE='{{ hostvars[groups['serveur_powerdns'][0]].ansible_host | default('') if (groups.get('serveur_powerdns') or []) else '' }}' FICHIER='{{ client_sante_plancher_fichier }}' if [[ -z "${DOMAINE}" || -z "${AUTORITE}" ]]; then echo "Pas d'autoritatif interne dans cet ecosysteme : rien a quoi comparer le plancher." exit 0 fi txt=$(dig +short +time=3 +tries=2 TXT "_plancher.${DOMAINE}" "@${AUTORITE}" 2>/dev/null | tr -d '"') if [[ "${txt}" != n=*" sha256="* ]]; then echo "L'autoritatif (${AUTORITE}) ne publie pas l'empreinte du plancher (_plancher.${DOMAINE} : ${txt:-rien}) — zone anterieure, ou injoignable : comparaison impossible." exit 1 fi # Les paires « nom adresse » du plancher pour CE domaine : toute ligne, tout nom qui finit # par `.` (le nom court des hotes, en alias, n'est pas compte ; la zone non plus). local_=$(python3 - "${FICHIER}" "${DOMAINE}" <<'PY' import hashlib, sys fichier, domaine = sys.argv[1], sys.argv[2] paires = set() try: lignes = open(fichier, encoding="utf-8").read().splitlines() except OSError: lignes = [] for l in lignes: champs = l.split("#", 1)[0].split() if len(champs) < 2: continue for nom in champs[1:]: if nom.endswith("." + domaine): paires.add(f"{nom} {champs[0]}") tri = sorted(paires) print(f"n={len(tri)} sha256={hashlib.sha256(chr(10).join(tri).encode()).hexdigest()}") for p in tri: print(p) PY ) empreinte=$(printf '%s\n' "${local_}" | head -1) n_pub=${txt#n=}; n_pub=${n_pub%% *} n_loc=${empreinte#n=}; n_loc=${n_loc%% *} perf="| noms=${n_loc};;;0; publies=${n_pub};;;0;" if [[ "${empreinte}" == "${txt}" ]]; then echo "Plancher conforme a l'autoritatif : ${n_loc} nom(s) de ${DOMAINE}, memes adresses. ${perf}" exit 0 fi # ILS DIFFERENT : on nomme ce qu'on peut. Chaque nom du plancher est demande a l'autoritatif. # Un nom qu'il ignore est en trop ici (ou une machine planifiee, que la zone ne publie pas # encore) ; une autre adresse est un readressage que l'un des deux n'a pas suivi. en_trop=(); readresses=(); connus=0; i=0 while read -r nom ip; do (( i++ >= 40 )) && break pub=$(dig +short +time=2 +tries=1 A "${nom}" "@${AUTORITE}" 2>/dev/null | tail -1) if [[ -z "${pub}" ]]; then en_trop+=("${nom%%.*}") elif [[ "${pub}" != "${ip}" ]]; then readresses+=("${nom%%.*}=${ip}/${pub}"); connus=$((connus + 1)) else connus=$((connus + 1)); fi done < <(printf '%s\n' "${local_}" | tail -n +2) manquants=$(( n_pub - connus )) msg="Le plancher de cette machine et l'autoritatif divergent (${n_loc} nom(s) ici, ${n_pub} publie(s))" [[ ${manquants} -gt 0 ]] && msg+=" ; ${manquants} nom(s) publie(s) absent(s) d'ici" [[ -n "${readresses[*]}" ]] && msg+=" ; autre adresse : ${readresses[*]}" [[ -n "${en_trop[*]}" ]] && msg+=" ; inconnu(s) de l'autoritatif : ${en_trop[*]}" msg+=". Rejouer hosts_statiques (ou serveur_powerdns, si c'est la zone qui date)." # Un nom qui MANQUE ou une adresse qui differe casse une resolution : critique. Des noms en # trop seulement (une machine retiree du plan) ne cassent rien : avertissement. if (( manquants > 0 )) || [[ -n "${readresses[*]}" ]]; then echo "${msg} ${perf}"; exit 2 fi echo "${msg} ${perf}"; exit 1