1 { config, lib, pkgs, ... }:
4 cfg = config.services.bind;
6 bindPkg = config.services.bind.package;
10 bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; }));
12 bindZoneOptions = { name, config, ... }: {
17 description = "Name of the zone.";
19 master = lib.mkOption {
20 description = "Master=false means slave server";
21 type = lib.types.bool;
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.";
27 masters = lib.mkOption {
28 type = lib.types.listOf lib.types.str;
29 description = "List of servers for inclusion in stub and secondary zones.";
31 slaves = lib.mkOption {
32 type = lib.types.listOf lib.types.str;
33 description = "Addresses who may request zone transfers.";
36 allowQuery = lib.mkOption {
37 type = lib.types.listOf lib.types.str;
39 List of address ranges allowed to query this zone. Instead of the address(es), this may instead
40 contain the single string "any".
44 extraConfig = lib.mkOption {
45 type = lib.types.lines;
46 description = "Extra zone config to be appended at the end of the zone section.";
52 confFile = pkgs.writeText "named.conf"
54 include "/etc/bind/rndc.key";
56 inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
59 acl cachenetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
60 acl badnetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
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";
76 ${ lib.concatMapStrings
77 ({ name, file, master ? true, slaves ? [], masters ? [], allowQuery ? [], extraConfig ? "" }:
80 type ${if master then "master" else "slave"};
85 ${lib.concatMapStrings (ip: "${ip};\n") slaves}
91 ${lib.concatMapStrings (ip: "${ip};\n") masters}
95 allow-query { ${lib.concatMapStrings (ip: "${ip}; ") allowQuery}};
99 (lib.attrValues cfg.zones) }
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;
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.
131 blockedNetworks = lib.mkOption {
133 type = lib.types.listOf lib.types.str;
135 What networks are just blocked.
139 ipv4Only = lib.mkOption {
141 type = lib.types.bool;
143 Only use ipv4, even if the host supports ipv6.
147 forwarders = lib.mkOption {
148 default = config.networking.nameservers;
149 defaultText = lib.literalExpression "config.networking.nameservers";
150 type = lib.types.listOf lib.types.str;
152 List of servers we should forward requests to.
156 forward = lib.mkOption {
158 type = lib.types.enum ["first" "only"];
160 Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'.
164 listenOn = lib.mkOption {
166 type = lib.types.listOf lib.types.str;
168 Interfaces to listen on.
172 listenOnIpv6 = lib.mkOption {
174 type = lib.types.listOf lib.types.str;
176 Ipv6 interfaces to listen on.
180 directory = lib.mkOption {
181 type = lib.types.str;
182 default = "/run/named";
183 description = "Working directory of BIND.";
186 zones = lib.mkOption {
188 type = with lib.types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (lib.types.submodule bindZoneOptions));
190 List of zones we claim authority over.
195 file = "/var/dns/example.com";
196 masters = [ "192.168.0.1" ];
203 extraConfig = lib.mkOption {
204 type = lib.types.lines;
207 Extra lines to be added verbatim to the generated named configuration file.
211 extraOptions = lib.mkOption {
212 type = lib.types.lines;
215 Extra lines to be added verbatim to the options section of the
216 generated named configuration file.
220 configFile = lib.mkOption {
221 type = lib.types.path;
223 defaultText = lib.literalExpression "confFile";
225 Overridable config file to use for named. By default, that
235 ###### implementation
237 config = lib.mkIf cfg.enable {
239 networking.resolvconf.useLocalResolver = lib.mkDefault true;
241 users.users.${bindUser} =
244 description = "BIND daemon user";
247 users.groups.${bindUser} = {};
249 systemd.tmpfiles.settings."bind" = lib.mkIf (cfg.directory != "/run/named") {
258 systemd.services.bind = {
259 description = "BIND Domain Name Server";
260 after = [ "network.target" ];
261 wantedBy = [ "multi-user.target" ];
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
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";
275 RuntimeDirectory = "named";
276 RuntimeDirectoryPreserve = "yes";
277 ConfigurationDirectory = "bind";
279 (lib.mapAttrsToList (name: config: if (lib.hasPrefix "/" config.file) then ("-${dirOf config.file}") else "") cfg.zones)
282 CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
283 AmbientCapabilities = "CAP_NET_BIND_SERVICE";
285 NoNewPrivileges = true;
287 ProtectSystem = "strict";
288 ReadOnlyPaths = "/sys";
291 PrivateDevices = true;
292 PrivateMounts = true;
293 ProtectHostname = true;
295 ProtectKernelTunables = true;
296 ProtectKernelModules = true;
297 ProtectKernelLogs = true;
298 ProtectControlGroups = true;
299 ProtectProc = "invisible";
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";
313 unitConfig.Documentation = "man:named(8)";