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