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