Files
PFVCluster/dns-cluster-setup/README.md
T
mrcharles a7fbad60ab docs: end-to-end gardening — links, stale refs, tailscale.md update
Comprehensive documentation gardening across the merged repo:

- tailscale.md: fully rewritten with current ground truth. The netinfra
  pair now runs production Technitium with all knel.net records
  replicated. Both LAN IPs resolve knel.net device names and recurse
  externally. The old "NXDOMAIN / zone is stale" findings are replaced
  with the resolved state and current recommendations.
- AGENTS.md: rewritten with Gitea-compatible clickable relative links
  to all key scripts and docs. Autonomous commit/push policy
  prominently documented. SSH user corrected to localuser.
- README.md: directory table and docs table now use clickable links.
- All .md cross-references converted to Gitea-renderable relative links.
- Stale path references (ProjectCode/, Project-Tests/, ProjectDocs/)
  updated to current names (provisioning/, tests/) across all docs.
- Stale repo name "FetchApply" / "KNELServerBuild" updated to
  "PFVCluster" in actionable docs; historical AI-review docs tagged
  with an HTML comment notice.
- REFACTORING-EXAMPLES.md: tagged as historical (pre-refactor patterns).
- tests/README.md, dns-cluster-setup/README.md, docs/DEPLOYMENT.md,
  docs/SECURITY.md: path references fixed to current structure.

🤖 Generated with [Crush](https://github.com/charmassociates/crush)

Assisted-by: GLM-5 via Crush <crush@charm.land>
2026-07-28 11:28:52 -05:00

7.4 KiB

Technitium DNS Cluster Setup

Replicates the production Technitium DNS Server from tailscale-router to the pfv-netinfra-01/02 pair and configures them as a primary/secondary cluster with automatic zone transfers.

Architecture

 tailscale-router (PRODUCTION — READ ONLY)
   └─ tsys-dns container (technitium/dns-server)
      └─ 124 zones (knel.net + reverse DNS)
      └─ Users + 2FA in auth.config
                │
          docker cp (export)
                │
                ▼
 ┌─ pfv-netinfra-01 (192.168.3.252) ──── PRIMARY ──────────┐
 │  tsys-dns container (Technitium on :5300)                │
 │  pihole container (Pi-hole on :53 → Technitium :5300)   │
 │  All zones are Primary                                   │
 │  Zone transfer allowed from 192.168.3.253               │
 └──────────────────────────────────────────────────────────┘
                │
          AXFR / IXFR + NOTIFY (DNS zone transfer, port 5300)
                │
                ▼
 ┌─ pfv-netinfra-02 (192.168.3.253) ─── SECONDARY ────────┐
 │  tsys-dns container (Technitium on :5300)                │
 │  pihole container (Pi-hole on :53 → Technitium :5300)   │
 │  All zones are Secondary (AXFR from 01)                  │
 └──────────────────────────────────────────────────────────┘

How clustering works

Technitium uses standard DNS zone transfers (AXFR/IXFR) for primary/secondary replication, not a proprietary protocol:

  1. Primary (01) holds all zones as authoritative primary zones.
  2. Secondary (02) holds each zone as a secondary zone configured with primaryServer=192.168.3.252:5300.
  3. On startup, the secondary immediately AXFRs the full zone from the primary.
  4. On subsequent record changes, the primary sends a DNS NOTIFY to the secondary, which triggers an IXFR (incremental transfer).
  5. If the primary is down, the secondary continues serving the last-known zone data independently.

Credentials and 2FA

The production auth.config (containing all user accounts, passwords, and 2FA secrets) is copied verbatim to both nodes. This means:

  • The same username, password, and 2FA device work on all three servers.
  • The web console is at http://<host>:5380/ on each node.
  • No credential changes are needed.

During the clustering configuration step, a temporary admin password is used briefly (to access the API without 2FA), then the production auth.config is restored. See "Security notes" below.

Prerequisites

  • SSH key access to all hosts as localuser with passwordless sudo.
  • The remote-dns.sh wrapper must be able to reach all hosts via Tailscale FQDN.
  • Docker + Docker Compose on netinfra-01/02 (already installed).
  • The production Technitium on tailscale-router must be running.

Usage

cd dns-cluster-setup/

# Step-by-step (recommended for first run):
./setup.sh export      # 1. Export config from tailscale-router (READ-ONLY)
./setup.sh deploy01    # 2. Deploy to netinfra-01 as primary
./setup.sh deploy02    # 3. Deploy to netinfra-02 as secondary clone
./setup.sh cluster     # 4. Configure clustering (01→02 zone transfers)
./setup.sh verify      # 5. Run all verification tests

# Or all at once:
./setup.sh all

Configuration overrides

All defaults can be overridden via environment variables:

Variable Default Description
PRIMARY_IP 192.168.3.252 netinfra-01 LAN IP
SECONDARY_IP 192.168.3.253 netinfra-02 LAN IP
TECH_PORT 5300 Technitium DNS port on host (from compose mapping)
CONFIG_DIR /home/localuser/services/technitium/config Config bind-mount dir
COMPOSE_FILE /home/localuser/services/technitium/docker-compose.yml Compose file
TEMP_ADMIN_PW KnelClusterSetup!2026 Temp admin password (used only during clustering, then discarded)

Scripts

Script Purpose
remote-dns.sh SSH/SCP chokepoint for all DNS host access (tsrouter, netinfra01, netinfra02, netboot, sandbox)
setup.sh Master orchestrator: export → deploy → cluster → verify
verify.sh Comprehensive 10-section verification suite
discover*.sh Read-only discovery probes (used during development, safe to keep)

What gets copied

From production /etc/dns/ (inside the container), excluding runtime data:

Copied (configuration) Excluded (runtime)
auth.config (users, passwords, 2FA) cache.bin (DNS cache)
dns.config (server settings) stats/ (query statistics)
webservice.config (web console) logs/ (log files)
allowed.config (zone transfer ACL)
blocked.config (blocked domains)
blocklist.config (blocklist settings)
blocklists/ (blocklist data)
zones/ (all 124 zone files)
scopes/ (DHCP scopes)
apps/ (Technitium apps)

Verification tests

The verify.sh script runs 10 categories of tests:

  1. Container health — both Technitium containers are Up
  2. API responds — web console API is reachable on both nodes
  3. Zone count — primary matches production; secondary matches primary
  4. Forward DNS — known knel.net records resolve identically on both nodes
  5. External DNS — both nodes can resolve external domains (github.com)
  6. Zone transfer (AXFR) — secondary can AXFR knel.net from primary
  7. Reverse DNS — PTR zones have SOA records on both nodes
  8. Production untouched — container still running, zone count unchanged
  9. Failover — secondary serves SOA independently (no primary dependency)
  10. Credentialsauth.config byte-size matches across all three nodes

Security notes

  • tailscale-router is never modified. The only operation is docker cp (read) to export the config. No writes, no restarts, no config changes.
  • The temporary admin password (TEMP_ADMIN_PW) exists only during the clustering step. After configuration, the production auth.config (with 2FA) is restored. The temp password is never persisted.
  • The export tarball (.export/technitium-production-config.tar.gz) contains production credentials. It is in .gitignore and should be deleted after setup: rm -rf dns-cluster-setup/.export/
  • Each node's existing config is backed up to config.backup-<timestamp> before replacement, so the change is reversible.

Recovery

If something goes wrong, each node has a backup:

# On netinfra-01 or netinfra-02:
cd /home/localuser/services/technitium/
docker compose down
mv config config.failed
mv config.backup-<timestamp> config
docker compose up -d

Validation on sandbox

After cluster setup, validate that client hosts use the pair correctly:

# From sectestbed-sandbox (or any client):
# Query primary directly:
dig @192.168.3.252 pfv-netinfra-01.knel.net

# Query secondary directly:
dig @192.168.3.253 pfv-netinfra-01.knel.net

# Both should return the same answer.

The KNELServerBuild provisioning code (provisioning/ConfigFiles/NTP/ntp.conf and provisioning/ConfigFiles/Resolv/resolv.conf) points clients at both servers for DNS and NTP redundancy. See docs/tailscale.md for the full DNS architecture analysis.