]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/services/security/tor.nix
nix: update patches and wip stuffs
[sourcephile-nix.git] / nixos / modules / services / security / tor.nix
1 { config, lib, pkgs, ... }:
2
3 with builtins;
4 with lib;
5
6 let
7 cfg = config.services.tor;
8 stateDir = "/var/lib/tor";
9 runDir = "/run/tor";
10 descriptionGeneric = option: ''
11 See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en#${option}">torrc manual</link>.
12 '';
13 bindsPrivilegedPort =
14 any (p0:
15 let p1 = if p0 ? "port" then p0.port else p0; in
16 if p1 == "auto" then false
17 else let p2 = if isInt p1 then p1 else toInt p1; in
18 p1 != null && 0 < p2 && p2 < 1024)
19 (flatten [
20 cfg.settings.ORPort
21 cfg.settings.DirPort
22 cfg.settings.DNSPort
23 cfg.settings.ExtORPort
24 cfg.settings.HTTPTunnelPort
25 cfg.settings.NATDPort
26 cfg.settings.SOCKSPort
27 cfg.settings.TransPort
28 ]);
29 optionBool = optionName: mkOption {
30 type = with types; nullOr bool;
31 default = null;
32 description = descriptionGeneric optionName;
33 };
34 optionInt = optionName: mkOption {
35 type = with types; nullOr int;
36 default = null;
37 description = descriptionGeneric optionName;
38 };
39 optionString = optionName: mkOption {
40 type = with types; nullOr str;
41 default = null;
42 description = descriptionGeneric optionName;
43 };
44 optionStrings = optionName: mkOption {
45 type = with types; listOf str;
46 default = [];
47 description = descriptionGeneric optionName;
48 };
49 optionAddress = mkOption {
50 type = with types; nullOr str;
51 default = null;
52 example = "0.0.0.0";
53 description = ''
54 IPv4 or IPv6 (if between brackets) address.
55 '';
56 };
57 optionUnix = mkOption {
58 type = with types; nullOr path;
59 default = null;
60 description = ''
61 Unix domain socket path to use.
62 '';
63 };
64 optionPort = mkOption {
65 type = with types; nullOr (oneOf [port (enum ["auto"])]);
66 default = null;
67 };
68 optionPorts = optionName: mkOption {
69 type = with types; listOf port;
70 default = [];
71 description = descriptionGeneric optionName;
72 };
73 optionIsolablePort = with types; oneOf [
74 port (enum ["auto"])
75 (submodule ({config, ...}: {
76 options = {
77 addr = optionAddress;
78 port = optionPort;
79 flags = optionFlags;
80 SessionGroup = mkOption { type = nullOr int; default = null; };
81 } // genAttrs isolateFlags (name: mkOption { type = types.bool; default = false; });
82 config = {
83 flags = filter (name: config.${name} == true) isolateFlags ++
84 optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
85 };
86 }))
87 ];
88 optionIsolablePorts = optionName: mkOption {
89 default = [];
90 type = with types; either optionIsolablePort (listOf optionIsolablePort);
91 description = descriptionGeneric optionName;
92 };
93 isolateFlags = [
94 "IsolateClientAddr"
95 "IsolateClientProtocol"
96 "IsolateDestAddr"
97 "IsolateDestPort"
98 "IsolateSOCKSAuth"
99 "KeepAliveIsolateSOCKSAuth"
100 ];
101 optionSOCKSPort = doConfig: let
102 flags = [
103 "CacheDNS" "CacheIPv4DNS" "CacheIPv6DNS" "GroupWritable" "IPv6Traffic"
104 "NoDNSRequest" "NoIPv4Traffic" "NoOnionTraffic" "OnionTrafficOnly"
105 "PreferIPv6" "PreferIPv6Automap" "PreferSOCKSNoAuth" "UseDNSCache"
106 "UseIPv4Cache" "UseIPv6Cache" "WorldWritable"
107 ] ++ isolateFlags;
108 in with types; oneOf [
109 port (submodule ({config, ...}: {
110 options = {
111 unix = optionUnix;
112 addr = optionAddress;
113 port = optionPort;
114 flags = optionFlags;
115 SessionGroup = mkOption { type = nullOr int; default = null; };
116 } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
117 config = mkIf doConfig { # Only add flags in SOCKSPort to avoid duplicates
118 flags = filter (name: config.${name} == true) flags ++
119 optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
120 };
121 }))
122 ];
123 optionFlags = mkOption {
124 type = with types; listOf str;
125 default = [];
126 };
127 optionORPort = optionName: mkOption {
128 default = [];
129 example = 443;
130 type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [
131 port
132 (enum ["auto"])
133 (submodule ({config, ...}:
134 let flags = [ "IPv4Only" "IPv6Only" "NoAdvertise" "NoListen" ];
135 in {
136 options = {
137 addr = optionAddress;
138 port = optionPort;
139 flags = optionFlags;
140 } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
141 config = {
142 flags = filter (name: config.${name} == true) flags;
143 };
144 }))
145 ]))];
146 description = descriptionGeneric optionName;
147 };
148 optionBandwith = optionName: mkOption {
149 type = with types; nullOr (either int str);
150 default = null;
151 description = descriptionGeneric optionName;
152 };
153 optionPath = optionName: mkOption {
154 type = with types; nullOr path;
155 default = null;
156 description = descriptionGeneric optionName;
157 };
158
159 mkValueString = k: v:
160 if v == null then ""
161 else if isBool v then
162 (if v then "1" else "0")
163 else if v ? "unix" && v.unix != null then
164 "unix:"+v.unix +
165 optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
166 else if v ? "port" && v.port != null then
167 optionalString (v ? "addr" && v.addr != null) "${v.addr}:" +
168 toString v.port +
169 optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
170 else if k == "ServerTransportPlugin" then
171 optionalString (v.transports != []) "${concatStringsSep "," v.transports} exec ${v.exec}"
172 else if k == "HidServAuth" then
173 concatMapStringsSep "\n${k} " (settings: settings.onion + " " settings.auth) v
174 else generators.mkValueStringDefault {} v;
175 genTorrc = settings:
176 generators.toKeyValue {
177 listsAsDuplicateKeys = true;
178 mkKeyValue = k: generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
179 }
180 (lib.mapAttrs (k: v:
181 # Not necesssary, but prettier rendering
182 if elem k [ "AutomapHostsSuffixes" "DirPolicy" "ExitPolicy" "SocksPolicy" ]
183 && v != []
184 then concatStringsSep "," v
185 else v)
186 (lib.filterAttrs (k: v: !(v == null || v == ""))
187 settings));
188 torrc = pkgs.writeText "torrc" (
189 genTorrc cfg.settings +
190 concatStrings (mapAttrsToList (name: onion:
191 "HiddenServiceDir ${onion.path}\n" +
192 genTorrc onion.settings) cfg.relay.onionServices)
193 );
194 in
195 {
196 imports = [
197 (mkRenamedOptionModule [ "services" "tor" "client" "dns" "automapHostsSuffixes" ] [ "services" "tor" "settings" "AutomapHostsSuffixes" ])
198 (mkRemovedOptionModule [ "services" "tor" "client" "dns" "isolationOptions" ] "Use services.tor.settings.DNSPort instead.")
199 (mkRemovedOptionModule [ "services" "tor" "client" "dns" "listenAddress" ] "Use services.tor.settings.DNSPort instead.")
200 (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] "Use services.privoxy.enable and services.privoxy.enableTor instead.")
201 (mkRemovedOptionModule [ "services" "tor" "client" "socksIsolationOptions" ] "Use services.tor.settings.SOCKSPort")
202 (mkRenamedOptionModule [ "services" "tor" "client" "socksPolicy" ] [ "services" "tor" "settings" "SocksPolicy" ])
203 (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "isolationOptions" ] "Use services.tor.settings.TransPort instead.")
204 (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "listenAddress" ] "Use services.tor.settings.TransPort instead.")
205 (mkRenamedOptionModule [ "services" "tor" "controlPort" ] [ "services" "tor" "settings" "ControlPort" ])
206 (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Plese use services.tor.settings instead.")
207 (mkRenamedOptionModule [ "services" "tor" "hiddenServices" ] [ "services" "tor" "relay" "onionServices" ])
208 (mkRenamedOptionModule [ "services" "tor" "relay" "accountingMax" ] [ "services" "tor" "settings" "AccountingMax" ])
209 (mkRenamedOptionModule [ "services" "tor" "relay" "accountingStart" ] [ "services" "tor" "settings" "AccountingStart" ])
210 (mkRenamedOptionModule [ "services" "tor" "relay" "address" ] [ "services" "tor" "settings" "Address" ])
211 (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthBurst" ] [ "services" "tor" "settings" "BandwidthBurst" ])
212 (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthRate" ] [ "services" "tor" "settings" "BandwidthRate" ])
213 (mkRenamedOptionModule [ "services" "tor" "relay" "bridgeTransports" ] [ "services" "tor" "settings" "ServerTransportPlugin" "transports" ])
214 (mkRenamedOptionModule [ "services" "tor" "relay" "contactInfo" ] [ "services" "tor" "settings" "ContactInfo" ])
215 (mkRenamedOptionModule [ "services" "tor" "relay" "exitPolicy" ] [ "services" "tor" "settings" "ExitPolicy" ])
216 (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
217 (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
218 (mkRenamedOptionModule [ "services" "tor" "relay" "nickname" ] [ "services" "tor" "settings" "Nickname" ])
219 (mkRenamedOptionModule [ "services" "tor" "relay" "port" ] [ "services" "tor" "settings" "ORPort" ])
220 (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "settings" "ORPort" ])
221 ];
222
223 options = {
224 services.tor = {
225 enable = mkEnableOption ''Tor daemon.
226 By default, the daemon is run without
227 relay, exit, bridge or client connectivity'';
228
229 openFirewall = mkEnableOption "opening of the relay port(s) in the firewall";
230
231 package = mkOption {
232 type = types.package;
233 default = pkgs.tor;
234 defaultText = "pkgs.tor";
235 example = literalExample "pkgs.tor";
236 description = "Tor package to use.";
237 };
238
239 enableGeoIP = mkEnableOption ''use of GeoIP databases.
240 Disabling this will disable by-country statistics for bridges and relays
241 and some client and third-party software functionality'' // { default = true; };
242
243 controlSocket.enable = mkEnableOption ''control socket,
244 created in <literal>${runDir}/control</literal>'';
245
246 client = {
247 enable = mkEnableOption ''the routing of application connections.
248 You might want to disable this if you plan running a dedicated Tor relay'';
249
250 transparentProxy.enable = mkEnableOption "transparent proxy";
251 dns.enable = mkEnableOption "DNS resolver";
252
253 socksListenAddress = mkOption {
254 type = optionSOCKSPort false;
255 default = {addr = "127.0.0.1"; port = 9050; IsolateDestAddr = true;};
256 example = {addr = "192.168.0.1"; port = 9090; IsolateDestAddr = true;};
257 description = ''
258 Bind to this address to listen for connections from
259 Socks-speaking applications.
260 '';
261 };
262
263 onionServices = mkOption {
264 description = descriptionGeneric "HiddenServiceDir";
265 default = {};
266 example = {
267 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" = {
268 clientAuthorizations = ["/run/keys/tor/alice.prv.x25519"];
269 };
270 };
271 type = types.attrsOf (types.submodule ({name, config, ...}: {
272 options.clientAuthorizations = mkOption {
273 description = ''
274 Clients' authorizations for a v3 hidden service,
275 as a list of files containing each one private key, in the format:
276 <screen>descriptor:x25519:&lt;base32-private-key&gt;</screen>
277 '' + descriptionGeneric "_client_authorization";
278 type = with types; listOf path;
279 default = [];
280 example = ["/run/keys/tor/alice.prv.x25519"];
281 };
282 }));
283 };
284 };
285
286 relay = {
287 enable = mkEnableOption ''relaying of Tor traffic for others.
288
289 See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
290 for details.
291
292 Setting this to true requires setting
293 <option>services.tor.relay.role</option>
294 and
295 <option>services.tor.settings.ORPort</option>
296 options'';
297
298 role = mkOption {
299 type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
300 description = ''
301 Your role in Tor network. There're several options:
302
303 <variablelist>
304 <varlistentry>
305 <term><literal>exit</literal></term>
306 <listitem>
307 <para>
308 An exit relay. This allows Tor users to access regular
309 Internet services through your public IP.
310 </para>
311
312 <important><para>
313 Running an exit relay may expose you to abuse
314 complaints. See
315 <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies"/>
316 for more info.
317 </para></important>
318
319 <para>
320 You can specify which services Tor users may access via
321 your exit relay using <option>settings.ExitPolicy</option> option.
322 </para>
323 </listitem>
324 </varlistentry>
325
326 <varlistentry>
327 <term><literal>relay</literal></term>
328 <listitem>
329 <para>
330 Regular relay. This allows Tor users to relay onion
331 traffic to other Tor nodes, but not to public
332 Internet.
333 </para>
334
335 <important><para>
336 Note that some misconfigured and/or disrespectful
337 towards privacy sites will block you even if your
338 relay is not an exit relay. That is, just being listed
339 in a public relay directory can have unwanted
340 consequences.
341
342 Which means you might not want to use
343 this role if you browse public Internet from the same
344 network as your relay, unless you want to write
345 e-mails to those sites (you should!).
346 </para></important>
347
348 <para>
349 See
350 <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" />
351 for more info.
352 </para>
353 </listitem>
354 </varlistentry>
355
356 <varlistentry>
357 <term><literal>bridge</literal></term>
358 <listitem>
359 <para>
360 Regular bridge. Works like a regular relay, but
361 doesn't list you in the public relay directory and
362 hides your Tor node behind obfs4proxy.
363 </para>
364
365 <para>
366 Using this option will make Tor advertise your bridge
367 to users through various mechanisms like
368 <link xlink:href="https://bridges.torproject.org/" />, though.
369 </para>
370
371 <important>
372 <para>
373 WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE.
374 Consult with your lawyer when in doubt.
375 </para>
376
377 <para>
378 This role should be safe to use in most situations
379 (unless the act of forwarding traffic for others is
380 a punishable offence under your local laws, which
381 would be pretty insane as it would make ISP illegal).
382 </para>
383 </important>
384
385 <para>
386 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
387 for more info.
388 </para>
389 </listitem>
390 </varlistentry>
391
392 <varlistentry>
393 <term><literal>private-bridge</literal></term>
394 <listitem>
395 <para>
396 Private bridge. Works like regular bridge, but does
397 not advertise your node in any way.
398 </para>
399
400 <para>
401 Using this role means that you won't contribute to Tor
402 network in any way unless you advertise your node
403 yourself in some way.
404 </para>
405
406 <para>
407 Use this if you want to run a private bridge, for
408 example because you'll give out your bridge addr
409 manually to your friends.
410 </para>
411
412 <para>
413 Switching to this role after measurable time in
414 "bridge" role is pretty useless as some Tor users
415 would have learned about your node already. In the
416 latter case you can still change
417 <option>port</option> option.
418 </para>
419
420 <para>
421 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
422 for more info.
423 </para>
424 </listitem>
425 </varlistentry>
426 </variablelist>
427 '';
428 };
429
430 onionServices = mkOption {
431 description = descriptionGeneric "HiddenServiceDir";
432 default = {};
433 example = {
434 "example.org/www" = {
435 map = [ 80 ];
436 authorizedClients = [
437 "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
438 ];
439 };
440 };
441 type = types.attrsOf (types.submodule ({name, config, ...}: {
442 options.path = mkOption {
443 type = types.path;
444 description = ''
445 Path where to store the data files of the hidden service.
446 If the <option>secretKey</option> is null
447 this defaults to <literal>${stateDir}/onion/$onion</literal>,
448 otherwise to <literal>${runDir}/onion/$onion</literal>.
449 '';
450 };
451 options.secretKey = mkOption {
452 type = with types; nullOr path;
453 default = null;
454 example = "/run/keys/tor/onion/expyuzz4wqqyqhjn/hs_ed25519_secret_key";
455 description = ''
456 Secret key of the onion service.
457 If null, Tor reuses any preexisting secret key (in <option>path</option>)
458 or generates a new one.
459 The associated public key and hostname are deterministically regenerated
460 from this file if they do not exist.
461 '';
462 };
463 options.authorizeClient = mkOption {
464 description = descriptionGeneric "HiddenServiceAuthorizeClient";
465 default = null;
466 type = types.nullOr (types.submodule ({...}: {
467 options = {
468 authType = mkOption {
469 type = types.enum [ "basic" "stealth" ];
470 description = ''
471 Either <literal>"basic"</literal> for a general-purpose authorization protocol
472 or <literal>"stealth"</literal> for a less scalable protocol
473 that also hides service activity from unauthorized clients.
474 '';
475 };
476 clientNames = mkOption {
477 type = with types; nonEmptyListOf (strMatching "[A-Za-z0-9+-_]+");
478 description = ''
479 Only clients that are listed here are authorized to access the hidden service.
480 Generated authorization data can be found in <filename>${stateDir}/onion/$name/hostname</filename>.
481 Clients need to put this authorization data in their configuration file using
482 <xref linkend="opt-services.tor.settings.HidServAuth"/>.
483 '';
484 };
485 };
486 }));
487 };
488 options.authorizedClients = mkOption {
489 description = ''
490 Authorized clients for a v3 hidden service,
491 as a list of public key, in the format:
492 <screen>descriptor:x25519:&lt;base32-public-key&gt;</screen>
493 '' + descriptionGeneric "_client_authorization";
494 type = with types; listOf str;
495 default = [];
496 example = ["descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"];
497 };
498 options.map = mkOption {
499 description = descriptionGeneric "HiddenServicePort";
500 type = with types; listOf (oneOf [
501 port (submodule ({...}: {
502 options = {
503 port = optionPort;
504 target = mkOption {
505 default = null;
506 type = nullOr (submodule ({...}: {
507 options = {
508 unix = optionUnix;
509 addr = optionAddress;
510 port = optionPort;
511 };
512 }));
513 };
514 };
515 }))
516 ]);
517 apply = map (v: if isInt v then {port=v; target=null;} else v);
518 };
519 options.version = mkOption {
520 description = descriptionGeneric "HiddenServiceVersion";
521 type = with types; nullOr (enum [2 3]);
522 default = null;
523 };
524 options.settings = mkOption {
525 description = ''
526 Settings of the onion service.
527 '' + descriptionGeneric "_hidden_service_options";
528 default = {};
529 type = types.submodule {
530 freeformType = with types;
531 (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // {
532 description = "settings option";
533 };
534 options.HiddenServiceAllowUnknownPorts = optionBool "HiddenServiceAllowUnknownPorts";
535 options.HiddenServiceDirGroupReadable = optionBool "HiddenServiceDirGroupReadable";
536 options.HiddenServiceExportCircuitID = mkOption {
537 description = descriptionGeneric "HiddenServiceExportCircuitID";
538 type = with types; nullOr (enum ["haproxy"]);
539 default = null;
540 };
541 options.HiddenServiceMaxStreams = mkOption {
542 description = descriptionGeneric "HiddenServiceMaxStreams";
543 type = with types; nullOr (ints.between 0 65535);
544 default = null;
545 };
546 options.HiddenServiceMaxStreamsCloseCircuit = optionBool "HiddenServiceMaxStreamsCloseCircuit";
547 options.HiddenServiceNumIntroductionPoints = mkOption {
548 description = descriptionGeneric "HiddenServiceNumIntroductionPoints";
549 type = with types; nullOr (ints.between 0 20);
550 default = null;
551 };
552 options.HiddenServiceSingleHopMode = optionBool "HiddenServiceSingleHopMode";
553 options.RendPostPeriod = optionString "RendPostPeriod";
554 };
555 };
556 config = {
557 path = mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}");
558 settings.HiddenServiceVersion = config.version;
559 settings.HiddenServiceAuthorizeClient =
560 if config.authorizeClient != null then
561 config.authorizeClient.authType + " " +
562 concatStringsSep "," config.authorizeClient.clientNames
563 else null;
564 settings.HiddenServicePort = map (p: mkValueString "" p.port + " " + mkValueString "" p.target) config.map;
565 };
566 }));
567 };
568 };
569
570 settings = mkOption {
571 description = ''
572 See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en">torrc manual</link>
573 for documentation.
574 '';
575 default = {};
576 type = types.submodule {
577 freeformType = with types;
578 (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // {
579 description = "settings option";
580 };
581 options.Address = optionString "Address";
582 options.AssumeReachable = optionBool "AssumeReachable";
583 options.AccountingMax = optionBandwith "AccountingMax";
584 options.AccountingStart = optionString "AccountingStart";
585 options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity";
586 options.AuthDirListBadExits = optionBool "AuthDirListBadExits";
587 options.AuthDirPinKeys = optionBool "AuthDirPinKeys";
588 options.AuthDirSharedRandomness = optionBool "AuthDirSharedRandomness";
589 options.AuthDirTestEd25519LinkKeys = optionBool "AuthDirTestEd25519LinkKeys";
590 options.AuthoritativeDirectory = optionBool "AuthoritativeDirectory";
591 options.AutomapHostsOnResolve = optionBool "AutomapHostsOnResolve";
592 options.AutomapHostsSuffixes = optionStrings "AutomapHostsSuffixes" // {
593 default = [".onion" ".exit"];
594 example = [".onion"];
595 };
596 options.BandwidthBurst = optionBandwith "BandwidthBurst";
597 options.BandwidthRate = optionBandwith "BandwidthRate";
598 options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir";
599 options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry";
600 options.BridgeRelay = optionBool "BridgeRelay" // { default = false; };
601 options.CacheDirectory = optionPath "CacheDirectory";
602 options.CacheDirectoryGroupReadable = optionBool "CacheDirectoryGroupReadable"; # default is null and like "auto"
603 options.CellStatistics = optionBool "CellStatistics";
604 options.ClientAutoIPv6ORPort = optionBool "ClientAutoIPv6ORPort";
605 options.ClientDNSRejectInternalAddresses = optionBool "ClientDNSRejectInternalAddresses";
606 options.ClientOnionAuthDir = mkOption {
607 description = descriptionGeneric "ClientOnionAuthDir";
608 default = null;
609 type = with types; nullOr path;
610 };
611 options.ClientPreferIPv6DirPort = optionBool "ClientPreferIPv6DirPort"; # default is null and like "auto"
612 options.ClientPreferIPv6ORPort = optionBool "ClientPreferIPv6ORPort"; # default is null and like "auto"
613 options.ClientRejectInternalAddresses = optionBool "ClientRejectInternalAddresses";
614 options.ClientUseIPv4 = optionBool "ClientUseIPv4";
615 options.ClientUseIPv6 = optionBool "ClientUseIPv6";
616 options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics";
617 options.ConstrainedSockets = optionBool "ConstrainedSockets";
618 options.ContactInfo = optionString "ContactInfo";
619 options.ControlPort = mkOption rec {
620 description = descriptionGeneric "ControlPort";
621 default = [];
622 example = [{port = 9051;}];
623 type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [
624 port (enum ["auto"]) (submodule ({config, ...}: let
625 flags = ["GroupWritable" "RelaxDirModeCheck" "WorldWritable"];
626 in {
627 options = {
628 unix = optionUnix;
629 flags = optionFlags;
630 addr = optionAddress;
631 port = optionPort;
632 } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
633 config = {
634 flags = filter (name: config.${name} == true) flags;
635 };
636 }))
637 ]))];
638 };
639 options.ControlPortFileGroupReadable= optionBool "ControlPortFileGroupReadable";
640 options.ControlPortWriteToFile = optionPath "ControlPortWriteToFile";
641 options.ControlSocket = optionPath "ControlSocket";
642 options.ControlSocketsGroupWritable = optionBool "ControlSocketsGroupWritable";
643 options.CookieAuthFile = optionPath "CookieAuthFile";
644 options.CookieAuthFileGroupReadable = optionBool "CookieAuthFileGroupReadable";
645 options.CookieAuthentication = optionBool "CookieAuthentication";
646 options.DataDirectory = optionPath "DataDirectory" // { default = stateDir; };
647 options.DataDirectoryGroupReadable = optionBool "DataDirectoryGroupReadable";
648 options.DirPortFrontPage = optionPath "DirPortFrontPage";
649 options.DirAllowPrivateAddresses = optionBool "DirAllowPrivateAddresses";
650 options.DormantCanceledByStartup = optionBool "DormantCanceledByStartup";
651 options.DormantOnFirstStartup = optionBool "DormantOnFirstStartup";
652 options.DormantTimeoutDisabledByIdleStreams = optionBool "DormantTimeoutDisabledByIdleStreams";
653 options.DirCache = optionBool "DirCache";
654 options.DirPolicy = mkOption {
655 description = descriptionGeneric "DirPolicy";
656 type = with types; listOf str;
657 default = [];
658 example = ["accept *:*"];
659 };
660 options.DirPort = optionORPort "DirPort";
661 options.DirReqStatistics = optionBool "DirReqStatistics";
662 options.DisableAllSwap = optionBool "DisableAllSwap";
663 options.DisableDebuggerAttachment = optionBool "DisableDebuggerAttachment";
664 options.DisableNetwork = optionBool "DisableNetwork";
665 options.DisableOOSCheck = optionBool "DisableOOSCheck";
666 options.DNSPort = optionIsolablePorts "DNSPort";
667 options.DoSCircuitCreationEnabled = optionBool "DoSCircuitCreationEnabled";
668 options.DoSConnectionEnabled = optionBool "DoSConnectionEnabled"; # default is null and like "auto"
669 options.DoSRefuseSingleHopClientRendezvous = optionBool "DoSRefuseSingleHopClientRendezvous";
670 options.DownloadExtraInfo = optionBool "DownloadExtraInfo";
671 options.EnforceDistinctSubnets = optionBool "EnforceDistinctSubnets";
672 options.EntryStatistics = optionBool "EntryStatistics";
673 options.ExitPolicy = optionStrings "ExitPolicy" // {
674 default = ["reject *:*"];
675 example = ["accept *:*"];
676 };
677 options.ExitPolicyRejectLocalInterfaces = optionBool "ExitPolicyRejectLocalInterfaces";
678 options.ExitPolicyRejectPrivate = optionBool "ExitPolicyRejectPrivate";
679 options.ExitPortStatistics = optionBool "ExitPortStatistics";
680 options.ExitRelay = optionBool "ExitRelay"; # default is null and like "auto"
681 options.ExtORPort = mkOption {
682 description = descriptionGeneric "ExtORPort";
683 default = null;
684 type = with types; nullOr (oneOf [
685 port (enum ["auto"]) (submodule ({...}: {
686 options = {
687 addr = optionAddress;
688 port = optionPort;
689 };
690 }))
691 ]);
692 apply = p: if isInt p || isString p then { port = p; } else p;
693 };
694 options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile";
695 options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable";
696 options.ExtendAllowPrivateAddresses = optionBool "ExtendAllowPrivateAddresses";
697 options.ExtraInfoStatistics = optionBool "ExtraInfoStatistics";
698 options.FascistFirewall = optionBool "FascistFirewall";
699 options.FetchDirInfoEarly = optionBool "FetchDirInfoEarly";
700 options.FetchDirInfoExtraEarly = optionBool "FetchDirInfoExtraEarly";
701 options.FetchHidServDescriptors = optionBool "FetchHidServDescriptors";
702 options.FetchServerDescriptors = optionBool "FetchServerDescriptors";
703 options.FetchUselessDescriptors = optionBool "FetchUselessDescriptors";
704 options.ReachableAddresses = optionStrings "ReachableAddresses";
705 options.ReachableDirAddresses = optionStrings "ReachableDirAddresses";
706 options.ReachableORAddresses = optionStrings "ReachableORAddresses";
707 options.GeoIPFile = optionPath "GeoIPFile";
708 options.GeoIPv6File = optionPath "GeoIPv6File";
709 options.GuardfractionFile = optionPath "GuardfractionFile";
710 options.HidServAuth = mkOption {
711 description = descriptionGeneric "HidServAuth";
712 default = [];
713 type = with types; listOf (oneOf [
714 (submodule {
715 options = {
716 onion = mkOption {
717 type = strMatching "[a-z2-7]{16}(\\.onion)?";
718 description = "Onion address.";
719 example = "xxxxxxxxxxxxxxxx.onion";
720 };
721 auth = mkOption {
722 type = strMatching "[A-Za-z0-9+/]{22}";
723 description = "Authentication cookie.";
724 };
725 };
726 })
727 ]);
728 };
729 options.HiddenServiceNonAnonymousMode = optionBool "HiddenServiceNonAnonymousMode";
730 options.HiddenServiceStatistics = optionBool "HiddenServiceStatistics";
731 options.HSLayer2Nodes = optionStrings "HSLayer2Nodes";
732 options.HSLayer3Nodes = optionStrings "HSLayer3Nodes";
733 options.HTTPTunnelPort = optionIsolablePorts "HTTPTunnelPort";
734 options.IPv6Exit = optionBool "IPv6Exit";
735 options.KeyDirectory = optionPath "KeyDirectory";
736 options.KeyDirectoryGroupReadable = optionBool "KeyDirectoryGroupReadable";
737 options.LogMessageDomains = optionBool "LogMessageDomains";
738 options.LongLivedPorts = optionPorts "LongLivedPorts";
739 options.MainloopStats = optionBool "MainloopStats";
740 options.MaxAdvertisedBandwidth = optionBandwith "MaxAdvertisedBandwidth";
741 options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness";
742 options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending";
743 options.NATDPort = optionIsolablePorts "NATDPort";
744 options.NewCircuitPeriod = optionInt "NewCircuitPeriod";
745 options.Nickname = optionString "Nickname";
746 options.ORPort = optionORPort "ORPort";
747 options.OfflineMasterKey = optionBool "OfflineMasterKey";
748 options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto"
749 options.PaddingStatistics = optionBool "PaddingStatistics";
750 options.PerConnBWBurst = optionBandwith "PerConnBWBurst";
751 options.PerConnBWRate = optionBandwith "PerConnBWRate";
752 options.PidFile = optionPath "PidFile";
753 options.ProtocolWarnings = optionBool "ProtocolWarnings";
754 options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors";
755 options.PublishServerDescriptor = mkOption {
756 description = descriptionGeneric "PublishServerDescriptor";
757 type = with types; nullOr (enum [false true 0 1 "0" "1" "v3" "bridge"]);
758 default = null;
759 };
760 options.ReducedExitPolicy = optionBool "ReducedExitPolicy";
761 options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto"
762 options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts";
763 options.RelayBandwidthBurst = optionBandwith "RelayBandwidthBurst";
764 options.RelayBandwidthRate = optionBandwith "RelayBandwidthRate";
765 #options.RunAsDaemon
766 options.Sandbox = optionBool "Sandbox";
767 options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig";
768 options.ServerDNSAllowNonRFC953Hostnames = optionBool "ServerDNSAllowNonRFC953Hostnames";
769 options.ServerDNSDetectHijacking = optionBool "ServerDNSDetectHijacking";
770 options.ServerDNSRandomizeCase = optionBool "ServerDNSRandomizeCase";
771 options.ServerDNSResolvConfFile = optionPath "ServerDNSResolvConfFile";
772 options.ServerDNSSearchDomains = optionBool "ServerDNSSearchDomains";
773 options.ServerTransportPlugin = mkOption {
774 description = descriptionGeneric "ServerTransportPlugin";
775 default = null;
776 type = with types; nullOr (submodule ({...}: {
777 options = {
778 transports = mkOption {
779 description = "List of pluggable transports.";
780 type = listOf str;
781 example = ["obfs2" "obfs3" "obfs4" "scramblesuit"];
782 };
783 exec = mkOption {
784 type = types.str;
785 description = "Command of pluggable transport.";
786 };
787 };
788 }));
789 };
790 options.SocksPolicy = optionStrings "SocksPolicy" // {
791 example = ["accept *:*"];
792 };
793 options.SOCKSPort = mkOption {
794 description = descriptionGeneric "SOCKSPort";
795 default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else [];
796 example = [{port = 9090;}];
797 type = types.listOf (optionSOCKSPort true);
798 };
799 options.TestingTorNetwork = optionBool "TestingTorNetwork";
800 options.TransPort = optionIsolablePorts "TransPort";
801 options.TransProxyType = mkOption {
802 description = descriptionGeneric "TransProxyType";
803 type = with types; nullOr (enum ["default" "TPROXY" "ipfw" "pf-divert"]);
804 default = null;
805 };
806 #options.TruncateLogFile
807 options.UnixSocksGroupWritable = optionBool "UnixSocksGroupWritable";
808 options.UseDefaultFallbackDirs = optionBool "UseDefaultFallbackDirs";
809 options.UseMicrodescriptors = optionBool "UseMicrodescriptors";
810 options.V3AuthUseLegacyKey = optionBool "V3AuthUseLegacyKey";
811 options.V3AuthoritativeDirectory = optionBool "V3AuthoritativeDirectory";
812 options.VersioningAuthoritativeDirectory = optionBool "VersioningAuthoritativeDirectory";
813 options.VirtualAddrNetworkIPv4 = optionString "VirtualAddrNetworkIPv4";
814 options.VirtualAddrNetworkIPv6 = optionString "VirtualAddrNetworkIPv6";
815 options.WarnPlaintextPorts = optionPorts "WarnPlaintextPorts";
816 };
817 };
818 };
819 };
820
821 config = mkIf cfg.enable {
822 # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
823 # sends a lot of stats
824 warnings = optional (cfg.settings.BridgeRelay &&
825 flatten (mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != [])
826 ''
827 Running Tor hidden services on a public relay makes the
828 presence of hidden services visible through simple statistical
829 analysis of publicly available data.
830 See https://trac.torproject.org/projects/tor/ticket/8742
831
832 You can safely ignore this warning if you don't intend to
833 actually hide your hidden services. In either case, you can
834 always create a container/VM with a separate Tor daemon instance.
835 '' ++
836 flatten (mapAttrsToList (n: o:
837 optional (o.settings.HiddenServiceVersion == 2) [
838 (optional (o.settings.HiddenServiceExportCircuitID != null) ''
839 HiddenServiceExportCircuitID is used in the HiddenService: ${n}
840 but this option is only for v3 hidden services.
841 '')
842 ] ++
843 optional (o.settings.HiddenServiceVersion != 2) [
844 (optional (o.settings.HiddenServiceAuthorizeClient != null) ''
845 HiddenServiceAuthorizeClient is used in the HiddenService: ${n}
846 but this option is only for v2 hidden services.
847 '')
848 (optional (o.settings.RendPostPeriod != null) ''
849 RendPostPeriod is used in the HiddenService: ${n}
850 but this option is only for v2 hidden services.
851 '')
852 ]
853 ) cfg.relay.onionServices);
854
855 users.groups.tor.gid = config.ids.gids.tor;
856 users.users.tor =
857 { description = "Tor Daemon User";
858 createHome = true;
859 home = stateDir;
860 group = "tor";
861 uid = config.ids.uids.tor;
862 };
863
864 services.tor.settings = mkMerge [
865 (mkIf cfg.enableGeoIP {
866 GeoIPFile = "${cfg.package.geoip}/share/tor/geoip";
867 GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6";
868 })
869 (mkIf cfg.controlSocket.enable {
870 ControlPort = [ { unix = runDir + "/control"; GroupWritable=true; RelaxDirModeCheck=true; } ];
871 })
872 (mkIf cfg.relay.enable (
873 optionalAttrs (cfg.relay.role != "exit") {
874 ExitPolicy = mkForce ["reject *:*"];
875 } //
876 optionalAttrs (elem cfg.relay.role ["bridge" "private-bridge"]) {
877 BridgeRelay = true;
878 ExtORPort.port = mkDefault "auto";
879 ServerTransportPlugin.transports = mkDefault ["obfs4"];
880 ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed";
881 } // optionalAttrs (cfg.relay.role == "private-bridge") {
882 ExtraInfoStatistics = false;
883 PublishServerDescriptor = false;
884 }
885 ))
886 (mkIf (!cfg.relay.enable) {
887 # Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings,
888 # because it would still enable Tor as a relay,
889 # which can trigger all sort of problems when not carefully done,
890 # like the blocklisting of the machine's IP addresses
891 # by some hosting providers...
892 DirPort = mkForce [];
893 ORPort = mkForce [];
894 PublishServerDescriptor = mkForce false;
895 })
896 (mkIf cfg.client.enable (
897 { SOCKSPort = [ cfg.client.socksListenAddress ];
898 } // optionalAttrs cfg.client.transparentProxy.enable {
899 TransPort = [{ addr = "127.0.0.1"; port = 9040; }];
900 } // optionalAttrs cfg.client.dns.enable {
901 DNSPort = [{ addr = "127.0.0.1"; port = 9053; }];
902 AutomapHostsOnResolve = true;
903 AutomapHostsSuffixes = cfg.client.dns.automapHostsSuffixes;
904 } // optionalAttrs (flatten (mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != []) {
905 ClientOnionAuthDir = runDir + "/ClientOnionAuthDir";
906 }
907 ))
908 ];
909
910 networking.firewall = mkIf cfg.openFirewall {
911 allowedTCPPorts =
912 concatMap (o: optional (isInt o && o > 0 || o ? "port" && isInt o.port && o.port > 0) o.port)
913 (flatten [
914 cfg.settings.ORPort
915 cfg.settings.DirPort
916 ]);
917 };
918
919 systemd.services.tor = {
920 description = "Tor Daemon";
921 path = [ pkgs.tor ];
922
923 wantedBy = [ "multi-user.target" ];
924 after = [ "network.target" ];
925 restartTriggers = [ torrc ];
926
927 serviceConfig = {
928 Type = "simple";
929 User = "tor";
930 Group = "tor";
931 ExecStartPre = [
932 "${cfg.package}/bin/tor -f ${torrc} --verify-config"
933 # DOC: Appendix G of https://spec.torproject.org/rend-spec-v3
934 ("+" + pkgs.writeShellScript "ExecStartPre" (concatStringsSep "\n" (flatten (["set -eu"] ++
935 mapAttrsToList (name: onion:
936 optional (onion.authorizedClients != []) ''
937 rm -rf ${escapeShellArg onion.path}/authorized_clients
938 install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} ${escapeShellArg onion.path}/authorized_clients
939 '' ++
940 imap0 (i: pubKey: ''
941 echo ${pubKey} |
942 install -o tor -g tor -m 0400 /dev/stdin ${escapeShellArg onion.path}/authorized_clients/${toString i}.auth
943 '') onion.authorizedClients ++
944 optional (onion.secretKey != null) ''
945 install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path}
946 key="$(cut -f1 -d: ${escapeShellArg onion.secretKey})"
947 case "$key" in
948 ("== ed25519v"*"-secret")
949 install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;;
950 (*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;;
951 esac
952 ''
953 ) cfg.relay.onionServices ++
954 mapAttrsToList (name: onion: imap0 (i: prvKeyPath:
955 let hostname = removeSuffix ".onion" name; in ''
956 printf "%s:" ${escapeShellArg hostname} | cat - ${escapeShellArg prvKeyPath} |
957 install -o tor -g tor -m 0700 /dev/stdin \
958 ${runDir}/ClientOnionAuthDir/${escapeShellArg hostname}.${toString i}.auth_private
959 '') onion.clientAuthorizations)
960 cfg.client.onionServices
961 ))))
962 ];
963 ExecStart = "${cfg.package}/bin/tor -f ${torrc}";
964 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
965 KillSignal = "SIGINT";
966 TimeoutSec = 30;
967 Restart = "on-failure";
968 LimitNOFILE = 32768;
969 RuntimeDirectory = [
970 # g+x allows access to the control socket
971 "tor"
972 "tor/root"
973 # g+x can't be removed in ExecStart=, but will be removed by Tor
974 "tor/ClientOnionAuthDir"
975 ];
976 RuntimeDirectoryMode = "0710";
977 StateDirectoryMode = "0700";
978 StateDirectory = [
979 "tor"
980 "tor/onion"
981 ] ++
982 flatten (mapAttrsToList (name: onion:
983 optional (onion.secretKey == null) "tor/onion/${name}"
984 ) cfg.relay.onionServices);
985 # The following options are only to optimize:
986 # systemd-analyze security tor
987 RootDirectory = runDir + "/root";
988 RootDirectoryStartOnly = true;
989 #InaccessiblePaths = [ "-+${runDir}/root" ];
990 UMask = "0066";
991 BindPaths = [ stateDir ];
992 BindReadOnlyPaths = [ storeDir "/etc" ];
993 AmbientCapabilities = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
994 CapabilityBoundingSet = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
995 # ProtectClock= adds DeviceAllow=char-rtc r
996 DeviceAllow = "";
997 LockPersonality = true;
998 MemoryDenyWriteExecute = true;
999 NoNewPrivileges = true;
1000 PrivateDevices = true;
1001 PrivateMounts = true;
1002 PrivateNetwork = mkDefault false;
1003 PrivateTmp = true;
1004 # Tor cannot currently bind privileged port when PrivateUsers=true,
1005 # see https://gitlab.torproject.org/legacy/trac/-/issues/20930
1006 PrivateUsers = !bindsPrivilegedPort;
1007 ProtectClock = true;
1008 ProtectControlGroups = true;
1009 ProtectHome = true;
1010 ProtectHostname = true;
1011 ProtectKernelLogs = true;
1012 ProtectKernelModules = true;
1013 ProtectKernelTunables = true;
1014 ProtectSystem = "strict";
1015 RemoveIPC = true;
1016 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
1017 RestrictNamespaces = true;
1018 RestrictRealtime = true;
1019 RestrictSUIDSGID = true;
1020 # See also the finer but experimental option settings.Sandbox
1021 SystemCallFilter = [
1022 "@system-service"
1023 # Groups in @system-service which do not contain a syscall listed by:
1024 # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' tor
1025 # in tests, and seem likely not necessary for tor.
1026 "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer"
1027 ];
1028 SystemCallArchitectures = "native";
1029 SystemCallErrorNumber = "EPERM";
1030 };
1031 };
1032
1033 environment.systemPackages = [ cfg.package ];
1034 };
1035
1036 meta.maintainers = with lib.maintainers; [ julm ];
1037 }