ddns-go: 6.7.7 -> 6.8.0 (#373902)
[NixPkgs.git] / nixos / modules / services / networking / bind.nix
blob39829eaa77ef273f843190a92d6f7050d89396d1
1 { config, lib, pkgs, ... }:
2 let
4   cfg = config.services.bind;
6   bindPkg = config.services.bind.package;
8   bindUser = "named";
10   bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; }));
12   bindZoneOptions = { name, config, ... }: {
13     options = {
14       name = lib.mkOption {
15         type = lib.types.str;
16         default = name;
17         description = "Name of the zone.";
18       };
19       master = lib.mkOption {
20         description = "Master=false means slave server";
21         type = lib.types.bool;
22       };
23       file = lib.mkOption {
24         type = lib.types.either lib.types.str lib.types.path;
25         description = "Zone file resource records contain columns of data, separated by whitespace, that define the record.";
26       };
27       masters = lib.mkOption {
28         type = lib.types.listOf lib.types.str;
29         description = "List of servers for inclusion in stub and secondary zones.";
30       };
31       slaves = lib.mkOption {
32         type = lib.types.listOf lib.types.str;
33         description = "Addresses who may request zone transfers.";
34         default = [ ];
35       };
36       allowQuery = lib.mkOption {
37         type = lib.types.listOf lib.types.str;
38         description = ''
39           List of address ranges allowed to query this zone. Instead of the address(es), this may instead
40           contain the single string "any".
41         '';
42         default = [ "any" ];
43       };
44       extraConfig = lib.mkOption {
45         type = lib.types.lines;
46         description = "Extra zone config to be appended at the end of the zone section.";
47         default = "";
48       };
49     };
50   };
52   confFile = pkgs.writeText "named.conf"
53     ''
54       include "/etc/bind/rndc.key";
55       controls {
56         inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
57       };
59       acl cachenetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
60       acl badnetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
62       options {
63         listen-on { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.listenOn} };
64         listen-on-v6 { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} };
65         allow-query-cache { cachenetworks; };
66         blackhole { badnetworks; };
67         forward ${cfg.forward};
68         forwarders { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
69         directory "${cfg.directory}";
70         pid-file "/run/named/named.pid";
71         ${cfg.extraOptions}
72       };
74       ${cfg.extraConfig}
76       ${ lib.concatMapStrings
77           ({ name, file, master ? true, slaves ? [], masters ? [], allowQuery ? [], extraConfig ? "" }:
78             ''
79               zone "${name}" {
80                 type ${if master then "master" else "slave"};
81                 file "${file}";
82                 ${ if master then
83                    ''
84                      allow-transfer {
85                        ${lib.concatMapStrings (ip: "${ip};\n") slaves}
86                      };
87                    ''
88                    else
89                    ''
90                      masters {
91                        ${lib.concatMapStrings (ip: "${ip};\n") masters}
92                      };
93                    ''
94                 }
95                 allow-query { ${lib.concatMapStrings (ip: "${ip}; ") allowQuery}};
96                 ${extraConfig}
97               };
98             '')
99           (lib.attrValues cfg.zones) }
100     '';
106   ###### interface
108   options = {
110     services.bind = {
112       enable = lib.mkEnableOption "BIND domain name server";
115       package = lib.mkPackageOption pkgs "bind" { };
117       cacheNetworks = lib.mkOption {
118         default = [ "127.0.0.0/24" "::1/128" ];
119         type = lib.types.listOf lib.types.str;
120         description = ''
121           What networks are allowed to use us as a resolver.  Note
122           that this is for recursive queries -- all networks are
123           allowed to query zones configured with the `zones` option
124           by default (although this may be overridden within each
125           zone's configuration, via the `allowQuery` option).
126           It is recommended that you limit cacheNetworks to avoid your
127           server being used for DNS amplification attacks.
128         '';
129       };
131       blockedNetworks = lib.mkOption {
132         default = [ ];
133         type = lib.types.listOf lib.types.str;
134         description = ''
135           What networks are just blocked.
136         '';
137       };
139       ipv4Only = lib.mkOption {
140         default = false;
141         type = lib.types.bool;
142         description = ''
143           Only use ipv4, even if the host supports ipv6.
144         '';
145       };
147       forwarders = lib.mkOption {
148         default = config.networking.nameservers;
149         defaultText = lib.literalExpression "config.networking.nameservers";
150         type = lib.types.listOf lib.types.str;
151         description = ''
152           List of servers we should forward requests to.
153         '';
154       };
156       forward = lib.mkOption {
157         default = "first";
158         type = lib.types.enum ["first" "only"];
159         description = ''
160           Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'.
161         '';
162       };
164       listenOn = lib.mkOption {
165         default = [ "any" ];
166         type = lib.types.listOf lib.types.str;
167         description = ''
168           Interfaces to listen on.
169         '';
170       };
172       listenOnIpv6 = lib.mkOption {
173         default = [ "any" ];
174         type = lib.types.listOf lib.types.str;
175         description = ''
176           Ipv6 interfaces to listen on.
177         '';
178       };
180       directory = lib.mkOption {
181         type = lib.types.str;
182         default = "/run/named";
183         description = "Working directory of BIND.";
184       };
186       zones = lib.mkOption {
187         default = [ ];
188         type = with lib.types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (lib.types.submodule bindZoneOptions));
189         description = ''
190           List of zones we claim authority over.
191         '';
192         example = {
193           "example.com" = {
194             master = false;
195             file = "/var/dns/example.com";
196             masters = [ "192.168.0.1" ];
197             slaves = [ ];
198             extraConfig = "";
199           };
200         };
201       };
203       extraConfig = lib.mkOption {
204         type = lib.types.lines;
205         default = "";
206         description = ''
207           Extra lines to be added verbatim to the generated named configuration file.
208         '';
209       };
211       extraOptions = lib.mkOption {
212         type = lib.types.lines;
213         default = "";
214         description = ''
215           Extra lines to be added verbatim to the options section of the
216           generated named configuration file.
217         '';
218       };
220       configFile = lib.mkOption {
221         type = lib.types.path;
222         default = confFile;
223         defaultText = lib.literalExpression "confFile";
224         description = ''
225           Overridable config file to use for named. By default, that
226           generated by nixos.
227         '';
228       };
230     };
232   };
235   ###### implementation
237   config = lib.mkIf cfg.enable {
239     networking.resolvconf.useLocalResolver = lib.mkDefault true;
241     users.users.${bindUser} =
242       {
243         group = bindUser;
244         description = "BIND daemon user";
245         isSystemUser = true;
246       };
247     users.groups.${bindUser} = {};
249     systemd.tmpfiles.settings."bind" = lib.mkIf (cfg.directory != "/run/named") {
250       ${cfg.directory} = {
251         d = {
252           user = bindUser;
253           group = bindUser;
254           age = "-";
255         };
256       };
257     };
258     systemd.services.bind = {
259       description = "BIND Domain Name Server";
260       after = [ "network.target" ];
261       wantedBy = [ "multi-user.target" ];
263       preStart = ''
264         if ! [ -f "/etc/bind/rndc.key" ]; then
265           ${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -a -A hmac-sha256 2>/dev/null
266         fi
267       '';
269       serviceConfig = {
270         Type = "forking"; # Set type to forking, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900788
271         ExecStart = "${bindPkg.out}/sbin/named ${lib.optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile}";
272         ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
273         ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
274         User = bindUser;
275         RuntimeDirectory = "named";
276         RuntimeDirectoryPreserve = "yes";
277         ConfigurationDirectory = "bind";
278         ReadWritePaths = [
279           (lib.mapAttrsToList (name: config: if (lib.hasPrefix "/" config.file) then ("-${dirOf config.file}") else "") cfg.zones)
280           cfg.directory
281         ];
282         CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
283         AmbientCapabilities = "CAP_NET_BIND_SERVICE";
284         # Security
285         NoNewPrivileges = true;
286         # Sandboxing
287         ProtectSystem = "strict";
288         ReadOnlyPaths = "/sys";
289         ProtectHome = true;
290         PrivateTmp = true;
291         PrivateDevices = true;
292         PrivateMounts = true;
293         ProtectHostname = true;
294         ProtectClock = true;
295         ProtectKernelTunables = true;
296         ProtectKernelModules = true;
297         ProtectKernelLogs = true;
298         ProtectControlGroups = true;
299         ProtectProc = "invisible";
300         ProcSubset = "pid";
301         RemoveIPC = true;
302         RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6 AF_NETLINK" ];
303         LockPersonality = true;
304         MemoryDenyWriteExecute = true;
305         RestrictRealtime = true;
306         RestrictSUIDSGID = true;
307         RestrictNamespaces = true;
308         # System Call Filtering
309         SystemCallArchitectures = "native";
310         SystemCallFilter = "~@mount @debug @clock @reboot @resources @privileged @obsolete acct modify_ldt add_key adjtimex clock_adjtime delete_module fanotify_init finit_module get_mempolicy init_module io_destroy io_getevents iopl ioperm io_setup io_submit io_cancel kcmp kexec_load keyctl lookup_dcookie migrate_pages move_pages open_by_handle_at perf_event_open process_vm_readv process_vm_writev ptrace remap_file_pages request_key set_mempolicy swapoff swapon uselib vmsplice";
311       };
313       unitConfig.Documentation = "man:named(8)";
314     };
315   };