#!/usr/bin/bash # # drift-check.sh — compare live netinfra DNS/DHCP config against git [#420][#469] # # Verifies that the running configuration on netinfra-01/02 matches the # files tracked in this repo. Founded after the 2026-09-01 DNS incident: # the live systems are production; git is the source of truth; drift is # a defect. # # Checks per node: # - /etc/dhcp/dhcpd.conf vs netinfra/dhcp/dhcpd-{primary,secondary}.conf # - /etc/pihole/pihole.toml vs netinfra/dns/pihole/netinfra-0{1,2}.pihole.toml # (secrets redacted on both sides before compare; "Last updated" line ignored) # - /etc/ntpsec/ntp.conf vs netinfra/ntp/ntp.conf (nodes are identical) # - Technitium zones (node 01, the replication primary): md5 manifest of the # binary DZ store vs netinfra/dns/technitium/zones/ [#630] # # Usage: # drift-check.sh [--node 01|02|all] (default: all) # Exit: 0 = in sync, 1 = drift detected, 2 = fetch failure # set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DNS_SETUP="$HERE/dns-cluster-setup" DHCP_DIR="$HERE/dhcp" PIHOLE_DIR="$HERE/dns/pihole" NTP_FILE="$HERE/ntp/ntp.conf" ZONES_DIR="$HERE/dns/technitium/zones" REMOTE_ZONE_DIR="/home/localuser/services/technitium/config/zones" REDACT='s/^( *pwhash *=).*/\1 "REDACTED"/; s/^( *totp_secret *=).*/\1 "REDACTED"/; s/^( *password *=).*/\1 "REDACTED"/' # redact_config — strip secrets from pihole.toml content redact_config() { sed -E "$REDACT"; } # normalize_toml — redact + drop churn lines (timestamps) normalize_toml() { redact_config | grep -v "Last updated on"; } # gen_manifest — sorted 'md5 name' manifest of *.zone files (name-sorted) gen_manifest() { (cd "$1" && md5sum -- *.zone 2>/dev/null | sort -k2); } # fetch — run via the remote-dns.sh chokepoint (env IPs honored) fetch() { local node="$1" cmd="$2" bash "$DNS_SETUP/remote-dns.sh" "netinfra${node}-root" "$cmd" 2>/dev/null } # check_file