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 = mkEnableOption "Unbound domain name server";
47
48 allowedAccess = mkOption {
49 default = [ "127.0.0.0/24" ];
50 type = types.listOf types.str;
51 description = "What networks are allowed to use unbound as a resolver.";
52 };
53
54 interfaces = mkOption {
55 default = [ "127.0.0.1" "::1" ];
56 type = types.listOf types.str;
57 description = "What addresses the server should listen on.";
58 };
59
60 forwardAddresses = mkOption {
61 default = [ ];
62 type = types.listOf types.str;
63 description = "What servers to forward queries to.";
64 };
65
66 enableRootTrustAnchor = mkOption {
67 default = true;
68 type = types.bool;
69 description = "Use and update root trust anchor for DNSSEC validation.";
70 };
71
72 extraConfig = mkOption {
73 default = "";
74 type = types.str;
75 description = "Extra lines of unbound config.";
76 };
77
78 };
79 };
80
81 ###### implementation
82
83 config = mkIf cfg.enable {
84
85 environment.systemPackages = [ pkgs.unbound ];
86
87 users.extraUsers = singleton {
88 name = "unbound";
89 uid = config.ids.uids.unbound;
90 description = "unbound daemon user";
91 home = stateDir;
92 createHome = true;
93 };
94
95 systemd.services.unbound = {
96 description = "Unbound recursive Domain Name Server";
97 after = [ "network.target" ];
98 before = [ "nss-lookup.target" ];
99 wants = [" nss-lookup.target" ];
100 wantedBy = [ "multi-user.target" ];
101
102 preStart = ''
103 mkdir -m 0755 -p ${stateDir}/dev/
104 cp ${confFile} ${stateDir}/unbound.conf
105 ${optionalString cfg.enableRootTrustAnchor ''
106 ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
107 chown unbound ${stateDir} ${rootTrustAnchorFile}
108 ''}
109 touch ${stateDir}/dev/random
110 ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random
111 '';
112
113 serviceConfig = {
114 ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf";
115 ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
116 };
117 };
118
119 };
120
121}