at master 8.4 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.pdns-recursor; 12 13 oneOrMore = type: with types; either type (listOf type); 14 valueType = 15 with types; 16 oneOf [ 17 int 18 str 19 bool 20 path 21 ]; 22 configType = with types; attrsOf (nullOr (oneOrMore valueType)); 23 24 toBool = val: if val then "yes" else "no"; 25 serialize = 26 val: 27 with types; 28 if str.check val then 29 val 30 else if int.check val then 31 toString val 32 else if path.check val then 33 toString val 34 else if bool.check val then 35 toBool val 36 else if builtins.isList val then 37 (concatMapStringsSep "," serialize val) 38 else 39 ""; 40 41 settingsFormat = pkgs.formats.yaml { }; 42 43 mkDefaultAttrs = mapAttrs (n: v: mkDefault v); 44 45 mkForwardZone = mapAttrsToList ( 46 zone: uri: { 47 inherit zone; 48 forwarders = [ uri ]; 49 } 50 ); 51 52 configFile = 53 if cfg.old-settings != { } then 54 # Convert recursor.conf to recursor.yml and merge it 55 let 56 conf = pkgs.writeText "recursor.conf" ( 57 concatStringsSep "\n" (mapAttrsToList (name: val: "${name}=${serialize val}") cfg.old-settings) 58 ); 59 60 yaml = settingsFormat.generate "recursor.yml" cfg.yaml-settings; 61 in 62 pkgs.runCommand "recursor-merged.yml" { } '' 63 ${pkgs.pdns-recursor}/bin/rec_control show-yaml --config ${conf} > override.yml 64 ${pkgs.yq-go}/bin/yq '. *= load("override.yml")' ${yaml} > $out 65 '' 66 else 67 settingsFormat.generate "recursor.yml" cfg.yaml-settings; 68 69in 70{ 71 options.services.pdns-recursor = { 72 enable = mkEnableOption "PowerDNS Recursor, a recursive DNS server"; 73 74 dns.address = mkOption { 75 type = oneOrMore types.str; 76 default = [ 77 "::" 78 "0.0.0.0" 79 ]; 80 description = '' 81 IP addresses Recursor DNS server will bind to. 82 ''; 83 }; 84 85 dns.port = mkOption { 86 type = types.port; 87 default = 53; 88 description = '' 89 Port number Recursor DNS server will bind to. 90 ''; 91 }; 92 93 dns.allowFrom = mkOption { 94 type = types.listOf types.str; 95 default = [ 96 "127.0.0.0/8" 97 "10.0.0.0/8" 98 "100.64.0.0/10" 99 "169.254.0.0/16" 100 "192.168.0.0/16" 101 "172.16.0.0/12" 102 "::1/128" 103 "fc00::/7" 104 "fe80::/10" 105 ]; 106 example = [ 107 "0.0.0.0/0" 108 "::/0" 109 ]; 110 description = '' 111 IP address ranges of clients allowed to make DNS queries. 112 ''; 113 }; 114 115 api.address = mkOption { 116 type = types.str; 117 default = "0.0.0.0"; 118 description = '' 119 IP address Recursor REST API server will bind to. 120 ''; 121 }; 122 123 api.port = mkOption { 124 type = types.port; 125 default = 8082; 126 description = '' 127 Port number Recursor REST API server will bind to. 128 ''; 129 }; 130 131 api.allowFrom = mkOption { 132 type = types.listOf types.str; 133 default = [ 134 "127.0.0.1" 135 "::1" 136 ]; 137 example = [ 138 "0.0.0.0/0" 139 "::/0" 140 ]; 141 description = '' 142 IP address ranges of clients allowed to make API requests. 143 ''; 144 }; 145 146 exportHosts = mkOption { 147 type = types.bool; 148 default = false; 149 description = '' 150 Whether to export names and IP addresses defined in /etc/hosts. 151 ''; 152 }; 153 154 forwardZones = mkOption { 155 type = types.attrs; 156 default = { }; 157 description = '' 158 DNS zones to be forwarded to other authoritative servers. 159 ''; 160 }; 161 162 forwardZonesRecurse = mkOption { 163 type = types.attrs; 164 example = { 165 eth = "[::1]:5353"; 166 }; 167 default = { }; 168 description = '' 169 DNS zones to be forwarded to other recursive servers. 170 ''; 171 }; 172 173 dnssecValidation = mkOption { 174 type = types.enum [ 175 "off" 176 "process-no-validate" 177 "process" 178 "log-fail" 179 "validate" 180 ]; 181 default = "validate"; 182 description = '' 183 Controls the level of DNSSEC processing done by the PowerDNS Recursor. 184 See <https://doc.powerdns.com/md/recursor/dnssec/> for a detailed explanation. 185 ''; 186 }; 187 188 serveRFC1918 = mkOption { 189 type = types.bool; 190 default = true; 191 description = '' 192 Whether to directly resolve the RFC1918 reverse-mapping domains: 193 `10.in-addr.arpa`, 194 `168.192.in-addr.arpa`, 195 `16-31.172.in-addr.arpa` 196 This saves load on the AS112 servers. 197 ''; 198 }; 199 200 old-settings = mkOption { 201 type = configType; 202 default = { }; 203 example = literalExpression '' 204 { 205 loglevel = 8; 206 log-common-errors = true; 207 } 208 ''; 209 description = '' 210 Older PowerDNS Recursor settings. Use this option to configure 211 Recursor settings not exposed in a NixOS option or to bypass one. 212 See the full documentation at 213 <https://doc.powerdns.com/recursor/settings.html> 214 for the available options. 215 216 ::: {.warning} 217 This option is provided for backward compatibility only 218 and will be removed in the next release of NixOS. 219 ::: 220 ''; 221 }; 222 223 yaml-settings = mkOption { 224 type = settingsFormat.type; 225 default = { }; 226 example = literalExpression '' 227 { 228 loglevel = 8; 229 log-common-errors = true; 230 } 231 ''; 232 description = '' 233 PowerDNS Recursor settings. Use this option to configure Recursor 234 settings not exposed in a NixOS option or to bypass one. 235 See the full documentation at 236 <https://doc.powerdns.com/recursor/yamlsettings.html> 237 for the available options. 238 ''; 239 }; 240 241 luaConfig = mkOption { 242 type = types.lines; 243 default = ""; 244 description = '' 245 The content Lua configuration file for PowerDNS Recursor. See 246 <https://doc.powerdns.com/recursor/lua-config/index.html>. 247 ''; 248 }; 249 }; 250 251 config = mkIf cfg.enable { 252 253 environment.etc."/pdns-recursor/recursor.yml".source = configFile; 254 255 networking.resolvconf.useLocalResolver = lib.mkDefault true; 256 257 services.pdns-recursor.yaml-settings = { 258 incoming = mkDefaultAttrs { 259 listen = cfg.dns.address; 260 port = cfg.dns.port; 261 allow_from = cfg.dns.allowFrom; 262 }; 263 264 webservice = mkDefaultAttrs { 265 address = cfg.api.address; 266 port = cfg.api.port; 267 allow_from = cfg.api.allowFrom; 268 }; 269 270 recursor = mkDefaultAttrs { 271 forward_zones = mkForwardZone cfg.forwardZones; 272 forward_zones_recurse = mkForwardZone cfg.forwardZonesRecurse; 273 export_etc_hosts = cfg.exportHosts; 274 serve_rfc1918 = cfg.serveRFC1918; 275 lua_config_file = pkgs.writeText "recursor.lua" cfg.luaConfig; 276 daemon = false; 277 write_pid = false; 278 }; 279 280 dnssec = mkDefaultAttrs { 281 validation = cfg.dnssecValidation; 282 }; 283 284 logging = mkDefaultAttrs { 285 timestamp = false; 286 disable_syslog = true; 287 }; 288 }; 289 290 systemd.packages = [ pkgs.pdns-recursor ]; 291 292 systemd.services.pdns-recursor = { 293 restartTriggers = [ config.environment.etc."/pdns-recursor/recursor.yml".source ]; 294 wantedBy = [ "multi-user.target" ]; 295 }; 296 297 users.users.pdns-recursor = { 298 isSystemUser = true; 299 group = "pdns-recursor"; 300 description = "PowerDNS Recursor daemon user"; 301 }; 302 303 users.groups.pdns-recursor = { }; 304 305 warnings = lib.optional (cfg.old-settings != { }) '' 306 pdns-recursor has changed its configuration file format from pdns-recursor.conf 307 (mapped to `services.pdns-recursor.old-settings`) to the newer pdns-recursor.yml 308 (mapped to `services.pdns-recursor.yaml-settings`). 309 310 Support for the older format will be removed in a future version, so please migrate 311 your settings over. See <https://doc.powerdns.com/recursor/yamlsettings.html>. 312 ''; 313 314 }; 315 316 imports = [ 317 (mkRemovedOptionModule [ 318 "services" 319 "pdns-recursor" 320 "extraConfig" 321 ] "To change extra Recursor settings use services.pdns-recursor.settings instead.") 322 323 (mkRenamedOptionModule 324 [ 325 "services" 326 "pdns-recursor" 327 "settings" 328 ] 329 [ 330 "services" 331 "pdns-recursor" 332 "old-settings" 333 ] 334 ) 335 ]; 336 337 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 338 339}