Merge pull request #101218 from andir/unbound-systemd

Changed files
+475 -43
nixos
doc
manual
release-notes
modules
services
networking
tests
pkgs
tools
networking
unbound
top-level
+56
nixos/doc/manual/release-notes/rl-2103.xml
···
All services should use <xref linkend="opt-systemd.services._name_.startLimitIntervalSec" /> or <literal>StartLimitIntervalSec</literal> in <xref linkend="opt-systemd.services._name_.unitConfig" /> instead.
</para>
</listitem>
</itemizedlist>
</section>
</section>
···
All services should use <xref linkend="opt-systemd.services._name_.startLimitIntervalSec" /> or <literal>StartLimitIntervalSec</literal> in <xref linkend="opt-systemd.services._name_.unitConfig" /> instead.
</para>
</listitem>
+
<listitem>
+
<para>
+
The Unbound DNS resolver service (<literal>services.unbound</literal>) has been refactored to allow reloading, control sockets and to fix startup ordering issues.
+
</para>
+
+
<para>
+
It is now possible to enable a local UNIX control socket for unbound by setting the <xref linkend="opt-services.unbound.localControlSocketPath" />
+
option.
+
</para>
+
+
<para>
+
Previously we just applied a very minimal set of restrictions and
+
trusted unbound to properly drop root privs and capabilities.
+
</para>
+
+
<para>
+
As of this we are (for the most part) just using the upstream
+
example unit file for unbound. The main difference is that we start
+
unbound as <literal>unbound</literal> user with the required capabilities instead of
+
letting unbound do the chroot &amp; uid/gid changes.
+
</para>
+
+
<para>
+
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 <literal>Type</literal> set to <literal>notify</literal>, therefore we are now also
+
using the <literal>unbound-with-systemd</literal> package here. Unbound will start up,
+
read the configuration files and start listening on the configured ports
+
before systemd will declare the unit <literal>active (running)</literal>.
+
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. Services depending on <literal>nss-lookup.target</literal> or <literal>unbound.service</literal>
+
are now be able to use unbound when those targets have been reached.
+
</para>
+
+
<para>
+
Aditionally to the much stricter runtime environmet the
+
<literal>/dev/urandom</literal> mount lines we previously had in the code (that would
+
randomly failed during the stop-phase) have been removed as systemd will take care of those for us.
+
</para>
+
+
<para>
+
The <literal>preStart</literal> script is now only required if we enabled the trust
+
anchor updates (which are still enabled by default).
+
</para>
+
+
<para>
+
Another benefit of the refactoring is that we can now issue reloads via
+
either <literal>pkill -HUP unbound</literal> and <literal>systemctl reload unbound</literal> 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. We are using the path <literal>/etc/unbound/unbound.conf</literal> as that is the
+
default in the CLI tooling which in turn enables us to use
+
<literal>unbound-control</literal> without passing a custom configuration location.
+
</para>
+
</listitem>
</itemizedlist>
</section>
</section>
+104 -33
nixos/modules/services/networking/unbound.nix
···
{ config, lib, pkgs, ... }:
with lib;
-
let
-
cfg = config.services.unbound;
stateDir = "/var/lib/unbound";
···
forward =
optionalString (any isLocalAddress cfg.forwardAddresses) ''
do-not-query-localhost: no
-
'' +
-
optionalString (cfg.forwardAddresses != []) ''
forward-zone:
name: .
-
'' +
-
concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses;
rootTrustAnchorFile = "${stateDir}/root.key";
···
confFile = pkgs.writeText "unbound.conf" ''
server:
directory: "${stateDir}"
username: unbound
-
chroot: "${stateDir}"
pidfile: ""
${interfaces}
${access}
${trustAnchor}
${cfg.extraConfig}
${forward}
'';
-
in
-
{
###### interface
···
package = mkOption {
type = types.package;
-
default = pkgs.unbound;
-
defaultText = "pkgs.unbound";
description = "The unbound package to use";
};
···
interfaces = mkOption {
default = [ "127.0.0.1" ] ++ optional config.networking.enableIPv6 "::1";
type = types.listOf types.str;
-
description = "What addresses the server should listen on.";
};
forwardAddresses = mkOption {
-
default = [ ];
type = types.listOf types.str;
description = "What servers to forward queries to.";
};
···
description = "Use and update root trust anchor for DNSSEC validation.";
};
extraConfig = mkOption {
default = "";
type = types.lines;
···
users.users.unbound = {
description = "unbound daemon user";
isSystemUser = true;
};
networking.resolvconf.useLocalResolver = mkDefault true;
systemd.services.unbound = {
description = "Unbound recursive Domain Name Server";
after = [ "network.target" ];
before = [ "nss-lookup.target" ];
-
wants = [ "nss-lookup.target" ];
-
wantedBy = [ "multi-user.target" ];
-
preStart = ''
-
mkdir -m 0755 -p ${stateDir}/dev/
-
cp ${confFile} ${stateDir}/unbound.conf
-
${optionalString cfg.enableRootTrustAnchor ''
-
${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
-
chown unbound ${stateDir} ${rootTrustAnchorFile}
-
''}
-
touch ${stateDir}/dev/random
-
${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random
'';
serviceConfig = {
-
ExecStart = "${cfg.package}/bin/unbound -d -c ${stateDir}/unbound.conf";
-
ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
-
ProtectSystem = true;
-
ProtectHome = true;
PrivateDevices = true;
-
Restart = "always";
-
RestartSec = "5s";
};
};
-
# If networkmanager is enabled, ask it to interface with unbound.
networking.networkmanager.dns = "unbound";
-
};
-
}
···
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.unbound;
stateDir = "/var/lib/unbound";
···
forward =
optionalString (any isLocalAddress cfg.forwardAddresses) ''
do-not-query-localhost: no
+
''
+
+ optionalString (cfg.forwardAddresses != []) ''
forward-zone:
name: .
+
''
+
+ concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses;
rootTrustAnchorFile = "${stateDir}/root.key";
···
confFile = pkgs.writeText "unbound.conf" ''
server:
+
ip-freebind: yes
directory: "${stateDir}"
username: unbound
+
chroot: ""
pidfile: ""
+
# when running under systemd there is no need to daemonize
+
do-daemonize: no
${interfaces}
${access}
${trustAnchor}
+
${lib.optionalString (cfg.localControlSocketPath != null) ''
+
remote-control:
+
control-enable: yes
+
control-interface: ${cfg.localControlSocketPath}
+
''}
${cfg.extraConfig}
${forward}
'';
in
{
###### interface
···
package = mkOption {
type = types.package;
+
default = pkgs.unbound-with-systemd;
+
defaultText = "pkgs.unbound-with-systemd";
description = "The unbound package to use";
};
···
interfaces = mkOption {
default = [ "127.0.0.1" ] ++ optional config.networking.enableIPv6 "::1";
type = types.listOf types.str;
+
description = ''
+
What addresses the server should listen on. This supports the interface syntax documented in
+
<citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+
'';
};
forwardAddresses = mkOption {
+
default = [];
type = types.listOf types.str;
description = "What servers to forward queries to.";
};
···
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>unbound</literal> group.
+
+
If this option is <literal>null</literal> remote control will not be
+
configured at all. Unbounds default values apply.
+
'';
+
};
+
extraConfig = mkOption {
default = "";
type = types.lines;
···
users.users.unbound = {
description = "unbound daemon user";
isSystemUser = true;
+
group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound");
+
};
+
+
# We need a group so that we can give users access to the configured
+
# control socket. Unbound allows access to the socket only to the unbound
+
# user and the primary group.
+
users.groups = lib.mkIf (cfg.localControlSocketPath != null) {
+
unbound = {};
};
networking.resolvconf.useLocalResolver = mkDefault true;
+
+
environment.etc."unbound/unbound.conf".source = confFile;
+
systemd.services.unbound = {
description = "Unbound recursive Domain Name Server";
after = [ "network.target" ];
before = [ "nss-lookup.target" ];
+
wantedBy = [ "multi-user.target" "nss-lookup.target" ];
+
preStart = lib.mkIf cfg.enableRootTrustAnchor ''
+
${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
'';
+
restartTriggers = [
+
confFile
+
];
+
serviceConfig = {
+
ExecStart = "${cfg.package}/bin/unbound -p -d -c /etc/unbound/unbound.conf";
+
ExecReload = "+/run/current-system/sw/bin/kill -HUP $MAINPID";
+
NotifyAccess = "main";
+
Type = "notify";
+
+
# FIXME: Which of these do we actualy need, can we drop the chroot flag?
+
AmbientCapabilities = [
+
"CAP_NET_BIND_SERVICE"
+
"CAP_NET_RAW"
+
"CAP_SETGID"
+
"CAP_SETUID"
+
"CAP_SYS_CHROOT"
+
"CAP_SYS_RESOURCE"
+
];
+
+
User = "unbound";
+
Group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound");
+
+
MemoryDenyWriteExecute = true;
+
NoNewPrivileges = true;
PrivateDevices = true;
+
PrivateTmp = true;
+
ProtectHome = true;
+
ProtectControlGroups = true;
+
ProtectKernelModules = true;
+
ProtectSystem = "strict";
+
RuntimeDirectory = "unbound";
+
ConfigurationDirectory = "unbound";
+
StateDirectory = "unbound";
+
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+
RestrictRealtime = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"~@clock"
+
"@cpu-emulation"
+
"@debug"
+
"@keyring"
+
"@module"
+
"mount"
+
"@obsolete"
+
"@resources"
+
];
+
RestrictNamespaces = true;
+
LockPersonality = true;
+
RestrictSUIDSGID = true;
};
};
# If networkmanager is enabled, ask it to interface with unbound.
networking.networkmanager.dns = "unbound";
};
}
+1
nixos/tests/all-tests.nix
···
trezord = handleTest ./trezord.nix {};
trickster = handleTest ./trickster.nix {};
tuptime = handleTest ./tuptime.nix {};
udisks2 = handleTest ./udisks2.nix {};
unit-php = handleTest ./web-servers/unit-php.nix {};
upnp = handleTest ./upnp.nix {};
···
trezord = handleTest ./trezord.nix {};
trickster = handleTest ./trickster.nix {};
tuptime = handleTest ./tuptime.nix {};
+
unbound = handleTest ./unbound.nix {};
udisks2 = handleTest ./udisks2.nix {};
unit-php = handleTest ./web-servers/unit-php.nix {};
upnp = handleTest ./upnp.nix {};
+278
nixos/tests/unbound.nix
···
···
+
/*
+
Test that our unbound module indeed works as most users would expect.
+
There are a few settings that we must consider when modifying the test. The
+
ususal use-cases for unbound are
+
* running a recursive DNS resolver on the local machine
+
* running a recursive DNS resolver on the local machine, forwarding to a local DNS server via UDP/53 & TCP/53
+
* running a recursive DNS resolver on the local machine, forwarding to a local DNS server via TCP/853 (DoT)
+
* running a recursive DNS resolver on a machine in the network awaiting input from clients over TCP/53 & UDP/53
+
* running a recursive DNS resolver on a machine in the network awaiting input from clients over TCP/853 (DoT)
+
+
In the below test setup we are trying to implement all of those use cases.
+
+
Another aspect that we cover is access to the local control UNIX socket. It
+
can optionally be enabled and users can optionally be in a group to gain
+
access. Users that are not in the group (except for root) should not have
+
access to that socket. Also, when there is no socket configured, users
+
shouldn't be able to access the control socket at all. Not even root.
+
*/
+
import ./make-test-python.nix ({ pkgs, lib, ... }:
+
let
+
# common client configuration that we can just use for the multitude of
+
# clients we are constructing
+
common = { lib, pkgs, ... }: {
+
config = {
+
environment.systemPackages = [ pkgs.knot-dns ];
+
+
# disable the root anchor update as we do not have internet access during
+
# the test execution
+
services.unbound.enableRootTrustAnchor = false;
+
};
+
};
+
+
cert = pkgs.runCommandNoCC "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=dns.example.local'
+
mkdir -p $out
+
cp key.pem cert.pem $out
+
'';
+
in
+
{
+
name = "unbound";
+
meta = with pkgs.stdenv.lib.maintainers; {
+
maintainers = [ andir ];
+
};
+
+
nodes = {
+
+
# The server that actually serves our zones, this tests unbounds authoriative mode
+
authoritative = { lib, pkgs, config, ... }: {
+
imports = [ common ];
+
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+
{ address = "192.168.0.1"; prefixLength = 24; }
+
];
+
networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+
{ address = "fd21::1"; prefixLength = 64; }
+
];
+
networking.firewall.allowedTCPPorts = [ 53 ];
+
networking.firewall.allowedUDPPorts = [ 53 ];
+
+
services.unbound = {
+
enable = true;
+
interfaces = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ];
+
allowedAccess = [ "192.168.0.0/24" "fd21::/64" "::1" "127.0.0.0/8" ];
+
extraConfig = ''
+
server:
+
local-data: "example.local. IN A 1.2.3.4"
+
local-data: "example.local. IN AAAA abcd::eeff"
+
'';
+
};
+
};
+
+
# The resolver that knows that fowards (only) to the authoritative server
+
# and listens on UDP/53, TCP/53 & TCP/853.
+
resolver = { lib, nodes, ... }: {
+
imports = [ common ];
+
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+
{ address = "192.168.0.2"; prefixLength = 24; }
+
];
+
networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+
{ address = "fd21::2"; prefixLength = 64; }
+
];
+
networking.firewall.allowedTCPPorts = [
+
53 # regular DNS
+
853 # DNS over TLS
+
];
+
networking.firewall.allowedUDPPorts = [ 53 ];
+
+
services.unbound = {
+
enable = true;
+
allowedAccess = [ "192.168.0.0/24" "fd21::/64" "::1" "127.0.0.0/8" ];
+
interfaces = [ "::1" "127.0.0.1" "192.168.0.2" "fd21::2" "192.168.0.2@853" "fd21::2@853" "::1@853" "127.0.0.1@853" ];
+
forwardAddresses = [
+
(lib.head nodes.authoritative.config.networking.interfaces.eth1.ipv6.addresses).address
+
(lib.head nodes.authoritative.config.networking.interfaces.eth1.ipv4.addresses).address
+
];
+
extraConfig = ''
+
server:
+
tls-service-pem: ${cert}/cert.pem
+
tls-service-key: ${cert}/key.pem
+
'';
+
};
+
};
+
+
# machine that runs a local unbound that will be reconfigured during test execution
+
local_resolver = { lib, nodes, config, ... }: {
+
imports = [ common ];
+
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+
{ address = "192.168.0.3"; prefixLength = 24; }
+
];
+
networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+
{ address = "fd21::3"; prefixLength = 64; }
+
];
+
networking.firewall.allowedTCPPorts = [
+
53 # regular DNS
+
];
+
networking.firewall.allowedUDPPorts = [ 53 ];
+
+
services.unbound = {
+
enable = true;
+
allowedAccess = [ "::1" "127.0.0.0/8" ];
+
interfaces = [ "::1" "127.0.0.1" ];
+
localControlSocketPath = "/run/unbound/unbound.ctl";
+
extraConfig = ''
+
include: "/etc/unbound/extra*.conf"
+
'';
+
};
+
+
users.users = {
+
# user that is permitted to access the unix socket
+
someuser.extraGroups = [
+
config.users.users.unbound.group
+
];
+
+
# user that is not permitted to access the unix socket
+
unauthorizeduser = {};
+
};
+
+
environment.etc = {
+
"unbound-extra1.conf".text = ''
+
forward-zone:
+
name: "example.local."
+
forward-addr: ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address}
+
forward-addr: ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}
+
'';
+
"unbound-extra2.conf".text = ''
+
auth-zone:
+
name: something.local.
+
zonefile: ${pkgs.writeText "zone" ''
+
something.local. IN A 3.4.5.6
+
''}
+
'';
+
};
+
};
+
+
+
# plain node that only has network access and doesn't run any part of the
+
# resolver software locally
+
client = { lib, nodes, ... }: {
+
imports = [ common ];
+
networking.nameservers = [
+
(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address
+
(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address
+
];
+
networking.interfaces.eth1.ipv4.addresses = [
+
{ address = "192.168.0.10"; prefixLength = 24; }
+
];
+
networking.interfaces.eth1.ipv6.addresses = [
+
{ address = "fd21::10"; prefixLength = 64; }
+
];
+
};
+
};
+
+
testScript = { nodes, ... }: ''
+
import typing
+
import json
+
+
zone = "example.local."
+
records = [("AAAA", "abcd::eeff"), ("A", "1.2.3.4")]
+
+
+
def query(
+
machine,
+
host: str,
+
query_type: str,
+
query: str,
+
expected: typing.Optional[str] = None,
+
args: typing.Optional[typing.List[str]] = None,
+
):
+
"""
+
Execute a single query and compare the result with expectation
+
"""
+
text_args = ""
+
if args:
+
text_args = " ".join(args)
+
+
out = machine.succeed(
+
f"kdig {text_args} {query} {query_type} @{host} +short"
+
).strip()
+
machine.log(f"{host} replied with {out}")
+
if expected:
+
assert expected == out, f"Expected `{expected}` but got `{out}`"
+
+
+
def test(machine, remotes, /, doh=False, zone=zone, records=records, args=[]):
+
"""
+
Run queries for the given remotes on the given machine.
+
"""
+
for query_type, expected in records:
+
for remote in remotes:
+
query(machine, remote, query_type, zone, expected, args)
+
query(machine, remote, query_type, zone, expected, ["+tcp"] + args)
+
if doh:
+
query(
+
machine,
+
remote,
+
query_type,
+
zone,
+
expected,
+
["+tcp", "+tls"] + args,
+
)
+
+
+
client.start()
+
authoritative.wait_for_unit("unbound.service")
+
+
# verify that we can resolve locally
+
with subtest("test the authoritative servers local responses"):
+
test(authoritative, ["::1", "127.0.0.1"])
+
+
resolver.wait_for_unit("unbound.service")
+
+
with subtest("root is unable to use unbounc-control when the socket is not configured"):
+
resolver.succeed("which unbound-control") # the binary must exist
+
resolver.fail("unbound-control list_forwards") # the invocation must fail
+
+
# verify that the resolver is able to resolve on all the local protocols
+
with subtest("test that the resolver resolves on all protocols and transports"):
+
test(resolver, ["::1", "127.0.0.1"], doh=True)
+
+
resolver.wait_for_unit("multi-user.target")
+
+
with subtest("client should be able to query the resolver"):
+
test(client, ["${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address}", "${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}"], doh=True)
+
+
# discard the client we do not need anymore
+
client.shutdown()
+
+
local_resolver.wait_for_unit("multi-user.target")
+
+
# link a new config file to /etc/unbound/extra.conf
+
local_resolver.succeed("ln -s /etc/unbound-extra1.conf /etc/unbound/extra1.conf")
+
+
# reload the server & ensure the forwarding works
+
with subtest("test that the local resolver resolves on all protocols and transports"):
+
local_resolver.succeed("systemctl reload unbound")
+
print(local_resolver.succeed("journalctl -u unbound -n 1000"))
+
test(local_resolver, ["::1", "127.0.0.1"], args=["+timeout=60"])
+
+
with subtest("test that we can use the unbound control socket"):
+
out = local_resolver.succeed(
+
"sudo -u someuser -- unbound-control list_forwards"
+
).strip()
+
+
# Thank you black! Can't really break this line into a readable version.
+
expected = "example.local. IN forward ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address} ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}"
+
assert out == expected, f"Expected `{expected}` but got `{out}` instead."
+
local_resolver.fail("sudo -u unauthorizeduser -- unbound-control list_forwards")
+
+
+
# link a new config file to /etc/unbound/extra.conf
+
local_resolver.succeed("ln -sf /etc/unbound-extra2.conf /etc/unbound/extra2.conf")
+
+
# reload the server & ensure the new local zone works
+
with subtest("test that we can query the new local zone"):
+
local_resolver.succeed("unbound-control reload")
+
r = [("A", "3.4.5.6")]
+
test(local_resolver, ["::1", "127.0.0.1"], zone="something.local.", records=r)
+
'';
+
})
+31 -9
pkgs/tools/networking/unbound/default.nix
···
-
{ stdenv, fetchurl, openssl, nettle, expat, libevent, dns-root-data }:
stdenv.mkDerivation rec {
pname = "unbound";
···
outputs = [ "out" "lib" "man" ]; # "dev" would only split ~20 kB
-
buildInputs = [ openssl nettle expat libevent ];
configureFlags = [
"--with-ssl=${openssl.dev}"
···
"--enable-relro-now"
] ++ stdenv.lib.optional stdenv.hostPlatform.isStatic [
"--disable-flto"
];
installFlags = [ "configfile=\${out}/etc/unbound/unbound.conf" ];
···
make unbound-event-install
'';
-
preFixup = stdenv.lib.optionalString (stdenv.isLinux && !stdenv.hostPlatform.isMusl) # XXX: revisit
# Build libunbound again, but only against nettle instead of openssl.
# This avoids gnutls.out -> unbound.lib -> openssl.out.
# There was some problem with this on Darwin; let's not complicate non-Linux.
···
buildPhase
installPhase
''
-
# get rid of runtime dependencies on $dev outputs
+ ''substituteInPlace "$lib/lib/libunbound.la" ''
-
+ stdenv.lib.concatMapStrings
-
(pkg: " --replace '-L${pkg.dev}/lib' '-L${pkg.out}/lib' --replace '-R${pkg.dev}/lib' '-R${pkg.out}/lib'")
-
buildInputs;
-
meta = with stdenv.lib; {
description = "Validating, recursive, and caching DNS resolver";
license = licenses.bsd3;
homepage = "https://www.unbound.net";
maintainers = with maintainers; [ ehmry fpletz globin ];
-
platforms = stdenv.lib.platforms.unix;
};
}
···
+
{ stdenv
+
, lib
+
, fetchurl
+
, openssl
+
, nettle
+
, expat
+
, libevent
+
, dns-root-data
+
, pkg-config
+
#
+
# By default unbound will not be built with systemd support. Unbound is a very
+
# commmon dependency. The transitive dependency closure of systemd also
+
# contains unbound.
+
# Since most (all?) (lib)unbound users outside of the unbound daemon usage do
+
# not need the systemd integration it is likely best to just default to no
+
# systemd integration.
+
# For the daemon use-case, that needs to notify systemd, use `unbound-with-systemd`.
+
#
+
, withSystemd ? false
+
, systemd ? null
+
}:
stdenv.mkDerivation rec {
pname = "unbound";
···
outputs = [ "out" "lib" "man" ]; # "dev" would only split ~20 kB
+
buildInputs = [ openssl nettle expat libevent ] ++ lib.optionals withSystemd [ pkg-config systemd ];
configureFlags = [
"--with-ssl=${openssl.dev}"
···
"--enable-relro-now"
] ++ stdenv.lib.optional stdenv.hostPlatform.isStatic [
"--disable-flto"
+
] ++ lib.optionals withSystemd [
+
"--enable-systemd"
];
installFlags = [ "configfile=\${out}/etc/unbound/unbound.conf" ];
···
make unbound-event-install
'';
+
preFixup = lib.optionalString (stdenv.isLinux && !stdenv.hostPlatform.isMusl) # XXX: revisit
# Build libunbound again, but only against nettle instead of openssl.
# This avoids gnutls.out -> unbound.lib -> openssl.out.
# There was some problem with this on Darwin; let's not complicate non-Linux.
···
buildPhase
installPhase
''
+
# get rid of runtime dependencies on $dev outputs
+ ''substituteInPlace "$lib/lib/libunbound.la" ''
+
+ lib.concatMapStrings
+
(pkg: lib.optionalString (pkg ? dev) " --replace '-L${pkg.dev}/lib' '-L${pkg.out}/lib' --replace '-R${pkg.dev}/lib' '-R${pkg.out}/lib'")
+
(builtins.filter (p: p != null) buildInputs);
+
meta = with lib; {
description = "Validating, recursive, and caching DNS resolver";
license = licenses.bsd3;
homepage = "https://www.unbound.net";
maintainers = with maintainers; [ ehmry fpletz globin ];
+
platforms = platforms.unix;
};
}
+5 -1
pkgs/top-level/all-packages.nix
···
unclutter-xfixes = callPackage ../tools/misc/unclutter-xfixes { };
-
unbound = callPackage ../tools/networking/unbound { };
unicorn = callPackage ../development/libraries/unicorn { };
···
unclutter-xfixes = callPackage ../tools/misc/unclutter-xfixes { };
+
unbound = callPackage ../tools/networking/unbound {};
+
+
unbound-with-systemd = unbound.override {
+
withSystemd = true;
+
};
unicorn = callPackage ../development/libraries/unicorn { };