nixpkgs/nixos/modules/services/networking/unbound.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

315 lines
11 KiB
Nix
Raw Normal View History

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.unbound;
yesOrNo = v: if v then "yes" else "no";
toOption = indent: n: v: "${indent}${toString n}: ${v}";
toConf = indent: n: v:
if builtins.isFloat v then (toOption indent n (builtins.toJSON v))
else if isInt v then (toOption indent n (toString v))
else if isBool v then (toOption indent n (yesOrNo v))
else if isString v then (toOption indent n v)
else if isList v then (concatMapStringsSep "\n" (toConf indent n) v)
else if isAttrs v then (concatStringsSep "\n" (
["${indent}${n}:"] ++ (
mapAttrsToList (toConf "${indent} ") v
)
))
else throw (traceSeq v "services.unbound.settings: unexpected type");
confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]);
confServer = concatStringsSep "\n" (mapAttrsToList (toConf " ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ]));
confFile = pkgs.writeText "unbound.conf" ''
server:
${optionalString (cfg.settings.server.define-tag != "") (toOption " " "define-tag" cfg.settings.server.define-tag)}
${confServer}
${confNoServer}
'';
rootTrustAnchorFile = "${cfg.stateDir}/root.key";
in {
###### interface
options = {
services.unbound = {
2016-08-30 18:28:30 +01:00
enable = mkEnableOption "Unbound domain name server";
2019-12-12 23:49:47 +00:00
package = mkOption {
type = types.package;
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
default = pkgs.unbound-with-systemd;
defaultText = literalExpression "pkgs.unbound-with-systemd";
2019-12-12 23:49:47 +00:00
description = "The unbound package to use";
};
user = mkOption {
type = types.str;
default = "unbound";
description = "User account under which unbound runs.";
};
group = mkOption {
type = types.str;
default = "unbound";
description = "Group under which unbound runs.";
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/unbound";
description = "Directory holding all state for unbound to run.";
};
resolveLocalQueries = mkOption {
type = types.bool;
default = true;
description = ''
Whether unbound should resolve local queries (i.e. add 127.0.0.1 to
/etc/resolv.conf).
'';
};
enableRootTrustAnchor = mkOption {
default = true;
type = types.bool;
description = "Use and update root trust anchor for DNSSEC validation.";
};
localControlSocketPath = mkOption {
default = null;
# FIXME: What is the proper type here so users can specify strings,
# paths and null?
# My guess would be `types.nullOr (types.either types.str types.path)`
# but I haven't verified yet.
type = types.nullOr types.str;
example = "/run/unbound/unbound.ctl";
description = ''
When not set to <literal>null</literal> this option defines the path
at which the unbound remote control socket should be created at. The
socket will be owned by the unbound user (<literal>unbound</literal>)
and group will be <literal>nogroup</literal>.
Users that should be permitted to access the socket must be in the
<literal>config.services.unbound.group</literal> group.
If this option is <literal>null</literal> remote control will not be
enabled. Unbounds default values apply.
'';
};
settings = mkOption {
default = {};
type = with types; submodule {
freeformType = let
validSettingsPrimitiveTypes = oneOf [ int str bool float ];
validSettingsTypes = oneOf [ validSettingsPrimitiveTypes (listOf validSettingsPrimitiveTypes) ];
settingsType = oneOf [ str (attrsOf validSettingsTypes) ];
in attrsOf (oneOf [ settingsType (listOf settingsType) ])
// { description = ''
unbound.conf configuration type. The format consist of an attribute
set of settings. Each settings can be either one value, a list of
values or an attribute set. The allowed values are integers,
strings, booleans or floats.
'';
};
options = {
remote-control.control-enable = mkOption {
type = bool;
default = false;
internal = true;
};
};
};
example = literalExpression ''
{
server = {
interface = [ "127.0.0.1" ];
};
forward-zone = [
{
name = ".";
forward-addr = "1.1.1.1@853#cloudflare-dns.com";
}
{
name = "example.org.";
forward-addr = [
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
}
];
remote-control.control-enable = true;
};
'';
description = ''
Declarative Unbound configuration
See the <citerefentry><refentrytitle>unbound.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> manpage for a list of
available options.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
services.unbound.settings = {
server = {
directory = mkDefault cfg.stateDir;
username = cfg.user;
chroot = ''""'';
pidfile = ''""'';
# when running under systemd there is no need to daemonize
do-daemonize = false;
interface = mkDefault ([ "127.0.0.1" ] ++ (optional config.networking.enableIPv6 "::1"));
access-control = mkDefault ([ "127.0.0.0/8 allow" ] ++ (optional config.networking.enableIPv6 "::1/128 allow"));
auto-trust-anchor-file = mkIf cfg.enableRootTrustAnchor rootTrustAnchorFile;
tls-cert-bundle = mkDefault "/etc/ssl/certs/ca-certificates.crt";
# prevent race conditions on system startup when interfaces are not yet
# configured
ip-freebind = mkDefault true;
define-tag = mkDefault "";
};
remote-control = {
control-enable = mkDefault false;
control-interface = mkDefault ([ "127.0.0.1" ] ++ (optional config.networking.enableIPv6 "::1"));
server-key-file = mkDefault "${cfg.stateDir}/unbound_server.key";
server-cert-file = mkDefault "${cfg.stateDir}/unbound_server.pem";
control-key-file = mkDefault "${cfg.stateDir}/unbound_control.key";
control-cert-file = mkDefault "${cfg.stateDir}/unbound_control.pem";
} // optionalAttrs (cfg.localControlSocketPath != null) {
control-enable = true;
control-interface = cfg.localControlSocketPath;
};
};
2019-12-12 23:49:47 +00:00
environment.systemPackages = [ cfg.package ];
users.users = mkIf (cfg.user == "unbound") {
unbound = {
description = "unbound daemon user";
isSystemUser = true;
group = cfg.group;
};
};
users.groups = mkIf (cfg.group == "unbound") {
unbound = {};
};
networking = mkIf cfg.resolveLocalQueries {
resolvconf = {
useLocalResolver = mkDefault true;
};
networkmanager.dns = "unbound";
};
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
environment.etc."unbound/unbound.conf".source = confFile;
systemd.services.unbound = {
2016-08-30 18:30:28 +01:00
description = "Unbound recursive Domain Name Server";
after = [ "network.target" ];
before = [ "nss-lookup.target" ];
wantedBy = [ "multi-user.target" "nss-lookup.target" ];
path = mkIf cfg.settings.remote-control.control-enable [ pkgs.openssl ];
preStart = ''
${optionalString cfg.enableRootTrustAnchor ''
${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
''}
${optionalString cfg.settings.remote-control.control-enable ''
${cfg.package}/bin/unbound-control-setup -d ${cfg.stateDir}
''}
2014-08-27 02:24:09 +01:00
'';
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
restartTriggers = [
confFile
];
2014-08-27 02:24:09 +01:00
serviceConfig = {
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
ExecStart = "${cfg.package}/bin/unbound -p -d -c /etc/unbound/unbound.conf";
ExecReload = "+/run/current-system/sw/bin/kill -HUP $MAINPID";
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
NotifyAccess = "main";
Type = "notify";
# FIXME: Which of these do we actualy need, can we drop the chroot flag?
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
AmbientCapabilities = [
"CAP_NET_BIND_SERVICE"
"CAP_NET_RAW"
"CAP_SETGID"
"CAP_SETUID"
"CAP_SYS_CHROOT"
"CAP_SYS_RESOURCE"
];
User = cfg.user;
Group = cfg.group;
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
PrivateTmp = true;
ProtectHome = true;
ProtectControlGroups = true;
ProtectKernelModules = true;
ProtectSystem = "strict";
RuntimeDirectory = "unbound";
ConfigurationDirectory = "unbound";
StateDirectory = "unbound";
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" "AF_UNIX" ];
nixos/modules/services/networking/unbound: update systemd unit Previously we just applied a very minimal set of restrictions and trusted unbound to properly drop root privs and capabilities. With this change I am (for the most part) just using the upstream example unit file for unbound. The main difference is that we start unbound was `unbound` user with the required capabilities instead of letting unbound do the chroot & uid/gid changes. The upstream unit configuration this is based on is a lot stricter with all kinds of permissions then our previous variant. It also came with the default of having the `Type` set to `notify`, therefore we are also using the `unbound-with-systemd` package here. Unbound will start up, read the configuration files and start listening on the configured ports before systemd will declare the unit "running". This will likely help with startup order and the occasional race condition during system activation where the DNS service is started but not yet ready to answer queries. Aditionally to the much stricter runtime environmet I removed the `/dev/urandom` mount lines we previously had in the code (that would randomly fail during `stop`-phase). The `preStart` script is now only required if we enabled the trust anchor updates (which are still enabled by default). Another beneefit of the refactoring is that we can now issue reloads via either `pkill -HUP unbound` or `systemctl reload unbound` to reload the running configuration without taking the daemon offline. A prerequisite of this was that unbound configuration is available on a well known path on the file system. I went for /etc/unbound/unbound.conf as that is the default in the CLI tooling which in turn enables us to use `unbound-control` without passing a custom configuration location.
2020-05-07 12:17:14 +01:00
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"~@clock"
"@cpu-emulation"
"@debug"
"@keyring"
"@module"
"mount"
"@obsolete"
"@resources"
];
RestrictNamespaces = true;
LockPersonality = true;
RestrictSUIDSGID = true;
Restart = "on-failure";
RestartSec = "5s";
2014-08-27 02:24:09 +01:00
};
};
};
imports = [
(mkRenamedOptionModule [ "services" "unbound" "interfaces" ] [ "services" "unbound" "settings" "server" "interface" ])
(mkChangedOptionModule [ "services" "unbound" "allowedAccess" ] [ "services" "unbound" "settings" "server" "access-control" ] (
config: map (value: "${value} allow") (getAttrFromPath [ "services" "unbound" "allowedAccess" ] config)
))
(mkRemovedOptionModule [ "services" "unbound" "forwardAddresses" ] ''
Add a new setting:
services.unbound.settings.forward-zone = [{
name = ".";
forward-addr = [ # Your current services.unbound.forwardAddresses ];
}];
If any of those addresses are local addresses (127.0.0.1 or ::1), you must
also set services.unbound.settings.server.do-not-query-localhost to false.
'')
(mkRemovedOptionModule [ "services" "unbound" "extraConfig" ] ''
You can use services.unbound.settings to add any configuration you want.
'')
];
}