{ pkgs, config }: let lib = pkgs.lib; in rec { ns = "dns.serv.zentralwerk.org"; internalNS = [ ns ]; # public servers (slaves) publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" ]; dynamicReverseZones = [ "73.20.172.in-addr.arpa" "74.20.172.in-addr.arpa" "75.20.172.in-addr.arpa" "76.20.172.in-addr.arpa" "77.20.172.in-addr.arpa" "78.20.172.in-addr.arpa" "79.20.172.in-addr.arpa" "99.22.172.in-addr.arpa" ]; localZones = let # ip6.arpa aggregation size in CIDR bits reverseZone6Size = 60; hosts4Records = hosts4: builtins.attrValues ( builtins.mapAttrs (name: addr: { inherit name; type = "A"; data = addr; }) hosts4 ); hosts6Records = hosts6: builtins.attrValues ( builtins.mapAttrs (name: addr: { inherit name; type = "AAAA"; data = addr; }) hosts6 ); # generate zones only for nets with hosts namedNets = lib.filterAttrs (name: { hosts4, hosts6, dynamicDomain, ... }: (hosts4 != [] && hosts6 != []) || dynamicDomain ) config.site.net; # converts an IPv4 address to its reverse DNS form ipv4ToReverse = ipv4: builtins.concatStringsSep "." ( lib.reverseList ( builtins.filter builtins.isString ( builtins.split "\\." ipv4 ) ) ) + ".in-addr.arpa"; # `{ "1,0.0.127.in-addr.arpa" = "lo.core.zentralwerk.dn42"; }` reverseHosts4 = builtins.foldl' (result: { hosts4, domainName, ... }: builtins.foldl' (result: host: result // { "${ipv4ToReverse hosts4.${host}}" = "${host}.${domainName}"; }) result (builtins.attrNames hosts4) ) {} (builtins.attrValues namedNets); # `[ "0.0.127.in-addr.arpa" ]` reverseZones4 = lib.unique ( builtins.attrNames ( builtins.foldl' (result: rname: let zone = builtins.head ( builtins.match "[[:digit:]]+\\.(.+)" rname ); in result // { "${zone}" = true; } ) {} (builtins.attrNames reverseHosts4) ) ++ dynamicReverseZones ); # turns `::` into `0000:0000:0000:0000:0000:0000:0000:0000` expandIpv6 = ipv6: if lib.hasPrefix "::" ipv6 then expandIpv6 "0${ipv6}" else if lib.hasSuffix "::" ipv6 then expandIpv6 "${ipv6}0" else let words = builtins.filter builtins.isString ( builtins.split ":" ipv6 ); fillWordCount = 8 - builtins.length words; fillWords = n: if n >= 0 then [ "0000" ] ++ fillWords (n - 1) else []; words' = builtins.concatMap (word: if word == "" then fillWords fillWordCount else [ word ] ) words; leftPad = padding: target: s: if builtins.stringLength s < target then leftPad padding target "${padding}${s}" else s; words'' = map (leftPad "0" 4) words'; in builtins.concatStringsSep ":" words''; # turns `::1` into `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa` ipv6ToReverse = ipv6: builtins.concatStringsSep "." ( lib.reverseList ( lib.stringToCharacters ( builtins.replaceStrings [":"] [""] (expandIpv6 ipv6) ) ) ) + ".ip6.arpa"; # `{ dn42 = { "...ip6.arpa" = "lo.core.zentralwerk.dn42"; }; }` reverseHosts6 = builtins.foldl' (result: net: lib.recursiveUpdate result ( builtins.mapAttrs (ctx: hosts: builtins.foldl' (result: host: let domain = if ctx == "dn42" then namedNets.${net}.domainName else if builtins.match "up.*" ctx != null then "${net}.zentralwerk.org" else throw "Invalid IPv6 context: ${ctx}"; in lib.recursiveUpdate result { "${ipv6ToReverse hosts.${host}}" = "${host}.${domain}"; } ) {} (builtins.attrNames hosts) ) namedNets.${net}.hosts6 )) {} (builtins.attrNames namedNets); # `{ dn42 = [ "....ip6.arpa" ]; }` reverseZones6 = builtins.mapAttrs (ctx: reverseHosts6ctx: builtins.attrNames ( builtins.foldl' (result: rname: result // { "${builtins.substring ((128 - reverseZone6Size) / 2) (72 - ((128 - reverseZone6Size) / 2)) rname}" = true; }) {} (builtins.attrNames reverseHosts6ctx) ) ) reverseHosts6; in [ { name = "zentralwerk.org"; ns = publicNS; records = []; } { name = "zentralwerk.dn42"; ns = internalNS; records = [ { name = "ipa"; type = "A"; data = config.site.net.serv.hosts4.ipa; } ]; } { name = "dyn.zentralwerk.org"; ns = publicNS; records = [ { name = "upstream1"; type = "A"; data = "24.134.104.53"; } { name = "upstream2"; type = "A"; data = "24.134.252.105"; } ]; } ] ++ builtins.concatLists ( builtins.attrValues ( builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, ... }: [ { name = "${net}.zentralwerk.dn42"; ns = internalNS; records = hosts4Records hosts4 ++ lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42); dynamic = dynamicDomain; } { name = "${net}.zentralwerk.org"; ns = publicNS; records = lib.optionals (hosts6 ? up4) (hosts6Records hosts6.up4); } ]) namedNets ) ) ++ map (zone: { name = zone; ns = internalNS; records = map (reverse: { name = builtins.head ( builtins.match "([[:digit:]]+)\\..*" reverse ); type = "PTR"; data = "${reverseHosts4.${reverse}}."; }) ( builtins.filter (lib.hasSuffix ".${zone}") (builtins.attrNames reverseHosts4) ); dynamic = builtins.elem zone dynamicReverseZones; }) reverseZones4 ++ builtins.concatMap (ctx: map (zone: { name = zone; ns = if ctx == "dn42" then internalNS else if builtins.match "up.*" ctx != null then publicNS else throw "Invalid IPv6 context: ${ctx}"; records = map (reverse: { name = builtins.substring 0 ((128 - reverseZone6Size) / 2 - 1) reverse; type = "PTR"; data = "${reverseHosts6.${ctx}.${reverse}}."; }) ( builtins.filter (lib.hasSuffix ".${zone}") (builtins.attrNames reverseHosts6.${ctx}) ); }) reverseZones6.${ctx} ) (builtins.attrNames reverseZones6); }