Skip to content

bind_primary

The hidden authoritative primary (home, 10.20.30.40) — internal, not in any NS record. Source of truth for the public zones; serves AXFR (TSIG edge-xfr) to the edge secondaries and answers RFC2136 ACME updates (TSIG acme) from the origin Caddy.

Config only. This role manages named.conf + the two TSIG key files. The zone source files and the DNSSEC keys (K*.{key,private,state}) are runtime state living in /var/named/dynamic — they are not in git (sensitive private keys + data). On a clean build the zone source files are created here and DNSSEC keys are generated by the dnssec-policy on first signing; on a rebuild, restore them from Proxmox/ZFS backup first, then run this role to configure named.

Built-in modules only. Applied by the bind primary play in site.yml (host bind).

Data/state boundary (same as the backends role)

In the role (config) On the host (state, restored from backup)
named.conf (zones, dnssec-policy, key-directory) dynamic/<zone> source files
/etc/named/edge-xfr.key, /etc/named/acme.key (from vault) dynamic/K*.{key,private,state} DNSSEC keys
/var/named/dynamic dir (perms/SELinux) dynamic/*.signed, *.jnl (named-generated)

Notes

  • The TSIG secrets come from vault (vault_bind_tsig_edge_xfr, vault_bind_tsig_acme, wired in group_vars/origin) and match the live key files — re-applying does not rotate them or break AXFR.
  • BIND on AlmaLinux 9 is 9.16.23-RH; dnssec-policy handles key generation and rollover, and the DS published at the registrar must match the active KSK.
  • named-checkconf validates before writing; the handler is rndc reconfig (no full restart, so no re-sign churn).

DNS policy enforcement (de-leak Tier-2 names)

The zones are runtime state (/var/named/dynamic, restored from backup), so a restore can reintroduce a public record for a name that should be internal-only (ha + jellyfin once leaked via CNAME->www). bind_primary_private_names (defaults) lists the Tier-2 names that must be NXDOMAIN in ${domain}; the dns-reconcile task dig-checks each on the local authoritative zone and nsupdate-deletes any that have a public record (using the bind_primary_update_key). It's idempotent (no-op when clean), runs on every converge, and is runnable alone after a restore: ansible-playbook … -l bind --tags dns-reconcile. To make a name public, remove it from bind_primary_private_names. (Public names stay in origin_caddy's vhost list — the two are the codified split-horizon policy.)