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