Initial attempt at reverse zones

This commit is contained in:
niten 2023-11-01 10:13:33 -07:00
parent d3272846b4
commit 793be43cc2
2 changed files with 91 additions and 11 deletions

View File

@ -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;
};
};
}

61
reverse-zone.nix Normal file
View File

@ -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)}
''