feat(netinfra): NTP + Technitium zones into git SoR; drift-check full stack [#630]

ntp.conf captured (nodes byte-identical); 171 Technitium DZ zone files
captured from the primary; drift-check now covers dhcpd/pihole/ntp on
both nodes plus a zones md5-manifest check. Live run caught real drift:
node-02 dhcpd.conf was a stale primary copy missing the #614 minisplit
reservation — reconciled git->02 with dhcpd -t gate, serial restart,
failover "Both servers normal", all 7 checks in sync. Red-green unit
tests extended (gen_manifest, tracked-file invariants).

Results: https://projects.knownelement.com/issues/630#note-2
This commit is contained in:
2026-09-02 16:33:33 -05:00
parent bdf61ebe49
commit ebefbe47a7
174 changed files with 119 additions and 0 deletions
+28
View File
@@ -11,6 +11,9 @@
# - /etc/dhcp/dhcpd.conf vs netinfra/dhcp/dhcpd-{primary,secondary}.conf # - /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 # - /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) # (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: # Usage:
# drift-check.sh [--node 01|02|all] (default: all) # drift-check.sh [--node 01|02|all] (default: all)
@@ -22,6 +25,9 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DNS_SETUP="$HERE/dns-cluster-setup" DNS_SETUP="$HERE/dns-cluster-setup"
DHCP_DIR="$HERE/dhcp" DHCP_DIR="$HERE/dhcp"
PIHOLE_DIR="$HERE/dns/pihole" 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='s/^( *pwhash *=).*/\1 "REDACTED"/; s/^( *totp_secret *=).*/\1 "REDACTED"/; s/^( *password *=).*/\1 "REDACTED"/'
@@ -31,6 +37,9 @@ redact_config() { sed -E "$REDACT"; }
# normalize_toml <stdin> <stdout> — redact + drop churn lines (timestamps) # normalize_toml <stdin> <stdout> — redact + drop churn lines (timestamps)
normalize_toml() { redact_config | grep -v "Last updated on"; } normalize_toml() { redact_config | grep -v "Last updated on"; }
# gen_manifest <dir> — sorted 'md5 name' manifest of *.zone files (name-sorted)
gen_manifest() { (cd "$1" && md5sum -- *.zone 2>/dev/null | sort -k2); }
# fetch <node> <remote-cmd> — run via the remote-dns.sh chokepoint (env IPs honored) # fetch <node> <remote-cmd> — run via the remote-dns.sh chokepoint (env IPs honored)
fetch() { fetch() {
local node="$1" cmd="$2" local node="$1" cmd="$2"
@@ -71,7 +80,26 @@ main() {
check_file "dhcpd.conf" "$node" "cat /etc/dhcp/dhcpd.conf" "$local_dhcp" cat || rc_total=1 check_file "dhcpd.conf" "$node" "cat /etc/dhcp/dhcpd.conf" "$local_dhcp" cat || rc_total=1
check_file "pihole.toml" "$node" \ check_file "pihole.toml" "$node" \
"docker exec pihole cat /etc/pihole/pihole.toml" "$local_pihole" normalize_toml || rc_total=1 "docker exec pihole cat /etc/pihole/pihole.toml" "$local_pihole" normalize_toml || rc_total=1
check_file "ntp.conf" "$node" "cat /etc/ntpsec/ntp.conf" "$NTP_FILE" cat || rc_total=1
done done
# Technitium zones: binary DZ files, compared by md5 manifest against the
# primary (netinfra-01) — the replication source for -02.
if [[ "$nodes" == *01* ]]; then
remote_manifest="$(fetch 01 "cd $REMOTE_ZONE_DIR && md5sum -- *.zone | sort -k2")"
if [ -z "$remote_manifest" ]; then
echo "DRIFT-ERROR: technitium zones: live fetch empty (node 01 unreachable?)"
rc_total=1
else
if diff -q <(gen_manifest "$ZONES_DIR") <(printf '%s\n' "$remote_manifest") >/dev/null 2>&1; then
echo "OK: technitium zones (node 01) in sync"
else
echo "DRIFT: technitium zones (node 01) manifest differs from git: $ZONES_DIR"
diff <(gen_manifest "$ZONES_DIR") <(printf '%s\n' "$remote_manifest") | head -10
rc_total=1
fi
fi
fi
if [ "$rc_total" -eq 0 ]; then if [ "$rc_total" -eq 0 ]; then
echo "drift-check: ALL IN SYNC" echo "drift-check: ALL IN SYNC"
else else

Some files were not shown because too many files have changed in this diff Show More