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 forward = optionalString (length cfg.forwardAddresses != 0)
16 "forward-zone:\n name: .\n" +
17 concatMapStrings (x: " forward-addr: ${x}\n") cfg.forwardAddresses;
18
19 rootTrustAnchorFile = "${stateDir}/root.key";
20
21 trustAnchor = optionalString cfg.enableRootTrustAnchor
22 "auto-trust-anchor-file: ${rootTrustAnchorFile}";
23
24 confFile = pkgs.writeText "unbound.conf" ''
25 server:
26 directory: "${stateDir}"
27 username: unbound
28 chroot: "${stateDir}"
29 pidfile: ""
30 ${interfaces}
31 ${access}
32 ${trustAnchor}
33 ${cfg.extraConfig}
34 ${forward}
35 '';
36
37in
38
39{
40
41 ###### interface
42
43 options = {
44 services.unbound = {
45
46 enable = mkOption {
47 default = false;
48 type = types.bool;
49 description = "Whether to enable the Unbound domain name server.";
50 };
51
52 allowedAccess = mkOption {
53 default = ["127.0.0.0/24"];
54 type = types.listOf types.str;
55 description = "What networks are allowed to use unbound as a resolver.";
56 };
57
58 interfaces = mkOption {
59 default = [ "127.0.0.1" "::1" ];
60 type = types.listOf types.str;
61 description = "What addresses the server should listen on.";
62 };
63
64 forwardAddresses = mkOption {
65 default = [ ];
66 type = types.listOf types.str;
67 description = "What servers to forward queries to.";
68 };
69
70 enableRootTrustAnchor = mkOption {
71 default = true;
72 type = types.bool;
73 description = "Use and update root trust anchor for DNSSEC validation.";
74 };
75
76 extraConfig = mkOption {
77 default = "";
78 type = types.str;
79 description = "Extra lines of unbound config.";
80 };
81
82 };
83 };
84
85 ###### implementation
86
87 config = mkIf cfg.enable {
88
89 environment.systemPackages = [ pkgs.unbound ];
90
91 users.extraUsers = singleton {
92 name = "unbound";
93 uid = config.ids.uids.unbound;
94 description = "unbound daemon user";
95 home = stateDir;
96 createHome = true;
97 };
98
99 systemd.services.unbound = {
100 description="Unbound recursive Domain Name Server";
101 after = [ "network.target" ];
102 before = [ "nss-lookup.target" ];
103 wants = [" nss-lookup.target" ];
104 wantedBy = [ "multi-user.target" ];
105
106 preStart = ''
107 mkdir -m 0755 -p ${stateDir}/dev/
108 cp ${confFile} ${stateDir}/unbound.conf
109 ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
110 chown unbound ${stateDir} ${rootTrustAnchorFile}
111 touch ${stateDir}/dev/random
112 ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random
113 '';
114
115 serviceConfig = {
116 ExecStart = "${pkgs.unbound}/sbin/unbound -d -c ${stateDir}/unbound.conf";
117 ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
118 };
119 };
120
121 };
122
123}