1{ config, pkgs, lib, ... }: 2 3with lib; 4 5let 6 cfg = config.services.nsd; 7 8 username = "nsd"; 9 stateDir = "/var/lib/nsd"; 10 pidFile = stateDir + "/var/nsd.pid"; 11 12 # build nsd with the options needed for the given config 13 nsdPkg = pkgs.nsd.override { 14 configFile = "${configFile}/nsd.conf"; 15 16 bind8Stats = cfg.bind8Stats; 17 ipv6 = cfg.ipv6; 18 ratelimit = cfg.ratelimit.enable; 19 rootServer = cfg.rootServer; 20 zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0; 21 }; 22 23 24 nsdEnv = pkgs.buildEnv { 25 name = "nsd-env"; 26 27 paths = [ configFile ] 28 ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs; 29 30 postBuild = '' 31 echo "checking zone files" 32 cd $out/zones 33 34 for zoneFile in *; do 35 echo "|- checking zone '$out/zones/$zoneFile'" 36 ${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || { 37 if grep -q \\\\\\$ "$zoneFile"; then 38 echo zone "$zoneFile" contains escaped dollar signes \\\$ 39 echo Escaping them is not needed any more. Please make shure \ 40 to unescape them where they prefix a variable name 41 fi 42 43 exit 1 44 } 45 done 46 47 echo "checking configuration file" 48 ${nsdPkg}/sbin/nsd-checkconf $out/nsd.conf 49 ''; 50 }; 51 52 writeZoneData = name: text: pkgs.writeTextFile { 53 inherit name text; 54 destination = "/zones/${name}"; 55 }; 56 57 58 # options are ordered alphanumerically by the nixos option name 59 configFile = pkgs.writeTextDir "nsd.conf" '' 60 server: 61 chroot: "${stateDir}" 62 username: ${username} 63 64 # The directory for zonefile: files. The daemon chdirs here. 65 zonesdir: "${stateDir}" 66 67 # the list of dynamically added zones. 68 database: "${stateDir}/var/nsd.db" 69 pidfile: "${pidFile}" 70 xfrdfile: "${stateDir}/var/xfrd.state" 71 xfrdir: "${stateDir}/tmp" 72 zonelistfile: "${stateDir}/var/zone.list" 73 74 # interfaces 75 ${forEach " ip-address: " cfg.interfaces} 76 77 ip-freebind: ${yesOrNo cfg.ipFreebind} 78 hide-version: ${yesOrNo cfg.hideVersion} 79 identity: "${cfg.identity}" 80 ip-transparent: ${yesOrNo cfg.ipTransparent} 81 do-ip4: ${yesOrNo cfg.ipv4} 82 ipv4-edns-size: ${toString cfg.ipv4EDNSSize} 83 do-ip6: ${yesOrNo cfg.ipv6} 84 ipv6-edns-size: ${toString cfg.ipv6EDNSSize} 85 log-time-ascii: ${yesOrNo cfg.logTimeAscii} 86 ${maybeString "nsid: " cfg.nsid} 87 port: ${toString cfg.port} 88 reuseport: ${yesOrNo cfg.reuseport} 89 round-robin: ${yesOrNo cfg.roundRobin} 90 server-count: ${toString cfg.serverCount} 91 ${maybeToString "statistics: " cfg.statistics} 92 tcp-count: ${toString cfg.tcpCount} 93 tcp-query-count: ${toString cfg.tcpQueryCount} 94 tcp-timeout: ${toString cfg.tcpTimeout} 95 verbosity: ${toString cfg.verbosity} 96 ${maybeString "version: " cfg.version} 97 xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout} 98 zonefiles-check: ${yesOrNo cfg.zonefilesCheck} 99 100 ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength} 101 ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength} 102 rrl-ratelimit: ${toString cfg.ratelimit.ratelimit} 103 ${maybeString "rrl-slip: " cfg.ratelimit.slip} 104 rrl-size: ${toString cfg.ratelimit.size} 105 rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit} 106 107 ${keyConfigFile} 108 109 remote-control: 110 control-enable: ${yesOrNo cfg.remoteControl.enable} 111 control-key-file: "${cfg.remoteControl.controlKeyFile}" 112 control-cert-file: "${cfg.remoteControl.controlCertFile}" 113 ${forEach " control-interface: " cfg.remoteControl.interfaces} 114 control-port: ${toString cfg.remoteControl.port} 115 server-key-file: "${cfg.remoteControl.serverKeyFile}" 116 server-cert-file: "${cfg.remoteControl.serverCertFile}" 117 118 ${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)} 119 120 ${cfg.extraConfig} 121 ''; 122 123 yesOrNo = b: if b then "yes" else "no"; 124 maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"''; 125 maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}''; 126 forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; 127 128 129 keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: '' 130 key: 131 name: "${keyName}" 132 algorithm: "${keyOptions.algorithm}" 133 include: "${stateDir}/private/${keyName}" 134 '') cfg.keys); 135 136 copyKeys = concatStrings (mapAttrsToList (keyName: keyOptions: '' 137 secret=$(cat "${keyOptions.keyFile}") 138 dest="${stateDir}/private/${keyName}" 139 echo " secret: \"$secret\"" > "$dest" 140 chown ${username}:${username} "$dest" 141 chmod 0400 "$dest" 142 '') cfg.keys); 143 144 145 # options are ordered alphanumerically by the nixos option name 146 zoneConfigFile = name: zone: '' 147 zone: 148 name: "${name}" 149 zonefile: "${stateDir}/zones/${name}" 150 ${maybeString "outgoing-interface: " zone.outgoingInterface} 151 ${forEach " rrl-whitelist: " zone.rrlWhitelist} 152 ${maybeString "zonestats: " zone.zoneStats} 153 154 ${maybeToString "max-refresh-time: " zone.maxRefreshSecs} 155 ${maybeToString "min-refresh-time: " zone.minRefreshSecs} 156 ${maybeToString "max-retry-time: " zone.maxRetrySecs} 157 ${maybeToString "min-retry-time: " zone.minRetrySecs} 158 159 allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} 160 ${forEach " allow-notify: " zone.allowNotify} 161 ${forEach " request-xfr: " zone.requestXFR} 162 163 ${forEach " notify: " zone.notify} 164 notify-retry: ${toString zone.notifyRetry} 165 ${forEach " provide-xfr: " zone.provideXFR} 166 ''; 167 168 zoneConfigs = zoneConfigs' {} "" { children = cfg.zones; }; 169 170 zoneConfigs' = parent: name: zone: 171 if !(zone ? children) || zone.children == null || zone.children == { } 172 # leaf -> actual zone 173 then listToAttrs [ (nameValuePair name (parent // zone)) ] 174 175 # fork -> pattern 176 else zipAttrsWith (name: head) ( 177 mapAttrsToList (name: child: zoneConfigs' (parent // zone // { children = {}; }) name child) 178 zone.children 179 ); 180 181 # fighting infinite recursion 182 zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true; 183 zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false; 184 zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false; 185 zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false; 186 zoneOptions4 = zoneOptionsRaw // childConfig zoneOptions5 false; 187 zoneOptions5 = zoneOptionsRaw // childConfig zoneOptions6 false; 188 zoneOptions6 = zoneOptionsRaw // childConfig null false; 189 190 childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; }; 191 192 # options are ordered alphanumerically 193 zoneOptionsRaw = types.submodule { 194 options = { 195 196 allowAXFRFallback = mkOption { 197 type = types.bool; 198 default = true; 199 description = '' 200 If NSD as secondary server should be allowed to AXFR if the primary 201 server does not allow IXFR. 202 ''; 203 }; 204 205 allowNotify = mkOption { 206 type = types.listOf types.str; 207 default = [ ]; 208 example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name" 209 "10.0.3.4&255.255.0.0 BLOCKED" 210 ]; 211 description = '' 212 Listed primary servers are allowed to notify this secondary server. 213 <screen><![CDATA[ 214 Format: <ip> <key-name | NOKEY | BLOCKED> 215 216 <ip> either a plain IPv4/IPv6 address or range. Valid patters for ranges: 217 * 10.0.0.0/24 # via subnet size 218 * 10.0.0.0&255.255.255.0 # via subnet mask 219 * 10.0.0.1-10.0.0.254 # via range 220 221 A optional port number could be added with a '@': 222 * 2001:1234::1@1234 223 224 <key-name | NOKEY | BLOCKED> 225 * <key-name> will use the specified TSIG key 226 * NOKEY no TSIG signature is required 227 * BLOCKED notifies from non-listed or blocked IPs will be ignored 228 * ]]></screen> 229 ''; 230 }; 231 232 children = mkOption { 233 default = {}; 234 description = '' 235 Children zones inherit all options of their parents. Attributes 236 defined in a child will overwrite the ones of its parent. Only 237 leaf zones will be actually served. This way it's possible to 238 define maybe zones which share most attributes without 239 duplicating everything. This mechanism replaces nsd's patterns 240 in a save and functional way. 241 ''; 242 }; 243 244 data = mkOption { 245 type = types.str; 246 default = ""; 247 example = ""; 248 description = '' 249 The actual zone data. This is the content of your zone file. 250 Use imports or pkgs.lib.readFile if you don't want this data in your config file. 251 ''; 252 }; 253 254 maxRefreshSecs = mkOption { 255 type = types.nullOr types.int; 256 default = null; 257 description = '' 258 Limit refresh time for secondary zones. This is the timer which 259 checks to see if the zone has to be refetched when it expires. 260 Normally the value from the SOA record is used, but this option 261 restricts that value. 262 ''; 263 }; 264 265 minRefreshSecs = mkOption { 266 type = types.nullOr types.int; 267 default = null; 268 description = '' 269 Limit refresh time for secondary zones. 270 ''; 271 }; 272 273 maxRetrySecs = mkOption { 274 type = types.nullOr types.int; 275 default = null; 276 description = '' 277 Limit retry time for secondary zones. This is the timeout after 278 a failed fetch attempt for the zone. Normally the value from 279 the SOA record is used, but this option restricts that value. 280 ''; 281 }; 282 283 minRetrySecs = mkOption { 284 type = types.nullOr types.int; 285 default = null; 286 description = '' 287 Limit retry time for secondary zones. 288 ''; 289 }; 290 291 292 notify = mkOption { 293 type = types.listOf types.str; 294 default = []; 295 example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ]; 296 description = '' 297 This primary server will notify all given secondary servers about 298 zone changes. 299 <screen><![CDATA[ 300 Format: <ip> <key-name | NOKEY> 301 302 <ip> a plain IPv4/IPv6 address with on optional port number (ip@port) 303 304 <key-name | NOKEY> 305 * <key-name> sign notifies with the specified key 306 * NOKEY don't sign notifies 307 ]]></screen> 308 ''; 309 }; 310 311 notifyRetry = mkOption { 312 type = types.int; 313 default = 5; 314 description = '' 315 Specifies the number of retries for failed notifies. Set this along with notify. 316 ''; 317 }; 318 319 outgoingInterface = mkOption { 320 type = types.nullOr types.str; 321 default = null; 322 example = "2000::1@1234"; 323 description = '' 324 This address will be used for zone-transfere requests if configured 325 as a secondary server or notifications in case of a primary server. 326 Supply either a plain IPv4 or IPv6 address with an optional port 327 number (ip@port). 328 ''; 329 }; 330 331 provideXFR = mkOption { 332 type = types.listOf types.str; 333 default = []; 334 example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ]; 335 description = '' 336 Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED 337 address range 192.0.2.0/24, 1.2.3.4&amp;255.255.0.0, 3.0.2.20-3.0.2.40 338 ''; 339 }; 340 341 requestXFR = mkOption { 342 type = types.listOf types.str; 343 default = []; 344 example = []; 345 description = '' 346 Format: <code>[AXFR|UDP] &lt;ip-address&gt; &lt;key-name | NOKEY&gt;</code> 347 ''; 348 }; 349 350 rrlWhitelist = mkOption { 351 type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]); 352 default = []; 353 description = '' 354 Whitelists the given rrl-types. 355 ''; 356 }; 357 358 zoneStats = mkOption { 359 type = types.nullOr types.str; 360 default = null; 361 example = "%s"; 362 description = '' 363 When set to something distinct to null NSD is able to collect 364 statistics per zone. All statistics of this zone(s) will be added 365 to the group specified by this given name. Use "%s" to use the zones 366 name as the group. The groups are output from nsd-control stats 367 and stats_noreset. 368 ''; 369 }; 370 371 }; 372 }; 373 374in 375{ 376 # options are ordered alphanumerically 377 options.services.nsd = { 378 379 enable = mkEnableOption "NSD authoritative DNS server"; 380 381 bind8Stats = mkEnableOption "BIND8 like statistics"; 382 383 extraConfig = mkOption { 384 type = types.str; 385 default = ""; 386 description = '' 387 Extra nsd config. 388 ''; 389 }; 390 391 hideVersion = mkOption { 392 type = types.bool; 393 default = true; 394 description = '' 395 Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries. 396 ''; 397 }; 398 399 identity = mkOption { 400 type = types.str; 401 default = "unidentified server"; 402 description = '' 403 Identify the server (CH TXT ID.SERVER entry). 404 ''; 405 }; 406 407 interfaces = mkOption { 408 type = types.listOf types.str; 409 default = [ "127.0.0.0" "::1" ]; 410 description = '' 411 What addresses the server should listen to. 412 ''; 413 }; 414 415 ipFreebind = mkOption { 416 type = types.bool; 417 default = false; 418 description = '' 419 Whether to bind to nonlocal addresses and interfaces that are down. 420 Similar to ip-transparent. 421 ''; 422 }; 423 424 ipTransparent = mkOption { 425 type = types.bool; 426 default = false; 427 description = '' 428 Allow binding to non local addresses. 429 ''; 430 }; 431 432 ipv4 = mkOption { 433 type = types.bool; 434 default = true; 435 description = '' 436 Whether to listen on IPv4 connections. 437 ''; 438 }; 439 440 ipv4EDNSSize = mkOption { 441 type = types.int; 442 default = 4096; 443 description = '' 444 Preferred EDNS buffer size for IPv4. 445 ''; 446 }; 447 448 ipv6 = mkOption { 449 type = types.bool; 450 default = true; 451 description = '' 452 Whether to listen on IPv6 connections. 453 ''; 454 }; 455 456 ipv6EDNSSize = mkOption { 457 type = types.int; 458 default = 4096; 459 description = '' 460 Preferred EDNS buffer size for IPv6. 461 ''; 462 }; 463 464 logTimeAscii = mkOption { 465 type = types.bool; 466 default = true; 467 description = '' 468 Log time in ascii, if false then in unix epoch seconds. 469 ''; 470 }; 471 472 nsid = mkOption { 473 type = types.nullOr types.str; 474 default = null; 475 description = '' 476 NSID identity (hex string, or "ascii_somestring"). 477 ''; 478 }; 479 480 port = mkOption { 481 type = types.int; 482 default = 53; 483 description = '' 484 Port the service should bind do. 485 ''; 486 }; 487 488 reuseport = mkOption { 489 type = types.bool; 490 default = pkgs.stdenv.isLinux; 491 description = '' 492 Whether to enable SO_REUSEPORT on all used sockets. This lets multiple 493 processes bind to the same port. This speeds up operation especially 494 if the server count is greater than one and makes fast restarts less 495 prone to fail 496 ''; 497 }; 498 499 rootServer = mkOption { 500 type = types.bool; 501 default = false; 502 description = '' 503 Whether this server will be a root server (a DNS root server, you 504 usually don't want that). 505 ''; 506 }; 507 508 roundRobin = mkEnableOption "round robin rotation of records"; 509 510 serverCount = mkOption { 511 type = types.int; 512 default = 1; 513 description = '' 514 Number of NSD servers to fork. Put the number of CPUs to use here. 515 ''; 516 }; 517 518 statistics = mkOption { 519 type = types.nullOr types.int; 520 default = null; 521 description = '' 522 Statistics are produced every number of seconds. Prints to log. 523 If null no statistics are logged. 524 ''; 525 }; 526 527 tcpCount = mkOption { 528 type = types.int; 529 default = 100; 530 description = '' 531 Maximum number of concurrent TCP connections per server. 532 ''; 533 }; 534 535 tcpQueryCount = mkOption { 536 type = types.int; 537 default = 0; 538 description = '' 539 Maximum number of queries served on a single TCP connection. 540 0 means no maximum. 541 ''; 542 }; 543 544 tcpTimeout = mkOption { 545 type = types.int; 546 default = 120; 547 description = '' 548 TCP timeout in seconds. 549 ''; 550 }; 551 552 verbosity = mkOption { 553 type = types.int; 554 default = 0; 555 description = '' 556 Verbosity level. 557 ''; 558 }; 559 560 version = mkOption { 561 type = types.nullOr types.str; 562 default = null; 563 description = '' 564 The version string replied for CH TXT version.server and version.bind 565 queries. Will use the compiled package version on null. 566 See hideVersion for enabling/disabling this responses. 567 ''; 568 }; 569 570 xfrdReloadTimeout = mkOption { 571 type = types.int; 572 default = 1; 573 description = '' 574 Number of seconds between reloads triggered by xfrd. 575 ''; 576 }; 577 578 zonefilesCheck = mkOption { 579 type = types.bool; 580 default = true; 581 description = '' 582 Whether to check mtime of all zone files on start and sighup. 583 ''; 584 }; 585 586 587 keys = mkOption { 588 type = types.attrsOf (types.submodule { 589 options = { 590 591 algorithm = mkOption { 592 type = types.str; 593 default = "hmac-sha256"; 594 description = '' 595 Authentication algorithm for this key. 596 ''; 597 }; 598 599 keyFile = mkOption { 600 type = types.path; 601 description = '' 602 Path to the file which contains the actual base64 encoded 603 key. The key will be copied into "${stateDir}/private" before 604 NSD starts. The copied file is only accessibly by the NSD 605 user. 606 ''; 607 }; 608 609 }; 610 }); 611 default = {}; 612 example = literalExample '' 613 { "tsig.example.org" = { 614 algorithm = "hmac-md5"; 615 keyFile = "/path/to/my/key"; 616 }; 617 } 618 ''; 619 description = '' 620 Define your TSIG keys here. 621 ''; 622 }; 623 624 625 ratelimit = { 626 627 enable = mkEnableOption "ratelimit capabilities"; 628 629 ipv4PrefixLength = mkOption { 630 type = types.nullOr types.int; 631 default = null; 632 description = '' 633 IPv4 prefix length. Addresses are grouped by netblock. 634 ''; 635 }; 636 637 ipv6PrefixLength = mkOption { 638 type = types.nullOr types.int; 639 default = null; 640 description = '' 641 IPv6 prefix length. Addresses are grouped by netblock. 642 ''; 643 }; 644 645 ratelimit = mkOption { 646 type = types.int; 647 default = 200; 648 description = '' 649 Max qps allowed from any query source. 650 0 means unlimited. With an verbosity of 2 blocked and 651 unblocked subnets will be logged. 652 ''; 653 }; 654 655 slip = mkOption { 656 type = types.nullOr types.int; 657 default = null; 658 description = '' 659 Number of packets that get discarded before replying a SLIP response. 660 0 disables SLIP responses. 1 will make every response a SLIP response. 661 ''; 662 }; 663 664 size = mkOption { 665 type = types.int; 666 default = 1000000; 667 description = '' 668 Size of the hashtable. More buckets use more memory but lower 669 the chance of hash hash collisions. 670 ''; 671 }; 672 673 whitelistRatelimit = mkOption { 674 type = types.int; 675 default = 2000; 676 description = '' 677 Max qps allowed from whitelisted sources. 678 0 means unlimited. Set the rrl-whitelist option for specific 679 queries to apply this limit instead of the default to them. 680 ''; 681 }; 682 683 }; 684 685 686 remoteControl = { 687 688 enable = mkEnableOption "remote control via nsd-control"; 689 690 controlCertFile = mkOption { 691 type = types.path; 692 default = "/etc/nsd/nsd_control.pem"; 693 description = '' 694 Path to the client certificate signed with the server certificate. 695 This file is used by nsd-control and generated by nsd-control-setup. 696 ''; 697 }; 698 699 controlKeyFile = mkOption { 700 type = types.path; 701 default = "/etc/nsd/nsd_control.key"; 702 description = '' 703 Path to the client private key, which is used by nsd-control 704 but not by the server. This file is generated by nsd-control-setup. 705 ''; 706 }; 707 708 interfaces = mkOption { 709 type = types.listOf types.str; 710 default = [ "127.0.0.1" "::1" ]; 711 description = '' 712 Which interfaces NSD should bind to for remote control. 713 ''; 714 }; 715 716 port = mkOption { 717 type = types.int; 718 default = 8952; 719 description = '' 720 Port number for remote control operations (uses TLS over TCP). 721 ''; 722 }; 723 724 serverCertFile = mkOption { 725 type = types.path; 726 default = "/etc/nsd/nsd_server.pem"; 727 description = '' 728 Path to the server self signed certificate, which is used by the server 729 but and by nsd-control. This file is generated by nsd-control-setup. 730 ''; 731 }; 732 733 serverKeyFile = mkOption { 734 type = types.path; 735 default = "/etc/nsd/nsd_server.key"; 736 description = '' 737 Path to the server private key, which is used by the server 738 but not by nsd-control. This file is generated by nsd-control-setup. 739 ''; 740 }; 741 742 }; 743 744 745 zones = mkOption { 746 type = types.attrsOf zoneOptions; 747 default = {}; 748 example = literalExample '' 749 { "serverGroup1" = { 750 provideXFR = [ "10.1.2.3 NOKEY" ]; 751 children = { 752 "example.com." = { 753 data = ''' 754 $ORIGIN example.com. 755 $TTL 86400 756 @ IN SOA a.ns.example.com. admin.example.com. ( 757 ... 758 '''; 759 }; 760 "example.org." = { 761 data = ''' 762 $ORIGIN example.org. 763 $TTL 86400 764 @ IN SOA a.ns.example.com. admin.example.com. ( 765 ... 766 '''; 767 }; 768 }; 769 }; 770 771 "example.net." = { 772 provideXFR = [ "10.3.2.1 NOKEY" ]; 773 data = ''' 774 ... 775 '''; 776 }; 777 } 778 ''; 779 description = '' 780 Define your zones here. Zones can cascade other zones and therefore 781 inherit settings from parent zones. Look at the definition of 782 children to learn about inheritance and child zones. 783 The given example will define 3 zones (example.(com|org|net).). Both 784 example.com. and example.org. inherit their configuration from 785 serverGroup1. 786 ''; 787 }; 788 789 }; 790 791 config = mkIf cfg.enable { 792 793 environment.systemPackages = [ nsdPkg ]; 794 795 users.extraGroups = singleton { 796 name = username; 797 gid = config.ids.gids.nsd; 798 }; 799 800 users.extraUsers = singleton { 801 name = username; 802 description = "NSD service user"; 803 home = stateDir; 804 createHome = true; 805 uid = config.ids.uids.nsd; 806 group = username; 807 }; 808 809 systemd.services.nsd = { 810 description = "NSD authoritative only domain name service"; 811 812 after = [ "keys.target" "network.target" ]; 813 wantedBy = [ "multi-user.target" ]; 814 wants = [ "keys.target" ]; 815 816 serviceConfig = { 817 ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf"; 818 StandardError = "null"; 819 PIDFile = pidFile; 820 Restart = "always"; 821 RestartSec = "4s"; 822 StartLimitBurst = 4; 823 StartLimitInterval = "5min"; 824 }; 825 826 preStart = '' 827 rm -Rf "${stateDir}/private/" 828 rm -Rf "${stateDir}/tmp/" 829 830 mkdir -m 0700 -p "${stateDir}/private" 831 mkdir -m 0700 -p "${stateDir}/tmp" 832 mkdir -m 0700 -p "${stateDir}/var" 833 834 cat > "${stateDir}/don't touch anything in here" << EOF 835 Everything in this directory except NSD's state in var is 836 automatically generated and will be purged and redeployed 837 by the nsd.service pre-start script. 838 EOF 839 840 chown ${username}:${username} -R "${stateDir}/private" 841 chown ${username}:${username} -R "${stateDir}/tmp" 842 chown ${username}:${username} -R "${stateDir}/var" 843 844 rm -rf "${stateDir}/zones" 845 cp -rL "${nsdEnv}/zones" "${stateDir}/zones" 846 847 ${copyKeys} 848 ''; 849 }; 850 851 }; 852 853 meta.maintainers = with lib.maintainers; [ hrdinka ]; 854}