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 ingroup_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-policyhandles key generation and rollover, and the DS published at the registrar must match the active KSK. named-checkconfvalidates before writing; the handler isrndc 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.)