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