at 23.05-pre 7.9 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.bind; 8 9 bindPkg = config.services.bind.package; 10 11 bindUser = "named"; 12 13 bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; })); 14 15 bindZoneOptions = { name, config, ... }: { 16 options = { 17 name = mkOption { 18 type = types.str; 19 default = name; 20 description = lib.mdDoc "Name of the zone."; 21 }; 22 master = mkOption { 23 description = lib.mdDoc "Master=false means slave server"; 24 type = types.bool; 25 }; 26 file = mkOption { 27 type = types.either types.str types.path; 28 description = lib.mdDoc "Zone file resource records contain columns of data, separated by whitespace, that define the record."; 29 }; 30 masters = mkOption { 31 type = types.listOf types.str; 32 description = lib.mdDoc "List of servers for inclusion in stub and secondary zones."; 33 }; 34 slaves = mkOption { 35 type = types.listOf types.str; 36 description = lib.mdDoc "Addresses who may request zone transfers."; 37 default = [ ]; 38 }; 39 extraConfig = mkOption { 40 type = types.str; 41 description = lib.mdDoc "Extra zone config to be appended at the end of the zone section."; 42 default = ""; 43 }; 44 }; 45 }; 46 47 confFile = pkgs.writeText "named.conf" 48 '' 49 include "/etc/bind/rndc.key"; 50 controls { 51 inet 127.0.0.1 allow {localhost;} keys {"rndc-key";}; 52 }; 53 54 acl cachenetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} }; 55 acl badnetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} }; 56 57 options { 58 listen-on { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOn} }; 59 listen-on-v6 { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} }; 60 allow-query { cachenetworks; }; 61 blackhole { badnetworks; }; 62 forward ${cfg.forward}; 63 forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} }; 64 directory "${cfg.directory}"; 65 pid-file "/run/named/named.pid"; 66 ${cfg.extraOptions} 67 }; 68 69 ${cfg.extraConfig} 70 71 ${ concatMapStrings 72 ({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }: 73 '' 74 zone "${name}" { 75 type ${if master then "master" else "slave"}; 76 file "${file}"; 77 ${ if master then 78 '' 79 allow-transfer { 80 ${concatMapStrings (ip: "${ip};\n") slaves} 81 }; 82 '' 83 else 84 '' 85 masters { 86 ${concatMapStrings (ip: "${ip};\n") masters} 87 }; 88 '' 89 } 90 allow-query { any; }; 91 ${extraConfig} 92 }; 93 '') 94 (attrValues cfg.zones) } 95 ''; 96 97in 98 99{ 100 101 ###### interface 102 103 options = { 104 105 services.bind = { 106 107 enable = mkEnableOption (lib.mdDoc "BIND domain name server"); 108 109 110 package = mkOption { 111 type = types.package; 112 default = pkgs.bind; 113 defaultText = literalExpression "pkgs.bind"; 114 description = lib.mdDoc "The BIND package to use."; 115 }; 116 117 cacheNetworks = mkOption { 118 default = [ "127.0.0.0/24" ]; 119 type = types.listOf types.str; 120 description = lib.mdDoc '' 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 It is recommended that you limit cacheNetworks to avoid your 125 server being used for DNS amplification attacks. 126 ''; 127 }; 128 129 blockedNetworks = mkOption { 130 default = [ ]; 131 type = types.listOf types.str; 132 description = lib.mdDoc '' 133 What networks are just blocked. 134 ''; 135 }; 136 137 ipv4Only = mkOption { 138 default = false; 139 type = types.bool; 140 description = lib.mdDoc '' 141 Only use ipv4, even if the host supports ipv6. 142 ''; 143 }; 144 145 forwarders = mkOption { 146 default = config.networking.nameservers; 147 defaultText = literalExpression "config.networking.nameservers"; 148 type = types.listOf types.str; 149 description = lib.mdDoc '' 150 List of servers we should forward requests to. 151 ''; 152 }; 153 154 forward = mkOption { 155 default = "first"; 156 type = types.enum ["first" "only"]; 157 description = lib.mdDoc '' 158 Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'. 159 ''; 160 }; 161 162 listenOn = mkOption { 163 default = [ "any" ]; 164 type = types.listOf types.str; 165 description = lib.mdDoc '' 166 Interfaces to listen on. 167 ''; 168 }; 169 170 listenOnIpv6 = mkOption { 171 default = [ "any" ]; 172 type = types.listOf types.str; 173 description = lib.mdDoc '' 174 Ipv6 interfaces to listen on. 175 ''; 176 }; 177 178 directory = mkOption { 179 type = types.str; 180 default = "/run/named"; 181 description = lib.mdDoc "Working directory of BIND."; 182 }; 183 184 zones = mkOption { 185 default = [ ]; 186 type = with types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (types.submodule bindZoneOptions)); 187 description = lib.mdDoc '' 188 List of zones we claim authority over. 189 ''; 190 example = { 191 "example.com" = { 192 master = false; 193 file = "/var/dns/example.com"; 194 masters = [ "192.168.0.1" ]; 195 slaves = [ ]; 196 extraConfig = ""; 197 }; 198 }; 199 }; 200 201 extraConfig = mkOption { 202 type = types.lines; 203 default = ""; 204 description = lib.mdDoc '' 205 Extra lines to be added verbatim to the generated named configuration file. 206 ''; 207 }; 208 209 extraOptions = mkOption { 210 type = types.lines; 211 default = ""; 212 description = lib.mdDoc '' 213 Extra lines to be added verbatim to the options section of the 214 generated named configuration file. 215 ''; 216 }; 217 218 configFile = mkOption { 219 type = types.path; 220 default = confFile; 221 defaultText = literalExpression "confFile"; 222 description = lib.mdDoc '' 223 Overridable config file to use for named. By default, that 224 generated by nixos. 225 ''; 226 }; 227 228 }; 229 230 }; 231 232 233 ###### implementation 234 235 config = mkIf cfg.enable { 236 237 networking.resolvconf.useLocalResolver = mkDefault true; 238 239 users.users.${bindUser} = 240 { 241 group = bindUser; 242 description = "BIND daemon user"; 243 isSystemUser = true; 244 }; 245 users.groups.${bindUser} = {}; 246 247 systemd.services.bind = { 248 description = "BIND Domain Name Server"; 249 after = [ "network.target" ]; 250 wantedBy = [ "multi-user.target" ]; 251 252 preStart = '' 253 mkdir -m 0755 -p /etc/bind 254 if ! [ -f "/etc/bind/rndc.key" ]; then 255 ${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null 256 fi 257 258 ${pkgs.coreutils}/bin/mkdir -p /run/named 259 chown ${bindUser} /run/named 260 261 ${pkgs.coreutils}/bin/mkdir -p ${cfg.directory} 262 chown ${bindUser} ${cfg.directory} 263 ''; 264 265 serviceConfig = { 266 ExecStart = "${bindPkg.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; 267 ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload"; 268 ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; 269 }; 270 271 unitConfig.Documentation = "man:named(8)"; 272 }; 273 }; 274}