{ hostName, config, lib, pkgs, self, inputs, ... }: let serial = let timestamp = toString self.lastModified; datePkg = pkgs.runCommandLocal "date-${timestamp}" {} '' date -d @${timestamp} +%Y%m%d%H > $out ''; in toString (import datePkg); generateZoneFile = { name, ns, records, dynamic }: builtins.toFile "${name}.zone" '' $ORIGIN ${name}. $TTL 1h @ IN SOA ${lib.dns.ns}. astro.spaceboyz.net. ( ${serial} ; serial 1h ; refresh 1m ; retry 2h ; expire 1m ; minimum ) ${lib.concatMapStrings (ns: " IN NS ${ns}.\n") ns} ${lib.concatMapStrings ({ name, type, data }: "${name} IN ${type} ${data}\n" ) records} ''; in { options = with lib; let recordOpts = { name = mkOption { description = "DNS label"; type = types.str; }; type = mkOption { type = types.enum [ "A" "AAAA" "PTR" ]; }; data = mkOption { type = types.str; }; }; zoneOpts = { name = mkOption { description = "DNS FQDN w/o trailing dot"; type = types.str; }; ns = mkOption { type = with types; listOf str; }; records = mkOption { type = with types; listOf (submodule { options = recordOpts; }); }; dynamic = mkOption { type = types.bool; default = false; }; }; in { site.dns.localZones = mkOption { type = with types; listOf (submodule { options = zoneOpts; }); }; }; config = { site.dns.localZones = lib.dns.localZones; services.bind = lib.mkIf config.site.hosts.${hostName}.services.dns.enable ( let generateZone = zone@{ name, dynamic, ... }: { inherit name; master = true; # allowed for zone-transfer slaves = [ # ns.c3d2.de "217.197.84.53" "2001:67c:1400:2240::a" # ns.spaceboyz.net "172.22.24.4" "2a01:4f9:4b:39ec::4" ]; file = if dynamic then "/var/db/bind/${name}.zone" else generateZoneFile zone; extraConfig = '' also-notify { # ns.c3d2.de 217.197.84.53; 2001:67c:1400:2240::a; # ns.spaceboyz.net 95.217.229.209; 2a01:4f9:4b:39ec::4; }; notify-source ${config.site.net.serv.hosts4.dns}; notify-source-v6 ${config.site.net.serv.hosts6.up1.dns}; '' + lib.optionalString dynamic '' allow-update { key "dyndns"; }; ''; }; in { enable = true; zones = map generateZone config.site.dns.localZones; extraConfig = '' key "dyndns" { algorithm hmac-sha256; secret "${inputs.zentralwerk-network-key.lib.dyndnsKey}"; }; ''; extraOptions = '' # allow underscores in dynamic hostnames ${lib.concatMapStringsSep "\n" (type: '' check-names ${type} ignore; '') [ "master" "slave" "response" ]} ''; }); systemd.services.create-dynamic-zones = { description = "Creates dynamic zone files"; requiredBy = [ "bind.service" ]; before = [ "bind.service" ]; serviceConfig.Type = "oneshot"; script = '' mkdir -p /var/db/bind ${lib.concatMapStringsSep "\n" (zone@{ name, ... }: '' [ -e /var/db/bind/${name}.zone ] || \ cp ${generateZoneFile zone} /var/db/bind/${name}.zone chown -R named /var/db/bind chmod -R u+rwX /var/db/bind '') ( builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones )} ''; }; systemd.services.update-dynamic-zones = { description = "Creates initial records in dynamic zone files"; requiredBy = [ "bind.service" ]; after = [ "bind.service" ]; serviceConfig.Type = "oneshot"; path = [ pkgs.dnsutils ]; script = '' ${lib.concatMapStrings (zone: '' nsupdate -y "hmac-sha256:dyndns:${inputs.zentralwerk-network-key.lib.dyndnsKey}" <