231 lines
7.0 KiB
Nix
231 lines
7.0 KiB
Nix
{ config, dns-nix, hostName, lib, pkgs, self, ... }:
|
|
|
|
let
|
|
serial = builtins.substring 0 10 self.lastModifiedDate;
|
|
|
|
generateZoneFile = let
|
|
util = dns-nix.util.${pkgs.system};
|
|
in { name, ns, records, ... }: util.writeZone name {
|
|
TTL = 60*60;
|
|
SOA = {
|
|
nameServer = "${lib.dns.ns}.";
|
|
adminEmail = "astro@spaceboyz.net";
|
|
serial = lib.toInt serial;
|
|
refresh = 1*60*60;
|
|
retry = 5*60;
|
|
expire = 2*60*60;
|
|
minimum = 1*60;
|
|
};
|
|
NS = map (a: a+".") ns;
|
|
subdomains = lib.foldl (a: b: lib.recursiveUpdate a b) { } (map ({ name, type, data }: {
|
|
${name}.${type} = [ data ];
|
|
}) records);
|
|
};
|
|
in
|
|
{
|
|
options =
|
|
with lib;
|
|
let
|
|
recordOpts = {
|
|
name = mkOption {
|
|
description = "DNS label";
|
|
type = types.str;
|
|
};
|
|
type = mkOption {
|
|
type = types.enum [ "A" "AAAA" "MX" "SRV" "CNAME" "TXT" "PTR" ];
|
|
};
|
|
data = mkOption {
|
|
type = types.oneOf [ types.str (types.attrsOf (types.oneOf [ types.int types.str ]))];
|
|
};
|
|
};
|
|
|
|
zoneOpts = {
|
|
name = mkOption {
|
|
description = "DNS FQDN w/o trailing dot";
|
|
type = types.str;
|
|
};
|
|
ns = mkOption {
|
|
type = with types; listOf str;
|
|
};
|
|
records = mkOption {
|
|
type = with types; listOf (submodule {
|
|
options = recordOpts;
|
|
});
|
|
};
|
|
dynamic = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
};
|
|
};
|
|
|
|
in {
|
|
site.dns.localZones = mkOption {
|
|
type = with types; listOf (submodule {
|
|
options = zoneOpts;
|
|
});
|
|
};
|
|
};
|
|
|
|
config = {
|
|
site.dns.localZones = lib.dns.localZones;
|
|
|
|
services.knot = lib.mkIf config.site.hosts.${hostName}.services.dns.enable (
|
|
let
|
|
generateZone = zone@{ name, dynamic, ... }: {
|
|
domain = name;
|
|
template = "zentralwerk";
|
|
acl = [ "zone_xfr" ] ++ lib.optional dynamic "dyndns";
|
|
file = if dynamic
|
|
then "/var/lib/knot/zones/${name}.zone"
|
|
else generateZoneFile zone;
|
|
notify = [ "all" ];
|
|
};
|
|
|
|
in {
|
|
enable = true;
|
|
settings = {
|
|
acl = [
|
|
{
|
|
id = "dyndns";
|
|
action = "update";
|
|
key = "dyndns";
|
|
}
|
|
{
|
|
id = "zone_xfr";
|
|
address = with config.site.net.serv; [
|
|
# ns.c3d2.de
|
|
hosts4.knot hosts6.dn42.knot hosts6.up4.knot
|
|
"2a00:8180:2c00:282:2041:cbff:fe0c:8516"
|
|
"fd23:42:c3d2:582:2041:cbff:fe0c:8516"
|
|
# ns.spaceboyz.net
|
|
"172.22.24.4" "95.217.229.209" "2a01:4f9:4b:39ec::4"
|
|
# ns1.supersandro.de
|
|
"188.34.196.104" "2a01:4f8:1c1c:1d38::1"
|
|
];
|
|
action = "transfer";
|
|
}
|
|
];
|
|
|
|
key = [ {
|
|
id = "dyndns";
|
|
algorithm = "hmac-sha256";
|
|
secret = config.site.dyndnsKey;
|
|
} ];
|
|
|
|
log = [ {
|
|
target = "syslog";
|
|
any = "info";
|
|
} ];
|
|
|
|
mod-stats = [ {
|
|
id = "default";
|
|
query-type = "on";
|
|
} ];
|
|
|
|
remote = let
|
|
via = with config.site.net.serv; [ hosts4.dns hosts6.up4.dns ];
|
|
in [
|
|
{
|
|
id = "ns.c3d2.de";
|
|
address = with config.site.net.serv; [ hosts4.knot hosts6.dn42.knot hosts6.up4.knot ];
|
|
inherit via;
|
|
} {
|
|
id = "ns.spaceboyz.net";
|
|
address = [ "172.22.24.4" "95.217.229.209" "2a01:4f9:4b:39ec::4" ];
|
|
inherit via;
|
|
} {
|
|
id = "ns1.supersandro.de";
|
|
address = [ /*"188.34.196.104"*/ "2a01:4f8:1c1c:1d38::1" ];
|
|
inherit via;
|
|
}
|
|
];
|
|
|
|
remotes = [ {
|
|
id = "all";
|
|
remote = [ "ns.c3d2.de" "ns.spaceboyz.net" "ns1.supersandro.de" ];
|
|
} ];
|
|
|
|
server = {
|
|
answer-rotation = true;
|
|
automatic-acl = true;
|
|
identity = "dns.serv.zentralwerk.org";
|
|
listen = with config.site.net; [
|
|
"127.0.0.1" "::1"
|
|
serv.hosts4.dns serv.hosts6.up4.dns serv.hosts6.dn42.dns
|
|
];
|
|
tcp-fastopen = true;
|
|
version = null;
|
|
};
|
|
|
|
template = [
|
|
{
|
|
# default is a magic name and is always loaded.
|
|
# Because we want to use catalog-role/catalog-zone settings for all zones *except* the catalog zone itself, we must split the templates
|
|
id = "default";
|
|
global-module = [ "mod-stats" ];
|
|
}
|
|
{
|
|
id = "zentralwerk";
|
|
catalog-role = "member";
|
|
catalog-zone = "zentralwerk.";
|
|
dnssec-signing = true;
|
|
journal-content = "all"; # required for zonefile-load=difference-no-serial and makes cold starts like zone reloads
|
|
module = "mod-stats/default";
|
|
semantic-checks = true;
|
|
serial-policy = "increment";
|
|
storage = "/var/lib/knot/zones";
|
|
zonefile-load = "difference-no-serial";
|
|
}
|
|
];
|
|
|
|
zone = [ {
|
|
acl = "zone_xfr";
|
|
catalog-role = "generate";
|
|
domain = "zentralwerk.";
|
|
notify = [ "ns1.supersandro.de" ];
|
|
storage = "/var/lib/knot/catalog";
|
|
} ] ++ map generateZone config.site.dns.localZones;
|
|
};
|
|
});
|
|
|
|
systemd.services = {
|
|
create-dynamic-zones = {
|
|
description = "Creates dynamic zone files";
|
|
requiredBy = [ "knot.service" ];
|
|
before = [ "knot.service" ];
|
|
serviceConfig.Type = "oneshot";
|
|
script = ''
|
|
mkdir -p /var/lib/knot/zones
|
|
|
|
${lib.concatMapStringsSep "\n" (zone@{ name, ... }: ''
|
|
[ -e /var/lib/knot/zones/${name}.zone ] || \
|
|
cp ${generateZoneFile zone} /var/lib/knot/zones/${name}.zone
|
|
chown -R knot /var/lib/knot/zones
|
|
chmod -R u+rwX /var/lib/knot/zones
|
|
'') (builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones)}
|
|
'';
|
|
};
|
|
|
|
update-dynamic-zones = {
|
|
description = "Creates initial records in dynamic zone files";
|
|
requiredBy = [ "knot.service" ];
|
|
after = [ "knot.service" ];
|
|
serviceConfig.Type = "oneshot";
|
|
path = [ pkgs.dnsutils ];
|
|
script = lib.concatMapStrings (zone: ''
|
|
nsupdate -v -y "hmac-sha256:dyndns:${config.site.dyndnsKey}" <<EOF
|
|
server localhost
|
|
|
|
${lib.concatMapStringsSep "\n" ({ name, type, data }: ''
|
|
delete ${name}.${zone.name}. IN ${type}
|
|
add ${name}.${zone.name}. 3600 IN ${type} ${data}
|
|
'') zone.records}
|
|
|
|
send
|
|
EOF
|
|
'') (builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones);
|
|
};
|
|
};
|
|
};
|
|
}
|