forked from zentralwerk/network
nixos-module/container/dns: factor zones out into config.site.dns.localZones
This commit is contained in:
parent
20c8821823
commit
b800691dad
|
@ -1,180 +1,182 @@
|
||||||
{ hostName, config, lib, pkgs, self, ... }:
|
{ hostName, config, lib, pkgs, self, ... }:
|
||||||
|
|
||||||
lib.mkIf config.site.hosts.${hostName}.services.dns.enable {
|
let
|
||||||
services.bind =
|
fqdn = "${hostName}.serv.zentralwerk.org";
|
||||||
|
# public servers (slaves)
|
||||||
|
publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options =
|
||||||
|
with lib;
|
||||||
let
|
let
|
||||||
fqdn = "${hostName}.serv.zentralwerk.org";
|
recordOpts = {
|
||||||
# public servers (slaves)
|
name = mkOption {
|
||||||
publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" ];
|
description = "DNS label";
|
||||||
# allowed for zone-transfer
|
type = types.str;
|
||||||
slaves = [
|
};
|
||||||
# ns.c3d2.de
|
type = mkOption {
|
||||||
"217.197.84.53" "2001:67c:1400:2240::a"
|
type = types.enum [ "A" "AAAA" "PTR" ];
|
||||||
# ns.spaceboyz.net
|
};
|
||||||
"172.22.24.4" "2a01:4f9:4b:39ec::4"
|
data = mkOption {
|
||||||
];
|
type = types.str;
|
||||||
# ip6.arpa aggregation size in CIDR bits
|
};
|
||||||
reverseZone6Size = 60;
|
|
||||||
|
|
||||||
serial =
|
|
||||||
let
|
|
||||||
timestamp = toString self.lastModified;
|
|
||||||
datePkg = pkgs.runCommandLocal "date-${timestamp}" {} ''
|
|
||||||
date -d @${timestamp} +%Y%m%d%H > $out
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
toString (import datePkg);
|
|
||||||
|
|
||||||
staticZone = { name, ns, records }: {
|
|
||||||
inherit name;
|
|
||||||
master = true;
|
|
||||||
file = builtins.toFile "${name}.zone" ''
|
|
||||||
$ORIGIN ${name}.
|
|
||||||
$TTL 1h
|
|
||||||
|
|
||||||
@ IN SOA ${fqdn}. astro.spaceboyz.net. (
|
|
||||||
${serial} ; serial
|
|
||||||
1h ; refresh
|
|
||||||
1m ; retry
|
|
||||||
2h ; expire
|
|
||||||
1m ; minimum
|
|
||||||
)
|
|
||||||
${lib.concatMapStrings (ns: " IN NS ${ns}.\n") ns}
|
|
||||||
|
|
||||||
${lib.concatMapStrings ({ name, type, data }:
|
|
||||||
"${name} IN ${type} ${data}\n"
|
|
||||||
) records}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hosts4Records = hosts4:
|
zoneOpts = {
|
||||||
builtins.attrValues (
|
name = mkOption {
|
||||||
builtins.mapAttrs (name: addr: {
|
description = "DNS FQDN w/o trailing dot";
|
||||||
inherit name;
|
type = types.str;
|
||||||
type = "A";
|
};
|
||||||
data = addr;
|
ns = mkOption {
|
||||||
}) hosts4
|
type = with types; listOf str;
|
||||||
);
|
};
|
||||||
hosts6Records = hosts6:
|
records = mkOption {
|
||||||
builtins.attrValues (
|
type = with types; listOf (submodule {
|
||||||
builtins.mapAttrs (name: addr: {
|
options = recordOpts;
|
||||||
inherit name;
|
});
|
||||||
type = "AAAA";
|
};
|
||||||
data = addr;
|
};
|
||||||
}) hosts6
|
|
||||||
);
|
|
||||||
|
|
||||||
# generate zones only for nets with hosts
|
|
||||||
namedNets = lib.filterAttrs (_: { 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 {
|
in {
|
||||||
enable = true;
|
site.dns.localZones = mkOption {
|
||||||
zones = [ (staticZone {
|
type = with types; listOf (submodule {
|
||||||
|
options = zoneOpts;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (_: { 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";
|
name = "zentralwerk.org";
|
||||||
ns = publicNS;
|
ns = publicNS;
|
||||||
records = [];
|
records = [];
|
||||||
}) (staticZone {
|
} {
|
||||||
name = "zentralwerk.dn42";
|
name = "zentralwerk.dn42";
|
||||||
ns = [ fqdn ];
|
ns = [ fqdn ];
|
||||||
records = [ {
|
records = [ {
|
||||||
|
@ -182,7 +184,7 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable {
|
||||||
type = "A";
|
type = "A";
|
||||||
data = config.site.net.serv.hosts4.ipa;
|
data = config.site.net.serv.hosts4.ipa;
|
||||||
} ];
|
} ];
|
||||||
}) (staticZone {
|
} {
|
||||||
name = "dyn.zentralwerk.org";
|
name = "dyn.zentralwerk.org";
|
||||||
ns = publicNS;
|
ns = publicNS;
|
||||||
# TODO: implement dyndns
|
# TODO: implement dyndns
|
||||||
|
@ -195,28 +197,28 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable {
|
||||||
type = "A";
|
type = "A";
|
||||||
data = "24.134.252.105";
|
data = "24.134.252.105";
|
||||||
} ];
|
} ];
|
||||||
}) ] ++ builtins.concatLists (
|
} ] ++ builtins.concatLists (
|
||||||
builtins.attrValues (
|
builtins.attrValues (
|
||||||
builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, ... }: [
|
builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, ... }: [
|
||||||
(if dynamicDomain
|
(if dynamicDomain
|
||||||
then throw "TODO"
|
then throw "TODO"
|
||||||
else staticZone {
|
else {
|
||||||
name = "${net}.zentralwerk.dn42";
|
name = "${net}.zentralwerk.dn42";
|
||||||
ns = [ fqdn ];
|
ns = [ fqdn ];
|
||||||
records =
|
records =
|
||||||
hosts4Records hosts4 ++
|
hosts4Records hosts4 ++
|
||||||
lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42);
|
lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42);
|
||||||
})
|
})
|
||||||
(staticZone {
|
{
|
||||||
name = "${net}.zentralwerk.org";
|
name = "${net}.zentralwerk.org";
|
||||||
ns = publicNS;
|
ns = publicNS;
|
||||||
records =
|
records =
|
||||||
lib.optionals (hosts6 ? up1) (hosts6Records hosts6.up1) ++
|
lib.optionals (hosts6 ? up1) (hosts6Records hosts6.up1) ++
|
||||||
lib.optionals (hosts6 ? up2) (hosts6Records hosts6.up2);
|
lib.optionals (hosts6 ? up2) (hosts6Records hosts6.up2);
|
||||||
})
|
}
|
||||||
]) namedNets
|
]) namedNets
|
||||||
)
|
)
|
||||||
) ++ map (zone: staticZone {
|
) ++ map (zone: {
|
||||||
name = zone;
|
name = zone;
|
||||||
ns = [ fqdn ];
|
ns = [ fqdn ];
|
||||||
records =
|
records =
|
||||||
|
@ -232,7 +234,7 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable {
|
||||||
);
|
);
|
||||||
}) reverseZones4
|
}) reverseZones4
|
||||||
++ builtins.concatMap (ctx:
|
++ builtins.concatMap (ctx:
|
||||||
map (zone: staticZone {
|
map (zone: {
|
||||||
name = zone;
|
name = zone;
|
||||||
ns =
|
ns =
|
||||||
if ctx == "dn42"
|
if ctx == "dn42"
|
||||||
|
@ -251,7 +253,51 @@ lib.mkIf config.site.hosts.${hostName}.services.dns.enable {
|
||||||
);
|
);
|
||||||
}) reverseZones6.${ctx}
|
}) reverseZones6.${ctx}
|
||||||
) (builtins.attrNames reverseZones6);
|
) (builtins.attrNames reverseZones6);
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: dyn
|
services.bind = lib.mkIf config.site.hosts.${hostName}.services.dns.enable (
|
||||||
|
let
|
||||||
|
serial =
|
||||||
|
let
|
||||||
|
timestamp = toString self.lastModified;
|
||||||
|
datePkg = pkgs.runCommandLocal "date-${timestamp}" {} ''
|
||||||
|
date -d @${timestamp} +%Y%m%d%H > $out
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
toString (import datePkg);
|
||||||
|
|
||||||
|
generateZone = { name, ns, records }: {
|
||||||
|
inherit name;
|
||||||
|
master = true;
|
||||||
|
# allowed for zone-transfer
|
||||||
|
slaves = [
|
||||||
|
# ns.c3d2.de
|
||||||
|
"217.197.84.53" "2001:67c:1400:2240::a"
|
||||||
|
# ns.spaceboyz.net
|
||||||
|
"172.22.24.4" "2a01:4f9:4b:39ec::4"
|
||||||
|
];
|
||||||
|
file = builtins.toFile "${name}.zone" ''
|
||||||
|
$ORIGIN ${name}.
|
||||||
|
$TTL 1h
|
||||||
|
|
||||||
|
@ IN SOA ${fqdn}. astro.spaceboyz.net. (
|
||||||
|
${serial} ; serial
|
||||||
|
1h ; refresh
|
||||||
|
1m ; retry
|
||||||
|
2h ; expire
|
||||||
|
1m ; minimum
|
||||||
|
)
|
||||||
|
${lib.concatMapStrings (ns: " IN NS ${ns}.\n") ns}
|
||||||
|
|
||||||
|
${lib.concatMapStrings ({ name, type, data }:
|
||||||
|
"${name} IN ${type} ${data}\n"
|
||||||
|
) records}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
enable = true;
|
||||||
|
zones = map generateZone config.site.dns.localZones;
|
||||||
|
});
|
||||||
|
|
||||||
|
# TODO: dyn
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user