From 1dbf16c9dff099caea6f3161c8d9c24db1dbb82a Mon Sep 17 00:00:00 2001 From: reachableceo Date: Wed, 2 Sep 2026 20:19:23 -0500 Subject: [PATCH] feat(dns): zone-snapshot tooling + sync rule; refresh stale snapshots [#630][#728] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Founder rule: every Technitium/DNS/DHCP change ends with a same-session SoR sync — zone-snapshot.sh then drift-check green, then commit. Added the tool (tar-pulls the DZ store from the primary via the chokepoint), wrote the rule into AGENTS.md (DNS change discipline + Key Scripts rows), refreshed 4 stale snapshots (knel.net, 1/3.168.192 reverse, 119.70.100 — incl. the deleted ultix-offstage PTR). drift-check: ALL IN SYNC. Also: last tsrouter mentions retired (AGENTS.md, setup.sh header). --- AGENTS.md | 19 +++++- netinfra/dns-cluster-setup/setup.sh | 3 +- netinfra/dns/technitium/zone-snapshot.sh | 57 ++++++++++++++++++ .../zones/1.168.192.in-addr.arpa.zone | Bin 6114 -> 6838 bytes .../zones/119.70.100.in-addr.arpa.zone | Bin 601 -> 521 bytes .../zones/3.168.192.in-addr.arpa.zone | Bin 9335 -> 9693 bytes netinfra/dns/technitium/zones/knel.net.zone | Bin 8849 -> 9839 bytes 7 files changed, 76 insertions(+), 3 deletions(-) create mode 100755 netinfra/dns/technitium/zone-snapshot.sh diff --git a/AGENTS.md b/AGENTS.md index 1dbf036..6b92641 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -378,7 +378,7 @@ vendor/ Vendored KNELShellFramework through the chokepoint scripts — [`tests/remote.sh`](tests/remote.sh) (Proxmox hosts + all VMs) or [`netinfra/dns-cluster-setup/remote-dns.sh`](netinfra/dns-cluster-setup/remote-dns.sh) - (DNS infra hosts: netinfra-01/02, tsrouter, netboot). NEVER call + (DNS infra hosts: netinfra-01/02, netboot). NEVER call `ssh`/`scp` directly — the harness blocks raw ssh and the command scanner rejects it. There are no exceptions. - **DNS names ONLY (NON-NEGOTIABLE):** NEVER use IP address literals @@ -444,6 +444,19 @@ an AWX playbook item.** The fleet converges to 100% IaC (founder mandate, - New manual fixes during incidents: fix first, codify immediately after. - Playbooks live in this repo, tested through the `sectestbed-*` fleet. +## DNS change discipline (NON-NEGOTIABLE) + +**Any change to Technitium records/zones (dns-cli or API), dhcpd, or the +live DNS/DHCP configs MUST end with a same-session git SoR sync.** +Founder rule, 2026-09-02 — drift-check exists because git went stale; do +not reopen that gap: + +1. Make the change (serial + health-gated across the redundant pair). +2. `bash netinfra/dns/technitium/zone-snapshot.sh` — refresh the DZ + snapshots from the primary. +3. `bash netinfra/dns/drift-check.sh` — must print `ALL IN SYNC`. +4. Commit + push the refreshed snapshots in the same session. + ## Redmine Tracking Policy **Redmine is the system of record for all work.** Do not track status, @@ -570,7 +583,9 @@ live in the centralized store at `~/.creds/discourse.env`. | [`scripts/check-rules.sh`](scripts/check-rules.sh) | Rule audit engine (shellcheck, image pinning, Discourse pointers, required files) | | [`scripts/setup-hooks.sh`](scripts/setup-hooks.sh) | Install git hooks (pre-commit, pre-push) | | [`tests/remote.sh`](tests/remote.sh) | **SSH chokepoint** — all Proxmox host + sandbox VM access routes here | -| [`netinfra/dns-cluster-setup/remote-dns.sh`](netinfra/dns-cluster-setup/remote-dns.sh) | SSH chokepoint for DNS infra hosts (netinfra-01/02, tsrouter, netboot) | +| [`netinfra/dns-cluster-setup/remote-dns.sh`](netinfra/dns-cluster-setup/remote-dns.sh) | SSH chokepoint for DNS infra hosts (netinfra-01/02, netboot) | +| [`netinfra/dns/technitium/zone-snapshot.sh`](netinfra/dns/technitium/zone-snapshot.sh) | Refresh git SoR Technitium zone snapshots from the primary — **required after every DNS change** | +| [`netinfra/dns/drift-check.sh`](netinfra/dns/drift-check.sh) | Live-vs-git drift audit for DHCP/DNS/NTP/zones — must be green before any DNS-adjacent push | | `redmine-cli` container | Redmine CLI (ticket read/write via `docker run`; see `tooling-cli/redmine/`) | | `discourse-cli` container | Discourse CLI (wiki topic read/write via `docker run`; see `tooling-cli/discourse/`) | | `dns-cli` container | Technitium DNS CLI (zones, list, add, delete, search, flush; see `tooling-cli/dns/`) | diff --git a/netinfra/dns-cluster-setup/setup.sh b/netinfra/dns-cluster-setup/setup.sh index 9ff1e0a..d6c9d25 100755 --- a/netinfra/dns-cluster-setup/setup.sh +++ b/netinfra/dns-cluster-setup/setup.sh @@ -116,7 +116,8 @@ api_call() { } # ----------------------------------------------------------------------------- -# Step 1: Export production config (READ-ONLY on tailscale-router) +# Step 1: Export production config (READ-ONLY on the primary; historically +# the retired tailscale-router) # ----------------------------------------------------------------------------- do_export() { log "=== STEP 1: Exporting production config from $PROD (READ-ONLY) ===" diff --git a/netinfra/dns/technitium/zone-snapshot.sh b/netinfra/dns/technitium/zone-snapshot.sh new file mode 100755 index 0000000..f3c7e2b --- /dev/null +++ b/netinfra/dns/technitium/zone-snapshot.sh @@ -0,0 +1,57 @@ +#!/usr/bin/bash +# +# zone-snapshot.sh — refresh the git SoR snapshot of the Technitium zone +# store [#630][#728] +# +# Pulls the binary DZ zone files from netinfra-01 (the replication primary) +# into netinfra/dns/technitium/zones/ via the remote-dns.sh chokepoint, then +# shows which snapshot files changed. +# +# RULE (founder, 2026-09-02): run this after EVERY Technitium record change +# and commit the refreshed snapshots in the same session, so the git SoR +# never goes stale. netinfra/dns/drift-check.sh verifies the result; the +# pre-push rule audit fails on a stale questions file and drift-check is the +# DNS counterpart: green before you push any DNS-adjacent change. +# +# Usage: +# zone-snapshot.sh refresh snapshots from the primary +# zone-snapshot.sh --check verify only (delegates to drift-check.sh) +# +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # netinfra/dns/technitium +REPO="$(cd "$HERE/../../.." && pwd)" +REMOTE_DNS="$HERE/../../dns-cluster-setup/remote-dns.sh" +ZONES_DIR="$HERE/zones" +REMOTE_ZONE_DIR="/home/localuser/services/technitium/config/zones" + +if [ "${1:-}" = "--check" ]; then + exec bash "$HERE/../drift-check.sh" +fi + +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT + +echo "Fetching zone store from netinfra-01 (primary)..." +bash "$REMOTE_DNS" netinfra01-root "cd '$REMOTE_ZONE_DIR' && tar cf - *.zone" > "$tmp/zones.tar" + +# Validate the archive before touching the tracked tree. +tar -tf "$tmp/zones.tar" >/dev/null + +mkdir -p "$ZONES_DIR" +find "$ZONES_DIR" -maxdepth 1 -name '*.zone' -delete +tar -xf "$tmp/zones.tar" -C "$ZONES_DIR" + +count="$(find "$ZONES_DIR" -maxdepth 1 -name '*.zone' | wc -l)" +echo "Snapshotted $count zone files into ${ZONES_DIR#"$REPO"/}" + +echo "Changed snapshot files (git):" +changed="$(git -C "$REPO" status --porcelain -- "$ZONES_DIR")" +if [ -n "$changed" ]; then + printf '%s\n' "$changed" | sed "s|^$REPO/||" | head -20 + printf '%s\n' "$changed" | wc -l | xargs -I{} echo "{} files changed — commit these in the same session (DNS sync rule)" +else + echo " (none — snapshots were already current)" +fi + +echo "Verify with: netinfra/dns/drift-check.sh" diff --git a/netinfra/dns/technitium/zones/1.168.192.in-addr.arpa.zone b/netinfra/dns/technitium/zones/1.168.192.in-addr.arpa.zone index 01b4a2d2858ff26cb78edd94f3c0c631be571821..ebbaacb8c30ac976282d3a71d966ace04fb44568 100644 GIT binary patch delta 283 zcmaE)zs+=ljL6M*p(zU(86bd>0m#y0m^_gscw_TJUQR|%#w)Ku@{|3Ts$R>EuQcrO9#3Y+U7;Rk}%~IZ3*PlN}gi!0Ol_>Okg;>M$@c zw=gg;>M(FxHGtV5%?EkWG#gD`$S1-i4|0?AIj5?b=`Q?}y;r3`C>|x2yOU+@< gOD$pGWMW`|xE$&;B(r7U8jMgifE>3uMyQPy06@J^WdHyG delta 72 zcmdmH`bd9*jEL6^=9C4D3=qJ`0Ay(}OrFRRys`Nq@8mo_PcEnwgDgl`dNP|}*<^Np P!Ogvbe9W7zMXOl>Q?n26 diff --git a/netinfra/dns/technitium/zones/119.70.100.in-addr.arpa.zone b/netinfra/dns/technitium/zones/119.70.100.in-addr.arpa.zone index e01a72c26887660db46e4e6d913644ef147ae693..814dde77a1cceb7f04bb59c63a85b67e174dc7d4 100644 GIT binary patch delta 88 zcmcb~(#bMGRzT15LdpV01_)qeV4Up7s4#ggqZbz=CnHpDG6$2xM57QHW(Ef476t}J Z9R^OT1~40@kAZ1&Amda<=E=_)l>jC{5R(7^ delta 109 zcmeBVxydp?R`}GxCo|_WGC%+$1CYf$Ign9yqOYYCh~2`#z^KE(Y1IH?GjcLQlutBF go4C-C3#JXMW$MH>!O7nlg(fbt7vyAOU_db#0EA%~+W-In diff --git a/netinfra/dns/technitium/zones/3.168.192.in-addr.arpa.zone b/netinfra/dns/technitium/zones/3.168.192.in-addr.arpa.zone index 3942d4692b6d955a1d3fb707ff616468cdf0e235..b4481c245411c5d09320ee3998c005340e6aa138 100644 GIT binary patch delta 187 zcmezFao2l-jL5SWp(zU(86bd>0m$-Vn0!zucw;lWBs(J~BS?PoMW)cnm6C$Ud@)A` z2Idw921XqQPOAnmo7vFFc=AJGMHO+Lg0wQ-l+2>c^wgq6mh8OL9Ok^#5};zJJq#)! jJESLXlq_R(+^iuL!N>&Bs(?!?Cldn$$SIpkrI|PZyje33 delta 70 zcmccX{oP}NjL40B4si<@86bd>0myP^n0!zucw;lWBoBxS0m>jj>B%Rh$|hGz3T_UN M_F&xnOy)ZW0C$QKK>z>% diff --git a/netinfra/dns/technitium/zones/knel.net.zone b/netinfra/dns/technitium/zones/knel.net.zone index 5914fd8b7abbeb077e3867736126460856e0d32a..7e20ba82b14fc19d03a59ba34e876da49f1b32f8 100644 GIT binary patch delta 564 zcmbQ}`rb#;C5nZQBRelOM=vk6gi*)yLdpV01_)qe0J01wDw=PWV~pqEV&r6e_97G{ zJ-L?CaPmVYlgaX2LhMlC$ptKyj5jA2G75`cc@0*@z*CS`rkj#kl$oAdlsGw%OPsNK z@&hJQrgdOtEFfiIUAMrxZoY%);sfa_&#cl-D$Plne2`a+<-iKYX%LNIl`J5YAdQN* z85o#b7#J9J7&xsOz-$!Fx<->51;iPrL-gJT*}~!%vjn8qf@!@Ch;Ie+Fjq-&WwCB~ zVpVAd3n(<0fuX^`zzPZhJ^=<1POx-wW@;{6cJh5DGpH$S5K|^USJVXACA;1h!(p&6 zWhk1wgEO6Jz3pa2E>&h;j3DBw=44`E0EhEtJKkp;yeK{b1&0bKI6#VE!3>Tp6lD;P jb4>@S0tf4688IDh8`AyA05Sm-WD6!cD6X8Ws8j#|nWd66tsg*M0X niZX9rA#jgl^EXKq?#Xeg3nypusB>^KF)*Au_+;kfZ>p65(Znna