Added various changes over time
This commit is contained in:
parent
5e602e9ff4
commit
179dda3ec6
|
@ -110,7 +110,6 @@ let
|
|||
heimdal-kadmind = {
|
||||
wantedBy = [ "heimdal-kdc.service" ];
|
||||
after = [ "heimdal-kdc.service" ];
|
||||
bindsTo = [ "heimdal-kdc.service" ];
|
||||
description = "Heimdal Kerberos Administration Server.";
|
||||
path = with pkgs; [ heimdal ];
|
||||
serviceConfig = {
|
||||
|
@ -149,7 +148,6 @@ let
|
|||
heimdal-kpasswdd = {
|
||||
wantedBy = [ "heimdal-kdc.service" ];
|
||||
after = [ "heimdal-kdc.service" ];
|
||||
bindsTo = [ "heimdal-kdc.service" ];
|
||||
description = "Heimdal Kerberos Password Server.";
|
||||
path = with pkgs; [ heimdal ];
|
||||
serviceConfig = {
|
||||
|
@ -188,7 +186,6 @@ let
|
|||
|
||||
heimdal-hprop = mkIf hasSecondary {
|
||||
wantedBy = [ "heimdal-kdc.service" ];
|
||||
bindsTo = [ "heimdal-kdc.service" ];
|
||||
after = [ "heimdal-kdc.service" ];
|
||||
description =
|
||||
"Service to propagate the KDC database to secondary servers.";
|
||||
|
@ -220,8 +217,8 @@ let
|
|||
"--config-file=${kdcConf}"
|
||||
"--"
|
||||
"dump"
|
||||
"--format=MIT"
|
||||
"$(echo ${staging-db})"
|
||||
"--format=Heimdal"
|
||||
"${staging-db}"
|
||||
]);
|
||||
ExecStart = pkgs.writeShellScript "kdc-hprop.sh"
|
||||
(concatStringsSep " " ([
|
||||
|
@ -239,36 +236,13 @@ let
|
|||
};
|
||||
|
||||
paths.heimdal-hprop = mkIf hasSecondary {
|
||||
wantedBy = [ "heimdal-kdc.service" ];
|
||||
bindsTo = [ "heimdal-kdc.service" ];
|
||||
wantedBy = [ "heimdal-hprop.service" ];
|
||||
bindsTo = [ "heimdal-hprop.service" ];
|
||||
after = [ "heimdal-kdc.service" ];
|
||||
pathConfig = { PathModified = cfg.kdc.database; };
|
||||
};
|
||||
};
|
||||
|
||||
# services.xinetd = {
|
||||
# enable = true;
|
||||
|
||||
# services = [
|
||||
# {
|
||||
# name = "kadmin";
|
||||
# user = cfg.user;
|
||||
# server = "${pkgs.heimdal}/libexec/heimdal/kadmind";
|
||||
# protocol = "tcp";
|
||||
# serverArgs =
|
||||
# "--config-file=${kdcConf} --keytab=${cfg.kdc.primary.keytabs.kadmind}";
|
||||
# }
|
||||
# {
|
||||
# name = "kpasswd";
|
||||
# user = cfg.user;
|
||||
# server = "${pkgs.heimdal}/libexec/heimdal/kpasswdd";
|
||||
# protocol = "udp";
|
||||
# serverArgs =
|
||||
# "--config-file=${kdcConf} --keytab=${cfg.kdc.primary.keytabs.kpasswdd}";
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 88 749 ];
|
||||
allowedUDPPorts = [ 88 464 ];
|
||||
|
@ -280,12 +254,12 @@ let
|
|||
let
|
||||
cfg = config.fudo.auth.kerberos;
|
||||
|
||||
kdcConf = pkgs.writeText "kdc.conf" ''
|
||||
kdcConf = pkgs.writeText "kdc.conf.template" ''
|
||||
[kdc]
|
||||
database = {
|
||||
realm = ${cfg.realm}
|
||||
dbname = sqlite:${cfg.kdc.database}
|
||||
mkey_file = ${cfg.kdc.master-key-file}
|
||||
mkey_file = __KEY_FILE__
|
||||
log_file = /dev/null
|
||||
}
|
||||
|
||||
|
@ -310,7 +284,7 @@ let
|
|||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
};
|
||||
groups."${cfg.group}" = { members = [ cfg.user ]; };
|
||||
groups."${cfg.group}".members = [ cfg.user ];
|
||||
};
|
||||
|
||||
systemd = {
|
||||
|
@ -343,6 +317,7 @@ let
|
|||
RestartSec = "5s";
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
SecureBits = "keep-caps";
|
||||
RuntimeDirectory = "heimdal-kdc-secondary";
|
||||
ExecStart = let
|
||||
ips = if (cfg.kdc.bind-addresses != [ ]) then
|
||||
cfg.kdc.bind-addresses
|
||||
|
@ -353,13 +328,15 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
heimdal-hpropd = {
|
||||
"heimdal-hpropd@" = {
|
||||
wantedBy = [ "heimdal-kdc-secondary.service" ];
|
||||
after = [ "heimdal-kdc-secondary.service" ];
|
||||
bindsTo = [ "heimdal-kdc-secondary.service" ];
|
||||
description = "Heimdal propagation listener server.";
|
||||
path = with pkgs; [ heimdal ];
|
||||
serviceConfig = {
|
||||
StandardInput = "socket";
|
||||
StandardOutput = "socket";
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
# PrivateMounts = true;
|
||||
|
@ -389,6 +366,14 @@ let
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
sockets.heimdal-hpropd = {
|
||||
wantedBy = [ "sockets.target" ];
|
||||
socketConfig = {
|
||||
ListenStream = "0.0.0.0:754";
|
||||
Accept = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ config, lib, pkgs, ... } @ toplevel:
|
||||
{ config, lib, pkgs, ... }@toplevel:
|
||||
|
||||
with lib;
|
||||
let
|
||||
|
@ -12,7 +12,8 @@ in {
|
|||
|
||||
required-services = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of systemd units on which the DNS backplane job depends.";
|
||||
description =
|
||||
"List of systemd units on which the DNS backplane job depends.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
|
@ -54,11 +55,12 @@ in {
|
|||
|
||||
password-file = mkOption {
|
||||
type = str;
|
||||
description = "File containing password for DNS backplane database user.";
|
||||
description =
|
||||
"File containing password for DNS backplane database user.";
|
||||
};
|
||||
|
||||
ssl-mode = mkOption {
|
||||
type = enum ["no" "yes" "full" "try" "require"];
|
||||
type = enum [ "no" "yes" "full" "try" "require" ];
|
||||
description = "SSL connection mode.";
|
||||
default = "require";
|
||||
};
|
||||
|
@ -86,9 +88,7 @@ in {
|
|||
home = backplane-dns-home;
|
||||
createHome = true;
|
||||
};
|
||||
groups.${cfg.group} = {
|
||||
members = [ cfg.user ];
|
||||
};
|
||||
groups.${cfg.group} = { members = [ cfg.user ]; };
|
||||
};
|
||||
|
||||
fudo.system.services = {
|
||||
|
@ -110,12 +110,12 @@ in {
|
|||
environment = {
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane-server;
|
||||
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane-role.role;
|
||||
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane-role.password-file;
|
||||
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE =
|
||||
cfg.backplane-role.password-file;
|
||||
|
||||
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.database.host;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.database.database;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
|
||||
cfg.database.username;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.database.username;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
|
||||
cfg.database.password-file;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_USE_SSL = cfg.database.ssl-mode;
|
||||
|
|
|
@ -7,20 +7,19 @@ let
|
|||
|
||||
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||
|
||||
generate-auth-file = name: files: let
|
||||
make-entry = name: passwd-file:
|
||||
''("${name}" . "${readFile passwd-file}")'';
|
||||
entries = mapAttrsToList make-entry files;
|
||||
content = concatStringsSep "\n" entries;
|
||||
in pkgs.writeText "${name}-backplane-auth.scm" "'(${content})";
|
||||
generate-auth-file = name: files:
|
||||
let
|
||||
make-entry = name: passwd-file:
|
||||
''("${name}" . "${readFile passwd-file}")'';
|
||||
entries = mapAttrsToList make-entry files;
|
||||
content = concatStringsSep "\n" entries;
|
||||
in pkgs.writeText "${name}-backplane-auth.scm" "'(${content})";
|
||||
|
||||
host-auth-file = generate-auth-file "host"
|
||||
(mapAttrs (hostname: hostOpts: hostOpts.password-file)
|
||||
cfg.client-hosts);
|
||||
(mapAttrs (hostname: hostOpts: hostOpts.password-file) cfg.client-hosts);
|
||||
|
||||
service-auth-file = generate-auth-file "service"
|
||||
(mapAttrs (service: serviceOpts: serviceOpts.password-file)
|
||||
cfg.services);
|
||||
(mapAttrs (service: serviceOpts: serviceOpts.password-file) cfg.services);
|
||||
|
||||
clientHostOpts = { name, ... }: {
|
||||
options = with types; {
|
||||
|
@ -49,13 +48,13 @@ in {
|
|||
client-hosts = mkOption {
|
||||
type = attrsOf (submodule clientHostOpts);
|
||||
description = "List of backplane client options.";
|
||||
default = {};
|
||||
default = { };
|
||||
};
|
||||
|
||||
services = mkOption {
|
||||
type = attrsOf (submodule serviceOpts);
|
||||
description = "List of backplane service options.";
|
||||
default = {};
|
||||
default = { };
|
||||
};
|
||||
|
||||
backplane-hostname = mkOption {
|
||||
|
@ -85,8 +84,7 @@ in {
|
|||
|
||||
jabber = {
|
||||
environment = {
|
||||
FUDO_HOST_PASSWD_FILE =
|
||||
host-secrets.backplane-host-auth.target-file;
|
||||
FUDO_HOST_PASSWD_FILE = host-secrets.backplane-host-auth.target-file;
|
||||
FUDO_SERVICE_PASSWD_FILE =
|
||||
host-secrets.backplane-service-auth.target-file;
|
||||
# GUILE_AUTO_COMPILE = "0";
|
||||
|
@ -98,22 +96,21 @@ in {
|
|||
site-config = {
|
||||
auth_method = "external";
|
||||
extauth_program =
|
||||
# "${pkgs.guile}/bin/guile -s /run/backplane-auth.scm";
|
||||
"${pkgs.guile}/bin/guile -s ${pkgs.backplane-auth}/backplane-auth.scm";
|
||||
extauth_pool_size = 3;
|
||||
auth_use_cache = false;
|
||||
|
||||
modules = {
|
||||
mod_adhoc = {};
|
||||
mod_adhoc = { };
|
||||
# mod_caps = {};
|
||||
# mod_carboncopy = {};
|
||||
# mod_client_state = {};
|
||||
mod_configure = {};
|
||||
mod_configure = { };
|
||||
# mod_disco = {};
|
||||
mod_fail2ban = {};
|
||||
mod_fail2ban = { };
|
||||
# mod_last = {};
|
||||
mod_offline.access_max_user_messages = 5000;
|
||||
mod_ping = {};
|
||||
mod_ping = { };
|
||||
# mod_pubsub = {
|
||||
# access_createnode = "pubsub_createnode";
|
||||
# ignore_pep_from_offline = true;
|
||||
|
@ -124,9 +121,9 @@ in {
|
|||
# ];
|
||||
# };
|
||||
# mod_roster = {};
|
||||
mod_stream_mgmt = {};
|
||||
mod_time = {};
|
||||
mod_version = {};
|
||||
mod_stream_mgmt = { };
|
||||
mod_time = { };
|
||||
mod_version = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -200,51 +200,7 @@ in {
|
|||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8065";
|
||||
proxyWebsockets = true;
|
||||
|
||||
# extraConfig = ''
|
||||
# client_max_body_size 50M;
|
||||
# proxy_set_header Connection "";
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
# proxy_buffers 256 16k;
|
||||
# proxy_buffer_size 16k;
|
||||
# proxy_read_timeout 600s;
|
||||
# proxy_cache mattermost_cache;
|
||||
# proxy_cache_revalidate on;
|
||||
# proxy_cache_min_uses 2;
|
||||
# proxy_cache_use_stale timeout;
|
||||
# proxy_cache_lock on;
|
||||
# proxy_http_version 1.1;
|
||||
# '';
|
||||
};
|
||||
|
||||
# locations."~ /api/v[0-9]+/(users/)?websocket$" = {
|
||||
# proxyPass = "http://127.0.0.1:8065";
|
||||
|
||||
# extraConfig = ''
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# client_max_body_size 50M;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
# proxy_buffers 256 16k;
|
||||
# proxy_buffer_size 16k;
|
||||
# client_body_timeout 60;
|
||||
# send_timeout 300;
|
||||
# lingering_timeout 5;
|
||||
# proxy_connect_timeout 90;
|
||||
# proxy_send_timeout 300;
|
||||
# proxy_read_timeout 90s;
|
||||
# '';
|
||||
# };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,13 +4,12 @@ with lib;
|
|||
let
|
||||
cfg = config.fudo.client.dns;
|
||||
|
||||
ssh-key-files =
|
||||
map (host-key: host-key.path) config.services.openssh.hostKeys;
|
||||
|
||||
ssh-key-args = concatStringsSep " " (map (file: "-f ${file}") ssh-key-files);
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
in {
|
||||
options.fudo.client.dns = {
|
||||
enable = mkEnableOption "Enable Backplane DNS client.";
|
||||
|
||||
ipv4 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
|
@ -68,7 +67,7 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users = {
|
||||
users = {
|
||||
|
@ -98,37 +97,96 @@ in {
|
|||
timerConfig = { OnCalendar = cfg.frequency; };
|
||||
};
|
||||
|
||||
services.backplane-dns-client-pw-file = {
|
||||
enable = true;
|
||||
requiredBy = [ "backplane-dns-client.service" ];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = { Type = "oneshot"; };
|
||||
script = ''
|
||||
chmod 400 ${cfg.password-file}
|
||||
chown ${cfg.user} ${cfg.password-file}
|
||||
'';
|
||||
};
|
||||
|
||||
services.backplane-dns-client = {
|
||||
enable = true;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
StandardOutput = "journal";
|
||||
User = cfg.user;
|
||||
ExecStart = pkgs.writeShellScript "start-backplane-dns-client.sh" ''
|
||||
${pkgs.backplane-dns-client}/bin/backplane-dns-client ${
|
||||
optionalString cfg.ipv4 "-4"
|
||||
} ${optionalString cfg.ipv6 "-6"} ${
|
||||
optionalString cfg.sshfp ssh-key-args
|
||||
} ${
|
||||
optionalString (cfg.external-interface != null)
|
||||
"--interface=${cfg.external-interface}"
|
||||
} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file}
|
||||
services = let sshfp-file = "/tmp/${hostname}-sshfp/fingerprints";
|
||||
in {
|
||||
backplane-dns-client-pw-file = {
|
||||
requiredBy = [ "backplane-dns-client.service" ];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = { Type = "oneshot"; };
|
||||
script = ''
|
||||
chmod 400 ${cfg.password-file}
|
||||
chown ${cfg.user} ${cfg.password-file}
|
||||
'';
|
||||
};
|
||||
# Needed to generate SSH fingerprinst
|
||||
path = [ pkgs.openssh ];
|
||||
reloadIfChanged = true;
|
||||
|
||||
backplane-dns-generate-sshfps = mkIf cfg.sshfp {
|
||||
requiredBy = [ "backplane-dns-client.service" ];
|
||||
before = [ "backplane-dns-client.service" ];
|
||||
path = with pkgs; [ coreutils openssh ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
PrivateDevices = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectHome = true;
|
||||
ProtectKernelLogs = true;
|
||||
#ProtectSystem = true;
|
||||
#LockPersonality = true;
|
||||
#PermissionsStartOnly = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
LimitNOFILE = 1024;
|
||||
ReadWritePaths = [ (dirOf sshfp-file) ];
|
||||
};
|
||||
script = let
|
||||
keyPaths = map (key: key.path) config.services.openssh.hostKeys;
|
||||
keyGenCmds = map (path:
|
||||
''
|
||||
ssh-keygen -r hostname -f "${path}" | sed 's/hostname IN SSHFP '// >> ${sshfp-file}'')
|
||||
keyPaths;
|
||||
in ''
|
||||
[ -f ${sshfp-file} ] && rm -f ${sshfp-file}
|
||||
SSHFP_DIR=$(dirname ${sshfp-file})
|
||||
[ -d $SSHFP_DIR ] || mkdir $SSHFP_DIR
|
||||
chown ${cfg.user} $SSHFP_DIR
|
||||
chmod go-rwx $SSHFP_DIR
|
||||
${concatStringsSep "\n" keyGenCmds}
|
||||
chown ${cfg.user} ${sshfp-file}
|
||||
chmod 600 ${sshfp-file}
|
||||
'';
|
||||
};
|
||||
|
||||
backplane-dns-client = {
|
||||
enable = true;
|
||||
path = with pkgs; [ coreutils ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
StandardOutput = "journal";
|
||||
User = cfg.user;
|
||||
ExecStart = pkgs.writeShellScript "start-backplane-dns-client.sh" ''
|
||||
SSHFP_ARGS=""
|
||||
${optionalString cfg.sshfp ''
|
||||
while read LINE; do SSHFP_ARGS="$SSHFP_ARGS --ssh-fp=\"$LINE\""; done < ${sshfp-file}
|
||||
''}
|
||||
CMD="${pkgs.backplaneDnsClient}/bin/backplane-dns-client ${
|
||||
optionalString cfg.ipv4 "-4"
|
||||
} ${optionalString cfg.ipv6 "-6"} ${
|
||||
optionalString cfg.sshfp "$SSHFP_ARGS"
|
||||
} ${
|
||||
optionalString (cfg.external-interface != null)
|
||||
"--interface=${cfg.external-interface}"
|
||||
} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file}"
|
||||
echo $CMD
|
||||
$CMD
|
||||
'';
|
||||
ExecStartPost = mkIf cfg.sshfp "rm ${sshfp-file}";
|
||||
PrivateDevices = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectHome = true;
|
||||
ProtectKernelLogs = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
ProtectSystem = true;
|
||||
LockPersonality = true;
|
||||
PermissionsStartOnly = true;
|
||||
RestrictRealtime = true;
|
||||
ReadOnlyPaths = [ sshfp-file ];
|
||||
LimitNOFILE = 1024;
|
||||
};
|
||||
reloadIfChanged = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -33,6 +33,7 @@ with lib; {
|
|||
./minecraft-clj.nix
|
||||
./minecraft-server.nix
|
||||
./netinfo-email.nix
|
||||
./nexus.nix
|
||||
./node-exporter.nix
|
||||
./nsd.nix
|
||||
./password.nix
|
||||
|
|
|
@ -20,6 +20,13 @@ let
|
|||
default = true;
|
||||
};
|
||||
|
||||
ksk = {
|
||||
key-file = mkOption {
|
||||
type = str;
|
||||
description = "Key-signing key for this zone.";
|
||||
};
|
||||
};
|
||||
|
||||
zone-definition = mkOption {
|
||||
type = submodule (import ../types/zone-definition.nix);
|
||||
description =
|
||||
|
@ -64,25 +71,30 @@ in {
|
|||
allowedUDPPorts = [ 53 ];
|
||||
};
|
||||
|
||||
fileSystems."/var/lib/nsd" = {
|
||||
device = cfg.state-directory;
|
||||
options = [ "bind" ];
|
||||
};
|
||||
# fileSystems."/var/lib/nsd" = {
|
||||
# device = cfg.state-directory;
|
||||
# options = [ "bind" ];
|
||||
# };
|
||||
|
||||
services.nsd = {
|
||||
enable = true;
|
||||
identity = cfg.identity;
|
||||
interfaces = cfg.listen-ips;
|
||||
# stateDir = cfg.state-directory;
|
||||
zones = mapAttrs' (dom: dom-cfg:
|
||||
let net-cfg = dom-cfg.zone-definition;
|
||||
in nameValuePair "${dom}." {
|
||||
dnssec = dom-cfg.dnssec;
|
||||
fudo = {
|
||||
nsd = {
|
||||
enable = true;
|
||||
identity = cfg.identity;
|
||||
interfaces = cfg.listen-ips;
|
||||
stateDirectory = cfg.state-directory;
|
||||
zones = mapAttrs' (dom: dom-cfg:
|
||||
let net-cfg = dom-cfg.zone-definition;
|
||||
in nameValuePair "${dom}." {
|
||||
dnssec = dom-cfg.dnssec;
|
||||
|
||||
data = pkgs.lib.dns.zoneToZonefile config.instance.build-timestamp dom
|
||||
dom-cfg.zone-definition;
|
||||
ksk.keyFile = dom-cfg.ksk.key-file;
|
||||
|
||||
}) cfg.domains;
|
||||
data =
|
||||
pkgs.lib.dns.zoneToZonefile config.instance.build-timestamp dom
|
||||
dom-cfg.zone-definition;
|
||||
|
||||
}) cfg.domains;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -182,6 +182,12 @@ let
|
|||
description = "Name of the DNS zone associated with domain.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
nexus.domains = mkOption {
|
||||
type = listOf str;
|
||||
description = "Nexus domains to which hosts in this domain belong.";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -130,10 +130,6 @@ in {
|
|||
stateDir = cfg.state-dir;
|
||||
rootUrl = "https://${cfg.hostname}/";
|
||||
user = mkIf (cfg.user != null) cfg.user;
|
||||
ssh = {
|
||||
#enable = true;
|
||||
clonePort = cfg.ssh.listen-port;
|
||||
};
|
||||
settings = mkIf (cfg.ssh != null) {
|
||||
server = {
|
||||
# Displayed in the clone URL
|
||||
|
|
|
@ -4,120 +4,114 @@ with lib;
|
|||
let
|
||||
hostname = config.instance.hostname;
|
||||
host-filesystems = config.fudo.hosts.${hostname}.encrypted-filesystems;
|
||||
|
||||
|
||||
optionalOrDefault = str: default: if (str != null) then str else default;
|
||||
|
||||
filesystemsToMountpointLists = mapAttrsToList
|
||||
(fs: fsOpts: fsOpts.mountpoints);
|
||||
filesystemsToMountpointLists =
|
||||
mapAttrsToList (fs: fsOpts: fsOpts.mountpoints);
|
||||
|
||||
concatMapAttrs = f: as: concatMap (i: i) (mapAttrsToList f as);
|
||||
|
||||
concatMapAttrsToList = f: attrs:
|
||||
concatMap (i: i) (mapAttrsToList f attrs);
|
||||
concatMapAttrsToList = f: attrs: concatMap (i: i) (mapAttrsToList f attrs);
|
||||
|
||||
in {
|
||||
config = {
|
||||
users.groups = let
|
||||
site-name = config.instance.local-site;
|
||||
site-hosts = filterAttrs
|
||||
(hostname: hostOpts: hostOpts.site == site-name)
|
||||
site-hosts = filterAttrs (hostname: hostOpts: hostOpts.site == site-name)
|
||||
config.fudo.hosts;
|
||||
site-mountpoints = concatMapAttrsToList
|
||||
(host: hostOpts: concatMapAttrsToList
|
||||
(fs: fsOpts: attrValues fsOpts.mountpoints)
|
||||
hostOpts.encrypted-filesystems)
|
||||
site-hosts;
|
||||
site-mountpoints = concatMapAttrsToList (host: hostOpts:
|
||||
concatMapAttrsToList (fs: fsOpts: attrValues fsOpts.mountpoints)
|
||||
hostOpts.encrypted-filesystems) site-hosts;
|
||||
in listToAttrs
|
||||
(map (mp: nameValuePair mp.group { members = mp.users; })
|
||||
site-mountpoints);
|
||||
(map (mp: nameValuePair mp.group { members = mp.users; }) site-mountpoints);
|
||||
|
||||
systemd = {
|
||||
# Ensure the mountpoints exist
|
||||
tmpfiles.rules = let
|
||||
mpPerms = mpOpts: if mpOpts.world-readable then "755" else "750";
|
||||
mountpointToPath = mp: mpOpts:
|
||||
"d '${mp}' ${mpPerms mpOpts} root ${optionalOrDefault mpOpts.group "-"} - -";
|
||||
filesystemsToMountpointLists = mapAttrsToList
|
||||
(fs: fsOpts: fsOpts.mountpoints);
|
||||
mountpointListsToPaths = concatMap
|
||||
(mps: mapAttrsToList mountpointToPath mps);
|
||||
"d '${mp}' ${mpPerms mpOpts} root ${
|
||||
optionalOrDefault mpOpts.group "-"
|
||||
} - -";
|
||||
filesystemsToMountpointLists =
|
||||
mapAttrsToList (fs: fsOpts: fsOpts.mountpoints);
|
||||
mountpointListsToPaths =
|
||||
concatMap (mps: mapAttrsToList mountpointToPath mps);
|
||||
in mountpointListsToPaths (filesystemsToMountpointLists host-filesystems);
|
||||
|
||||
# Actual mounts of decrypted filesystems
|
||||
mounts = let
|
||||
filesystems = mapAttrsToList
|
||||
(fs: opts: { filesystem = fs; opts = opts; })
|
||||
host-filesystems;
|
||||
filesystems = mapAttrsToList (fs: opts: {
|
||||
filesystem = fs;
|
||||
opts = opts;
|
||||
}) host-filesystems;
|
||||
|
||||
mounts = concatMap
|
||||
(fs: mapAttrsToList
|
||||
(mp: mp-opts:
|
||||
{
|
||||
what = "/dev/mapper/${fs.filesystem}";
|
||||
type = fs.opts.filesystem-type;
|
||||
where = mp;
|
||||
options = concatStringsSep "," (fs.opts.options ++ mp-opts.options);
|
||||
description = "${fs.opts.filesystem-type} filesystem on ${fs.filesystem} mounted to ${mp}";
|
||||
requires = [ "${fs.filesystem}-decrypt.service" ];
|
||||
partOf = [ "${fs.filesystem}.target" ];
|
||||
wantedBy = [ "${fs.filesystem}.target" ];
|
||||
})
|
||||
fs.opts.mountpoints)
|
||||
filesystems;
|
||||
mounts = concatMap (fs:
|
||||
mapAttrsToList (mp: mp-opts: {
|
||||
what = "/dev/mapper/${fs.filesystem}";
|
||||
type = fs.opts.filesystem-type;
|
||||
where = mp;
|
||||
options = concatStringsSep "," (fs.opts.options ++ mp-opts.options);
|
||||
description =
|
||||
"${fs.opts.filesystem-type} filesystem on ${fs.filesystem} mounted to ${mp}";
|
||||
requires = [ "${fs.filesystem}-decrypt.service" ];
|
||||
partOf = [ "${fs.filesystem}.target" ];
|
||||
wantedBy = [ "${fs.filesystem}.target" ];
|
||||
}) fs.opts.mountpoints) filesystems;
|
||||
in mounts;
|
||||
|
||||
# Jobs to decrypt the encrypted devices
|
||||
services = mapAttrs' (filesystem-name: opts:
|
||||
nameValuePair "${filesystem-name}-decrypt"
|
||||
{
|
||||
description = "Decrypt the ${filesystem-name} filesystem when the key is available at ${opts.key-path}";
|
||||
path = with pkgs; [ cryptsetup ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = pkgs.writeShellScript "decrypt-${filesystem-name}.sh" ''
|
||||
[ -e /dev/mapper/${filesystem-name} ] || cryptsetup open --type luks --key-file ${opts.key-path} ${opts.encrypted-device} ${filesystem-name}
|
||||
'';
|
||||
ExecStartPost = pkgs.writeShellScript "remove-${filesystem-name}-key.sh" ''
|
||||
nameValuePair "${filesystem-name}-decrypt" {
|
||||
description =
|
||||
"Decrypt the ${filesystem-name} filesystem when the key is available at ${opts.key-path}";
|
||||
path = with pkgs; [ cryptsetup ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = pkgs.writeShellScript "decrypt-${filesystem-name}.sh" ''
|
||||
[ -e /dev/mapper/${filesystem-name} ] || cryptsetup open --type ${opts.type} --key-file ${opts.key-path} ${opts.encrypted-device} ${filesystem-name}
|
||||
'';
|
||||
ExecStartPost = mkIf opts.remove-key
|
||||
(pkgs.writeShellScript "remove-${filesystem-name}-key.sh" ''
|
||||
rm ${opts.key-path}
|
||||
'';
|
||||
ExecStop = pkgs.writeShellScript "close-${filesystem-name}.sh" ''
|
||||
cryptsetup close /dev/mapper/${filesystem-name}
|
||||
'';
|
||||
};
|
||||
restartIfChanged = true;
|
||||
})
|
||||
host-filesystems;
|
||||
'');
|
||||
ExecStop = pkgs.writeShellScript "close-${filesystem-name}.sh" ''
|
||||
cryptsetup close /dev/mapper/${filesystem-name}
|
||||
'';
|
||||
};
|
||||
restartIfChanged = true;
|
||||
}) host-filesystems;
|
||||
|
||||
# Watch the path of the key, trigger decrypt when it's available
|
||||
paths = let
|
||||
decryption-jobs = mapAttrs' (filesystem-name: opts:
|
||||
nameValuePair "${filesystem-name}-decrypt"
|
||||
{
|
||||
wantedBy = [ "default.target" ];
|
||||
description = "Watch for decryption key, then decrypt the target filesystem.";
|
||||
pathConfig = {
|
||||
PathExists = opts.key-path;
|
||||
Unit = "${filesystem-name}-decrypt.service";
|
||||
};
|
||||
}) host-filesystems;
|
||||
nameValuePair "${filesystem-name}-decrypt" {
|
||||
wantedBy = [ "default.target" ];
|
||||
description =
|
||||
"Watch for decryption key, then decrypt the target filesystem.";
|
||||
pathConfig = {
|
||||
PathExists = opts.key-path;
|
||||
Unit = "${filesystem-name}-decrypt.service";
|
||||
};
|
||||
}) host-filesystems;
|
||||
|
||||
post-decryption-jobs = mapAttrs' (filesystem-name: opts:
|
||||
nameValuePair "${filesystem-name}-mount"
|
||||
{
|
||||
wantedBy = [ "default.target" ];
|
||||
description = "Mount ${filesystem-name} filesystems once the decrypted device is available.";
|
||||
pathConfig = {
|
||||
PathExists = "/dev/mapper/${filesystem-name}";
|
||||
Unit = "${filesystem-name}.target";
|
||||
};
|
||||
}) host-filesystems;
|
||||
nameValuePair "${filesystem-name}-mount" {
|
||||
wantedBy = [ "default.target" ];
|
||||
description =
|
||||
"Mount ${filesystem-name} filesystems once the decrypted device is available.";
|
||||
pathConfig = {
|
||||
PathExists = "/dev/mapper/${filesystem-name}";
|
||||
Unit = "${filesystem-name}.target";
|
||||
};
|
||||
}) host-filesystems;
|
||||
in decryption-jobs // post-decryption-jobs;
|
||||
|
||||
targets = mapAttrs (filesystem-name: opts:
|
||||
{
|
||||
description = "${filesystem-name} enabled and available.";
|
||||
}) host-filesystems;
|
||||
targets = mapAttrs (filesystem-name: opts: {
|
||||
description = "${filesystem-name} enabled and available.";
|
||||
}) host-filesystems;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,32 +9,32 @@ let
|
|||
let
|
||||
dkim_key = "${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.key";
|
||||
dkim_txt = "${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.txt";
|
||||
in
|
||||
''
|
||||
if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ]
|
||||
then
|
||||
${cfg.dkim.package}/bin/opendkim-genkey -s "${cfg.dkim.selector}" \
|
||||
-d "${dom}" \
|
||||
--bits="${toString cfg.dkim.key-bits}" \
|
||||
--directory="${cfg.dkim.key-directory}"
|
||||
mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.private" "${dkim_key}"
|
||||
mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.txt" "${dkim_txt}"
|
||||
echo "Generated key for domain ${dom} selector ${cfg.dkim.selector}"
|
||||
fi
|
||||
'';
|
||||
in ''
|
||||
if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ]; then
|
||||
${cfg.dkim.package}/bin/opendkim-genkey -s "${cfg.dkim.selector}" \
|
||||
-d "${dom}" \
|
||||
--bits="${toString cfg.dkim.key-bits}" \
|
||||
--directory="${cfg.dkim.key-directory}"
|
||||
mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.private" "${dkim_key}"
|
||||
mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.txt" "${dkim_txt}"
|
||||
echo "Generated key for domain ${dom} selector ${cfg.dkim.selector}"
|
||||
fi
|
||||
'';
|
||||
|
||||
createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.local-domains);
|
||||
createAllCerts =
|
||||
lib.concatStringsSep "\n" (map createDomainDkimCert cfg.local-domains);
|
||||
|
||||
keyTable = pkgs.writeText "opendkim-KeyTable"
|
||||
(lib.concatStringsSep "\n" (lib.flip map cfg.local-domains
|
||||
(dom: "${dom} ${dom}:${cfg.dkim.selector}:${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.key")));
|
||||
keyTable = pkgs.writeText "opendkim-KeyTable" (lib.concatStringsSep "\n"
|
||||
(lib.flip map cfg.local-domains (dom:
|
||||
"${dom} ${dom}:${cfg.dkim.selector}:${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.key")));
|
||||
signingTable = pkgs.writeText "opendkim-SigningTable"
|
||||
(lib.concatStringsSep "\n" (lib.flip map cfg.local-domains (dom: "${dom} ${dom}")));
|
||||
(lib.concatStringsSep "\n"
|
||||
(lib.flip map cfg.local-domains (dom: "${dom} ${dom}")));
|
||||
|
||||
dkim = config.services.opendkim;
|
||||
args = [ "-f" "-l" ] ++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
|
||||
in
|
||||
{
|
||||
args = [ "-f" "-l" ]
|
||||
++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
|
||||
in {
|
||||
|
||||
options.fudo.mail-server.dkim = {
|
||||
signing = mkOption {
|
||||
|
@ -59,12 +59,12 @@ in
|
|||
type = types.int;
|
||||
default = 2048;
|
||||
description = ''
|
||||
How many bits in generated DKIM keys. RFC6376 advises minimum 1024-bit keys.
|
||||
How many bits in generated DKIM keys. RFC6376 advises minimum 1024-bit keys.
|
||||
|
||||
If you have already deployed a key with a different number of bits than specified
|
||||
here, then you should use a different selector (dkimSelector). In order to get
|
||||
this package to generate a key with the new number of bits, you will either have to
|
||||
change the selector or delete the old key file.
|
||||
If you have already deployed a key with a different number of bits than specified
|
||||
here, then you should use a different selector (dkimSelector). In order to get
|
||||
this package to generate a key with the new number of bits, you will either have to
|
||||
change the selector or delete the old key file.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -81,16 +81,16 @@ in
|
|||
selector = cfg.dkim.selector;
|
||||
domains = "csl:${builtins.concatStringsSep "," cfg.local-domains}";
|
||||
configFile = pkgs.writeText "opendkim.conf" (''
|
||||
Canonicalization relaxed/simple
|
||||
UMask 0002
|
||||
Socket ${dkim.socket}
|
||||
KeyTable file:${keyTable}
|
||||
SigningTable file:${signingTable}
|
||||
'' + (lib.optionalString cfg.debug ''
|
||||
Syslog yes
|
||||
SyslogSuccess yes
|
||||
LogWhy yes
|
||||
''));
|
||||
Canonicalization relaxed/simple
|
||||
UMask 0002
|
||||
Socket ${dkim.socket}
|
||||
KeyTable file:${keyTable}
|
||||
SigningTable file:${signingTable}
|
||||
'' + (lib.optionalString cfg.debug ''
|
||||
Syslog yes
|
||||
SyslogSuccess yes
|
||||
LogWhy yes
|
||||
''));
|
||||
};
|
||||
|
||||
users.users = {
|
||||
|
@ -102,7 +102,8 @@ in
|
|||
systemd.services.opendkim = {
|
||||
preStart = lib.mkForce createAllCerts;
|
||||
serviceConfig = {
|
||||
ExecStart = lib.mkForce "${cfg.dkim.package}/bin/opendkim ${escapeShellArgs args}";
|
||||
ExecStart = lib.mkForce
|
||||
"${cfg.dkim.package}/bin/opendkim ${escapeShellArgs args}";
|
||||
PermissionsStartOnly = lib.mkForce false;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
let
|
||||
domainOpts = { name, ... }: {
|
||||
options = with types; {
|
||||
domain = mkOption {
|
||||
type = str;
|
||||
default = name;
|
||||
};
|
||||
|
||||
servers = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of servers for this Nexus domain.";
|
||||
};
|
||||
|
||||
dns-servers = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of DNS servers for this Nexus domain.";
|
||||
};
|
||||
|
||||
gssapi-realm = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
trusted-networks = mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
records = let
|
||||
recordOpts = { name, ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = str;
|
||||
description = "Name of this record.";
|
||||
default = name;
|
||||
};
|
||||
|
||||
type = mkOption {
|
||||
type = str;
|
||||
description = "Record type of this record.";
|
||||
};
|
||||
|
||||
content = mkOption {
|
||||
type = str;
|
||||
description = "Data associated with this record.";
|
||||
};
|
||||
};
|
||||
};
|
||||
in mkOption {
|
||||
type = listOf (submodule recordOpts);
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
options.fudo.nexus = with types; {
|
||||
domains = mkOption {
|
||||
type = attrsOf (submodule domainOpts);
|
||||
description = "Nexus domain configurations.";
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
312
lib/fudo/nsd.nix
312
lib/fudo/nsd.nix
|
@ -1,8 +1,6 @@
|
|||
# ## NOTE:
|
||||
## This is a copy of the upstream version, which allows for overriding the state directory
|
||||
|
||||
## OBSOLETE
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
@ -10,9 +8,9 @@ with lib;
|
|||
let
|
||||
cfg = config.fudo.nsd;
|
||||
|
||||
username = "nsd";
|
||||
stateDir = cfg.stateDir;
|
||||
pidFile = stateDir + "/var/nsd.pid";
|
||||
username = cfg.user;
|
||||
stateDir = cfg.stateDirectory;
|
||||
pidFile = "${stateDir}/run/nsd.pid";
|
||||
|
||||
# build nsd with the options needed for the given config
|
||||
nsdPkg = pkgs.nsd.override {
|
||||
|
@ -44,6 +42,7 @@ let
|
|||
postBuild = ''
|
||||
echo "checking zone files"
|
||||
cd $out/zones
|
||||
|
||||
for zoneFile in *; do
|
||||
echo "|- checking zone '$out/zones/$zoneFile'"
|
||||
${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || {
|
||||
|
@ -52,9 +51,11 @@ let
|
|||
echo Escaping them is not needed any more. Please make sure \
|
||||
to unescape them where they prefix a variable name.
|
||||
fi
|
||||
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
echo "checking configuration file"
|
||||
# Save original config file including key references...
|
||||
cp $out/nsd.conf{,.orig}
|
||||
|
@ -79,16 +80,20 @@ let
|
|||
server:
|
||||
chroot: "${stateDir}"
|
||||
username: ${username}
|
||||
|
||||
# The directory for zonefile: files. The daemon chdirs here.
|
||||
zonesdir: "${stateDir}"
|
||||
|
||||
# the list of dynamically added zones.
|
||||
database: "${stateDir}/var/nsd.db"
|
||||
pidfile: "${pidFile}"
|
||||
xfrdfile: "${stateDir}/var/xfrd.state"
|
||||
xfrdir: "${stateDir}/tmp"
|
||||
zonelistfile: "${stateDir}/var/zone.list"
|
||||
|
||||
# interfaces
|
||||
${forEach " ip-address: " cfg.interfaces}
|
||||
|
||||
ip-freebind: ${yesOrNo cfg.ipFreebind}
|
||||
hide-version: ${yesOrNo cfg.hideVersion}
|
||||
identity: "${cfg.identity}"
|
||||
|
@ -111,13 +116,16 @@ let
|
|||
${maybeString "version: " cfg.version}
|
||||
xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout}
|
||||
zonefiles-check: ${yesOrNo cfg.zonefilesCheck}
|
||||
|
||||
${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength}
|
||||
${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength}
|
||||
rrl-ratelimit: ${toString cfg.ratelimit.ratelimit}
|
||||
${maybeString "rrl-slip: " cfg.ratelimit.slip}
|
||||
rrl-size: ${toString cfg.ratelimit.size}
|
||||
rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit}
|
||||
|
||||
${keyConfigFile}
|
||||
|
||||
remote-control:
|
||||
control-enable: ${yesOrNo cfg.remoteControl.enable}
|
||||
control-key-file: "${cfg.remoteControl.controlKeyFile}"
|
||||
|
@ -126,7 +134,9 @@ let
|
|||
control-port: ${toString cfg.remoteControl.port}
|
||||
server-key-file: "${cfg.remoteControl.serverKeyFile}"
|
||||
server-cert-file: "${cfg.remoteControl.serverCertFile}"
|
||||
|
||||
${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
|
@ -159,13 +169,16 @@ let
|
|||
${maybeString "outgoing-interface: " zone.outgoingInterface}
|
||||
${forEach " rrl-whitelist: " zone.rrlWhitelist}
|
||||
${maybeString "zonestats: " zone.zoneStats}
|
||||
|
||||
${maybeToString "max-refresh-time: " zone.maxRefreshSecs}
|
||||
${maybeToString "min-refresh-time: " zone.minRefreshSecs}
|
||||
${maybeToString "max-retry-time: " zone.maxRetrySecs}
|
||||
${maybeToString "min-retry-time: " zone.minRetrySecs}
|
||||
|
||||
allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback}
|
||||
${forEach " allow-notify: " zone.allowNotify}
|
||||
${forEach " request-xfr: " zone.requestXFR}
|
||||
|
||||
${forEach " notify: " zone.notify}
|
||||
notify-retry: ${toString zone.notifyRetry}
|
||||
${forEach " provide-xfr: " zone.provideXFR}
|
||||
|
@ -194,7 +207,7 @@ let
|
|||
allowAXFRFallback = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
If NSD as secondary server should be allowed to AXFR if the primary
|
||||
server does not allow IXFR.
|
||||
'';
|
||||
|
@ -208,21 +221,24 @@ let
|
|||
"10.0.0.1-10.0.0.5 my_tsig_key_name"
|
||||
"10.0.3.4&255.255.0.0 BLOCKED"
|
||||
];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Listed primary servers are allowed to notify this secondary server.
|
||||
<screen><![CDATA[
|
||||
Format: <ip> <key-name | NOKEY | BLOCKED>
|
||||
<ip> either a plain IPv4/IPv6 address or range. Valid patters for ranges:
|
||||
* 10.0.0.0/24 # via subnet size
|
||||
* 10.0.0.0&255.255.255.0 # via subnet mask
|
||||
* 10.0.0.1-10.0.0.254 # via range
|
||||
|
||||
Format: `<ip> <key-name | NOKEY | BLOCKED>`
|
||||
|
||||
`<ip>` either a plain IPv4/IPv6 address or range.
|
||||
Valid patters for ranges:
|
||||
* `10.0.0.0/24`: via subnet size
|
||||
* `10.0.0.0&255.255.255.0`: via subnet mask
|
||||
* `10.0.0.1-10.0.0.254`: via range
|
||||
|
||||
A optional port number could be added with a '@':
|
||||
* 2001:1234::1@1234
|
||||
<key-name | NOKEY | BLOCKED>
|
||||
* <key-name> will use the specified TSIG key
|
||||
* NOKEY no TSIG signature is required
|
||||
* BLOCKED notifies from non-listed or blocked IPs will be ignored
|
||||
* ]]></screen>
|
||||
* `2001:1234::1@1234`
|
||||
|
||||
`<key-name | NOKEY | BLOCKED>`
|
||||
* `<key-name>` will use the specified TSIG key
|
||||
* `NOKEY` no TSIG signature is required
|
||||
* `BLOCKED`notifies from non-listed or blocked IPs will be ignored
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -235,7 +251,7 @@ let
|
|||
# to default values, breaking the parent inheriting function.
|
||||
type = types.attrsOf types.anything;
|
||||
default = { };
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Children zones inherit all options of their parents. Attributes
|
||||
defined in a child will overwrite the ones of its parent. Only
|
||||
leaf zones will be actually served. This way it's possible to
|
||||
|
@ -248,29 +264,29 @@ let
|
|||
data = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
The actual zone data. This is the content of your zone file.
|
||||
Use imports or pkgs.lib.readFile if you don't want this data in your config file.
|
||||
'';
|
||||
};
|
||||
|
||||
dnssec = mkEnableOption "DNSSEC";
|
||||
dnssec = mkEnableOption (lib.mdDoc "DNSSEC");
|
||||
|
||||
dnssecPolicy = {
|
||||
algorithm = mkOption {
|
||||
type = types.str;
|
||||
default = "RSASHA256";
|
||||
description = "Which algorithm to use for DNSSEC";
|
||||
description = lib.mdDoc "Which algorithm to use for DNSSEC";
|
||||
};
|
||||
keyttl = mkOption {
|
||||
type = types.str;
|
||||
default = "1h";
|
||||
description = "TTL for dnssec records";
|
||||
description = lib.mdDoc "TTL for dnssec records";
|
||||
};
|
||||
coverage = mkOption {
|
||||
type = types.str;
|
||||
default = "1y";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time.
|
||||
'';
|
||||
};
|
||||
|
@ -282,7 +298,7 @@ let
|
|||
postPublish = "1w";
|
||||
rollPeriod = "1mo";
|
||||
};
|
||||
description = "Key policy for zone signing keys";
|
||||
description = lib.mdDoc "Key policy for zone signing keys";
|
||||
};
|
||||
ksk = mkOption {
|
||||
type = keyPolicy;
|
||||
|
@ -292,14 +308,22 @@ let
|
|||
postPublish = "1mo";
|
||||
rollPeriod = "0";
|
||||
};
|
||||
description = "Key policy for key signing keys";
|
||||
description = lib.mdDoc "Key policy for key signing keys";
|
||||
};
|
||||
};
|
||||
|
||||
ksk = {
|
||||
keyFile = mkOption {
|
||||
type = types.str;
|
||||
description =
|
||||
"Location of the zone key-signing key file on the local host.";
|
||||
};
|
||||
};
|
||||
|
||||
maxRefreshSecs = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Limit refresh time for secondary zones. This is the timer which
|
||||
checks to see if the zone has to be refetched when it expires.
|
||||
Normally the value from the SOA record is used, but this option
|
||||
|
@ -310,7 +334,7 @@ let
|
|||
minRefreshSecs = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Limit refresh time for secondary zones.
|
||||
'';
|
||||
};
|
||||
|
@ -318,7 +342,7 @@ let
|
|||
maxRetrySecs = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Limit retry time for secondary zones. This is the timeout after
|
||||
a failed fetch attempt for the zone. Normally the value from
|
||||
the SOA record is used, but this option restricts that value.
|
||||
|
@ -328,7 +352,7 @@ let
|
|||
minRetrySecs = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Limit retry time for secondary zones.
|
||||
'';
|
||||
};
|
||||
|
@ -337,23 +361,24 @@ let
|
|||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
This primary server will notify all given secondary servers about
|
||||
zone changes.
|
||||
<screen><![CDATA[
|
||||
Format: <ip> <key-name | NOKEY>
|
||||
<ip> a plain IPv4/IPv6 address with on optional port number (ip@port)
|
||||
<key-name | NOKEY>
|
||||
* <key-name> sign notifies with the specified key
|
||||
* NOKEY don't sign notifies
|
||||
]]></screen>
|
||||
|
||||
Format: `<ip> <key-name | NOKEY>`
|
||||
|
||||
`<ip>` a plain IPv4/IPv6 address with on optional port number (ip@port)
|
||||
|
||||
`<key-name | NOKEY>`
|
||||
- `<key-name>` sign notifies with the specified key
|
||||
- `NOKEY` don't sign notifies
|
||||
'';
|
||||
};
|
||||
|
||||
notifyRetry = mkOption {
|
||||
type = types.int;
|
||||
default = 5;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Specifies the number of retries for failed notifies. Set this along with notify.
|
||||
'';
|
||||
};
|
||||
|
@ -362,8 +387,8 @@ let
|
|||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "2000::1@1234";
|
||||
description = ''
|
||||
This address will be used for zone-transfere requests if configured
|
||||
description = lib.mdDoc ''
|
||||
This address will be used for zone-transfer requests if configured
|
||||
as a secondary server or notifications in case of a primary server.
|
||||
Supply either a plain IPv4 or IPv6 address with an optional port
|
||||
number (ip@port).
|
||||
|
@ -374,17 +399,17 @@ let
|
|||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED
|
||||
address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40
|
||||
address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40
|
||||
'';
|
||||
};
|
||||
|
||||
requestXFR = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Format: <code>[AXFR|UDP] <ip-address> <key-name | NOKEY></code>
|
||||
description = lib.mdDoc ''
|
||||
Format: `[AXFR|UDP] <ip-address> <key-name | NOKEY>`
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -403,7 +428,7 @@ let
|
|||
"all"
|
||||
]);
|
||||
default = [ ];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whitelists the given rrl-types.
|
||||
'';
|
||||
};
|
||||
|
@ -412,7 +437,7 @@ let
|
|||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "%s";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
When set to something distinct to null NSD is able to collect
|
||||
statistics per zone. All statistics of this zone(s) will be added
|
||||
to the group specified by this given name. Use "%s" to use the zones
|
||||
|
@ -427,19 +452,20 @@ let
|
|||
options = {
|
||||
keySize = mkOption {
|
||||
type = types.int;
|
||||
description = "Key size in bits";
|
||||
description = lib.mdDoc "Key size in bits";
|
||||
};
|
||||
prePublish = mkOption {
|
||||
type = types.str;
|
||||
description = "How long in advance to publish new keys";
|
||||
description = lib.mdDoc "How long in advance to publish new keys";
|
||||
};
|
||||
postPublish = mkOption {
|
||||
type = types.str;
|
||||
description = "How long after deactivation to keep a key in the zone";
|
||||
description =
|
||||
lib.mdDoc "How long after deactivation to keep a key in the zone";
|
||||
};
|
||||
rollPeriod = mkOption {
|
||||
type = types.str;
|
||||
description = "How frequently to change keys";
|
||||
description = lib.mdDoc "How frequently to change keys";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -450,20 +476,42 @@ let
|
|||
dnssec = dnssecZones != { };
|
||||
|
||||
dnssecTools = pkgs.bind.override { enablePython = true; };
|
||||
ldnsTools = pkgs.ldns.examples;
|
||||
|
||||
signZones = optionalString dnssec ''
|
||||
signZones = let
|
||||
dnssecZoneNames = attrNames dnssecZones;
|
||||
mkdirCmds = map (zone: ''
|
||||
mkdir -p ${stateDir}/dnssec/${zone}
|
||||
chown ${username}:${username} ${stateDir}/dnssec/${zone}
|
||||
chmod 0600 ${stateDir}/dnssec/${zone}
|
||||
'') (attrNames dnssecZones);
|
||||
in optionalString dnssec ''
|
||||
mkdir -p ${stateDir}/dnssec
|
||||
chown ${username}:${username} ${stateDir}/dnssec
|
||||
chmod 0600 ${stateDir}/dnssec
|
||||
${concatStrings (mapAttrsToList signZone dnssecZones)}
|
||||
${concatStringsSep "\n" mkdirCmds}
|
||||
|
||||
${concatStringsSep "\n" (mapAttrsToList signZone dnssecZones)}
|
||||
'';
|
||||
signZone = name: zone: ''
|
||||
${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${
|
||||
policyFile name zone.dnssecPolicy
|
||||
} ${name}
|
||||
${dnssecTools}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name}
|
||||
${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name}
|
||||
${pkgs.nsdSignZone}/bin/nsd-sign-zone \
|
||||
--verbose \
|
||||
--domain=${name} \
|
||||
--ksk-file=${zone.ksk.keyFile} \
|
||||
--zsk-metadata=${stateDir}/dnssec/${name}/metadata.json \
|
||||
${stateDir}/zones/${name}
|
||||
${nsdPkg}/sbin/nsd-checkzone \
|
||||
${name} \
|
||||
${stateDir}/zones/${name}.signed &&
|
||||
mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name}
|
||||
'';
|
||||
# signZone = name: zone: ''
|
||||
# ${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${
|
||||
# policyFile name zone.dnssecPolicy
|
||||
# } ${name}
|
||||
# ${dnssecTools}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name}
|
||||
# ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name}
|
||||
# '';
|
||||
policyFile = name: policy:
|
||||
pkgs.writeText "${name}.policy" ''
|
||||
zone ${name} {
|
||||
|
@ -484,20 +532,26 @@ in {
|
|||
# options are ordered alphanumerically
|
||||
options.fudo.nsd = {
|
||||
|
||||
enable = mkEnableOption "NSD authoritative DNS server";
|
||||
enable = mkEnableOption (lib.mdDoc "NSD authoritative DNS server");
|
||||
|
||||
stateDir = mkOption {
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "Directory at which to store NSD state data.";
|
||||
description = "User as which to run the NSD server.";
|
||||
default = "nsd";
|
||||
};
|
||||
|
||||
stateDirectory = mkOption {
|
||||
type = types.str;
|
||||
description = "Location at which to store NSD state.";
|
||||
default = "/var/lib/nsd";
|
||||
};
|
||||
|
||||
bind8Stats = mkEnableOption "BIND8 like statistics";
|
||||
bind8Stats = mkEnableOption (lib.mdDoc "BIND8 like statistics");
|
||||
|
||||
dnssecInterval = mkOption {
|
||||
type = types.str;
|
||||
default = "1h";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
How often to check whether dnssec key rollover is required
|
||||
'';
|
||||
};
|
||||
|
@ -505,7 +559,7 @@ in {
|
|||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Extra nsd config.
|
||||
'';
|
||||
};
|
||||
|
@ -513,7 +567,7 @@ in {
|
|||
hideVersion = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries.
|
||||
'';
|
||||
};
|
||||
|
@ -521,7 +575,7 @@ in {
|
|||
identity = mkOption {
|
||||
type = types.str;
|
||||
default = "unidentified server";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Identify the server (CH TXT ID.SERVER entry).
|
||||
'';
|
||||
};
|
||||
|
@ -529,7 +583,7 @@ in {
|
|||
interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "127.0.0.0" "::1" ];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
What addresses the server should listen to.
|
||||
'';
|
||||
};
|
||||
|
@ -537,7 +591,7 @@ in {
|
|||
ipFreebind = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether to bind to nonlocal addresses and interfaces that are down.
|
||||
Similar to ip-transparent.
|
||||
'';
|
||||
|
@ -546,7 +600,7 @@ in {
|
|||
ipTransparent = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Allow binding to non local addresses.
|
||||
'';
|
||||
};
|
||||
|
@ -554,7 +608,7 @@ in {
|
|||
ipv4 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether to listen on IPv4 connections.
|
||||
'';
|
||||
};
|
||||
|
@ -562,7 +616,7 @@ in {
|
|||
ipv4EDNSSize = mkOption {
|
||||
type = types.int;
|
||||
default = 4096;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Preferred EDNS buffer size for IPv4.
|
||||
'';
|
||||
};
|
||||
|
@ -570,7 +624,7 @@ in {
|
|||
ipv6 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether to listen on IPv6 connections.
|
||||
'';
|
||||
};
|
||||
|
@ -578,7 +632,7 @@ in {
|
|||
ipv6EDNSSize = mkOption {
|
||||
type = types.int;
|
||||
default = 4096;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Preferred EDNS buffer size for IPv6.
|
||||
'';
|
||||
};
|
||||
|
@ -586,7 +640,7 @@ in {
|
|||
logTimeAscii = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Log time in ascii, if false then in unix epoch seconds.
|
||||
'';
|
||||
};
|
||||
|
@ -594,15 +648,15 @@ in {
|
|||
nsid = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
NSID identity (hex string, or "ascii_somestring").
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
type = types.port;
|
||||
default = 53;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Port the service should bind do.
|
||||
'';
|
||||
};
|
||||
|
@ -611,7 +665,7 @@ in {
|
|||
type = types.bool;
|
||||
default = pkgs.stdenv.isLinux;
|
||||
defaultText = literalExpression "pkgs.stdenv.isLinux";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether to enable SO_REUSEPORT on all used sockets. This lets multiple
|
||||
processes bind to the same port. This speeds up operation especially
|
||||
if the server count is greater than one and makes fast restarts less
|
||||
|
@ -622,18 +676,18 @@ in {
|
|||
rootServer = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether this server will be a root server (a DNS root server, you
|
||||
usually don't want that).
|
||||
'';
|
||||
};
|
||||
|
||||
roundRobin = mkEnableOption "round robin rotation of records";
|
||||
roundRobin = mkEnableOption (lib.mdDoc "round robin rotation of records");
|
||||
|
||||
serverCount = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Number of NSD servers to fork. Put the number of CPUs to use here.
|
||||
'';
|
||||
};
|
||||
|
@ -641,7 +695,7 @@ in {
|
|||
statistics = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Statistics are produced every number of seconds. Prints to log.
|
||||
If null no statistics are logged.
|
||||
'';
|
||||
|
@ -650,7 +704,7 @@ in {
|
|||
tcpCount = mkOption {
|
||||
type = types.int;
|
||||
default = 100;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Maximum number of concurrent TCP connections per server.
|
||||
'';
|
||||
};
|
||||
|
@ -658,7 +712,7 @@ in {
|
|||
tcpQueryCount = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Maximum number of queries served on a single TCP connection.
|
||||
0 means no maximum.
|
||||
'';
|
||||
|
@ -667,7 +721,7 @@ in {
|
|||
tcpTimeout = mkOption {
|
||||
type = types.int;
|
||||
default = 120;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
TCP timeout in seconds.
|
||||
'';
|
||||
};
|
||||
|
@ -675,7 +729,7 @@ in {
|
|||
verbosity = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Verbosity level.
|
||||
'';
|
||||
};
|
||||
|
@ -683,7 +737,7 @@ in {
|
|||
version = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
The version string replied for CH TXT version.server and version.bind
|
||||
queries. Will use the compiled package version on null.
|
||||
See hideVersion for enabling/disabling this responses.
|
||||
|
@ -693,7 +747,7 @@ in {
|
|||
xfrdReloadTimeout = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Number of seconds between reloads triggered by xfrd.
|
||||
'';
|
||||
};
|
||||
|
@ -701,7 +755,7 @@ in {
|
|||
zonefilesCheck = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Whether to check mtime of all zone files on start and sighup.
|
||||
'';
|
||||
};
|
||||
|
@ -713,14 +767,14 @@ in {
|
|||
algorithm = mkOption {
|
||||
type = types.str;
|
||||
default = "hmac-sha256";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Authentication algorithm for this key.
|
||||
'';
|
||||
};
|
||||
|
||||
keyFile = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Path to the file which contains the actual base64 encoded
|
||||
key. The key will be copied into "${stateDir}/private" before
|
||||
NSD starts. The copied file is only accessibly by the NSD
|
||||
|
@ -738,19 +792,19 @@ in {
|
|||
};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Define your TSIG keys here.
|
||||
'';
|
||||
};
|
||||
|
||||
ratelimit = {
|
||||
|
||||
enable = mkEnableOption "ratelimit capabilities";
|
||||
enable = mkEnableOption (lib.mdDoc "ratelimit capabilities");
|
||||
|
||||
ipv4PrefixLength = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
IPv4 prefix length. Addresses are grouped by netblock.
|
||||
'';
|
||||
};
|
||||
|
@ -758,7 +812,7 @@ in {
|
|||
ipv6PrefixLength = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
IPv6 prefix length. Addresses are grouped by netblock.
|
||||
'';
|
||||
};
|
||||
|
@ -766,7 +820,7 @@ in {
|
|||
ratelimit = mkOption {
|
||||
type = types.int;
|
||||
default = 200;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Max qps allowed from any query source.
|
||||
0 means unlimited. With an verbosity of 2 blocked and
|
||||
unblocked subnets will be logged.
|
||||
|
@ -776,7 +830,7 @@ in {
|
|||
slip = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Number of packets that get discarded before replying a SLIP response.
|
||||
0 disables SLIP responses. 1 will make every response a SLIP response.
|
||||
'';
|
||||
|
@ -785,7 +839,7 @@ in {
|
|||
size = mkOption {
|
||||
type = types.int;
|
||||
default = 1000000;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Size of the hashtable. More buckets use more memory but lower
|
||||
the chance of hash hash collisions.
|
||||
'';
|
||||
|
@ -794,7 +848,7 @@ in {
|
|||
whitelistRatelimit = mkOption {
|
||||
type = types.int;
|
||||
default = 2000;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Max qps allowed from whitelisted sources.
|
||||
0 means unlimited. Set the rrl-whitelist option for specific
|
||||
queries to apply this limit instead of the default to them.
|
||||
|
@ -805,12 +859,12 @@ in {
|
|||
|
||||
remoteControl = {
|
||||
|
||||
enable = mkEnableOption "remote control via nsd-control";
|
||||
enable = mkEnableOption (lib.mdDoc "remote control via nsd-control");
|
||||
|
||||
controlCertFile = mkOption {
|
||||
type = types.path;
|
||||
default = "/etc/nsd/nsd_control.pem";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Path to the client certificate signed with the server certificate.
|
||||
This file is used by nsd-control and generated by nsd-control-setup.
|
||||
'';
|
||||
|
@ -819,7 +873,7 @@ in {
|
|||
controlKeyFile = mkOption {
|
||||
type = types.path;
|
||||
default = "/etc/nsd/nsd_control.key";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Path to the client private key, which is used by nsd-control
|
||||
but not by the server. This file is generated by nsd-control-setup.
|
||||
'';
|
||||
|
@ -828,15 +882,15 @@ in {
|
|||
interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "127.0.0.1" "::1" ];
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Which interfaces NSD should bind to for remote control.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
type = types.port;
|
||||
default = 8952;
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Port number for remote control operations (uses TLS over TCP).
|
||||
'';
|
||||
};
|
||||
|
@ -844,7 +898,7 @@ in {
|
|||
serverCertFile = mkOption {
|
||||
type = types.path;
|
||||
default = "/etc/nsd/nsd_server.pem";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Path to the server self signed certificate, which is used by the server
|
||||
but and by nsd-control. This file is generated by nsd-control-setup.
|
||||
'';
|
||||
|
@ -853,7 +907,7 @@ in {
|
|||
serverKeyFile = mkOption {
|
||||
type = types.path;
|
||||
default = "/etc/nsd/nsd_server.key";
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Path to the server private key, which is used by the server
|
||||
but not by nsd-control. This file is generated by nsd-control-setup.
|
||||
'';
|
||||
|
@ -886,6 +940,7 @@ in {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
"example.net." = {
|
||||
provideXFR = [ "10.3.2.1 NOKEY" ];
|
||||
data = '''
|
||||
|
@ -894,7 +949,7 @@ in {
|
|||
};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
description = lib.mdDoc ''
|
||||
Define your zones here. Zones can cascade other zones and therefore
|
||||
inherit settings from parent zones. Look at the definition of
|
||||
children to learn about inheritance and child zones.
|
||||
|
@ -918,14 +973,15 @@ in {
|
|||
etc."nsd/nsd.conf".source = "${configFile}/nsd.conf";
|
||||
};
|
||||
|
||||
users.groups.${username}.gid = config.ids.gids.nsd;
|
||||
|
||||
users.users.${username} = {
|
||||
description = "NSD service user";
|
||||
home = stateDir;
|
||||
createHome = true;
|
||||
uid = config.ids.uids.nsd;
|
||||
group = username;
|
||||
users = {
|
||||
users."${username}" = {
|
||||
description = "NSD service user";
|
||||
home = stateDir;
|
||||
createHome = true;
|
||||
uid = config.ids.uids.nsd;
|
||||
group = username;
|
||||
};
|
||||
groups."${username}".gid = config.ids.gids.nsd;
|
||||
};
|
||||
|
||||
systemd.services.nsd = {
|
||||
|
@ -947,19 +1003,24 @@ in {
|
|||
preStart = ''
|
||||
rm -Rf "${stateDir}/private/"
|
||||
rm -Rf "${stateDir}/tmp/"
|
||||
|
||||
mkdir -m 0700 -p "${stateDir}/private"
|
||||
mkdir -m 0700 -p "${stateDir}/tmp"
|
||||
mkdir -m 0700 -p "${stateDir}/var"
|
||||
|
||||
cat > "${stateDir}/don't touch anything in here" << EOF
|
||||
Everything in this directory except NSD's state in var and dnssec
|
||||
is automatically generated and will be purged and redeployed by
|
||||
the nsd.service pre-start script.
|
||||
EOF
|
||||
|
||||
chown ${username}:${username} -R "${stateDir}/private"
|
||||
chown ${username}:${username} -R "${stateDir}/tmp"
|
||||
chown ${username}:${username} -R "${stateDir}/var"
|
||||
|
||||
rm -rf "${stateDir}/zones"
|
||||
cp -rL "${nsdEnv}/zones" "${stateDir}/zones"
|
||||
|
||||
${copyKeys}
|
||||
'';
|
||||
};
|
||||
|
@ -968,6 +1029,7 @@ in {
|
|||
description = "Automatic DNSSEC key rollover";
|
||||
|
||||
wantedBy = [ "nsd.service" ];
|
||||
before = [ "nsd.service" ];
|
||||
|
||||
timerConfig = {
|
||||
OnActiveSec = cfg.dnssecInterval;
|
||||
|
@ -981,6 +1043,22 @@ in {
|
|||
wantedBy = [ "nsd.service" ];
|
||||
before = [ "nsd.service" ];
|
||||
|
||||
preStart = let
|
||||
zoneRotateCmd = zone:
|
||||
let zoneDir = "${stateDir}/dnssec/${zone}";
|
||||
in ''
|
||||
mkdir -p ${zoneDir}
|
||||
${pkgs.nsdRotateKeys}/bin/nsd-rotate-keys \
|
||||
--key-directory=${zoneDir} \
|
||||
--validity-period=30 \
|
||||
--period-overlap=10 \
|
||||
--metadata=${zoneDir}/metadata.json \
|
||||
--verbose \
|
||||
${zone}
|
||||
'';
|
||||
zoneRotateCmds = map zoneRotateCmd (lib.attrNames dnssecZones);
|
||||
in lib.concatStringsSep "\n" zoneRotateCmds;
|
||||
|
||||
script = signZones;
|
||||
|
||||
postStop = ''
|
||||
|
|
|
@ -218,7 +218,7 @@ in {
|
|||
cfg.secret-paths;
|
||||
|
||||
in {
|
||||
tmpfiles.rules = host-secret-paths ++ build-secret-paths;
|
||||
tmpfiles.rules = unique (host-secret-paths ++ build-secret-paths);
|
||||
|
||||
services = host-secret-services // {
|
||||
fudo-secrets-watcher = mkIf (length cfg.secret-paths > 0) {
|
||||
|
|
|
@ -139,9 +139,16 @@ let
|
|||
|
||||
local-gateway = mkOption {
|
||||
type = nullOr str;
|
||||
description = "If this is a NAT site, this should point to the host acting as network gateway.";
|
||||
description =
|
||||
"If this is a NAT site, this should point to the host acting as network gateway.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
nexus.domains = mkOption {
|
||||
type = listOf str;
|
||||
description = "Nexus domains to which hosts at this site belong.";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,20 +4,21 @@ with lib;
|
|||
let passwd = import ../passwd.nix { inherit lib; };
|
||||
|
||||
in rec {
|
||||
encryptedFSOpts = { ... }:
|
||||
encryptedFSOpts = { name, ... }:
|
||||
let
|
||||
mountpoint = { mp, ... }: {
|
||||
mountpoint = { name, ... }: {
|
||||
options = with types; {
|
||||
mountpoint = mkOption {
|
||||
type = str;
|
||||
description = "Path at which to mount the filesystem.";
|
||||
default = mp;
|
||||
default = name;
|
||||
};
|
||||
|
||||
options = mkOption {
|
||||
type = listOf str;
|
||||
description =
|
||||
"List of filesystem options specific to this mountpoint (eg: subvol).";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
|
@ -59,6 +60,18 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
type = mkOption {
|
||||
type = enum [ "luks" "luks2" ];
|
||||
description = "Type of the LUKS encryption.";
|
||||
default = "luks";
|
||||
};
|
||||
|
||||
remove-key = mkOption {
|
||||
type = bool;
|
||||
description = "Remove key once the filesystem is decrypted.";
|
||||
default = true;
|
||||
};
|
||||
|
||||
filesystem-type = mkOption {
|
||||
type = str;
|
||||
description = "Filesystem type of the decrypted filesystem.";
|
||||
|
@ -67,6 +80,7 @@ in rec {
|
|||
options = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of filesystem options with which to mount.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
mountpoints = mkOption {
|
||||
|
|
Loading…
Reference in New Issue