{ config, lib }: rec { ns = "dns.serv.zentralwerk.org"; internalNS = [ ns ]; # public servers (slaves) publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" "ns1.supersandro.de" ]; publicIPv4 = config.site.hosts.upstream4.interfaces.up4-pppoe.upstream.staticIpv4Address; dynamicReverseZones4 = [ "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" "22.10.in-addr.arpa" ]; dynamicReverseZones6 = [ "2.0.0.0.c.2.0.8.1.8.0.0.a.2.ip6.arpa" "4.1.b.a.c.a.2.8.3.5.f.0.a.2.ip6.arpa" "5.0.2.d.3.c.2.4.0.0.3.2.d.f.ip6.arpa" ]; mapI = start: end: f: if start >= end then [] else [ (f start) ] ++ mapI (start + 1) end f; isRfc1918Reverse = reverse: builtins.any (suffix: lib.hasSuffix suffix reverse) ([ "10.in-addr.arpa" "168.192.in-addr.arpa" ] ++ mapI 0 32 (i: "${toString (16 + i)}.172.in-addr.arpa" )); localZones = let # ip6.arpa aggregation size in CIDR bits reverseZone6Size = 56; 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) ) ++ dynamicReverseZones4 ); # 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 "${net}.zentralwerk.dn42" else namedNets.${net}.domainName; 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 = "@"; type = "A"; data = publicIPv4; } { name = "www"; type = "A"; data = publicIPv4; } { name = "@"; type = "AAAA"; data = config.site.net.serv.hosts6.up4.network-homepage; } { name = "www"; type = "AAAA"; data = config.site.net.serv.hosts6.up4.network-homepage; } ]; } { name = "zentralwerk.dn42"; ns = internalNS; records = [ ]; } { name = "dyn.zentralwerk.org"; ns = publicNS; records = [ { name = "upstream4"; type = "A"; data = publicIPv4; } ]; } ] ++ builtins.concatLists ( builtins.attrValues ( builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, extraRecords, ... }: [ { name = "${net}.zentralwerk.dn42"; ns = internalNS; records = lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42) ++ extraRecords; } { name = "${net}.zentralwerk.org"; ns = publicNS; records = hosts4Records hosts4 ++ lib.optionals (hosts6 ? up4) (hosts6Records hosts6.up4) ++ lib.optionals (hosts6 ? flpk) (hosts6Records hosts6.flpk) ++ extraRecords; dynamic = dynamicDomain; } ]) namedNets ) ) ++ map (zone: { name = zone; ns = if isRfc1918Reverse zone then internalNS else publicNS; 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 dynamicReverseZones4; }) reverseZones4 ++ builtins.concatMap (ctx: map (zone: { name = zone; ns = if ctx == "dn42" then internalNS else publicNS; 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}) ); dynamic = builtins.elem zone dynamicReverseZones6; }) reverseZones6.${ctx} ) (builtins.attrNames reverseZones6); }