diff --git a/nix/nixos-module/container/dns.nix b/nix/nixos-module/container/dns.nix index a568f82..776d991 100644 --- a/nix/nixos-module/container/dns.nix +++ b/nix/nixos-module/container/dns.nix @@ -13,6 +13,8 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable { # ns.spaceboyz.net "172.22.24.4" "2a01:4f9:4b:39ec::4" ]; + # ip6.arpa aggregation size in CIDR bits + reverseZone6Size = 60; serial = let @@ -97,6 +99,75 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable { } ) {} (builtins.attrNames reverseHosts4) ); + + # 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 { enable = true; zones = [ (staticZone { @@ -145,24 +216,42 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable { }) ]) namedNets ) - ) ++ map (zone: - staticZone { + ) ++ map (zone: staticZone { + name = zone; + ns = [ fqdn ]; + records = + map (reverse: { + name = builtins.head ( + builtins.match "([[:digit:]]+)\\..*" reverse + ); + type = "PTR"; + data = "${reverseHosts4.${reverse}}."; + }) ( + builtins.filter (lib.hasSuffix ".${zone}") + (builtins.attrNames reverseHosts4) + ); + }) reverseZones4 + ++ builtins.concatMap (ctx: + map (zone: staticZone { name = zone; - ns = [ fqdn ]; + ns = + if ctx == "dn42" + then [ fqdn ] + else if builtins.match "up.*" ctx != null + then publicNS + else throw "Invalid IPv6 context: ${ctx}"; records = map (reverse: { - name = builtins.head ( - builtins.match "([[:digit:]]+)\\..*" reverse - ); + name = builtins.substring 0 ((128 - reverseZone6Size) / 2 - 1) reverse; type = "PTR"; - data = reverseHosts4.${reverse}; + data = "${reverseHosts6.${ctx}.${reverse}}."; }) ( builtins.filter (lib.hasSuffix ".${zone}") - (builtins.attrNames reverseHosts4) + (builtins.attrNames reverseHosts6.${ctx}) ); - } - ) reverseZones4; + }) reverseZones6.${ctx} + ) (builtins.attrNames reverseZones6); }; - # TODO: zentralwerk.{org,dn42}, reverse, dyn, ipa.zentralwerk.dn42 + # TODO: dyn }