1{
2 config,
3 lib,
4 options,
5 pkgs,
6 ...
7}:
8let
9 cfg = config.services.tor;
10 opt = options.services.tor;
11 stateDir = "/var/lib/tor";
12 runDir = "/run/tor";
13 descriptionGeneric = option: ''
14 See [torrc manual](https://2019.www.torproject.org/docs/tor-manual.html.en#${option}).
15 '';
16 bindsPrivilegedPort =
17 lib.any
18 (
19 p0:
20 let
21 p1 = if p0 ? "port" then p0.port else p0;
22 in
23 if p1 == "auto" then
24 false
25 else
26 let
27 p2 = if lib.isInt p1 then p1 else lib.toInt p1;
28 in
29 p1 != null && 0 < p2 && p2 < 1024
30 )
31 (
32 lib.flatten [
33 cfg.settings.ORPort
34 cfg.settings.DirPort
35 cfg.settings.DNSPort
36 cfg.settings.ExtORPort
37 cfg.settings.HTTPTunnelPort
38 cfg.settings.NATDPort
39 cfg.settings.SOCKSPort
40 cfg.settings.TransPort
41 ]
42 );
43 optionBool =
44 optionName:
45 lib.mkOption {
46 type = with lib.types; nullOr bool;
47 default = null;
48 description = (descriptionGeneric optionName);
49 };
50 optionInt =
51 optionName:
52 lib.mkOption {
53 type = with lib.types; nullOr int;
54 default = null;
55 description = (descriptionGeneric optionName);
56 };
57 optionString =
58 optionName:
59 lib.mkOption {
60 type = with lib.types; nullOr str;
61 default = null;
62 description = (descriptionGeneric optionName);
63 };
64 optionStrings =
65 optionName:
66 lib.mkOption {
67 type = with lib.types; listOf str;
68 default = [ ];
69 description = (descriptionGeneric optionName);
70 };
71 optionAddress = lib.mkOption {
72 type = with lib.types; nullOr str;
73 default = null;
74 example = "0.0.0.0";
75 description = ''
76 IPv4 or IPv6 (if between brackets) address.
77 '';
78 };
79 optionUnix = lib.mkOption {
80 type = with lib.types; nullOr path;
81 default = null;
82 description = ''
83 Unix domain socket path to use.
84 '';
85 };
86 optionPort = lib.mkOption {
87 type =
88 with lib.types;
89 nullOr (oneOf [
90 port
91 (enum [ "auto" ])
92 ]);
93 default = null;
94 };
95 optionPorts =
96 optionName:
97 lib.mkOption {
98 type = with lib.types; listOf port;
99 default = [ ];
100 description = (descriptionGeneric optionName);
101 };
102 optionIsolablePort =
103 with lib.types;
104 oneOf [
105 port
106 (enum [ "auto" ])
107 (submodule (
108 { config, ... }:
109 {
110 options =
111 {
112 addr = optionAddress;
113 port = optionPort;
114 flags = optionFlags;
115 SessionGroup = lib.mkOption {
116 type = nullOr int;
117 default = null;
118 };
119 }
120 // lib.genAttrs isolateFlags (
121 name:
122 lib.mkOption {
123 type = types.bool;
124 default = false;
125 }
126 );
127 config = {
128 flags =
129 lib.filter (name: config.${name} == true) isolateFlags
130 ++ lib.optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
131 };
132 }
133 ))
134 ];
135 optionIsolablePorts =
136 optionName:
137 lib.mkOption {
138 default = [ ];
139 type = with lib.types; either optionIsolablePort (listOf optionIsolablePort);
140 description = (descriptionGeneric optionName);
141 };
142 isolateFlags = [
143 "IsolateClientAddr"
144 "IsolateClientProtocol"
145 "IsolateDestAddr"
146 "IsolateDestPort"
147 "IsolateSOCKSAuth"
148 "KeepAliveIsolateSOCKSAuth"
149 ];
150 optionSOCKSPort =
151 doConfig:
152 let
153 flags = [
154 "CacheDNS"
155 "CacheIPv4DNS"
156 "CacheIPv6DNS"
157 "GroupWritable"
158 "IPv6Traffic"
159 "NoDNSRequest"
160 "NoIPv4Traffic"
161 "NoOnionTraffic"
162 "OnionTrafficOnly"
163 "PreferIPv6"
164 "PreferIPv6Automap"
165 "PreferSOCKSNoAuth"
166 "UseDNSCache"
167 "UseIPv4Cache"
168 "UseIPv6Cache"
169 "WorldWritable"
170 ] ++ isolateFlags;
171 in
172 with lib.types;
173 oneOf [
174 port
175 (submodule (
176 { config, ... }:
177 {
178 options =
179 {
180 unix = optionUnix;
181 addr = optionAddress;
182 port = optionPort;
183 flags = optionFlags;
184 SessionGroup = lib.mkOption {
185 type = nullOr int;
186 default = null;
187 };
188 }
189 // lib.genAttrs flags (
190 name:
191 lib.mkOption {
192 type = types.bool;
193 default = false;
194 }
195 );
196 config = lib.mkIf doConfig {
197 # Only add flags in SOCKSPort to avoid duplicates
198 flags =
199 lib.filter (name: config.${name} == true) flags
200 ++ lib.optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
201 };
202 }
203 ))
204 ];
205 optionFlags = lib.mkOption {
206 type = with lib.types; listOf str;
207 default = [ ];
208 };
209 optionORPort =
210 optionName:
211 lib.mkOption {
212 default = [ ];
213 example = 443;
214 type =
215 with lib.types;
216 oneOf [
217 port
218 (enum [ "auto" ])
219 (listOf (oneOf [
220 port
221 (enum [ "auto" ])
222 (submodule (
223 { config, ... }:
224 let
225 flags = [
226 "IPv4Only"
227 "IPv6Only"
228 "NoAdvertise"
229 "NoListen"
230 ];
231 in
232 {
233 options =
234 {
235 addr = optionAddress;
236 port = optionPort;
237 flags = optionFlags;
238 }
239 // lib.genAttrs flags (
240 name:
241 lib.mkOption {
242 type = types.bool;
243 default = false;
244 }
245 );
246 config = {
247 flags = lib.filter (name: config.${name} == true) flags;
248 };
249 }
250 ))
251 ]))
252 ];
253 description = (descriptionGeneric optionName);
254 };
255 optionBandwidth =
256 optionName:
257 lib.mkOption {
258 type = with lib.types; nullOr (either int str);
259 default = null;
260 description = (descriptionGeneric optionName);
261 };
262 optionPath =
263 optionName:
264 lib.mkOption {
265 type = with lib.types; nullOr path;
266 default = null;
267 description = (descriptionGeneric optionName);
268 };
269
270 mkValueString =
271 k: v:
272 if v == null then
273 ""
274 else if lib.isBool v then
275 (if v then "1" else "0")
276 else if v ? "unix" && v.unix != null then
277 "unix:" + v.unix + lib.optionalString (v ? "flags") (" " + lib.concatStringsSep " " v.flags)
278 else if v ? "port" && v.port != null then
279 lib.optionalString (v ? "addr" && v.addr != null) "${v.addr}:"
280 + toString v.port
281 + lib.optionalString (v ? "flags") (" " + lib.concatStringsSep " " v.flags)
282 else if k == "ServerTransportPlugin" then
283 lib.optionalString (v.transports != [ ]) "${lib.concatStringsSep "," v.transports} exec ${v.exec}"
284 else if k == "HidServAuth" then
285 v.onion + " " + v.auth
286 else
287 lib.generators.mkValueStringDefault { } v;
288 genTorrc =
289 settings:
290 lib.generators.toKeyValue
291 {
292 listsAsDuplicateKeys = true;
293 mkKeyValue = k: lib.generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
294 }
295 (
296 lib.mapAttrs (
297 k: v:
298 # Not necesssary, but prettier rendering
299 if
300 lib.elem k [
301 "AutomapHostsSuffixes"
302 "DirPolicy"
303 "ExitPolicy"
304 "SocksPolicy"
305 ]
306 && v != [ ]
307 then
308 lib.concatStringsSep "," v
309 else
310 v
311 ) (lib.filterAttrs (k: v: !(v == null || v == "")) settings)
312 );
313 torrc = pkgs.writeText "torrc" (
314 genTorrc cfg.settings
315 + lib.concatStrings (
316 lib.mapAttrsToList (
317 name: onion: "HiddenServiceDir ${onion.path}\n" + genTorrc onion.settings
318 ) cfg.relay.onionServices
319 )
320 );
321in
322{
323 imports = [
324 (lib.mkRenamedOptionModule
325 [ "services" "tor" "client" "dns" "automapHostsSuffixes" ]
326 [ "services" "tor" "settings" "AutomapHostsSuffixes" ]
327 )
328 (lib.mkRemovedOptionModule [
329 "services"
330 "tor"
331 "client"
332 "dns"
333 "isolationOptions"
334 ] "Use services.tor.settings.DNSPort instead.")
335 (lib.mkRemovedOptionModule [
336 "services"
337 "tor"
338 "client"
339 "dns"
340 "listenAddress"
341 ] "Use services.tor.settings.DNSPort instead.")
342 (lib.mkRemovedOptionModule [
343 "services"
344 "tor"
345 "client"
346 "privoxy"
347 "enable"
348 ] "Use services.privoxy.enable and services.privoxy.enableTor instead.")
349 (lib.mkRemovedOptionModule [
350 "services"
351 "tor"
352 "client"
353 "socksIsolationOptions"
354 ] "Use services.tor.settings.SOCKSPort instead.")
355 (lib.mkRemovedOptionModule [
356 "services"
357 "tor"
358 "client"
359 "socksListenAddressFaster"
360 ] "Use services.tor.settings.SOCKSPort instead.")
361 (lib.mkRenamedOptionModule
362 [ "services" "tor" "client" "socksPolicy" ]
363 [ "services" "tor" "settings" "SocksPolicy" ]
364 )
365 (lib.mkRemovedOptionModule [
366 "services"
367 "tor"
368 "client"
369 "transparentProxy"
370 "isolationOptions"
371 ] "Use services.tor.settings.TransPort instead.")
372 (lib.mkRemovedOptionModule [
373 "services"
374 "tor"
375 "client"
376 "transparentProxy"
377 "listenAddress"
378 ] "Use services.tor.settings.TransPort instead.")
379 (lib.mkRenamedOptionModule
380 [ "services" "tor" "controlPort" ]
381 [ "services" "tor" "settings" "ControlPort" ]
382 )
383 (lib.mkRemovedOptionModule [
384 "services"
385 "tor"
386 "extraConfig"
387 ] "Please use services.tor.settings instead.")
388 (lib.mkRenamedOptionModule
389 [ "services" "tor" "hiddenServices" ]
390 [ "services" "tor" "relay" "onionServices" ]
391 )
392 (lib.mkRenamedOptionModule
393 [ "services" "tor" "relay" "accountingMax" ]
394 [ "services" "tor" "settings" "AccountingMax" ]
395 )
396 (lib.mkRenamedOptionModule
397 [ "services" "tor" "relay" "accountingStart" ]
398 [ "services" "tor" "settings" "AccountingStart" ]
399 )
400 (lib.mkRenamedOptionModule
401 [ "services" "tor" "relay" "address" ]
402 [ "services" "tor" "settings" "Address" ]
403 )
404 (lib.mkRenamedOptionModule
405 [ "services" "tor" "relay" "bandwidthBurst" ]
406 [ "services" "tor" "settings" "BandwidthBurst" ]
407 )
408 (lib.mkRenamedOptionModule
409 [ "services" "tor" "relay" "bandwidthRate" ]
410 [ "services" "tor" "settings" "BandwidthRate" ]
411 )
412 (lib.mkRenamedOptionModule
413 [ "services" "tor" "relay" "bridgeTransports" ]
414 [ "services" "tor" "settings" "ServerTransportPlugin" "transports" ]
415 )
416 (lib.mkRenamedOptionModule
417 [ "services" "tor" "relay" "contactInfo" ]
418 [ "services" "tor" "settings" "ContactInfo" ]
419 )
420 (lib.mkRenamedOptionModule
421 [ "services" "tor" "relay" "exitPolicy" ]
422 [ "services" "tor" "settings" "ExitPolicy" ]
423 )
424 (lib.mkRemovedOptionModule [
425 "services"
426 "tor"
427 "relay"
428 "isBridge"
429 ] "Use services.tor.relay.role instead.")
430 (lib.mkRemovedOptionModule [
431 "services"
432 "tor"
433 "relay"
434 "isExit"
435 ] "Use services.tor.relay.role instead.")
436 (lib.mkRenamedOptionModule
437 [ "services" "tor" "relay" "nickname" ]
438 [ "services" "tor" "settings" "Nickname" ]
439 )
440 (lib.mkRenamedOptionModule
441 [ "services" "tor" "relay" "port" ]
442 [ "services" "tor" "settings" "ORPort" ]
443 )
444 (lib.mkRenamedOptionModule
445 [ "services" "tor" "relay" "portSpec" ]
446 [ "services" "tor" "settings" "ORPort" ]
447 )
448 ];
449
450 options = {
451 services.tor = {
452 enable = lib.mkEnableOption ''
453 Tor daemon.
454 By default, the daemon is run without
455 relay, exit, bridge or client connectivity'';
456
457 openFirewall = lib.mkEnableOption "opening of the relay port(s) in the firewall";
458
459 package = lib.mkPackageOption pkgs "tor" { };
460
461 enableGeoIP =
462 lib.mkEnableOption ''
463 use of GeoIP databases.
464 Disabling this will disable by-country statistics for bridges and relays
465 and some client and third-party software functionality''
466 // {
467 default = true;
468 };
469
470 controlSocket.enable = lib.mkEnableOption ''
471 control socket,
472 created in `${runDir}/control`'';
473
474 client = {
475 enable = lib.mkEnableOption ''
476 the routing of application connections.
477 You might want to disable this if you plan running a dedicated Tor relay'';
478
479 transparentProxy.enable = lib.mkEnableOption "transparent proxy";
480 dns.enable = lib.mkEnableOption "DNS resolver";
481
482 socksListenAddress = lib.mkOption {
483 type = optionSOCKSPort false;
484 default = {
485 addr = "127.0.0.1";
486 port = 9050;
487 IsolateDestAddr = true;
488 };
489 example = {
490 addr = "192.168.0.1";
491 port = 9090;
492 IsolateDestAddr = true;
493 };
494 description = ''
495 Bind to this address to listen for connections from
496 Socks-speaking applications.
497 '';
498 };
499
500 onionServices = lib.mkOption {
501 description = (descriptionGeneric "HiddenServiceDir");
502 default = { };
503 example = {
504 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" = {
505 clientAuthorizations = [ "/run/keys/tor/alice.prv.x25519" ];
506 };
507 };
508 type = lib.types.attrsOf (
509 lib.types.submodule (
510 { ... }:
511 {
512 options.clientAuthorizations = lib.mkOption {
513 description = ''
514 Clients' authorizations for a v3 onion service,
515 as a list of files containing each one private key, in the format:
516 ```
517 descriptor:x25519:<base32-private-key>
518 ```
519 ${descriptionGeneric "_client_authorization"}
520 '';
521 type = with lib.types; listOf path;
522 default = [ ];
523 example = [ "/run/keys/tor/alice.prv.x25519" ];
524 };
525 }
526 )
527 );
528 };
529 };
530
531 relay = {
532 enable = lib.mkEnableOption "tor relaying" // {
533 description = ''
534 Whether to enable relaying of Tor traffic for others.
535
536 See <https://www.torproject.org/docs/tor-doc-relay>
537 for details.
538
539 Setting this to true requires setting
540 {option}`services.tor.relay.role`
541 and
542 {option}`services.tor.settings.ORPort`
543 options.
544 '';
545 };
546
547 role = lib.mkOption {
548 type = lib.types.enum [
549 "exit"
550 "relay"
551 "bridge"
552 "private-bridge"
553 ];
554 description = ''
555 Your role in Tor network. There're several options:
556
557 - `exit`:
558 An exit relay. This allows Tor users to access regular
559 Internet services through your public IP.
560
561 You can specify which services Tor users may access via
562 your exit relay using {option}`settings.ExitPolicy` option.
563
564 - `relay`:
565 Regular relay. This allows Tor users to relay onion
566 traffic to other Tor nodes, but not to public
567 Internet.
568
569 See
570 <https://www.torproject.org/docs/tor-doc-relay.html.en>
571 for more info.
572
573 - `bridge`:
574 Regular bridge. Works like a regular relay, but
575 doesn't list you in the public relay directory and
576 hides your Tor node behind obfs4proxy.
577
578 Using this option will make Tor advertise your bridge
579 to users through various mechanisms like
580 <https://bridges.torproject.org/>, though.
581
582 See <https://www.torproject.org/docs/bridges.html.en>
583 for more info.
584
585 - `private-bridge`:
586 Private bridge. Works like regular bridge, but does
587 not advertise your node in any way.
588
589 Using this role means that you won't contribute to Tor
590 network in any way unless you advertise your node
591 yourself in some way.
592
593 Use this if you want to run a private bridge, for
594 example because you'll give out your bridge addr
595 manually to your friends.
596
597 Switching to this role after measurable time in
598 "bridge" role is pretty useless as some Tor users
599 would have learned about your node already. In the
600 latter case you can still change
601 {option}`port` option.
602
603 See <https://www.torproject.org/docs/bridges.html.en>
604 for more info.
605
606 ::: {.important}
607 Running an exit relay may expose you to abuse
608 complaints. See
609 <https://www.torproject.org/faq.html.en#ExitPolicies>
610 for more info.
611 :::
612
613 ::: {.important}
614 Note that some misconfigured and/or disrespectful
615 towards privacy sites will block you even if your
616 relay is not an exit relay. That is, just being listed
617 in a public relay directory can have unwanted
618 consequences.
619
620 Which means you might not want to use
621 this role if you browse public Internet from the same
622 network as your relay, unless you want to write
623 e-mails to those sites (you should!).
624 :::
625
626 ::: {.important}
627 WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE.
628 Consult with your lawyer when in doubt.
629
630 The `bridge` role should be safe to use in most situations
631 (unless the act of forwarding traffic for others is
632 a punishable offence under your local laws, which
633 would be pretty insane as it would make ISP illegal).
634 :::
635 '';
636 };
637
638 onionServices = lib.mkOption {
639 description = (descriptionGeneric "HiddenServiceDir");
640 default = { };
641 example = {
642 "example.org/www" = {
643 map = [ 80 ];
644 authorizedClients = [
645 "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
646 ];
647 };
648 };
649 type = lib.types.attrsOf (
650 lib.types.submodule (
651 { name, config, ... }:
652 {
653 options.path = lib.mkOption {
654 type = lib.types.path;
655 description = ''
656 Path where to store the data files of the hidden service.
657 If the {option}`secretKey` is null
658 this defaults to `${stateDir}/onion/$onion`,
659 otherwise to `${runDir}/onion/$onion`.
660 '';
661 };
662 options.secretKey = lib.mkOption {
663 type = with lib.types; nullOr path;
664 default = null;
665 example = "/run/keys/tor/onion/expyuzz4wqqyqhjn/hs_ed25519_secret_key";
666 description = ''
667 Secret key of the onion service.
668 If null, Tor reuses any preexisting secret key (in {option}`path`)
669 or generates a new one.
670 The associated public key and hostname are deterministically regenerated
671 from this file if they do not exist.
672 '';
673 };
674 options.authorizeClient = lib.mkOption {
675 description = (descriptionGeneric "HiddenServiceAuthorizeClient");
676 default = null;
677 type = lib.types.nullOr (
678 lib.types.submodule (
679 { ... }:
680 {
681 options = {
682 authType = lib.mkOption {
683 type = lib.types.enum [
684 "basic"
685 "stealth"
686 ];
687 description = ''
688 Either `"basic"` for a general-purpose authorization protocol
689 or `"stealth"` for a less scalable protocol
690 that also hides service activity from unauthorized clients.
691 '';
692 };
693 clientNames = lib.mkOption {
694 type = with lib.types; nonEmptyListOf (strMatching "[A-Za-z0-9+-_]+");
695 description = ''
696 Only clients that are listed here are authorized to access the hidden service.
697 Generated authorization data can be found in {file}`${stateDir}/onion/$name/hostname`.
698 Clients need to put this authorization data in their configuration file using
699 [](#opt-services.tor.settings.HidServAuth).
700 '';
701 };
702 };
703 }
704 )
705 );
706 };
707 options.authorizedClients = lib.mkOption {
708 description = ''
709 Authorized clients for a v3 onion service,
710 as a list of public key, in the format:
711 ```
712 descriptor:x25519:<base32-public-key>
713 ```
714 ${descriptionGeneric "_client_authorization"}
715 '';
716 type = with lib.types; listOf str;
717 default = [ ];
718 example = [ "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ];
719 };
720 options.map = lib.mkOption {
721 description = (descriptionGeneric "HiddenServicePort");
722 type =
723 with lib.types;
724 listOf (oneOf [
725 port
726 (submodule (
727 { ... }:
728 {
729 options = {
730 port = optionPort;
731 target = lib.mkOption {
732 default = null;
733 type = nullOr (
734 submodule (
735 { ... }:
736 {
737 options = {
738 unix = optionUnix;
739 addr = optionAddress;
740 port = optionPort;
741 };
742 }
743 )
744 );
745 };
746 };
747 }
748 ))
749 ]);
750 apply = map (
751 v:
752 if lib.isInt v then
753 {
754 port = v;
755 target = null;
756 }
757 else
758 v
759 );
760 };
761 options.version = lib.mkOption {
762 description = (descriptionGeneric "HiddenServiceVersion");
763 type =
764 with lib.types;
765 nullOr (enum [
766 2
767 3
768 ]);
769 default = null;
770 };
771 options.settings = lib.mkOption {
772 description = ''
773 Settings of the onion service.
774 ${descriptionGeneric "_hidden_service_options"}
775 '';
776 default = { };
777 type = lib.types.submodule {
778 freeformType =
779 with lib.types;
780 (attrsOf (
781 nullOr (oneOf [
782 str
783 int
784 bool
785 (listOf str)
786 ])
787 ))
788 // {
789 description = "settings option";
790 };
791 options.HiddenServiceAllowUnknownPorts = optionBool "HiddenServiceAllowUnknownPorts";
792 options.HiddenServiceDirGroupReadable = optionBool "HiddenServiceDirGroupReadable";
793 options.HiddenServiceExportCircuitID = lib.mkOption {
794 description = (descriptionGeneric "HiddenServiceExportCircuitID");
795 type = with lib.types; nullOr (enum [ "haproxy" ]);
796 default = null;
797 };
798 options.HiddenServiceMaxStreams = lib.mkOption {
799 description = (descriptionGeneric "HiddenServiceMaxStreams");
800 type = with lib.types; nullOr (ints.between 0 65535);
801 default = null;
802 };
803 options.HiddenServiceMaxStreamsCloseCircuit = optionBool "HiddenServiceMaxStreamsCloseCircuit";
804 options.HiddenServiceNumIntroductionPoints = lib.mkOption {
805 description = (descriptionGeneric "HiddenServiceNumIntroductionPoints");
806 type = with lib.types; nullOr (ints.between 0 20);
807 default = null;
808 };
809 options.HiddenServiceSingleHopMode = optionBool "HiddenServiceSingleHopMode";
810 options.RendPostPeriod = optionString "RendPostPeriod";
811 };
812 };
813 config = {
814 path = lib.mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}");
815 settings.HiddenServiceVersion = config.version;
816 settings.HiddenServiceAuthorizeClient =
817 if config.authorizeClient != null then
818 config.authorizeClient.authType + " " + lib.concatStringsSep "," config.authorizeClient.clientNames
819 else
820 null;
821 settings.HiddenServicePort = map (
822 p: mkValueString "" p.port + " " + mkValueString "" p.target
823 ) config.map;
824 };
825 }
826 )
827 );
828 };
829 };
830
831 settings = lib.mkOption {
832 description = ''
833 See [torrc manual](https://2019.www.torproject.org/docs/tor-manual.html.en)
834 for documentation.
835 '';
836 default = { };
837 type = lib.types.submodule {
838 freeformType =
839 with lib.types;
840 (attrsOf (
841 nullOr (oneOf [
842 str
843 int
844 bool
845 (listOf str)
846 ])
847 ))
848 // {
849 description = "settings option";
850 };
851 options.Address = optionString "Address";
852 options.AssumeReachable = optionBool "AssumeReachable";
853 options.AccountingMax = optionBandwidth "AccountingMax";
854 options.AccountingStart = optionString "AccountingStart";
855 options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity";
856 options.AuthDirListBadExits = optionBool "AuthDirListBadExits";
857 options.AuthDirPinKeys = optionBool "AuthDirPinKeys";
858 options.AuthDirSharedRandomness = optionBool "AuthDirSharedRandomness";
859 options.AuthDirTestEd25519LinkKeys = optionBool "AuthDirTestEd25519LinkKeys";
860 options.AuthoritativeDirectory = optionBool "AuthoritativeDirectory";
861 options.AutomapHostsOnResolve = optionBool "AutomapHostsOnResolve";
862 options.AutomapHostsSuffixes = optionStrings "AutomapHostsSuffixes" // {
863 default = [
864 ".onion"
865 ".exit"
866 ];
867 example = [ ".onion" ];
868 };
869 options.BandwidthBurst = optionBandwidth "BandwidthBurst";
870 options.BandwidthRate = optionBandwidth "BandwidthRate";
871 options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir";
872 options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry";
873 options.BridgeRelay = optionBool "BridgeRelay" // {
874 default = false;
875 };
876 options.CacheDirectory = optionPath "CacheDirectory";
877 options.CacheDirectoryGroupReadable = optionBool "CacheDirectoryGroupReadable"; # default is null and like "auto"
878 options.CellStatistics = optionBool "CellStatistics";
879 options.ClientAutoIPv6ORPort = optionBool "ClientAutoIPv6ORPort";
880 options.ClientDNSRejectInternalAddresses = optionBool "ClientDNSRejectInternalAddresses";
881 options.ClientOnionAuthDir = lib.mkOption {
882 description = (descriptionGeneric "ClientOnionAuthDir");
883 default = null;
884 type = with lib.types; nullOr path;
885 };
886 options.ClientPreferIPv6DirPort = optionBool "ClientPreferIPv6DirPort"; # default is null and like "auto"
887 options.ClientPreferIPv6ORPort = optionBool "ClientPreferIPv6ORPort"; # default is null and like "auto"
888 options.ClientRejectInternalAddresses = optionBool "ClientRejectInternalAddresses";
889 options.ClientUseIPv4 = optionBool "ClientUseIPv4";
890 options.ClientUseIPv6 = optionBool "ClientUseIPv6";
891 options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics";
892 options.ConstrainedSockets = optionBool "ConstrainedSockets";
893 options.ContactInfo = optionString "ContactInfo";
894 options.ControlPort = lib.mkOption {
895 description = (descriptionGeneric "ControlPort");
896 default = [ ];
897 example = [ { port = 9051; } ];
898 type =
899 with lib.types;
900 oneOf [
901 port
902 (enum [ "auto" ])
903 (listOf (oneOf [
904 port
905 (enum [ "auto" ])
906 (submodule (
907 { config, ... }:
908 let
909 flags = [
910 "GroupWritable"
911 "RelaxDirModeCheck"
912 "WorldWritable"
913 ];
914 in
915 {
916 options =
917 {
918 unix = optionUnix;
919 flags = optionFlags;
920 addr = optionAddress;
921 port = optionPort;
922 }
923 // lib.genAttrs flags (
924 name:
925 lib.mkOption {
926 type = types.bool;
927 default = false;
928 }
929 );
930 config = {
931 flags = lib.filter (name: config.${name} == true) flags;
932 };
933 }
934 ))
935 ]))
936 ];
937 };
938 options.ControlPortFileGroupReadable = optionBool "ControlPortFileGroupReadable";
939 options.ControlPortWriteToFile = optionPath "ControlPortWriteToFile";
940 options.ControlSocket = optionPath "ControlSocket";
941 options.ControlSocketsGroupWritable = optionBool "ControlSocketsGroupWritable";
942 options.CookieAuthFile = optionPath "CookieAuthFile";
943 options.CookieAuthFileGroupReadable = optionBool "CookieAuthFileGroupReadable";
944 options.CookieAuthentication = optionBool "CookieAuthentication";
945 options.DataDirectory = optionPath "DataDirectory" // {
946 default = stateDir;
947 };
948 options.DataDirectoryGroupReadable = optionBool "DataDirectoryGroupReadable";
949 options.DirPortFrontPage = optionPath "DirPortFrontPage";
950 options.DirAllowPrivateAddresses = optionBool "DirAllowPrivateAddresses";
951 options.DormantCanceledByStartup = optionBool "DormantCanceledByStartup";
952 options.DormantOnFirstStartup = optionBool "DormantOnFirstStartup";
953 options.DormantTimeoutDisabledByIdleStreams = optionBool "DormantTimeoutDisabledByIdleStreams";
954 options.DirCache = optionBool "DirCache";
955 options.DirPolicy = lib.mkOption {
956 description = (descriptionGeneric "DirPolicy");
957 type = with lib.types; listOf str;
958 default = [ ];
959 example = [ "accept *:*" ];
960 };
961 options.DirPort = optionORPort "DirPort";
962 options.DirReqStatistics = optionBool "DirReqStatistics";
963 options.DisableAllSwap = optionBool "DisableAllSwap";
964 options.DisableDebuggerAttachment = optionBool "DisableDebuggerAttachment";
965 options.DisableNetwork = optionBool "DisableNetwork";
966 options.DisableOOSCheck = optionBool "DisableOOSCheck";
967 options.DNSPort = optionIsolablePorts "DNSPort";
968 options.DoSCircuitCreationEnabled = optionBool "DoSCircuitCreationEnabled";
969 options.DoSConnectionEnabled = optionBool "DoSConnectionEnabled"; # default is null and like "auto"
970 options.DoSRefuseSingleHopClientRendezvous = optionBool "DoSRefuseSingleHopClientRendezvous";
971 options.DownloadExtraInfo = optionBool "DownloadExtraInfo";
972 options.EnforceDistinctSubnets = optionBool "EnforceDistinctSubnets";
973 options.EntryStatistics = optionBool "EntryStatistics";
974 options.ExitPolicy = optionStrings "ExitPolicy" // {
975 default = [ "reject *:*" ];
976 example = [ "accept *:*" ];
977 };
978 options.ExitPolicyRejectLocalInterfaces = optionBool "ExitPolicyRejectLocalInterfaces";
979 options.ExitPolicyRejectPrivate = optionBool "ExitPolicyRejectPrivate";
980 options.ExitPortStatistics = optionBool "ExitPortStatistics";
981 options.ExitRelay = optionBool "ExitRelay"; # default is null and like "auto"
982 options.ExtORPort = lib.mkOption {
983 description = (descriptionGeneric "ExtORPort");
984 default = null;
985 type =
986 with lib.types;
987 nullOr (oneOf [
988 port
989 (enum [ "auto" ])
990 (submodule (
991 { ... }:
992 {
993 options = {
994 addr = optionAddress;
995 port = optionPort;
996 };
997 }
998 ))
999 ]);
1000 apply = p: if lib.isInt p || lib.isString p then { port = p; } else p;
1001 };
1002 options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile";
1003 options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable";
1004 options.ExtendAllowPrivateAddresses = optionBool "ExtendAllowPrivateAddresses";
1005 options.ExtraInfoStatistics = optionBool "ExtraInfoStatistics";
1006 options.FascistFirewall = optionBool "FascistFirewall";
1007 options.FetchDirInfoEarly = optionBool "FetchDirInfoEarly";
1008 options.FetchDirInfoExtraEarly = optionBool "FetchDirInfoExtraEarly";
1009 options.FetchHidServDescriptors = optionBool "FetchHidServDescriptors";
1010 options.FetchServerDescriptors = optionBool "FetchServerDescriptors";
1011 options.FetchUselessDescriptors = optionBool "FetchUselessDescriptors";
1012 options.ReachableAddresses = optionStrings "ReachableAddresses";
1013 options.ReachableDirAddresses = optionStrings "ReachableDirAddresses";
1014 options.ReachableORAddresses = optionStrings "ReachableORAddresses";
1015 options.GeoIPFile = optionPath "GeoIPFile";
1016 options.GeoIPv6File = optionPath "GeoIPv6File";
1017 options.GuardfractionFile = optionPath "GuardfractionFile";
1018 options.HidServAuth = lib.mkOption {
1019 description = (descriptionGeneric "HidServAuth");
1020 default = [ ];
1021 type =
1022 with lib.types;
1023 listOf (oneOf [
1024 (submodule {
1025 options = {
1026 onion = lib.mkOption {
1027 type = strMatching "[a-z2-7]{16}\\.onion";
1028 description = "Onion address.";
1029 example = "xxxxxxxxxxxxxxxx.onion";
1030 };
1031 auth = lib.mkOption {
1032 type = strMatching "[A-Za-z0-9+/]{22}";
1033 description = "Authentication cookie.";
1034 };
1035 };
1036 })
1037 ]);
1038 example = [
1039 {
1040 onion = "xxxxxxxxxxxxxxxx.onion";
1041 auth = "xxxxxxxxxxxxxxxxxxxxxx";
1042 }
1043 ];
1044 };
1045 options.HiddenServiceNonAnonymousMode = optionBool "HiddenServiceNonAnonymousMode";
1046 options.HiddenServiceStatistics = optionBool "HiddenServiceStatistics";
1047 options.HSLayer2Nodes = optionStrings "HSLayer2Nodes";
1048 options.HSLayer3Nodes = optionStrings "HSLayer3Nodes";
1049 options.HTTPTunnelPort = optionIsolablePorts "HTTPTunnelPort";
1050 options.IPv6Exit = optionBool "IPv6Exit";
1051 options.KeyDirectory = optionPath "KeyDirectory";
1052 options.KeyDirectoryGroupReadable = optionBool "KeyDirectoryGroupReadable";
1053 options.LogMessageDomains = optionBool "LogMessageDomains";
1054 options.LongLivedPorts = optionPorts "LongLivedPorts";
1055 options.MainloopStats = optionBool "MainloopStats";
1056 options.MaxAdvertisedBandwidth = optionBandwidth "MaxAdvertisedBandwidth";
1057 options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness";
1058 options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending";
1059 options.NATDPort = optionIsolablePorts "NATDPort";
1060 options.NewCircuitPeriod = optionInt "NewCircuitPeriod";
1061 options.Nickname = optionString "Nickname";
1062 options.ORPort = optionORPort "ORPort";
1063 options.OfflineMasterKey = optionBool "OfflineMasterKey";
1064 options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto"
1065 options.PaddingStatistics = optionBool "PaddingStatistics";
1066 options.PerConnBWBurst = optionBandwidth "PerConnBWBurst";
1067 options.PerConnBWRate = optionBandwidth "PerConnBWRate";
1068 options.PidFile = optionPath "PidFile";
1069 options.ProtocolWarnings = optionBool "ProtocolWarnings";
1070 options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors";
1071 options.PublishServerDescriptor = lib.mkOption {
1072 description = (descriptionGeneric "PublishServerDescriptor");
1073 type =
1074 with lib.types;
1075 nullOr (enum [
1076 false
1077 true
1078 0
1079 1
1080 "0"
1081 "1"
1082 "v3"
1083 "bridge"
1084 ]);
1085 default = null;
1086 };
1087 options.ReducedExitPolicy = optionBool "ReducedExitPolicy";
1088 options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto"
1089 options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts";
1090 options.RelayBandwidthBurst = optionBandwidth "RelayBandwidthBurst";
1091 options.RelayBandwidthRate = optionBandwidth "RelayBandwidthRate";
1092 #options.RunAsDaemon
1093 options.Sandbox = optionBool "Sandbox";
1094 options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig";
1095 options.ServerDNSAllowNonRFC953Hostnames = optionBool "ServerDNSAllowNonRFC953Hostnames";
1096 options.ServerDNSDetectHijacking = optionBool "ServerDNSDetectHijacking";
1097 options.ServerDNSRandomizeCase = optionBool "ServerDNSRandomizeCase";
1098 options.ServerDNSResolvConfFile = optionPath "ServerDNSResolvConfFile";
1099 options.ServerDNSSearchDomains = optionBool "ServerDNSSearchDomains";
1100 options.ServerTransportPlugin = lib.mkOption {
1101 description = (descriptionGeneric "ServerTransportPlugin");
1102 default = null;
1103 type =
1104 with lib.types;
1105 nullOr (
1106 submodule (
1107 { ... }:
1108 {
1109 options = {
1110 transports = lib.mkOption {
1111 description = "List of pluggable transports.";
1112 type = listOf str;
1113 example = [
1114 "obfs2"
1115 "obfs3"
1116 "obfs4"
1117 "scramblesuit"
1118 ];
1119 };
1120 exec = lib.mkOption {
1121 type = types.str;
1122 description = "Command of pluggable transport.";
1123 };
1124 };
1125 }
1126 )
1127 );
1128 };
1129 options.ShutdownWaitLength = lib.mkOption {
1130 type = lib.types.int;
1131 default = 30;
1132 description = (descriptionGeneric "ShutdownWaitLength");
1133 };
1134 options.SocksPolicy = optionStrings "SocksPolicy" // {
1135 example = [ "accept *:*" ];
1136 };
1137 options.SOCKSPort = lib.mkOption {
1138 description = (descriptionGeneric "SOCKSPort");
1139 default = lib.optionals cfg.settings.HiddenServiceNonAnonymousMode [ { port = 0; } ];
1140 defaultText = lib.literalExpression ''
1141 if config.${opt.settings}.HiddenServiceNonAnonymousMode == true
1142 then [ { port = 0; } ]
1143 else [ ]
1144 '';
1145 example = [ { port = 9090; } ];
1146 type = lib.types.listOf (optionSOCKSPort true);
1147 };
1148 options.TestingTorNetwork = optionBool "TestingTorNetwork";
1149 options.TransPort = optionIsolablePorts "TransPort";
1150 options.TransProxyType = lib.mkOption {
1151 description = (descriptionGeneric "TransProxyType");
1152 type =
1153 with lib.types;
1154 nullOr (enum [
1155 "default"
1156 "TPROXY"
1157 "ipfw"
1158 "pf-divert"
1159 ]);
1160 default = null;
1161 };
1162 #options.TruncateLogFile
1163 options.UnixSocksGroupWritable = optionBool "UnixSocksGroupWritable";
1164 options.UseDefaultFallbackDirs = optionBool "UseDefaultFallbackDirs";
1165 options.UseMicrodescriptors = optionBool "UseMicrodescriptors";
1166 options.V3AuthUseLegacyKey = optionBool "V3AuthUseLegacyKey";
1167 options.V3AuthoritativeDirectory = optionBool "V3AuthoritativeDirectory";
1168 options.VersioningAuthoritativeDirectory = optionBool "VersioningAuthoritativeDirectory";
1169 options.VirtualAddrNetworkIPv4 = optionString "VirtualAddrNetworkIPv4";
1170 options.VirtualAddrNetworkIPv6 = optionString "VirtualAddrNetworkIPv6";
1171 options.WarnPlaintextPorts = optionPorts "WarnPlaintextPorts";
1172 };
1173 };
1174 };
1175 };
1176
1177 config = lib.mkIf cfg.enable {
1178 # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
1179 # sends a lot of stats
1180 warnings =
1181 lib.optional
1182 (
1183 cfg.settings.BridgeRelay
1184 && lib.flatten (lib.mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != [ ]
1185 )
1186 ''
1187 Running Tor hidden services on a public relay makes the
1188 presence of hidden services visible through simple statistical
1189 analysis of publicly available data.
1190 See https://trac.torproject.org/projects/tor/ticket/8742
1191
1192 You can safely ignore this warning if you don't intend to
1193 actually hide your hidden services. In either case, you can
1194 always create a container/VM with a separate Tor daemon instance.
1195 ''
1196 ++ lib.flatten (
1197 lib.mapAttrsToList (
1198 n: o:
1199 lib.optionals (o.settings.HiddenServiceVersion == 2) [
1200 (lib.optional (o.settings.HiddenServiceExportCircuitID != null) ''
1201 HiddenServiceExportCircuitID is used in the HiddenService: ${n}
1202 but this option is only for v3 hidden services.
1203 '')
1204 ]
1205 ++ lib.optionals (o.settings.HiddenServiceVersion != 2) [
1206 (lib.optional (o.settings.HiddenServiceAuthorizeClient != null) ''
1207 HiddenServiceAuthorizeClient is used in the HiddenService: ${n}
1208 but this option is only for v2 hidden services.
1209 '')
1210 (lib.optional (o.settings.RendPostPeriod != null) ''
1211 RendPostPeriod is used in the HiddenService: ${n}
1212 but this option is only for v2 hidden services.
1213 '')
1214 ]
1215 ) cfg.relay.onionServices
1216 );
1217
1218 users.groups.tor.gid = config.ids.gids.tor;
1219 users.users.tor = {
1220 description = "Tor Daemon User";
1221 createHome = true;
1222 home = stateDir;
1223 group = "tor";
1224 uid = config.ids.uids.tor;
1225 };
1226
1227 services.tor.settings = lib.mkMerge [
1228 (lib.mkIf cfg.enableGeoIP {
1229 GeoIPFile = "${cfg.package.geoip}/share/tor/geoip";
1230 GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6";
1231 })
1232 (lib.mkIf cfg.controlSocket.enable {
1233 ControlPort = [
1234 {
1235 unix = runDir + "/control";
1236 GroupWritable = true;
1237 RelaxDirModeCheck = true;
1238 }
1239 ];
1240 })
1241 (lib.mkIf cfg.relay.enable (
1242 lib.optionalAttrs (cfg.relay.role != "exit") {
1243 ExitPolicy = lib.mkForce [ "reject *:*" ];
1244 }
1245 //
1246 lib.optionalAttrs
1247 (lib.elem cfg.relay.role [
1248 "bridge"
1249 "private-bridge"
1250 ])
1251 {
1252 BridgeRelay = true;
1253 ExtORPort.port = lib.mkDefault "auto";
1254 ServerTransportPlugin.transports = lib.mkDefault [ "obfs4" ];
1255 ServerTransportPlugin.exec = lib.mkDefault "${lib.getExe pkgs.obfs4} managed";
1256 }
1257 // lib.optionalAttrs (cfg.relay.role == "private-bridge") {
1258 ExtraInfoStatistics = false;
1259 PublishServerDescriptor = false;
1260 }
1261 ))
1262 (lib.mkIf (!cfg.relay.enable) {
1263 # Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings,
1264 # because it would still enable Tor as a relay,
1265 # which can trigger all sort of problems when not carefully done,
1266 # like the blocklisting of the machine's IP addresses
1267 # by some hosting providers...
1268 DirPort = lib.mkForce [ ];
1269 ORPort = lib.mkForce [ ];
1270 PublishServerDescriptor = lib.mkForce false;
1271 })
1272 (lib.mkIf (!cfg.client.enable) {
1273 # Make sure application connections via SOCKS are disabled
1274 # when services.tor.client.enable is false
1275 SOCKSPort = lib.mkForce [ 0 ];
1276 })
1277 (lib.mkIf cfg.client.enable (
1278 {
1279 SOCKSPort = [ cfg.client.socksListenAddress ];
1280 }
1281 // lib.optionalAttrs cfg.client.transparentProxy.enable {
1282 TransPort = [
1283 {
1284 addr = "127.0.0.1";
1285 port = 9040;
1286 }
1287 ];
1288 }
1289 // lib.optionalAttrs cfg.client.dns.enable {
1290 DNSPort = [
1291 {
1292 addr = "127.0.0.1";
1293 port = 9053;
1294 }
1295 ];
1296 AutomapHostsOnResolve = true;
1297 }
1298 //
1299 lib.optionalAttrs
1300 (lib.flatten (lib.mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != [ ])
1301 {
1302 ClientOnionAuthDir = runDir + "/ClientOnionAuthDir";
1303 }
1304 ))
1305 ];
1306
1307 networking.firewall = lib.mkIf cfg.openFirewall {
1308 allowedTCPPorts =
1309 lib.concatMap
1310 (
1311 o:
1312 if lib.isInt o && o > 0 then
1313 [ o ]
1314 else
1315 lib.optionals (o ? "port" && lib.isInt o.port && o.port > 0) [ o.port ]
1316 )
1317 (
1318 lib.flatten [
1319 cfg.settings.ORPort
1320 cfg.settings.DirPort
1321 ]
1322 );
1323 };
1324
1325 systemd.services.tor = {
1326 description = "Tor Daemon";
1327 documentation = [ "man:tor(8)" ];
1328 path = [ pkgs.tor ];
1329
1330 wantedBy = [ "multi-user.target" ];
1331 after = [ "network.target" ];
1332 restartTriggers = [ torrc ];
1333
1334 serviceConfig = {
1335 Type = "simple";
1336 User = "tor";
1337 Group = "tor";
1338 ExecStartPre = [
1339 "${cfg.package}/bin/tor -f ${torrc} --verify-config"
1340 # DOC: Appendix G of https://spec.torproject.org/rend-spec-v3
1341 (
1342 "+"
1343 + pkgs.writeShellScript "ExecStartPre" (
1344 lib.concatStringsSep "\n" (
1345 lib.flatten (
1346 [ "set -eu" ]
1347 ++ lib.mapAttrsToList (
1348 name: onion:
1349 lib.optional (onion.authorizedClients != [ ]) ''
1350 rm -rf ${lib.escapeShellArg onion.path}/authorized_clients
1351 install -d -o tor -g tor -m 0700 ${lib.escapeShellArg onion.path} ${lib.escapeShellArg onion.path}/authorized_clients
1352 ''
1353 ++ lib.imap0 (i: pubKey: ''
1354 echo ${pubKey} |
1355 install -o tor -g tor -m 0400 /dev/stdin ${lib.escapeShellArg onion.path}/authorized_clients/${toString i}.auth
1356 '') onion.authorizedClients
1357 ++ lib.optional (onion.secretKey != null) ''
1358 install -d -o tor -g tor -m 0700 ${lib.escapeShellArg onion.path}
1359 key="$(cut -f1 -d: ${lib.escapeShellArg onion.secretKey} | head -1)"
1360 case "$key" in
1361 ("== ed25519v"*"-secret")
1362 install -o tor -g tor -m 0400 ${lib.escapeShellArg onion.secretKey} ${lib.escapeShellArg onion.path}/hs_ed25519_secret_key;;
1363 (*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;;
1364 esac
1365 ''
1366 ) cfg.relay.onionServices
1367 ++ lib.mapAttrsToList (
1368 name: onion:
1369 lib.imap0 (
1370 i: prvKeyPath:
1371 let
1372 hostname = lib.removeSuffix ".onion" name;
1373 in
1374 ''
1375 printf "%s:" ${lib.escapeShellArg hostname} | cat - ${lib.escapeShellArg prvKeyPath} |
1376 install -o tor -g tor -m 0700 /dev/stdin \
1377 ${runDir}/ClientOnionAuthDir/${lib.escapeShellArg hostname}.${toString i}.auth_private
1378 ''
1379 ) onion.clientAuthorizations
1380 ) cfg.client.onionServices
1381 )
1382 )
1383 )
1384 )
1385 ];
1386 ExecStart = "${cfg.package}/bin/tor -f ${torrc}";
1387 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
1388 KillSignal = "SIGINT";
1389 TimeoutSec = cfg.settings.ShutdownWaitLength + 30; # Wait a bit longer than ShutdownWaitLength before actually timing out
1390 Restart = "on-failure";
1391 LimitNOFILE = 32768;
1392 RuntimeDirectory = [
1393 # g+x allows access to the control socket
1394 "tor"
1395 "tor/root"
1396 # g+x can't be removed in ExecStart=, but will be removed by Tor
1397 "tor/ClientOnionAuthDir"
1398 ];
1399 RuntimeDirectoryMode = "0710";
1400 StateDirectoryMode = "0700";
1401 StateDirectory =
1402 [
1403 "tor"
1404 "tor/onion"
1405 ]
1406 ++ lib.flatten (
1407 lib.mapAttrsToList (
1408 name: onion: lib.optional (onion.secretKey == null) "tor/onion/${name}"
1409 ) cfg.relay.onionServices
1410 );
1411 # The following options are only to optimize:
1412 # systemd-analyze security tor
1413 RootDirectory = runDir + "/root";
1414 RootDirectoryStartOnly = true;
1415 #InaccessiblePaths = [ "-+${runDir}/root" ];
1416 UMask = "0066";
1417 BindPaths = [ stateDir ];
1418 BindReadOnlyPaths =
1419 [
1420 builtins.storeDir
1421 "/etc"
1422 ]
1423 ++ lib.optionals config.services.resolved.enable [
1424 "/run/systemd/resolve/stub-resolv.conf"
1425 "/run/systemd/resolve/resolv.conf"
1426 ];
1427 AmbientCapabilities = [ "" ] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
1428 CapabilityBoundingSet = [ "" ] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
1429 # ProtectClock= adds DeviceAllow=char-rtc r
1430 DeviceAllow = "";
1431 LockPersonality = true;
1432 MemoryDenyWriteExecute = true;
1433 NoNewPrivileges = true;
1434 PrivateDevices = true;
1435 PrivateMounts = true;
1436 PrivateNetwork = lib.mkDefault false;
1437 PrivateTmp = true;
1438 # Tor cannot currently bind privileged port when PrivateUsers=true,
1439 # see https://gitlab.torproject.org/legacy/trac/-/issues/20930
1440 PrivateUsers = !bindsPrivilegedPort;
1441 ProcSubset = "pid";
1442 ProtectClock = true;
1443 ProtectControlGroups = true;
1444 ProtectHome = true;
1445 ProtectHostname = true;
1446 ProtectKernelLogs = true;
1447 ProtectKernelModules = true;
1448 ProtectKernelTunables = true;
1449 ProtectProc = "invisible";
1450 ProtectSystem = "strict";
1451 RemoveIPC = true;
1452 RestrictAddressFamilies = [
1453 "AF_UNIX"
1454 "AF_INET"
1455 "AF_INET6"
1456 "AF_NETLINK"
1457 ];
1458 RestrictNamespaces = true;
1459 RestrictRealtime = true;
1460 RestrictSUIDSGID = true;
1461 # See also the finer but experimental option settings.Sandbox
1462 SystemCallFilter = [
1463 "@system-service"
1464 # Groups in @system-service which do not contain a syscall listed by:
1465 # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' tor
1466 # in tests, and seem likely not necessary for tor.
1467 "~@aio"
1468 "~@chown"
1469 "~@keyring"
1470 "~@memlock"
1471 "~@resources"
1472 "~@setuid"
1473 "~@timer"
1474 ];
1475 SystemCallArchitectures = "native";
1476 SystemCallErrorNumber = "EPERM";
1477 };
1478 };
1479
1480 environment.systemPackages = [ cfg.package ];
1481 };
1482
1483 meta.maintainers = with lib.maintainers; [ julm ];
1484}