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&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] <ip-address> <key-name | NOKEY></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}