1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.tor;
7 torDirectory = "/var/lib/tor";
8
9 opt = name: value: optionalString (value != null) "${name} ${value}";
10 optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}";
11
12 isolationOptions = {
13 type = types.listOf (types.enum [
14 "IsolateClientAddr"
15 "IsolateSOCKSAuth"
16 "IsolateClientProtocol"
17 "IsolateDestPort"
18 "IsolateDestAddr"
19 ]);
20 default = [];
21 example = [
22 "IsolateClientAddr"
23 "IsolateSOCKSAuth"
24 "IsolateClientProtocol"
25 "IsolateDestPort"
26 "IsolateDestAddr"
27 ];
28 description = "Tor isolation options";
29 };
30
31
32 torRc = ''
33 User tor
34 DataDirectory ${torDirectory}
35 ${optionalString cfg.enableGeoIP ''
36 GeoIPFile ${pkgs.tor.geoip}/share/tor/geoip
37 GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
38 ''}
39
40 ${optint "ControlPort" cfg.controlPort}
41 ''
42 # Client connection config
43 + optionalString cfg.client.enable ''
44 SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
45 SOCKSPort ${cfg.client.socksListenAddressFaster}
46 ${opt "SocksPolicy" cfg.client.socksPolicy}
47
48 ${optionalString cfg.client.transparentProxy.enable ''
49 TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
50 ''}
51
52 ${optionalString cfg.client.dns.enable ''
53 DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
54 AutomapHostsOnResolve 1
55 AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
56 ''}
57 ''
58 # Relay config
59 + optionalString cfg.relay.enable ''
60 ORPort ${toString cfg.relay.port}
61 ${opt "Address" cfg.relay.address}
62 ${opt "Nickname" cfg.relay.nickname}
63 ${opt "ContactInfo" cfg.relay.contactInfo}
64
65 ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
66 ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
67 ${opt "AccountingMax" cfg.relay.accountingMax}
68 ${opt "AccountingStart" cfg.relay.accountingStart}
69
70 ${if (cfg.relay.role == "exit") then
71 opt "ExitPolicy" cfg.relay.exitPolicy
72 else
73 "ExitPolicy reject *:*"}
74
75 ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
76 BridgeRelay 1
77 ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
78 ExtORPort auto
79 ${optionalString (cfg.relay.role == "private-bridge") ''
80 ExtraInfoStatistics 0
81 PublishServerDescriptor 0
82 ''}
83 ''}
84 ''
85 # Hidden services
86 + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
87 HiddenServiceDir ${torDirectory}/onion/${v.name}
88 ${flip concatMapStrings v.map (p: ''
89 HiddenServicePort ${toString p.port} ${p.destination}
90 '')}
91 ${optionalString (v.authorizeClient != null) ''
92 HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
93 ''}
94 ''))
95 + cfg.extraConfig;
96
97 torRcFile = pkgs.writeText "torrc" torRc;
98
99in
100{
101 options = {
102 services.tor = {
103 enable = mkOption {
104 type = types.bool;
105 default = false;
106 description = ''
107 Enable the Tor daemon. By default, the daemon is run without
108 relay, exit, bridge or client connectivity.
109 '';
110 };
111
112 enableGeoIP = mkOption {
113 type = types.bool;
114 default = true;
115 description = ''
116 Whenever to configure Tor daemon to use GeoIP databases.
117
118 Disabling this will disable by-country statistics for
119 bridges and relays and some client and third-party software
120 functionality.
121 '';
122 };
123
124 extraConfig = mkOption {
125 type = types.lines;
126 default = "";
127 description = ''
128 Extra configuration. Contents will be added verbatim to the
129 configuration file at the end.
130 '';
131 };
132
133 controlPort = mkOption {
134 type = types.nullOr (types.either types.int types.str);
135 default = null;
136 example = 9051;
137 description = ''
138 If set, Tor will accept connections on the specified port
139 and allow them to control the tor process.
140 '';
141 };
142
143 client = {
144 enable = mkOption {
145 type = types.bool;
146 default = false;
147 description = ''
148 Whether to enable Tor daemon to route application
149 connections. You might want to disable this if you plan
150 running a dedicated Tor relay.
151 '';
152 };
153
154 socksListenAddress = mkOption {
155 type = types.str;
156 default = "127.0.0.1:9050";
157 example = "192.168.0.1:9100";
158 description = ''
159 Bind to this address to listen for connections from
160 Socks-speaking applications. Provides strong circuit
161 isolation, separate circuit per IP address.
162 '';
163 };
164
165 socksListenAddressFaster = mkOption {
166 type = types.str;
167 default = "127.0.0.1:9063";
168 example = "192.168.0.1:9101";
169 description = ''
170 Bind to this address to listen for connections from
171 Socks-speaking applications. Same as
172 <option>socksListenAddress</option> but uses weaker
173 circuit isolation to provide performance suitable for a
174 web browser.
175 '';
176 };
177
178 socksPolicy = mkOption {
179 type = types.nullOr types.str;
180 default = null;
181 example = "accept 192.168.0.0/16, reject *";
182 description = ''
183 Entry policies to allow/deny SOCKS requests based on IP
184 address. First entry that matches wins. If no SocksPolicy
185 is set, we accept all (and only) requests from
186 <option>socksListenAddress</option>.
187 '';
188 };
189
190 socksIsolationOptions = mkOption (isolationOptions // {
191 default = ["IsolateDestAddr"];
192 });
193
194 transparentProxy = {
195 enable = mkOption {
196 type = types.bool;
197 default = false;
198 description = "Whether to enable tor transaprent proxy";
199 };
200
201 listenAddress = mkOption {
202 type = types.str;
203 default = "127.0.0.1:9040";
204 example = "192.168.0.1:9040";
205 description = ''
206 Bind transparent proxy to this address.
207 '';
208 };
209
210 isolationOptions = mkOption isolationOptions;
211 };
212
213 dns = {
214 enable = mkOption {
215 type = types.bool;
216 default = false;
217 description = "Whether to enable tor dns resolver";
218 };
219
220 listenAddress = mkOption {
221 type = types.str;
222 default = "127.0.0.1:9053";
223 example = "192.168.0.1:9053";
224 description = ''
225 Bind tor dns to this address.
226 '';
227 };
228
229 isolationOptions = mkOption isolationOptions;
230
231 automapHostsSuffixes = mkOption {
232 type = types.listOf types.str;
233 default = [".onion" ".exit"];
234 example = [".onion"];
235 description = "List of suffixes to use with automapHostsOnResolve";
236 };
237 };
238
239 privoxy.enable = mkOption {
240 type = types.bool;
241 default = true;
242 description = ''
243 Whether to enable and configure the system Privoxy to use Tor's
244 faster port, suitable for HTTP.
245
246 To have anonymity, protocols need to be scrubbed of identifying
247 information, and this can be accomplished for HTTP by Privoxy.
248
249 Privoxy can also be useful for KDE torification. A good setup would be:
250 setting SOCKS proxy to the default Tor port, providing maximum
251 circuit isolation where possible; and setting HTTP proxy to Privoxy
252 to route HTTP traffic over faster, but less isolated port.
253 '';
254 };
255 };
256
257 relay = {
258 enable = mkOption {
259 type = types.bool;
260 default = false;
261 description = ''
262 Whether to enable relaying TOR traffic for others.
263
264 See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
265 for details.
266
267 Setting this to true requires setting
268 <option>services.tor.relay.role</option>
269 and
270 <option>services.tor.relay.port</option>
271 options.
272 '';
273 };
274
275 role = mkOption {
276 type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
277 description = ''
278 Your role in Tor network. There're several options:
279
280 <variablelist>
281 <varlistentry>
282 <term><literal>exit</literal></term>
283 <listitem>
284 <para>
285 An exit relay. This allows Tor users to access regular
286 Internet services through your public IP.
287 </para>
288
289 <important><para>
290 Running an exit relay may expose you to abuse
291 complaints. See
292 <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
293 for more info.
294 </para></important>
295
296 <para>
297 You can specify which services Tor users may access via
298 your exit relay using <option>exitPolicy</option> option.
299 </para>
300 </listitem>
301 </varlistentry>
302
303 <varlistentry>
304 <term><literal>relay</literal></term>
305 <listitem>
306 <para>
307 Regular relay. This allows Tor users to relay onion
308 traffic to other Tor nodes, but not to public
309 Internet.
310 </para>
311
312 <important><para>
313 Note that some misconfigured and/or disrespectful
314 towards privacy sites will block you even if your
315 relay is not an exit relay. That is, just being listed
316 in a public relay directory can have unwanted
317 consequences.
318
319 Which means you might not want to use
320 this role if you browse public Internet from the same
321 network as your relay, unless you want to write
322 e-mails to those sites (you should!).
323 </para></important>
324
325 <para>
326 See
327 <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" />
328 for more info.
329 </para>
330 </listitem>
331 </varlistentry>
332
333 <varlistentry>
334 <term><literal>bridge</literal></term>
335 <listitem>
336 <para>
337 Regular bridge. Works like a regular relay, but
338 doesn't list you in the public relay directory and
339 hides your Tor node behind obfsproxy.
340 </para>
341
342 <para>
343 Using this option will make Tor advertise your bridge
344 to users through various mechanisms like
345 <link xlink:href="https://bridges.torproject.org/" />, though.
346 </para>
347
348 <important>
349 <para>
350 WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVISE.
351 Consult with your lawer when in doubt.
352 </para>
353
354 <para>
355 This role should be safe to use in most situations
356 (unless the act of forwarding traffic for others is
357 a punishable offence under your local laws, which
358 would be pretty insane as it would make ISP
359 illegal).
360 </para>
361 </important>
362
363 <para>
364 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
365 for more info.
366 </para>
367 </listitem>
368 </varlistentry>
369
370 <varlistentry>
371 <term><literal>private-bridge</literal></term>
372 <listitem>
373 <para>
374 Private bridge. Works like regular bridge, but does
375 not advertise your node in any way.
376 </para>
377
378 <para>
379 Using this role means that you won't contribute to Tor
380 network in any way unless you advertise your node
381 yourself in some way.
382 </para>
383
384 <para>
385 Use this if you want to run a private bridge, for
386 example because you'll give out your bridge address
387 manually to your friends.
388 </para>
389
390 <para>
391 Switching to this role after measurable time in
392 "bridge" role is pretty useless as some Tor users
393 would have learned about your node already. In the
394 latter case you can still change
395 <option>port</option> option.
396 </para>
397
398 <para>
399 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
400 for more info.
401 </para>
402 </listitem>
403 </varlistentry>
404 </variablelist>
405 '';
406 };
407
408 nickname = mkOption {
409 type = types.str;
410 default = "anonymous";
411 description = ''
412 A unique handle for your TOR relay.
413 '';
414 };
415
416 contactInfo = mkOption {
417 type = types.nullOr types.str;
418 default = null;
419 example = "admin@relay.com";
420 description = ''
421 Contact information for the relay owner (e.g. a mail
422 address and GPG key ID).
423 '';
424 };
425
426 accountingMax = mkOption {
427 type = types.nullOr types.str;
428 default = null;
429 example = "450 GBytes";
430 description = ''
431 Specify maximum bandwidth allowed during an accounting period. This
432 allows you to limit overall tor bandwidth over some time period.
433 See the <literal>AccountingMax</literal> option by looking at the
434 tor manual <citerefentry><refentrytitle>tor</refentrytitle>
435 <manvolnum>1</manvolnum></citerefentry> for more.
436
437 Note this limit applies individually to upload and
438 download; if you specify <literal>"500 GBytes"</literal>
439 here, then you may transfer up to 1 TBytes of overall
440 bandwidth (500 GB upload, 500 GB download).
441 '';
442 };
443
444 accountingStart = mkOption {
445 type = types.nullOr types.str;
446 default = null;
447 example = "month 1 1:00";
448 description = ''
449 Specify length of an accounting period. This allows you to limit
450 overall tor bandwidth over some time period. See the
451 <literal>AccountingStart</literal> option by looking at the tor
452 manual <citerefentry><refentrytitle>tor</refentrytitle>
453 <manvolnum>1</manvolnum></citerefentry> for more.
454 '';
455 };
456
457 bandwidthRate = mkOption {
458 type = types.nullOr types.int;
459 default = null;
460 example = 100;
461 description = ''
462 Specify this to limit the bandwidth usage of relayed (server)
463 traffic. Your own traffic is still unthrottled. Units: bytes/second.
464 '';
465 };
466
467 bandwidthBurst = mkOption {
468 type = types.nullOr types.int;
469 default = cfg.relay.bandwidthRate;
470 example = 200;
471 description = ''
472 Specify this to allow bursts of the bandwidth usage of relayed (server)
473 traffic. The average usage will still be as specified in relayBandwidthRate.
474 Your own traffic is still unthrottled. Units: bytes/second.
475 '';
476 };
477
478 address = mkOption {
479 type = types.nullOr types.str;
480 default = null;
481 example = "noname.example.com";
482 description = ''
483 The IP address or full DNS name for advertised address of your relay.
484 Leave unset and Tor will guess.
485 '';
486 };
487
488 port = mkOption {
489 type = types.either types.int types.str;
490 example = 143;
491 description = ''
492 What port to advertise for Tor connections. This corresponds to the
493 <literal>ORPort</literal> section in the Tor manual; see
494 <citerefentry><refentrytitle>tor</refentrytitle>
495 <manvolnum>1</manvolnum></citerefentry> for more details.
496
497 At a minimum, you should just specify the port for the
498 relay to listen on; a common one like 143, 22, 80, or 443
499 to help Tor users who may have very restrictive port-based
500 firewalls.
501 '';
502 };
503
504 exitPolicy = mkOption {
505 type = types.nullOr types.str;
506 default = null;
507 example = "accept *:6660-6667,reject *:*";
508 description = ''
509 A comma-separated list of exit policies. They're
510 considered first to last, and the first match wins. If you
511 want to _replace_ the default exit policy, end this with
512 either a reject *:* or an accept *:*. Otherwise, you're
513 _augmenting_ (prepending to) the default exit policy.
514 Leave commented to just use the default, which is
515 available in the man page or at
516 <link xlink:href="https://www.torproject.org/documentation.html" />.
517
518 Look at
519 <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" />
520 for issues you might encounter if you use the default
521 exit policy.
522
523 If certain IPs and ports are blocked externally, e.g. by
524 your firewall, you should update your exit policy to
525 reflect this -- otherwise Tor users will be told that
526 those destinations are down.
527 '';
528 };
529 };
530
531 hiddenServices = mkOption {
532 description = ''
533 A set of static hidden services that terminate their Tor
534 circuits at this node.
535
536 Every element in this set declares a virtual onion host.
537
538 You can specify your onion address by putting corresponding
539 private key to an appropriate place in ${torDirectory}.
540
541 For services without private keys in ${torDirectory} Tor
542 daemon will generate random key pairs (which implies random
543 onion addresses) on restart. The latter could take a while,
544 please be patient.
545
546 <note><para>
547 Hidden services can be useful even if you don't intend to
548 actually <emphasis>hide</emphasis> them, since they can
549 also be seen as a kind of NAT traversal mechanism.
550
551 E.g. the example will make your sshd, whatever runs on
552 "8080" and your mail server available from anywhere where
553 the Tor network is available (which, with the help from
554 bridges, is pretty much everywhere), even if both client
555 and server machines are behind NAT you have no control
556 over.
557 </para></note>
558 '';
559 default = {};
560 example = literalExample ''
561 { "my-hidden-service-example".map = [
562 { port = 22; } # map ssh port to this machine's ssh
563 { port = 80; toPort = 8080; } # map http port to whatever runs on 8080
564 { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can
565 ];
566 }
567 '';
568 type = types.loaOf (types.submodule ({name, config, ...}: {
569 options = {
570
571 name = mkOption {
572 type = types.str;
573 description = ''
574 Name of this tor hidden service.
575
576 This is purely descriptive.
577
578 After restarting Tor daemon you should be able to
579 find your .onion address in
580 <literal>${torDirectory}/onion/$name/hostname</literal>.
581 '';
582 };
583
584 map = mkOption {
585 default = [];
586 description = "Port mapping for this hidden service.";
587 type = types.listOf (types.submodule ({config, ...}: {
588 options = {
589
590 port = mkOption {
591 type = types.either types.int types.str;
592 example = 80;
593 description = ''
594 Hidden service port to "bind to".
595 '';
596 };
597
598 destination = mkOption {
599 internal = true;
600 type = types.str;
601 description = "Forward these connections where?";
602 };
603
604 toHost = mkOption {
605 type = types.str;
606 default = "127.0.0.1";
607 description = "Mapping destination host.";
608 };
609
610 toPort = mkOption {
611 type = types.either types.int types.str;
612 example = 8080;
613 description = "Mapping destination port.";
614 };
615
616 };
617
618 config = {
619 toPort = mkDefault config.port;
620 destination = mkDefault "${config.toHost}:${toString config.toPort}";
621 };
622 }));
623 };
624
625 authorizeClient = mkOption {
626 default = null;
627 description = "If configured, the hidden service is accessible for authorized clients only.";
628 type = types.nullOr (types.submodule ({config, ...}: {
629
630 options = {
631
632 authType = mkOption {
633 type = types.enum [ "basic" "stealth" ];
634 description = ''
635 Either <literal>"basic"</literal> for a general-purpose authorization protocol
636 or <literal>"stealth"</literal> for a less scalable protocol
637 that also hides service activity from unauthorized clients.
638 '';
639 };
640
641 clientNames = mkOption {
642 type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
643 description = ''
644 Only clients that are listed here are authorized to access the hidden service.
645 Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>.
646 Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
647 '';
648 };
649 };
650 }));
651 };
652 };
653
654 config = {
655 name = mkDefault name;
656 };
657 }));
658 };
659 };
660 };
661
662 config = mkIf cfg.enable {
663 # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
664 # sends a lot of stats
665 warnings = optional (cfg.relay.enable && cfg.hiddenServices != {})
666 ''
667 Running Tor hidden services on a public relay makes the
668 presence of hidden services visible through simple statistical
669 analysis of publicly available data.
670
671 You can safely ignore this warning if you don't intend to
672 actually hide your hidden services. In either case, you can
673 always create a container/VM with a separate Tor daemon instance.
674 '';
675
676 users.extraGroups.tor.gid = config.ids.gids.tor;
677 users.extraUsers.tor =
678 { description = "Tor Daemon User";
679 createHome = true;
680 home = torDirectory;
681 group = "tor";
682 uid = config.ids.uids.tor;
683 };
684
685 systemd.services.tor =
686 { description = "Tor Daemon";
687 path = [ pkgs.tor ];
688
689 wantedBy = [ "multi-user.target" ];
690 after = [ "network.target" ];
691 restartTriggers = [ torRcFile ];
692
693 # Translated from the upstream contrib/dist/tor.service.in
694 preStart = ''
695 install -o tor -g tor -d ${torDirectory}/onion
696 ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
697 '';
698
699 serviceConfig =
700 { Type = "simple";
701 ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
702 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
703 KillSignal = "SIGINT";
704 TimeoutSec = 30;
705 Restart = "on-failure";
706 LimitNOFILE = 32768;
707
708 # Hardening
709 # Note: DevicePolicy is set to 'closed', although the
710 # minimal permissions are really:
711 # DeviceAllow /dev/null rw
712 # DeviceAllow /dev/urandom r
713 # .. but we can't specify DeviceAllow multiple times. 'closed'
714 # is close enough.
715 PrivateTmp = "yes";
716 DevicePolicy = "closed";
717 InaccessibleDirectories = "/home";
718 ReadOnlyDirectories = "/";
719 ReadWriteDirectories = torDirectory;
720 NoNewPrivileges = "yes";
721 };
722 };
723
724 environment.systemPackages = [ pkgs.tor ];
725
726 services.privoxy = mkIf (cfg.client.enable && cfg.client.privoxy.enable) {
727 enable = true;
728 extraConfig = ''
729 forward-socks4a / ${cfg.client.socksListenAddressFaster} .
730 toggle 1
731 enable-remote-toggle 0
732 enable-edit-actions 0
733 enable-remote-http-toggle 0
734 '';
735 };
736 };
737}