Files
cnx-network-clan/docs/src/dns.md
T
Berwn b8bea27a9c Update runbook docs for web01 reverse proxy and per-host ACME keys
Reflect web01 in the machines table and monitoring scrape list, note Grafana is
now also published publicly via web01's reverse proxy, add the CNX Uptime
dashboard, and document the dedicated acme_mx1/acme_web01 DNS-01 keys.
2026-06-21 03:17:51 +07:00

3.8 KiB

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 <domain>.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). 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 <zone> 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 <zone>.

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.<zone> 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 <zone>

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.