authoritative-dns: Init
This commit is contained in:
parent
7a7d38c2f0
commit
8519bada60
177
modules/authoritative-dns.nix
Normal file
177
modules/authoritative-dns.nix
Normal file
|
@ -0,0 +1,177 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sbruder.knot;
|
||||
|
||||
primaryHost = "vueko";
|
||||
secondaryHosts = [ "okarin" ];
|
||||
|
||||
isPrimaryHost = config.networking.hostName == primaryHost;
|
||||
isSecondaryHost = lib.elem config.networking.hostName secondaryHosts;
|
||||
|
||||
addresses = {
|
||||
vueko = [ "168.119.176.53" "2a01:4f8:c012:2f4::1" ];
|
||||
okarin = [ "82.165.242.252" "2001:8d8:1800:8627::1" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sbruder.knot.generated-zones = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.path;
|
||||
default = { };
|
||||
description = "List of zones generated by a nix expression";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (isPrimaryHost || isSecondaryHost) {
|
||||
services.knot = {
|
||||
enable = true;
|
||||
keyFiles = [
|
||||
# Managed in separate repository.
|
||||
# It includes all statically managed zones.
|
||||
# Even though it is not a key,
|
||||
# it needs to be included here
|
||||
# so the module disables configuration checks.
|
||||
"/var/lib/knot/static.conf"
|
||||
];
|
||||
extraConfig = ''
|
||||
server:
|
||||
${lib.concatStringsSep "\n" (map (address: " listen: ${address}@53") addresses.${config.networking.hostName})}
|
||||
automatic-acl: on
|
||||
|
||||
log:
|
||||
- target: syslog
|
||||
any: info
|
||||
remote:
|
||||
${lib.concatStrings (lib.mapAttrsToList (host: hostAddresses: ''
|
||||
- id: ${host}
|
||||
address: [${lib.concatStringsSep ", " hostAddresses}]
|
||||
'') addresses)}
|
||||
'' + (lib.optionalString isPrimaryHost ''
|
||||
# HACK: this string just continues the previous section
|
||||
- id: inwx
|
||||
# INWX only allows the specification of one primary DNS,
|
||||
# which limits the IP protocol usable for zone transfers to one.
|
||||
address: [185.181.104.96]
|
||||
|
||||
policy:
|
||||
- id: default
|
||||
nsec3: on
|
||||
|
||||
template:
|
||||
- id: default
|
||||
storage: /var/lib/knot/zones/
|
||||
semantic-checks: on
|
||||
# auto increment serial
|
||||
zonefile-sync: -1
|
||||
zonefile-load: difference-no-serial
|
||||
journal-content: all
|
||||
# secondary
|
||||
notify: [inwx, ${lib.concatStringsSep ", " secondaryHosts}]
|
||||
# dnssec
|
||||
dnssec-signing: on
|
||||
dnssec-policy: default
|
||||
- id: nix-generated
|
||||
storage: /var/lib/knot/nix-zones/
|
||||
semantic-checks: on
|
||||
# auto increment serial
|
||||
zonefile-sync: -1
|
||||
zonefile-load: difference-no-serial
|
||||
journal-content: all
|
||||
|
||||
zone:
|
||||
${lib.concatMapStrings (domain: ''
|
||||
- domain: ${domain}
|
||||
template: nix-generated
|
||||
'') (lib.attrNames cfg.generated-zones)}
|
||||
'') + (lib.optionalString isSecondaryHost ''
|
||||
acl:
|
||||
- id: primary_notify
|
||||
address: [${lib.concatStringsSep ", " (lib.flatten addresses.${primaryHost})}]
|
||||
action: notify
|
||||
|
||||
template:
|
||||
- id: default
|
||||
master: [${primaryHost}]
|
||||
acl: [primary_notify]
|
||||
'');
|
||||
};
|
||||
|
||||
users.users.knot = {
|
||||
openssh.authorizedKeys.keys = config.sbruder.pubkeys.trustedKeys;
|
||||
shell = pkgs.bashInteractive;
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"f /var/lib/knot/static.conf 0644 knot knot - -"
|
||||
] ++ (lib.optionals isPrimaryHost [
|
||||
"d /var/lib/knot/nix-zones 0755 knot knot - -"
|
||||
]);
|
||||
|
||||
systemd.services.knot-generated-zones = lib.mkIf isPrimaryHost {
|
||||
wantedBy = [ "knot.service" ];
|
||||
after = [ "knot.service" ];
|
||||
path = with pkgs; [ knot-dns ];
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: zonefile: ''
|
||||
kzonecheck -o ${lib.escapeShellArg domain} ${lib.escapeShellArg zonefile}
|
||||
target=/var/lib/knot/nix-zones/${lib.escapeShellArg domain}.zone
|
||||
if [ -h "$target" ]; then
|
||||
pre_target="$(readlink "$target")"
|
||||
else
|
||||
pre_target="/invalid/path"
|
||||
fi
|
||||
ln -sf ${lib.escapeShellArg zonefile} "$target"
|
||||
if [ "$pre_target" != ${lib.escapeShellArg domain} ]; then
|
||||
echo -n "Zone for ${lib.escapeShellArg domain} changed, reloading… "
|
||||
knotc zone-reload ${lib.escapeShellArg domain}
|
||||
fi
|
||||
'') cfg.generated-zones)}
|
||||
'';
|
||||
restartTriggers = lib.attrValues cfg.generated-zones;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
User = "knot";
|
||||
|
||||
BindReadOnlyPaths = [ "/run/knot/knot.sock" ];
|
||||
CapabilityBoundingSet = ""; # clear
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateNetwork = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = true;
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" ]; # knot socket
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 53 ];
|
||||
allowedUDPPorts = [ 53 ];
|
||||
};
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = isPrimaryHost -> (lib.hasAttr "vpn.sbruder.de" cfg.generated-zones);
|
||||
message = "The authoritative DNS module requires the server the wg-home wireguard server to run on the same host.";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
imports = [
|
||||
../pkgs/modules.nix
|
||||
./ausweisapp.nix
|
||||
./authoritative-dns.nix
|
||||
./cups.nix
|
||||
./docker.nix
|
||||
./fancontrol.nix
|
||||
|
|
|
@ -126,38 +126,18 @@ in
|
|||
|
||||
networking.firewall = {
|
||||
trustedInterfaces = [ "wg-home" ];
|
||||
allowedUDPPorts = lib.optionals enableServer [
|
||||
serverPort
|
||||
53
|
||||
];
|
||||
allowedUDPPorts = lib.optional enableServer serverPort;
|
||||
};
|
||||
|
||||
services.bind = lib.mkIf enableServer {
|
||||
enable = true;
|
||||
zones = lib.singleton {
|
||||
name = "vpn.sbruder.de";
|
||||
master = true;
|
||||
file =
|
||||
let
|
||||
# !!! very hacky
|
||||
hexStringToInt = hex: (builtins.fromTOML "int = 0x${hex}").int;
|
||||
|
||||
peerRecords = lib.concatStrings
|
||||
sbruder.knot.generated-zones."vpn.sbruder.de" = pkgs.writeText "vpn.sbruder.de.zone" (''
|
||||
; having $ORIGIN set here fails
|
||||
@ IN SOA ${serverHostName}.sbruder.de. hostmaster.sbruder.de. 1 86400 10800 3600000 3600
|
||||
@ IN NS ${serverHostName}.sbruder.de.
|
||||
'' + lib.concatStrings
|
||||
(lib.mapAttrsToList
|
||||
(peer: peerConfig: ''
|
||||
${peer} IN A ${peerConfig.address}
|
||||
'')
|
||||
peers);
|
||||
|
||||
peerRecordsHash = builtins.hashString "sha256" peerRecords;
|
||||
serial = hexStringToInt (lib.substring 0 8 peerRecordsHash);
|
||||
in
|
||||
pkgs.writeText "vpn.sbruder.de.zone" (''
|
||||
$TTL 3600
|
||||
@ IN SOA ${serverHostName}.sbruder.de. hostmaster.sbruder.de. ${toString serial} 28800 3600 604800 3600
|
||||
@ IN NS ${serverHostName}.sbruder.de.
|
||||
'' + peerRecords);
|
||||
};
|
||||
};
|
||||
peers));
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue