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