From 4d41e241b328379bd20c00f7ff0daf8af6f7674e Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 6 May 2021 16:24:31 +0200 Subject: [PATCH] lib/dns: refactor localZones --- nix/lib/default.nix | 4 +- nix/lib/dns.nix | 227 +++++++++++++++++++++++++++++ nix/nixos-module/container/dns.nix | 222 +--------------------------- 3 files changed, 232 insertions(+), 221 deletions(-) create mode 100644 nix/lib/dns.nix diff --git a/nix/lib/default.nix b/nix/lib/default.nix index e3a0ac3..8ad930a 100644 --- a/nix/lib/default.nix +++ b/nix/lib/default.nix @@ -1,6 +1,6 @@ { self, gpgKey, pkgs }: -{ +rec { config = import ./config { inherit self pkgs gpgKey; }; saltPillarFor = import ./salt-support/salt-pillar.nix { @@ -12,4 +12,6 @@ netmasks = import ./netmasks.nix; subnet = import ./subnet { inherit pkgs; }; + + dns = import ./dns.nix { inherit pkgs config; }; } diff --git a/nix/lib/dns.nix b/nix/lib/dns.nix new file mode 100644 index 000000000..9ae3de7 --- /dev/null +++ b/nix/lib/dns.nix @@ -0,0 +1,227 @@ +{ 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 = [ + "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" + ]; + + 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 = builtins.attrNames ( + builtins.foldl' (result: rname: + let + zone = builtins.head ( + builtins.match "[[:digit:]]+\\.(.+)" rname + ); + in result // { + "${zone}" = true; + } + ) {} (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 [ { + 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 ? up1) (hosts6Records hosts6.up1) ++ + lib.optionals (hosts6 ? up2) (hosts6Records hosts6.up2); + } + ]) 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); +} diff --git a/nix/nixos-module/container/dns.nix b/nix/nixos-module/container/dns.nix index 3fa2586..0466fda 100644 --- a/nix/nixos-module/container/dns.nix +++ b/nix/nixos-module/container/dns.nix @@ -1,18 +1,6 @@ { hostName, config, lib, pkgs, self, inputs, ... }: let - fqdn = "${hostName}.serv.zentralwerk.org"; - # public servers (slaves) - publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" ]; - dynamicReverseZones = [ - "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" - ]; - serial = let timestamp = toString self.lastModified; @@ -27,7 +15,7 @@ let $ORIGIN ${name}. $TTL 1h - @ IN SOA ${fqdn}. astro.spaceboyz.net. ( + @ IN SOA ${lib.dns.ns}. astro.spaceboyz.net. ( ${serial} ; serial 1h ; refresh 1m ; retry @@ -86,213 +74,7 @@ in }; config = { - site.dns.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 = builtins.attrNames ( - builtins.foldl' (result: rname: - let - zone = builtins.head ( - builtins.match "[[:digit:]]+\\.(.+)" rname - ); - in result // { - "${zone}" = true; - } - ) {} (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 [ { - name = "zentralwerk.org"; - ns = publicNS; - records = []; - } { - name = "zentralwerk.dn42"; - ns = [ fqdn ]; - records = [ { - name = "ipa"; - type = "A"; - data = config.site.net.serv.hosts4.ipa; - } ]; - } { - name = "dyn.zentralwerk.org"; - ns = publicNS; - # TODO: implement dyndns - 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 = [ fqdn ]; - records = - hosts4Records hosts4 ++ - lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42); - dynamic = dynamicDomain; - } - { - name = "${net}.zentralwerk.org"; - ns = publicNS; - records = - lib.optionals (hosts6 ? up1) (hosts6Records hosts6.up1) ++ - lib.optionals (hosts6 ? up2) (hosts6Records hosts6.up2); - } - ]) namedNets - ) - ) ++ map (zone: { - 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) - ); - dynamic = builtins.elem zone dynamicReverseZones; - }) reverseZones4 - ++ builtins.concatMap (ctx: - map (zone: { - name = zone; - 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.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); + site.dns.localZones = lib.dns.localZones; services.bind = lib.mkIf config.site.hosts.${hostName}.services.dns.enable ( let