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 (names that must be NXDOMAIN)

The zones are runtime state (/var/named/dynamic, restored from backup), so a restore can reintroduce a public record that policy says must not exist (ha + jellyfin once leaked via CNAME->www). The dns-reconcile task dig-checks each listed name on the local authoritative zone and nsupdate-deletes any that has 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.

Two inputs, one loop — deliberately one reconciler, because a second one is a second thing that can silently stop. They are kept as separate lists because the reason differs, and the reason determines the fix:

list what it means how you "undo" an entry
bind_primary_private_names Tier-2 names, internal split-horizon only — reachable via apps-proxy/mgmt-proxy/WG, never the edge remove it from the list to make the name public
bind_primary_retired_names names this apex no longer serves because the service moved to another domain you don't — point people at the new name

Retired names must be actively deleted, not just left alone: the old name kept resolving to a box now serving a different apex, so every visit got a cert for a name the client never asked for — a TLS name-mismatch warning on a link anyone with an old invite still clicks. NXDOMAIN is the honest answer; a security warning is not. (First entry: meet, retired 2026-08-19 after the 2026-08-15 distributed-domain cutover.)

(Public names stay in origin_caddy's vhost list — the two are the codified split-horizon policy.)