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