Files
cnx-network-clan/machines/ns1/configuration.nix
T
Berwn 33ac7e106b Add VictoriaMetrics + Grafana DNS monitoring over the mesh
control runs VictoriaMetrics (loopback) and Grafana; every machine exports
node metrics and the nameservers export Knot stats (mod-stats + knot-exporter).
Scraping and the Grafana UI ride the ZeroTier mesh only, scoped by nftables to
the mesh /88; the public side stays closed by the Hetzner cloud firewall. The
provisioned DNS dashboard includes a per-zone SOA serial table to catch
primary/secondary drift. ZeroTier ULAs are centralised in mesh-hosts.nix.
2026-06-17 10:17:27 +07:00

89 lines
2.9 KiB
Nix

{ config, pkgs, ... }:
let
domains = import ../../modules/dns/domains.nix;
in
{
imports = [
../../modules/dns/authoritative.nix
../../modules/static-ipv6.nix
../../modules/monitoring/exporters.nix
];
clan.core.sops.defaultGroups = [ "admins" ];
# Public IPv6 (matches the ns1 AAAA glue); SLAAC doesn't bring it up here.
cnx.staticIPv6 = {
enable = true;
address = "2a01:4f8:c014:b5c5::1";
};
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.<zone>; 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.
services.knot.settings.policy = [
{
id = "cnx";
algorithm = "ecdsap256sha256";
}
];
# ns1 = primary (master): loads each zone from its file and serves it to ns2.
# zonefile-load = difference-no-serial lets us edit records without touching the
# SOA serial; Knot diffs the file, assigns a unixtime serial, signs the zone,
# then notifies ns2 and lets it pull the signed zone via AXFR/IXFR. unixtime is
# strictly monotonic per reload, so two zone versions can never share a serial
# (the failure mode dateserial's 2-digit daily counter allowed after a journal reset).
services.knot.settings.zone = map (d: {
domain = d;
file = ../../modules/dns/zones + "/${d}.zone";
"zonefile-load" = "difference-no-serial";
"zonefile-sync" = "-1";
"journal-content" = "all"; # required by difference-no-serial; holds the live signed zone
"serial-policy" = "unixtime";
"dnssec-signing" = true;
"dnssec-policy" = "cnx";
notify = [ "ns2" ];
acl = [
"acl_ns2"
"acl_acme"
]; # ns2 transfers; acme_ddns key does DNS-01 updates
}) domains;
}