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".
42 NOTE: This overrides the global-level `allow-query` setting, which is set to the contents
47 extraConfig = lib.mkOption {
48 type = lib.types.lines;
49 description = "Extra zone config to be appended at the end of the zone section.";
55 confFile = pkgs.writeText "named.conf"
57 include "/etc/bind/rndc.key";
59 inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
62 acl cachenetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
63 acl badnetworks { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
66 listen-on { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.listenOn} };
67 listen-on-v6 { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} };
68 allow-query { cachenetworks; };
69 blackhole { badnetworks; };
70 forward ${cfg.forward};
71 forwarders { ${lib.concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
72 directory "${cfg.directory}";
73 pid-file "/run/named/named.pid";
79 ${ lib.concatMapStrings
80 ({ name, file, master ? true, slaves ? [], masters ? [], allowQuery ? [], extraConfig ? "" }:
83 type ${if master then "master" else "slave"};
88 ${lib.concatMapStrings (ip: "${ip};\n") slaves}
94 ${lib.concatMapStrings (ip: "${ip};\n") masters}
98 allow-query { ${lib.concatMapStrings (ip: "${ip}; ") allowQuery}};
102 (lib.attrValues cfg.zones) }
115 enable = lib.mkEnableOption "BIND domain name server";
118 package = lib.mkPackageOption pkgs "bind" { };
120 cacheNetworks = lib.mkOption {
121 default = [ "127.0.0.0/24" "::1/128" ];
122 type = lib.types.listOf lib.types.str;
124 What networks are allowed to use us as a resolver. Note
125 that this is for recursive queries -- all networks are
126 allowed to query zones configured with the `zones` option
127 by default (although this may be overridden within each
128 zone's configuration, via the `allowQuery` option).
129 It is recommended that you limit cacheNetworks to avoid your
130 server being used for DNS amplification attacks.
134 blockedNetworks = lib.mkOption {
136 type = lib.types.listOf lib.types.str;
138 What networks are just blocked.
142 ipv4Only = lib.mkOption {
144 type = lib.types.bool;
146 Only use ipv4, even if the host supports ipv6.
150 forwarders = lib.mkOption {
151 default = config.networking.nameservers;
152 defaultText = lib.literalExpression "config.networking.nameservers";
153 type = lib.types.listOf lib.types.str;
155 List of servers we should forward requests to.
159 forward = lib.mkOption {
161 type = lib.types.enum ["first" "only"];
163 Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'.
167 listenOn = lib.mkOption {
169 type = lib.types.listOf lib.types.str;
171 Interfaces to listen on.
175 listenOnIpv6 = lib.mkOption {
177 type = lib.types.listOf lib.types.str;
179 Ipv6 interfaces to listen on.
183 directory = lib.mkOption {
184 type = lib.types.str;
185 default = "/run/named";
186 description = "Working directory of BIND.";
189 zones = lib.mkOption {
191 type = with lib.types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (lib.types.submodule bindZoneOptions));
193 List of zones we claim authority over.
198 file = "/var/dns/example.com";
199 masters = [ "192.168.0.1" ];
206 extraConfig = lib.mkOption {
207 type = lib.types.lines;
210 Extra lines to be added verbatim to the generated named configuration file.
214 extraOptions = lib.mkOption {
215 type = lib.types.lines;
218 Extra lines to be added verbatim to the options section of the
219 generated named configuration file.
223 configFile = lib.mkOption {
224 type = lib.types.path;
226 defaultText = lib.literalExpression "confFile";
228 Overridable config file to use for named. By default, that
238 ###### implementation
240 config = lib.mkIf cfg.enable {
242 networking.resolvconf.useLocalResolver = lib.mkDefault true;
244 users.users.${bindUser} =
247 description = "BIND daemon user";
250 users.groups.${bindUser} = {};
252 systemd.services.bind = {
253 description = "BIND Domain Name Server";
254 after = [ "network.target" ];
255 wantedBy = [ "multi-user.target" ];
258 mkdir -m 0755 -p /etc/bind
259 if ! [ -f "/etc/bind/rndc.key" ]; then
260 ${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null
263 ${pkgs.coreutils}/bin/mkdir -p /run/named
264 chown ${bindUser} /run/named
266 ${pkgs.coreutils}/bin/mkdir -p ${cfg.directory}
267 chown ${bindUser} ${cfg.directory}
271 Type = "forking"; # Set type to forking, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900788
272 ExecStart = "${bindPkg.out}/sbin/named -u ${bindUser} ${lib.optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile}";
273 ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
274 ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
277 unitConfig.Documentation = "man:named(8)";