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