# DNS Authoritative DNS for three zones, served by Knot: - `cnx.network` - `buildfor.life` - `cnx.email` Add a zone in `modules/dns/domains.nix` **and** drop a matching `.zone` file in `modules/dns/zones/`. ## Primary / secondary - **`ns1` = primary (master).** Loads each zone from its file, signs it, and notifies `ns2`. Config in `machines/ns1/configuration.nix`. - **`ns2` = secondary (slave).** Pulls every zone from `ns1` (AXFR/IXFR) and accepts its NOTIFY. Config in `machines/ns2/configuration.nix`. Zone transfers run **over the ZeroTier mesh**, authenticated with a shared TSIG key (`dns-tsig`, a clan var copied to both machines). ## Serial handling `ns1` uses `zonefile-load = difference-no-serial` with `serial-policy = unixtime`: edit records without touching the SOA serial — Knot diffs the file, assigns a strictly-monotonic unixtime serial, signs, and transfers. `journal-content = all` holds the live signed zone (required by `difference-no-serial`). ## DNSSEC Automatic signing on `ns1` only, policy `cnx`: ECDSA P-256/SHA-256. The ZSK auto-rolls; the KSK is kept stable, so the DS at the registrar only changes on a manual KSK rollover. ### Registrar DS records Knot manages all key material itself on `ns1` (the only signer); the KSK/ZSK private keys live in the KASP keystore under `/var/lib/knot` (backed up nightly — see [Backups](./backups.md)). You never touch the private keys directly. What a registrar needs is the **DS record** for a zone's KSK, which anchors the zone into the parent's chain of trust. Generate it on `ns1` — the `keymgr` wrapper is already pointed at Knot's config, and it runs as the `knot` user that owns the keystore: ``` sudo -u knot keymgr ds ``` e.g. `sudo -u knot keymgr cnx.email ds`. Paste the printed DS record (key tag, algorithm 13, digest type, digest) into the registrar's DNSSEC form for that domain. Repeat per signed zone (`cnx.network`, `buildfor.life`, `cnx.email`) at whichever registrar holds each delegation. After submitting, confirm the parent publishes it with `dig +short DS `. The ZSK rolls automatically and needs no registrar action; only a **KSK rollover** requires re-submitting the DS. > **Pending (manual):** submit DS records for `buildfor.life` and `cnx.email` > once they're at a DNSSEC-capable registrar. ## ACME DNS-01 Certificates are issued by `_acme-challenge` TXT updates that `ns1` accepts over TSIG, signs, and transfers to `ns2` (which never needs these keys). Each consumer gets its **own** key, scoped by an ACL to exactly the owner names it needs and attached only to the zone it lives in — so a leaked key can write nothing but its own challenges. - **`acme_ddns`** (`acl_acme`) — the general key, scoped to `TXT` at or under `_acme-challenge.` and attached to every zone. Client config: ``` clan vars get ns1 dns-acme-tsig/acme.conf ``` - **`acme_mx1`** (`acl_acme_mx1`) — held only by `mx1`, scoped to `_acme-challenge.{mx1,mta-sts,mail}` and attached only to `cnx.email` (the mail cert plus its MTA-STS and client-alias SANs). Secret shared via the `dns-acme-mx1-secret` generator. - **`acme_web01`** (`acl_acme_web01`) — held only by `web01`, scoped to `_acme-challenge` and attached only to `cnx.network` (where the wildcard `*.cnx.network` challenge lands, at the apex). Secret shared via the `dns-acme-web01-secret` generator. ## Runbook: stale secondary If `ns2` serves stale records while SOA serials match (e.g. after a manual zone edit that didn't bump the serial as expected), force a fresh transfer on `ns2`: ``` knotc zone-retransfer ``` Watch the **CNX DNS** Grafana dashboard: the per-nameserver SOA serial table should agree across `ns1`/`ns2`, and "seconds until zone expiry" on the secondary should reset on each successful transfer rather than counting toward zero.