From 793be43cc26d8956496228735be4da2db6d32a92 Mon Sep 17 00:00:00 2001 From: niten Date: Wed, 1 Nov 2023 10:13:33 -0700 Subject: [PATCH] Initial attempt at reverse zones --- authoritative-dns.nix | 41 +++++++++++++++++++++-------- reverse-zone.nix | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 reverse-zone.nix diff --git a/authoritative-dns.nix b/authoritative-dns.nix index e42d986..ef7ec08 100644 --- a/authoritative-dns.nix +++ b/authoritative-dns.nix @@ -8,6 +8,8 @@ let zoneToZonefile = import ./zone-to-zonefile.nix { inherit lib; }; + reverseZonefile = import ./reverse-zone.nix { inherit pkgs; }; + domainOpts = { name, ... }: { options = with types; { domain = mkOption { @@ -29,6 +31,13 @@ let type = submodule zoneOpts; description = "Definition of network zone to be served."; }; + + reverse-zones = mkOption { + type = listOf str; + description = + "List of subnets for which to generate reverse lookup zones."; + default = [ ]; + }; }; }; @@ -74,17 +83,27 @@ in { identity = cfg.identity; interfaces = cfg.listen-ips; stateDirectory = cfg.state-directory; - zones = mapAttrs' (domain: domainCfg: - nameValuePair "${domain}." { - dnssec = domainCfg.ksk.key-file != null; - ksk.keyFile = - mkIf (domainCfg.ksk.key-file != null) domainCfg.ksk.key-file; - data = zoneToZonefile { - inherit domain; - inherit (cfg) timestamp; - inherit (domainCfg) zone; - }; - }) cfg.domains; + zones = let + forwardZones = mapAttrs' (domain: domainCfg: + nameValuePair "${domain}." { + dnssec = domainCfg.ksk.key-file != null; + ksk.keyFile = + mkIf (domainCfg.ksk.key-file != null) domainCfg.ksk.key-file; + data = zoneToZonefile { + inherit domain; + inherit (cfg) timestamp; + inherit (domainCfg) zone; + }; + }) cfg.domains; + reverseZones = concatMapAttrs (domain: domainOpts: + genAttrs domainOpts.reverse-zones (network: + reverseZonefile { + inherit domain network; + inherit (domainOpts.zone) nameservers; + ipHostMap = cfg.ip-host-map; + serial = cfg.timestamp; + })) cfg.domains; + in forwardZones // reverseZones; }; }; } diff --git a/reverse-zone.nix b/reverse-zone.nix new file mode 100644 index 0000000..00a3dba --- /dev/null +++ b/reverse-zone.nix @@ -0,0 +1,61 @@ +{ pkgs, ... }: + +{ domain, network, nameservers, ipHostMap, serial, zoneTTL ? 10800 +, refresh ? 3600, retry ? 1800, expire ? 604800, minimum ? 3600 }: + +with pkgs.lib; +let + range = base: top: + assert base < top; + let + rangeFun = base: top: + if base == top then [ ] else [ base ] ++ (rangeFun (base + 1) top); + in rangeFun base top; + + getNetworkHosts = network: + selectAttrs (ip: _: pkgs.lib.ip.ipv4OnNetwork ip network); + + getLastIpComponent = ip: head (reverseList (splitString "." ip)); + + getNetworkZoneName = network: + let + netElems = splitString "/" network; + netIp = elemAt netElems 0; + netMask = elemAt netElems 1; + reversedNetIp = + concatStringsSep "." (tail (reverseList (splitString "." netIp))); + in if netMask == "24" then + "${reversedNetIp}.in-addr.arpa." + else + "${getLastIpComponent netIp}-${netMask}.${reversedNetIp}.in-addr.arpa."; + + generateReverseZoneEntries = network: domain: ipHostMap: + let + networkHostsByComponent = + mapAttrs' (ip: hostname: nameValuePair (getLastIpComponent ip) hostname) + (networkHosts network ipHostMap); + ptrEntry = ip: hostname: "#{ip} IN PTR ${hostname}."; + getHostname = n: + if hasAttr networkHostsByComponent n then + networkHostByComponent n + else + "unassigned-${n}.${domain}"; + minIp = toInt (getLastIpComponent (networkMinIp network)); + maxIp = toInt (getLastIpComponent (networkMaxButOneIp network)); + in map (n: ptrEntry n (getHostname (toString n))) (range minIp maxIp); + + nameserverEntries = map (nameserver: "@ IN NS ${nameserver}.") nameservers; + +in nameValuePair "${getNetworkZoneName network}" '' + $ORIGIN ${getNetworkZoneName network} + $TTL ${toString zoneTTL} + @ IN SOA ${head nameservers}. hostmaster.${domain}. ( + ${serial} + ${toString refresh} + ${toString retry} + ${toString expire} + ${toString minimum} + ) + ${concatStringsSep "\n" nameserverEntries} + ${concatStringsSep "\n" (generateReverseZoneEntries network domain ipHostMap)} +''