diff --git a/machines/ns1/configuration.nix b/machines/ns1/configuration.nix index 37cec57..722d0f4 100644 --- a/machines/ns1/configuration.nix +++ b/machines/ns1/configuration.nix @@ -1,4 +1,4 @@ -{ ... }: +{ config, pkgs, ... }: let domains = import ../../modules/dns/domains.nix; in @@ -10,6 +10,40 @@ in time.timeZone = "Etc/GMT-1"; # UTC+1 (fixed offset, no DST) services.timesyncd.enable = true; + # ACME DNS-01 (RFC 2136): a dedicated TSIG key, scoped to ns1 only, that an + # external ACME client uses to write _acme-challenge TXT records. acl_acme + # (referenced by each zone below) limits the key to TXT updates at or under + # _acme-challenge.; Knot then signs the record and transfers it to ns2, + # which never needs this key. Retrieve the secret for the client with: + # clan vars get ns1 dns-acme-tsig/acme.conf + clan.core.vars.generators.dns-acme-tsig = { + files."acme.conf" = { + secret = true; + owner = "knot"; + group = "knot"; + }; + runtimeInputs = [ pkgs.knot-dns ]; + script = '' + keymgr -t acme_ddns hmac-sha256 > "$out"/acme.conf + ''; + }; + + services.knot.keyFiles = [ + config.clan.core.vars.generators.dns-acme-tsig.files."acme.conf".path + ]; + + services.knot.settings.acl = [ + { + id = "acl_acme"; + key = "acme_ddns"; + action = [ "update" ]; + "update-type" = [ "TXT" ]; + "update-owner" = "name"; + "update-owner-match" = "sub-or-equal"; + "update-owner-name" = [ "_acme-challenge" ]; + } + ]; + # Automatic DNSSEC signing policy (primary only). ECDSA P-256/SHA-256 with # Knot's default key management: the ZSK auto-rolls and the KSK is kept stable, # so the DS at the registrar only changes on a manual KSK rollover. @@ -34,6 +68,6 @@ in "dnssec-signing" = true; "dnssec-policy" = "cnx"; notify = [ "ns2" ]; - acl = [ "acl_ns2" ]; + acl = [ "acl_ns2" "acl_acme" ]; # ns2 transfers; acme_ddns key does DNS-01 updates }) domains; }