1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.unbound;
8
9 stateDir = "/var/lib/unbound";
10
11 access = concatMapStrings (x: " access-control: ${x} allow\n") cfg.allowedAccess;
12
13 interfaces = concatMapStrings (x: " interface: ${x}\n") cfg.interfaces;
14
15 isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1";
16
17 forward =
18 optionalString (any isLocalAddress cfg.forwardAddresses) ''
19 do-not-query-localhost: no
20 '' +
21 optionalString (cfg.forwardAddresses != []) ''
22 forward-zone:
23 name: .
24 '' +
25 concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses;
26
27 rootTrustAnchorFile = "${stateDir}/root.key";
28
29 trustAnchor = optionalString cfg.enableRootTrustAnchor
30 "auto-trust-anchor-file: ${rootTrustAnchorFile}";
31
32 confFile = pkgs.writeText "unbound.conf" ''
33 server:
34 directory: "${stateDir}"
35 username: unbound
36 chroot: "${stateDir}"
37 pidfile: ""
38 ${interfaces}
39 ${access}
40 ${trustAnchor}
41 ${cfg.extraConfig}
42 ${forward}
43 '';
44
45in
46
47{
48
49 ###### interface
50
51 options = {
52 services.unbound = {
53
54 enable = mkEnableOption "Unbound domain name server";
55
56 allowedAccess = mkOption {
57 default = [ "127.0.0.0/24" ];
58 type = types.listOf types.str;
59 description = "What networks are allowed to use unbound as a resolver.";
60 };
61
62 interfaces = mkOption {
63 default = [ "127.0.0.1" "::1" ];
64 type = types.listOf types.str;
65 description = "What addresses the server should listen on.";
66 };
67
68 forwardAddresses = mkOption {
69 default = [ ];
70 type = types.listOf types.str;
71 description = "What servers to forward queries to.";
72 };
73
74 enableRootTrustAnchor = mkOption {
75 default = true;
76 type = types.bool;
77 description = "Use and update root trust anchor for DNSSEC validation.";
78 };
79
80 extraConfig = mkOption {
81 default = "";
82 type = types.lines;
83 description = ''
84 Extra unbound config. See
85 <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8
86 </manvolnum></citerefentry>.
87 '';
88 };
89
90 };
91 };
92
93 ###### implementation
94
95 config = mkIf cfg.enable {
96
97 environment.systemPackages = [ pkgs.unbound ];
98
99 users.users.unbound = {
100 description = "unbound daemon user";
101 isSystemUser = true;
102 };
103
104 systemd.services.unbound = {
105 description = "Unbound recursive Domain Name Server";
106 after = [ "network.target" ];
107 before = [ "nss-lookup.target" ];
108 wants = [" nss-lookup.target" ];
109 wantedBy = [ "multi-user.target" ];
110
111 preStart = ''
112 mkdir -m 0755 -p ${stateDir}/dev/
113 cp ${confFile} ${stateDir}/unbound.conf
114 ${optionalString cfg.enableRootTrustAnchor ''
115 ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
116 chown unbound ${stateDir} ${rootTrustAnchorFile}
117 ''}
118 touch ${stateDir}/dev/random
119 ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random
120 '';
121
122 serviceConfig = {
123 ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf";
124 ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
125
126 ProtectSystem = true;
127 ProtectHome = true;
128 PrivateDevices = true;
129 };
130 };
131
132 };
133
134}