1 diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
2 index e3b34cb0c61..7caae328203 100644
3 --- a/nixos/modules/services/networking/privoxy.nix
4 +++ b/nixos/modules/services/networking/privoxy.nix
6 ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles}
7 ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles}
8 '' + optionalString cfg.enableTor ''
9 - forward-socks4a / ${config.services.tor.client.socksListenAddressFaster} .
10 + forward-socks5t / 127.0.0.1:9063 .
12 enable-remote-toggle 0
14 @@ -123,6 +123,11 @@ in
15 serviceConfig.ProtectSystem = "full";
18 + services.tor.settings.SOCKSPort = mkIf cfg.enableTor [
19 + # Route HTTP traffic over a faster port (without IsolateDestAddr).
20 + { addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; }
25 meta.maintainers = with lib.maintainers; [ rnhmjoj ];
26 diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
27 index 1cceee065b1..2f89d6bc1df 100644
28 --- a/nixos/modules/services/security/tor.nix
29 +++ b/nixos/modules/services/security/tor.nix
31 { config, lib, pkgs, ... }:
37 cfg = config.services.tor;
38 - torDirectory = "/var/lib/tor";
39 - torRunDirectory = "/run/tor";
41 - opt = name: value: optionalString (value != null) "${name} ${value}";
42 - optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}";
44 - isolationOptions = {
45 - type = types.listOf (types.enum [
48 - "IsolateClientProtocol"
51 + stateDir = "/var/lib/tor";
52 + runDir = "/run/tor";
53 + descriptionGeneric = option: ''
54 + See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en#${option}">torrc manual</link>.
56 + bindsPrivilegedPort =
58 + let p1 = if p0 ? "port" then p0.port else p0; in
59 + if p1 == "auto" then false
60 + else let p2 = if isInt p1 then p1 else toInt p1; in
61 + p1 != null && 0 < p2 && p2 < 1024)
64 + cfg.settings.DirPort
65 + cfg.settings.DNSPort
66 + cfg.settings.ExtORPort
67 + cfg.settings.HTTPTunnelPort
68 + cfg.settings.NATDPort
69 + cfg.settings.SOCKSPort
70 + cfg.settings.TransPort
72 + optionBool = optionName: mkOption {
73 + type = with types; nullOr bool;
75 + description = descriptionGeneric optionName;
77 + optionInt = optionName: mkOption {
78 + type = with types; nullOr int;
80 + description = descriptionGeneric optionName;
82 + optionString = optionName: mkOption {
83 + type = with types; nullOr str;
85 + description = descriptionGeneric optionName;
87 + optionStrings = optionName: mkOption {
88 + type = with types; listOf str;
93 - "IsolateClientProtocol"
96 + description = descriptionGeneric optionName;
98 + optionAddress = mkOption {
99 + type = with types; nullOr str;
101 + example = "0.0.0.0";
103 + IPv4 or IPv6 (if between brackets) address.
106 + optionUnix = mkOption {
107 + type = with types; nullOr path;
110 + Unix domain socket path to use.
113 + optionPort = mkOption {
114 + type = with types; nullOr (oneOf [port (enum ["auto"])]);
117 + optionPorts = optionName: mkOption {
118 + type = with types; listOf port;
120 + description = descriptionGeneric optionName;
122 + optionIsolablePort = with types; oneOf [
123 + port (enum ["auto"])
124 + (submodule ({config, ...}: {
126 + addr = optionAddress;
128 + flags = optionFlags;
129 + SessionGroup = mkOption { type = nullOr int; default = null; };
130 + } // genAttrs isolateFlags (name: mkOption { type = types.bool; default = false; });
132 + flags = filter (name: config.${name} == true) isolateFlags ++
133 + optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
137 + optionIsolablePorts = optionName: mkOption {
139 + type = with types; either optionIsolablePort (listOf optionIsolablePort);
140 + description = descriptionGeneric optionName;
143 + "IsolateClientAddr"
144 + "IsolateClientProtocol"
148 + "KeepAliveIsolateSOCKSAuth"
150 + optionSOCKSPort = doConfig: let
152 + "CacheDNS" "CacheIPv4DNS" "CacheIPv6DNS" "GroupWritable" "IPv6Traffic"
153 + "NoDNSRequest" "NoIPv4Traffic" "NoOnionTraffic" "OnionTrafficOnly"
154 + "PreferIPv6" "PreferIPv6Automap" "PreferSOCKSNoAuth" "UseDNSCache"
155 + "UseIPv4Cache" "UseIPv6Cache" "WorldWritable"
157 + in with types; oneOf [
158 + port (submodule ({config, ...}: {
161 + addr = optionAddress;
163 + flags = optionFlags;
164 + SessionGroup = mkOption { type = nullOr int; default = null; };
165 + } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
166 + config = mkIf doConfig { # Only add flags in SOCKSPort to avoid duplicates
167 + flags = filter (name: config.${name} == true) flags ++
168 + optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
172 - description = "Tor isolation options";
173 + optionFlags = mkOption {
174 + type = with types; listOf str;
177 + optionORPort = optionName: mkOption {
180 + type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [
183 + (submodule ({config, ...}:
184 + let flags = [ "IPv4Only" "IPv6Only" "NoAdvertise" "NoListen" ];
187 + addr = optionAddress;
189 + flags = optionFlags;
190 + } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
192 + flags = filter (name: config.${name} == true) flags;
196 + description = descriptionGeneric optionName;
198 + optionBandwith = optionName: mkOption {
199 + type = with types; nullOr (either int str);
201 + description = descriptionGeneric optionName;
203 + optionPath = optionName: mkOption {
204 + type = with types; nullOr path;
206 + description = descriptionGeneric optionName;
212 - DataDirectory ${torDirectory}
213 - ${optionalString cfg.enableGeoIP ''
214 - GeoIPFile ${cfg.package.geoip}/share/tor/geoip
215 - GeoIPv6File ${cfg.package.geoip}/share/tor/geoip6
218 - ${optint "ControlPort" cfg.controlPort}
219 - ${optionalString cfg.controlSocket.enable "ControlPort unix:${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
221 - # Client connection config
222 - + optionalString cfg.client.enable ''
223 - SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
224 - SOCKSPort ${cfg.client.socksListenAddressFaster}
225 - ${opt "SocksPolicy" cfg.client.socksPolicy}
227 - ${optionalString cfg.client.transparentProxy.enable ''
228 - TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
231 - ${optionalString cfg.client.dns.enable ''
232 - DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
233 - AutomapHostsOnResolve 1
234 - AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
237 - # Explicitly disable the SOCKS server if the client is disabled. In
238 - # particular, this makes non-anonymous hidden services possible.
239 - + optionalString (! cfg.client.enable) ''
243 - + optionalString cfg.relay.enable ''
244 - ORPort ${toString cfg.relay.port}
245 - ${opt "Address" cfg.relay.address}
246 - ${opt "Nickname" cfg.relay.nickname}
247 - ${opt "ContactInfo" cfg.relay.contactInfo}
249 - ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
250 - ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
251 - ${opt "AccountingMax" cfg.relay.accountingMax}
252 - ${opt "AccountingStart" cfg.relay.accountingStart}
254 - ${if (cfg.relay.role == "exit") then
255 - opt "ExitPolicy" cfg.relay.exitPolicy
257 - "ExitPolicy reject *:*"}
259 - ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
261 - ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed
263 - ${optionalString (cfg.relay.role == "private-bridge") ''
264 - ExtraInfoStatistics 0
265 - PublishServerDescriptor 0
270 - + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
271 - HiddenServiceDir ${torDirectory}/onion/${v.name}
272 - ${optionalString (v.version != null) "HiddenServiceVersion ${toString v.version}"}
273 - ${flip concatMapStrings v.map (p: ''
274 - HiddenServicePort ${toString p.port} ${p.destination}
276 - ${optionalString (v.authorizeClient != null) ''
277 - HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
282 - torRcFile = pkgs.writeText "torrc" torRc;
284 + mkValueString = k: v:
285 + if v == null then ""
286 + else if isBool v then
287 + (if v then "1" else "0")
288 + else if v ? "unix" && v.unix != null then
290 + optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
291 + else if v ? "port" && v.port != null then
292 + optionalString (v ? "addr" && v.addr != null) "${v.addr}:" +
294 + optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
295 + else if k == "ServerTransportPlugin" then
296 + optionalString (v.transports != []) "${concatStringsSep "," v.transports} exec ${v.exec}"
297 + else if k == "HidServAuth" then
298 + concatMapStringsSep "\n${k} " (settings: settings.onion + " " settings.auth) v
299 + else generators.mkValueStringDefault {} v;
300 + genTorrc = settings:
301 + generators.toKeyValue {
302 + listsAsDuplicateKeys = true;
303 + mkKeyValue = k: generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
305 + (lib.mapAttrs (k: v:
306 + # Not necesssary, but prettier rendering
307 + if elem k [ "AutomapHostsSuffixes" "DirPolicy" "ExitPolicy" "SocksPolicy" ]
309 + then concatStringsSep "," v
311 + (lib.filterAttrs (k: v: !(v == null || v == ""))
313 + torrc = pkgs.writeText "torrc" (
314 + genTorrc cfg.settings +
315 + concatStrings (mapAttrsToList (name: onion:
316 + "HiddenServiceDir ${onion.path}\n" +
317 + genTorrc onion.settings) cfg.relay.onionServices)
322 - (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] ''
323 - Use services.privoxy.enable and services.privoxy.enableTor instead.
325 - (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
326 + (mkRenamedOptionModule [ "services" "tor" "client" "dns" "automapHostsSuffixes" ] [ "services" "tor" "settings" "AutomapHostsSuffixes" ])
327 + (mkRemovedOptionModule [ "services" "tor" "client" "dns" "isolationOptions" ] "Use services.tor.settings.DNSPort instead.")
328 + (mkRemovedOptionModule [ "services" "tor" "client" "dns" "listenAddress" ] "Use services.tor.settings.DNSPort instead.")
329 + (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] "Use services.privoxy.enable and services.privoxy.enableTor instead.")
330 + (mkRemovedOptionModule [ "services" "tor" "client" "socksIsolationOptions" ] "Use services.tor.settings.SOCKSPort")
331 + (mkRenamedOptionModule [ "services" "tor" "client" "socksPolicy" ] [ "services" "tor" "settings" "SocksPolicy" ])
332 + (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "isolationOptions" ] "Use services.tor.settings.TransPort instead.")
333 + (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "listenAddress" ] "Use services.tor.settings.TransPort instead.")
334 + (mkRenamedOptionModule [ "services" "tor" "controlPort" ] [ "services" "tor" "settings" "ControlPort" ])
335 + (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Plese use services.tor.settings instead.")
336 + (mkRenamedOptionModule [ "services" "tor" "hiddenServices" ] [ "services" "tor" "relay" "onionServices" ])
337 + (mkRenamedOptionModule [ "services" "tor" "relay" "accountingMax" ] [ "services" "tor" "settings" "AccountingMax" ])
338 + (mkRenamedOptionModule [ "services" "tor" "relay" "accountingStart" ] [ "services" "tor" "settings" "AccountingStart" ])
339 + (mkRenamedOptionModule [ "services" "tor" "relay" "address" ] [ "services" "tor" "settings" "Address" ])
340 + (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthBurst" ] [ "services" "tor" "settings" "BandwidthBurst" ])
341 + (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthRate" ] [ "services" "tor" "settings" "BandwidthRate" ])
342 + (mkRenamedOptionModule [ "services" "tor" "relay" "bridgeTransports" ] [ "services" "tor" "settings" "ServerTransportPlugin" "transports" ])
343 + (mkRenamedOptionModule [ "services" "tor" "relay" "contactInfo" ] [ "services" "tor" "settings" "ContactInfo" ])
344 + (mkRenamedOptionModule [ "services" "tor" "relay" "exitPolicy" ] [ "services" "tor" "settings" "ExitPolicy" ])
345 (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
346 (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
347 + (mkRenamedOptionModule [ "services" "tor" "relay" "nickname" ] [ "services" "tor" "settings" "Nickname" ])
348 + (mkRenamedOptionModule [ "services" "tor" "relay" "port" ] [ "services" "tor" "settings" "ORPort" ])
349 + (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "settings" "ORPort" ])
354 - enable = mkOption {
358 - Enable the Tor daemon. By default, the daemon is run without
359 - relay, exit, bridge or client connectivity.
362 + enable = mkEnableOption ''Tor daemon.
363 + By default, the daemon is run without
364 + relay, exit, bridge or client connectivity'';
366 + openFirewall = mkEnableOption "opening of the relay port(s) in the firewall";
369 type = types.package;
371 defaultText = "pkgs.tor";
372 example = literalExample "pkgs.tor";
376 + description = "Tor package to use.";
379 - enableGeoIP = mkOption {
383 - Whenever to configure Tor daemon to use GeoIP databases.
384 + enableGeoIP = mkEnableOption ''use of GeoIP databases.
385 + Disabling this will disable by-country statistics for bridges and relays
386 + and some client and third-party software functionality'' // { default = true; };
388 - Disabling this will disable by-country statistics for
389 - bridges and relays and some client and third-party software
394 - extraConfig = mkOption {
395 - type = types.lines;
398 - Extra configuration. Contents will be added verbatim to the
399 - configuration file at the end.
403 - controlPort = mkOption {
404 - type = types.nullOr (types.either types.int types.str);
408 - If set, Tor will accept connections on the specified port
409 - and allow them to control the tor process.
414 - enable = mkOption {
418 - Whether to enable Tor control socket. Control socket is created
419 - in <literal>${torRunDirectory}/control</literal>
423 + controlSocket.enable = mkEnableOption ''control socket,
424 + created in <literal>${runDir}/control</literal>'';
427 - enable = mkOption {
431 - Whether to enable Tor daemon to route application
432 - connections. You might want to disable this if you plan
433 - running a dedicated Tor relay.
436 + enable = mkEnableOption ''the routing of application connections.
437 + You might want to disable this if you plan running a dedicated Tor relay'';
439 + transparentProxy.enable = mkEnableOption "transparent proxy";
440 + dns.enable = mkEnableOption "DNS resolver";
442 socksListenAddress = mkOption {
444 - default = "127.0.0.1:9050";
445 - example = "192.168.0.1:9100";
446 + type = optionSOCKSPort false;
447 + default = {addr = "127.0.0.1"; port = 9050; IsolateDestAddr = true;};
448 + example = {addr = "192.168.0.1"; port = 9090; IsolateDestAddr = true;};
450 Bind to this address to listen for connections from
451 - Socks-speaking applications. Provides strong circuit
452 - isolation, separate circuit per IP address.
453 + Socks-speaking applications.
457 - socksListenAddressFaster = mkOption {
459 - default = "127.0.0.1:9063";
460 - example = "192.168.0.1:9101";
462 - Bind to this address to listen for connections from
463 - Socks-speaking applications. Same as
464 - <option>socksListenAddress</option> but uses weaker
465 - circuit isolation to provide performance suitable for a
470 - socksPolicy = mkOption {
471 - type = types.nullOr types.str;
473 - example = "accept 192.168.0.0/16, reject *";
475 - Entry policies to allow/deny SOCKS requests based on IP
476 - address. First entry that matches wins. If no SocksPolicy
477 - is set, we accept all (and only) requests from
478 - <option>socksListenAddress</option>.
482 - socksIsolationOptions = mkOption (isolationOptions // {
483 - default = ["IsolateDestAddr"];
486 - transparentProxy = {
487 - enable = mkOption {
490 - description = "Whether to enable tor transparent proxy";
493 - listenAddress = mkOption {
495 - default = "127.0.0.1:9040";
496 - example = "192.168.0.1:9040";
498 - Bind transparent proxy to this address.
502 - isolationOptions = mkOption isolationOptions;
506 - enable = mkOption {
509 - description = "Whether to enable tor dns resolver";
512 - listenAddress = mkOption {
514 - default = "127.0.0.1:9053";
515 - example = "192.168.0.1:9053";
517 - Bind tor dns to this address.
521 - isolationOptions = mkOption isolationOptions;
523 - automapHostsSuffixes = mkOption {
524 - type = types.listOf types.str;
525 - default = [".onion" ".exit"];
526 - example = [".onion"];
527 - description = "List of suffixes to use with automapHostsOnResolve";
528 + onionServices = mkOption {
529 + description = descriptionGeneric "HiddenServiceDir";
532 + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" = {
533 + clientAuthorizations = ["/run/keys/tor/alice.prv.x25519"];
536 + type = types.attrsOf (types.submodule ({name, config, ...}: {
537 + options.clientAuthorizations = mkOption {
539 + Clients' authorizations for a v3 hidden service,
540 + as a list of files containing each one private key, in the format:
541 + <screen>descriptor:x25519:<base32-private-key></screen>
542 + '' + descriptionGeneric "_client_authorization";
543 + type = with types; listOf path;
545 + example = ["/run/keys/tor/alice.prv.x25519"];
552 - enable = mkOption {
556 - Whether to enable relaying TOR traffic for others.
557 + enable = mkEnableOption ''relaying of Tor traffic for others.
559 - See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
561 + See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
564 - Setting this to true requires setting
565 - <option>services.tor.relay.role</option>
567 - <option>services.tor.relay.port</option>
571 + Setting this to true requires setting
572 + <option>services.tor.relay.role</option>
574 + <option>services.tor.settings.ORPort</option>
578 type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
579 @@ -310,13 +312,13 @@ in
581 Running an exit relay may expose you to abuse
583 - <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
584 + <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies"/>
589 You can specify which services Tor users may access via
590 - your exit relay using <option>exitPolicy</option> option.
591 + your exit relay using <option>settings.ExitPolicy</option> option.
595 @@ -369,15 +371,14 @@ in
598 WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE.
599 - Consult with your lawer when in doubt.
600 + Consult with your lawyer when in doubt.
604 This role should be safe to use in most situations
605 (unless the act of forwarding traffic for others is
606 a punishable offence under your local laws, which
607 - would be pretty insane as it would make ISP
609 + would be pretty insane as it would make ISP illegal).
613 @@ -404,7 +405,7 @@ in
616 Use this if you want to run a private bridge, for
617 - example because you'll give out your bridge address
618 + example because you'll give out your bridge addr
619 manually to your friends.
622 @@ -426,269 +427,393 @@ in
626 - bridgeTransports = mkOption {
627 - type = types.listOf types.str;
628 - default = ["obfs4"];
629 - example = ["obfs2" "obfs3" "obfs4" "scramblesuit"];
630 - description = "List of pluggable transports";
633 - nickname = mkOption {
635 - default = "anonymous";
637 - A unique handle for your TOR relay.
641 - contactInfo = mkOption {
642 - type = types.nullOr types.str;
644 - example = "admin@relay.com";
646 - Contact information for the relay owner (e.g. a mail
647 - address and GPG key ID).
651 - accountingMax = mkOption {
652 - type = types.nullOr types.str;
654 - example = "450 GBytes";
656 - Specify maximum bandwidth allowed during an accounting period. This
657 - allows you to limit overall tor bandwidth over some time period.
658 - See the <literal>AccountingMax</literal> option by looking at the
659 - tor manual <citerefentry><refentrytitle>tor</refentrytitle>
660 - <manvolnum>1</manvolnum></citerefentry> for more.
662 - Note this limit applies individually to upload and
663 - download; if you specify <literal>"500 GBytes"</literal>
664 - here, then you may transfer up to 1 TBytes of overall
665 - bandwidth (500 GB upload, 500 GB download).
669 - accountingStart = mkOption {
670 - type = types.nullOr types.str;
672 - example = "month 1 1:00";
674 - Specify length of an accounting period. This allows you to limit
675 - overall tor bandwidth over some time period. See the
676 - <literal>AccountingStart</literal> option by looking at the tor
677 - manual <citerefentry><refentrytitle>tor</refentrytitle>
678 - <manvolnum>1</manvolnum></citerefentry> for more.
682 - bandwidthRate = mkOption {
683 - type = types.nullOr types.int;
687 - Specify this to limit the bandwidth usage of relayed (server)
688 - traffic. Your own traffic is still unthrottled. Units: bytes/second.
692 - bandwidthBurst = mkOption {
693 - type = types.nullOr types.int;
694 - default = cfg.relay.bandwidthRate;
697 - Specify this to allow bursts of the bandwidth usage of relayed (server)
698 - traffic. The average usage will still be as specified in relayBandwidthRate.
699 - Your own traffic is still unthrottled. Units: bytes/second.
703 - address = mkOption {
704 - type = types.nullOr types.str;
706 - example = "noname.example.com";
708 - The IP address or full DNS name for advertised address of your relay.
709 - Leave unset and Tor will guess.
714 - type = types.either types.int types.str;
717 - What port to advertise for Tor connections. This corresponds to the
718 - <literal>ORPort</literal> section in the Tor manual; see
719 - <citerefentry><refentrytitle>tor</refentrytitle>
720 - <manvolnum>1</manvolnum></citerefentry> for more details.
722 - At a minimum, you should just specify the port for the
723 - relay to listen on; a common one like 143, 22, 80, or 443
724 - to help Tor users who may have very restrictive port-based
729 - exitPolicy = mkOption {
730 - type = types.nullOr types.str;
732 - example = "accept *:6660-6667,reject *:*";
734 - A comma-separated list of exit policies. They're
735 - considered first to last, and the first match wins. If you
736 - want to _replace_ the default exit policy, end this with
737 - either a reject *:* or an accept *:*. Otherwise, you're
738 - _augmenting_ (prepending to) the default exit policy.
739 - Leave commented to just use the default, which is
740 - available in the man page or at
741 - <link xlink:href="https://www.torproject.org/documentation.html" />.
744 - <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" />
745 - for issues you might encounter if you use the default
748 - If certain IPs and ports are blocked externally, e.g. by
749 - your firewall, you should update your exit policy to
750 - reflect this -- otherwise Tor users will be told that
751 - those destinations are down.
753 + onionServices = mkOption {
754 + description = descriptionGeneric "HiddenServiceDir";
757 + "example.org/www" = {
759 + authorizedClients = [
760 + "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
764 + type = types.attrsOf (types.submodule ({name, config, ...}: {
765 + options.path = mkOption {
768 + Path where to store the data files of the hidden service.
769 + If the <option>secretKey</option> is null
770 + this defaults to <literal>${stateDir}/onion/$onion</literal>,
771 + otherwise to <literal>${runDir}/onion/$onion</literal>.
774 + options.secretKey = mkOption {
775 + type = with types; nullOr path;
777 + example = "/run/keys/tor/onion/expyuzz4wqqyqhjn/hs_ed25519_secret_key";
779 + Secret key of the onion service.
780 + If null, Tor reuses any preexisting secret key (in <option>path</option>)
781 + or generates a new one.
782 + The associated public key and hostname are deterministically regenerated
783 + from this file if they do not exist.
786 + options.authorizeClient = mkOption {
787 + description = descriptionGeneric "HiddenServiceAuthorizeClient";
789 + type = types.nullOr (types.submodule ({...}: {
791 + authType = mkOption {
792 + type = types.enum [ "basic" "stealth" ];
794 + Either <literal>"basic"</literal> for a general-purpose authorization protocol
795 + or <literal>"stealth"</literal> for a less scalable protocol
796 + that also hides service activity from unauthorized clients.
799 + clientNames = mkOption {
800 + type = with types; nonEmptyListOf (strMatching "[A-Za-z0-9+-_]+");
802 + Only clients that are listed here are authorized to access the hidden service.
803 + Generated authorization data can be found in <filename>${stateDir}/onion/$name/hostname</filename>.
804 + Clients need to put this authorization data in their configuration file using
805 + <xref linkend="opt-services.tor.settings.HidServAuth"/>.
811 + options.authorizedClients = mkOption {
813 + Authorized clients for a v3 hidden service,
814 + as a list of public key, in the format:
815 + <screen>descriptor:x25519:<base32-public-key></screen>
816 + '' + descriptionGeneric "_client_authorization";
817 + type = with types; listOf str;
819 + example = ["descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"];
821 + options.map = mkOption {
822 + description = descriptionGeneric "HiddenServicePort";
823 + type = with types; listOf (oneOf [
824 + port (submodule ({...}: {
827 + target = mkOption {
829 + type = nullOr (submodule ({...}: {
832 + addr = optionAddress;
840 + apply = map (v: if isInt v then {port=v; target=null;} else v);
842 + options.version = mkOption {
843 + description = descriptionGeneric "HiddenServiceVersion";
844 + type = with types; nullOr (enum [2 3]);
847 + options.settings = mkOption {
849 + Settings of the onion service.
850 + '' + descriptionGeneric "_hidden_service_options";
852 + type = types.submodule {
853 + freeformType = with types;
854 + (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // {
855 + description = "settings option";
857 + options.HiddenServiceAllowUnknownPorts = optionBool "HiddenServiceAllowUnknownPorts";
858 + options.HiddenServiceDirGroupReadable = optionBool "HiddenServiceDirGroupReadable";
859 + options.HiddenServiceExportCircuitID = mkOption {
860 + description = descriptionGeneric "HiddenServiceExportCircuitID";
861 + type = with types; nullOr (enum ["haproxy"]);
864 + options.HiddenServiceMaxStreams = mkOption {
865 + description = descriptionGeneric "HiddenServiceMaxStreams";
866 + type = with types; nullOr (ints.between 0 65535);
869 + options.HiddenServiceMaxStreamsCloseCircuit = optionBool "HiddenServiceMaxStreamsCloseCircuit";
870 + options.HiddenServiceNumIntroductionPoints = mkOption {
871 + description = descriptionGeneric "HiddenServiceNumIntroductionPoints";
872 + type = with types; nullOr (ints.between 0 20);
875 + options.HiddenServiceSingleHopMode = optionBool "HiddenServiceSingleHopMode";
876 + options.RendPostPeriod = optionString "RendPostPeriod";
880 + path = mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}");
881 + settings.HiddenServiceVersion = config.version;
882 + settings.HiddenServiceAuthorizeClient =
883 + if config.authorizeClient != null then
884 + config.authorizeClient.authType + " " +
885 + concatStringsSep "," config.authorizeClient.clientNames
887 + settings.HiddenServicePort = map (p: mkValueString "" p.port + " " + mkValueString "" p.target) config.map;
893 - hiddenServices = mkOption {
894 + settings = mkOption {
896 - A set of static hidden services that terminate their Tor
897 - circuits at this node.
899 - Every element in this set declares a virtual onion host.
901 - You can specify your onion address by putting corresponding
902 - private key to an appropriate place in ${torDirectory}.
904 - For services without private keys in ${torDirectory} Tor
905 - daemon will generate random key pairs (which implies random
906 - onion addresses) on restart. The latter could take a while,
910 - Hidden services can be useful even if you don't intend to
911 - actually <emphasis>hide</emphasis> them, since they can
912 - also be seen as a kind of NAT traversal mechanism.
914 - E.g. the example will make your sshd, whatever runs on
915 - "8080" and your mail server available from anywhere where
916 - the Tor network is available (which, with the help from
917 - bridges, is pretty much everywhere), even if both client
918 - and server machines are behind NAT you have no control
921 + See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en">torrc manual</link>
925 - example = literalExample ''
926 - { "my-hidden-service-example".map = [
927 - { port = 22; } # map ssh port to this machine's ssh
928 - { port = 80; toPort = 8080; } # map http port to whatever runs on 8080
929 - { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can
933 - type = types.attrsOf (types.submodule ({name, ...}: {
939 - Name of this tor hidden service.
941 - This is purely descriptive.
943 - After restarting Tor daemon you should be able to
944 - find your .onion address in
945 - <literal>${torDirectory}/onion/$name/hostname</literal>.
951 - description = "Port mapping for this hidden service.";
952 - type = types.listOf (types.submodule ({config, ...}: {
956 - type = types.either types.int types.str;
959 - Hidden service port to "bind to".
963 - destination = mkOption {
966 - description = "Forward these connections where?";
969 - toHost = mkOption {
971 - default = "127.0.0.1";
972 - description = "Mapping destination host.";
975 - toPort = mkOption {
976 - type = types.either types.int types.str;
978 - description = "Mapping destination port.";
984 - toPort = mkDefault config.port;
985 - destination = mkDefault "${config.toHost}:${toString config.toPort}";
990 - authorizeClient = mkOption {
992 - description = "If configured, the hidden service is accessible for authorized clients only.";
993 - type = types.nullOr (types.submodule ({...}: {
997 - authType = mkOption {
998 - type = types.enum [ "basic" "stealth" ];
1000 - Either <literal>"basic"</literal> for a general-purpose authorization protocol
1001 - or <literal>"stealth"</literal> for a less scalable protocol
1002 - that also hides service activity from unauthorized clients.
1006 - clientNames = mkOption {
1007 - type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
1009 - Only clients that are listed here are authorized to access the hidden service.
1010 - Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>.
1011 - Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
1018 - version = mkOption {
1020 - description = "Rendezvous service descriptor version to publish for the hidden service. Currently, versions 2 and 3 are supported. (Default: 2)";
1021 - type = types.nullOr (types.enum [ 2 3 ]);
1023 + type = types.submodule {
1024 + freeformType = with types;
1025 + (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // {
1026 + description = "settings option";
1028 + options.Address = optionString "Address";
1029 + options.AssumeReachable = optionBool "AssumeReachable";
1030 + options.AccountingMax = optionBandwith "AccountingMax";
1031 + options.AccountingStart = optionString "AccountingStart";
1032 + options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity";
1033 + options.AuthDirListBadExits = optionBool "AuthDirListBadExits";
1034 + options.AuthDirPinKeys = optionBool "AuthDirPinKeys";
1035 + options.AuthDirSharedRandomness = optionBool "AuthDirSharedRandomness";
1036 + options.AuthDirTestEd25519LinkKeys = optionBool "AuthDirTestEd25519LinkKeys";
1037 + options.AuthoritativeDirectory = optionBool "AuthoritativeDirectory";
1038 + options.AutomapHostsOnResolve = optionBool "AutomapHostsOnResolve";
1039 + options.AutomapHostsSuffixes = optionStrings "AutomapHostsSuffixes" // {
1040 + default = [".onion" ".exit"];
1041 + example = [".onion"];
1045 - name = mkDefault name;
1046 + options.BandwidthBurst = optionBandwith "BandwidthBurst";
1047 + options.BandwidthRate = optionBandwith "BandwidthRate";
1048 + options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir";
1049 + options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry";
1050 + options.BridgeRelay = optionBool "BridgeRelay" // { default = false; };
1051 + options.CacheDirectory = optionPath "CacheDirectory";
1052 + options.CacheDirectoryGroupReadable = optionBool "CacheDirectoryGroupReadable"; # default is null and like "auto"
1053 + options.CellStatistics = optionBool "CellStatistics";
1054 + options.ClientAutoIPv6ORPort = optionBool "ClientAutoIPv6ORPort";
1055 + options.ClientDNSRejectInternalAddresses = optionBool "ClientDNSRejectInternalAddresses";
1056 + options.ClientOnionAuthDir = mkOption {
1057 + description = descriptionGeneric "ClientOnionAuthDir";
1059 + type = with types; nullOr path;
1062 + options.ClientPreferIPv6DirPort = optionBool "ClientPreferIPv6DirPort"; # default is null and like "auto"
1063 + options.ClientPreferIPv6ORPort = optionBool "ClientPreferIPv6ORPort"; # default is null and like "auto"
1064 + options.ClientRejectInternalAddresses = optionBool "ClientRejectInternalAddresses";
1065 + options.ClientUseIPv4 = optionBool "ClientUseIPv4";
1066 + options.ClientUseIPv6 = optionBool "ClientUseIPv6";
1067 + options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics";
1068 + options.ConstrainedSockets = optionBool "ConstrainedSockets";
1069 + options.ContactInfo = optionString "ContactInfo";
1070 + options.ControlPort = mkOption rec {
1071 + description = descriptionGeneric "ControlPort";
1073 + example = [{port = 9051;}];
1074 + type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [
1075 + port (enum ["auto"]) (submodule ({config, ...}: let
1076 + flags = ["GroupWritable" "RelaxDirModeCheck" "WorldWritable"];
1079 + unix = optionUnix;
1080 + flags = optionFlags;
1081 + addr = optionAddress;
1082 + port = optionPort;
1083 + } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
1085 + flags = filter (name: config.${name} == true) flags;
1090 + options.ControlPortFileGroupReadable= optionBool "ControlPortFileGroupReadable";
1091 + options.ControlPortWriteToFile = optionPath "ControlPortWriteToFile";
1092 + options.ControlSocket = optionPath "ControlSocket";
1093 + options.ControlSocketsGroupWritable = optionBool "ControlSocketsGroupWritable";
1094 + options.CookieAuthFile = optionPath "CookieAuthFile";
1095 + options.CookieAuthFileGroupReadable = optionBool "CookieAuthFileGroupReadable";
1096 + options.CookieAuthentication = optionBool "CookieAuthentication";
1097 + options.DataDirectory = optionPath "DataDirectory" // { default = stateDir; };
1098 + options.DataDirectoryGroupReadable = optionBool "DataDirectoryGroupReadable";
1099 + options.DirPortFrontPage = optionPath "DirPortFrontPage";
1100 + options.DirAllowPrivateAddresses = optionBool "DirAllowPrivateAddresses";
1101 + options.DormantCanceledByStartup = optionBool "DormantCanceledByStartup";
1102 + options.DormantOnFirstStartup = optionBool "DormantOnFirstStartup";
1103 + options.DormantTimeoutDisabledByIdleStreams = optionBool "DormantTimeoutDisabledByIdleStreams";
1104 + options.DirCache = optionBool "DirCache";
1105 + options.DirPolicy = mkOption {
1106 + description = descriptionGeneric "DirPolicy";
1107 + type = with types; listOf str;
1109 + example = ["accept *:*"];
1111 + options.DirPort = optionORPort "DirPort";
1112 + options.DirReqStatistics = optionBool "DirReqStatistics";
1113 + options.DisableAllSwap = optionBool "DisableAllSwap";
1114 + options.DisableDebuggerAttachment = optionBool "DisableDebuggerAttachment";
1115 + options.DisableNetwork = optionBool "DisableNetwork";
1116 + options.DisableOOSCheck = optionBool "DisableOOSCheck";
1117 + options.DNSPort = optionIsolablePorts "DNSPort";
1118 + options.DoSCircuitCreationEnabled = optionBool "DoSCircuitCreationEnabled";
1119 + options.DoSConnectionEnabled = optionBool "DoSConnectionEnabled"; # default is null and like "auto"
1120 + options.DoSRefuseSingleHopClientRendezvous = optionBool "DoSRefuseSingleHopClientRendezvous";
1121 + options.DownloadExtraInfo = optionBool "DownloadExtraInfo";
1122 + options.EnforceDistinctSubnets = optionBool "EnforceDistinctSubnets";
1123 + options.EntryStatistics = optionBool "EntryStatistics";
1124 + options.ExitPolicy = optionStrings "ExitPolicy" // {
1125 + default = ["reject *:*"];
1126 + example = ["accept *:*"];
1128 + options.ExitPolicyRejectLocalInterfaces = optionBool "ExitPolicyRejectLocalInterfaces";
1129 + options.ExitPolicyRejectPrivate = optionBool "ExitPolicyRejectPrivate";
1130 + options.ExitPortStatistics = optionBool "ExitPortStatistics";
1131 + options.ExitRelay = optionBool "ExitRelay"; # default is null and like "auto"
1132 + options.ExtORPort = mkOption {
1133 + description = descriptionGeneric "ExtORPort";
1135 + type = with types; nullOr (oneOf [
1136 + port (enum ["auto"]) (submodule ({...}: {
1138 + addr = optionAddress;
1139 + port = optionPort;
1143 + apply = p: if isInt p || isString p then { port = p; } else p;
1145 + options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile";
1146 + options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable";
1147 + options.ExtendAllowPrivateAddresses = optionBool "ExtendAllowPrivateAddresses";
1148 + options.ExtraInfoStatistics = optionBool "ExtraInfoStatistics";
1149 + options.FascistFirewall = optionBool "FascistFirewall";
1150 + options.FetchDirInfoEarly = optionBool "FetchDirInfoEarly";
1151 + options.FetchDirInfoExtraEarly = optionBool "FetchDirInfoExtraEarly";
1152 + options.FetchHidServDescriptors = optionBool "FetchHidServDescriptors";
1153 + options.FetchServerDescriptors = optionBool "FetchServerDescriptors";
1154 + options.FetchUselessDescriptors = optionBool "FetchUselessDescriptors";
1155 + options.ReachableAddresses = optionStrings "ReachableAddresses";
1156 + options.ReachableDirAddresses = optionStrings "ReachableDirAddresses";
1157 + options.ReachableORAddresses = optionStrings "ReachableORAddresses";
1158 + options.GeoIPFile = optionPath "GeoIPFile";
1159 + options.GeoIPv6File = optionPath "GeoIPv6File";
1160 + options.GuardfractionFile = optionPath "GuardfractionFile";
1161 + options.HidServAuth = mkOption {
1162 + description = descriptionGeneric "HidServAuth";
1164 + type = with types; listOf (oneOf [
1167 + onion = mkOption {
1168 + type = strMatching "[a-z2-7]{16}(\\.onion)?";
1169 + description = "Onion address.";
1170 + example = "xxxxxxxxxxxxxxxx.onion";
1173 + type = strMatching "[A-Za-z0-9+/]{22}";
1174 + description = "Authentication cookie.";
1180 + options.HiddenServiceNonAnonymousMode = optionBool "HiddenServiceNonAnonymousMode";
1181 + options.HiddenServiceStatistics = optionBool "HiddenServiceStatistics";
1182 + options.HSLayer2Nodes = optionStrings "HSLayer2Nodes";
1183 + options.HSLayer3Nodes = optionStrings "HSLayer3Nodes";
1184 + options.HTTPTunnelPort = optionIsolablePorts "HTTPTunnelPort";
1185 + options.IPv6Exit = optionBool "IPv6Exit";
1186 + options.KeyDirectory = optionPath "KeyDirectory";
1187 + options.KeyDirectoryGroupReadable = optionBool "KeyDirectoryGroupReadable";
1188 + options.LogMessageDomains = optionBool "LogMessageDomains";
1189 + options.LongLivedPorts = optionPorts "LongLivedPorts";
1190 + options.MainloopStats = optionBool "MainloopStats";
1191 + options.MaxAdvertisedBandwidth = optionBandwith "MaxAdvertisedBandwidth";
1192 + options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness";
1193 + options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending";
1194 + options.NATDPort = optionIsolablePorts "NATDPort";
1195 + options.NewCircuitPeriod = optionInt "NewCircuitPeriod";
1196 + options.Nickname = optionString "Nickname";
1197 + options.ORPort = optionORPort "ORPort";
1198 + options.OfflineMasterKey = optionBool "OfflineMasterKey";
1199 + options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto"
1200 + options.PaddingStatistics = optionBool "PaddingStatistics";
1201 + options.PerConnBWBurst = optionBandwith "PerConnBWBurst";
1202 + options.PerConnBWRate = optionBandwith "PerConnBWRate";
1203 + options.PidFile = optionPath "PidFile";
1204 + options.ProtocolWarnings = optionBool "ProtocolWarnings";
1205 + options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors";
1206 + options.PublishServerDescriptor = mkOption {
1207 + description = descriptionGeneric "PublishServerDescriptor";
1208 + type = with types; nullOr (enum [false true 0 1 "0" "1" "v3" "bridge"]);
1211 + options.ReducedExitPolicy = optionBool "ReducedExitPolicy";
1212 + options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto"
1213 + options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts";
1214 + options.RelayBandwidthBurst = optionBandwith "RelayBandwidthBurst";
1215 + options.RelayBandwidthRate = optionBandwith "RelayBandwidthRate";
1216 + #options.RunAsDaemon
1217 + options.Sandbox = optionBool "Sandbox";
1218 + options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig";
1219 + options.ServerDNSAllowNonRFC953Hostnames = optionBool "ServerDNSAllowNonRFC953Hostnames";
1220 + options.ServerDNSDetectHijacking = optionBool "ServerDNSDetectHijacking";
1221 + options.ServerDNSRandomizeCase = optionBool "ServerDNSRandomizeCase";
1222 + options.ServerDNSResolvConfFile = optionPath "ServerDNSResolvConfFile";
1223 + options.ServerDNSSearchDomains = optionBool "ServerDNSSearchDomains";
1224 + options.ServerTransportPlugin = mkOption {
1225 + description = descriptionGeneric "ServerTransportPlugin";
1227 + type = with types; nullOr (submodule ({...}: {
1229 + transports = mkOption {
1230 + description = "List of pluggable transports.";
1231 + type = listOf str;
1232 + example = ["obfs2" "obfs3" "obfs4" "scramblesuit"];
1236 + description = "Command of pluggable transport.";
1241 + options.SocksPolicy = optionStrings "SocksPolicy" // {
1242 + example = ["accept *:*"];
1244 + options.SOCKSPort = mkOption {
1245 + description = descriptionGeneric "SOCKSPort";
1246 + default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else [];
1247 + example = [{port = 9090;}];
1248 + type = types.listOf (optionSOCKSPort true);
1250 + options.TestingTorNetwork = optionBool "TestingTorNetwork";
1251 + options.TransPort = optionIsolablePorts "TransPort";
1252 + options.TransProxyType = mkOption {
1253 + description = descriptionGeneric "TransProxyType";
1254 + type = with types; nullOr (enum ["default" "TPROXY" "ipfw" "pf-divert"]);
1257 + #options.TruncateLogFile
1258 + options.UnixSocksGroupWritable = optionBool "UnixSocksGroupWritable";
1259 + options.UseDefaultFallbackDirs = optionBool "UseDefaultFallbackDirs";
1260 + options.UseMicrodescriptors = optionBool "UseMicrodescriptors";
1261 + options.V3AuthUseLegacyKey = optionBool "V3AuthUseLegacyKey";
1262 + options.V3AuthoritativeDirectory = optionBool "V3AuthoritativeDirectory";
1263 + options.VersioningAuthoritativeDirectory = optionBool "VersioningAuthoritativeDirectory";
1264 + options.VirtualAddrNetworkIPv4 = optionString "VirtualAddrNetworkIPv4";
1265 + options.VirtualAddrNetworkIPv6 = optionString "VirtualAddrNetworkIPv6";
1266 + options.WarnPlaintextPorts = optionPorts "WarnPlaintextPorts";
1271 @@ -696,79 +821,217 @@ in
1272 config = mkIf cfg.enable {
1273 # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
1274 # sends a lot of stats
1275 - warnings = optional (cfg.relay.enable && cfg.hiddenServices != {})
1276 + warnings = optional (cfg.settings.BridgeRelay &&
1277 + flatten (mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != [])
1279 Running Tor hidden services on a public relay makes the
1280 presence of hidden services visible through simple statistical
1281 analysis of publicly available data.
1282 + See https://trac.torproject.org/projects/tor/ticket/8742
1284 You can safely ignore this warning if you don't intend to
1285 actually hide your hidden services. In either case, you can
1286 always create a container/VM with a separate Tor daemon instance.
1289 + flatten (mapAttrsToList (n: o:
1290 + optional (o.settings.HiddenServiceVersion == 2) [
1291 + (optional (o.settings.HiddenServiceExportCircuitID != null) ''
1292 + HiddenServiceExportCircuitID is used in the HiddenService: ${n}
1293 + but this option is only for v3 hidden services.
1296 + optional (o.settings.HiddenServiceVersion != 2) [
1297 + (optional (o.settings.HiddenServiceAuthorizeClient != null) ''
1298 + HiddenServiceAuthorizeClient is used in the HiddenService: ${n}
1299 + but this option is only for v2 hidden services.
1301 + (optional (o.settings.RendPostPeriod != null) ''
1302 + RendPostPeriod is used in the HiddenService: ${n}
1303 + but this option is only for v2 hidden services.
1306 + ) cfg.relay.onionServices);
1308 users.groups.tor.gid = config.ids.gids.tor;
1310 { description = "Tor Daemon User";
1312 - home = torDirectory;
1315 uid = config.ids.uids.tor;
1318 - # We have to do this instead of using RuntimeDirectory option in
1319 - # the service below because systemd has no way to set owners of
1320 - # RuntimeDirectory and putting this into the service below
1321 - # requires that service to relax it's sandbox since this needs
1323 - systemd.services.tor-init =
1324 - { description = "Tor Daemon Init";
1325 - wantedBy = [ "tor.service" ];
1327 - install -m 0700 -o tor -g tor -d ${torDirectory} ${torDirectory}/onion
1328 - install -m 0750 -o tor -g tor -d ${torRunDirectory}
1332 - RemainAfterExit = true;
1336 - systemd.services.tor =
1337 - { description = "Tor Daemon";
1338 - path = [ pkgs.tor ];
1340 - wantedBy = [ "multi-user.target" ];
1341 - after = [ "tor-init.service" "network.target" ];
1342 - restartTriggers = [ torRcFile ];
1345 - { Type = "simple";
1346 - # Translated from the upstream contrib/dist/tor.service.in
1347 - ExecStartPre = "${cfg.package}/bin/tor -f ${torRcFile} --verify-config";
1348 - ExecStart = "${cfg.package}/bin/tor -f ${torRcFile}";
1349 - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
1350 - KillSignal = "SIGINT";
1352 - Restart = "on-failure";
1353 - LimitNOFILE = 32768;
1356 - # this seems to unshare /run despite what systemd.exec(5) says
1357 - PrivateTmp = mkIf (!cfg.controlSocket.enable) "yes";
1358 - PrivateDevices = "yes";
1359 - ProtectHome = "yes";
1360 - ProtectSystem = "strict";
1361 - InaccessiblePaths = "/home";
1362 - ReadOnlyPaths = "/";
1363 - ReadWritePaths = [ torDirectory torRunDirectory ];
1364 - NoNewPrivileges = "yes";
1366 - # tor.service.in has this in, but this line it fails to spawn a namespace when using hidden services
1367 - #CapabilityBoundingSet = "CAP_SETUID CAP_SETGID CAP_NET_BIND_SERVICE";
1369 + services.tor.settings = mkMerge [
1370 + (mkIf cfg.enableGeoIP {
1371 + GeoIPFile = "${cfg.package.geoip}/share/tor/geoip";
1372 + GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6";
1374 + (mkIf cfg.controlSocket.enable {
1375 + ControlPort = [ { unix = runDir + "/control"; GroupWritable=true; RelaxDirModeCheck=true; } ];
1377 + (mkIf cfg.relay.enable (
1378 + optionalAttrs (cfg.relay.role != "exit") {
1379 + ExitPolicy = mkForce ["reject *:*"];
1381 + optionalAttrs (elem cfg.relay.role ["bridge" "private-bridge"]) {
1382 + BridgeRelay = true;
1383 + ExtORPort.port = mkDefault "auto";
1384 + ServerTransportPlugin.transports = mkDefault ["obfs4"];
1385 + ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed";
1386 + } // optionalAttrs (cfg.relay.role == "private-bridge") {
1387 + ExtraInfoStatistics = false;
1388 + PublishServerDescriptor = false;
1391 + (mkIf (!cfg.relay.enable) {
1392 + # Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings,
1393 + # because it would still enable Tor as a relay,
1394 + # which can trigger all sort of problems when not carefully done,
1395 + # like the blocklisting of the machine's IP addresses
1396 + # by some hosting providers...
1397 + DirPort = mkForce [];
1398 + ORPort = mkForce [];
1399 + PublishServerDescriptor = mkForce false;
1401 + (mkIf cfg.client.enable (
1402 + { SOCKSPort = [ cfg.client.socksListenAddress ];
1403 + } // optionalAttrs cfg.client.transparentProxy.enable {
1404 + TransPort = [{ addr = "127.0.0.1"; port = 9040; }];
1405 + } // optionalAttrs cfg.client.dns.enable {
1406 + DNSPort = [{ addr = "127.0.0.1"; port = 9053; }];
1407 + AutomapHostsOnResolve = true;
1408 + AutomapHostsSuffixes = cfg.client.dns.automapHostsSuffixes;
1409 + } // optionalAttrs (flatten (mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != []) {
1410 + ClientOnionAuthDir = runDir + "/ClientOnionAuthDir";
1415 + networking.firewall = mkIf cfg.openFirewall {
1417 + concatMap (o: optional (isInt o && o > 0 || o ? "port" && isInt o.port && o.port > 0) o.port)
1419 + cfg.settings.ORPort
1420 + cfg.settings.DirPort
1424 + systemd.services.tor = {
1425 + description = "Tor Daemon";
1426 + path = [ pkgs.tor ];
1428 + wantedBy = [ "multi-user.target" ];
1429 + after = [ "network.target" ];
1430 + restartTriggers = [ torrc ];
1437 + "${cfg.package}/bin/tor -f ${torrc} --verify-config"
1438 + # DOC: Appendix G of https://spec.torproject.org/rend-spec-v3
1439 + ("+" + pkgs.writeShellScript "ExecStartPre" (concatStringsSep "\n" (flatten (["set -eu"] ++
1440 + mapAttrsToList (name: onion:
1441 + optional (onion.authorizedClients != []) ''
1442 + rm -rf ${escapeShellArg onion.path}/authorized_clients
1443 + install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} ${escapeShellArg onion.path}/authorized_clients
1445 + imap0 (i: pubKey: ''
1447 + install -o tor -g tor -m 0400 /dev/stdin ${escapeShellArg onion.path}/authorized_clients/${toString i}.auth
1448 + '') onion.authorizedClients ++
1449 + optional (onion.secretKey != null) ''
1450 + install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path}
1451 + key="$(cut -f1 -d: ${escapeShellArg onion.secretKey})"
1453 + ("== ed25519v"*"-secret")
1454 + install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;;
1455 + (*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;;
1458 + ) cfg.relay.onionServices ++
1459 + mapAttrsToList (name: onion: imap0 (i: prvKeyPath:
1460 + let hostname = removeSuffix ".onion" name; in ''
1461 + printf "%s:" ${escapeShellArg hostname} | cat - ${escapeShellArg prvKeyPath} |
1462 + install -o tor -g tor -m 0700 /dev/stdin \
1463 + ${runDir}/ClientOnionAuthDir/${escapeShellArg hostname}.${toString i}.auth_private
1464 + '') onion.clientAuthorizations)
1465 + cfg.client.onionServices
1468 + ExecStart = "${cfg.package}/bin/tor -f ${torrc}";
1469 + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
1470 + KillSignal = "SIGINT";
1472 + Restart = "on-failure";
1473 + LimitNOFILE = 32768;
1474 + RuntimeDirectory = [
1475 + # g+x allows access to the control socket
1478 + # g+x can't be removed in ExecStart=, but will be removed by Tor
1479 + "tor/ClientOnionAuthDir"
1481 + RuntimeDirectoryMode = "0710";
1482 + StateDirectoryMode = "0700";
1483 + StateDirectory = [
1487 + flatten (mapAttrsToList (name: onion:
1488 + optional (onion.secretKey == null) "tor/onion/${name}"
1489 + ) cfg.relay.onionServices);
1490 + # The following options are only to optimize:
1491 + # systemd-analyze security tor
1492 + RootDirectory = runDir + "/root";
1493 + RootDirectoryStartOnly = true;
1494 + #InaccessiblePaths = [ "-+${runDir}/root" ];
1496 + BindPaths = [ stateDir ];
1497 + BindReadOnlyPaths = [ storeDir "/etc" ];
1498 + AmbientCapabilities = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
1499 + CapabilityBoundingSet = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
1500 + # ProtectClock= adds DeviceAllow=char-rtc r
1502 + LockPersonality = true;
1503 + MemoryDenyWriteExecute = true;
1504 + NoNewPrivileges = true;
1505 + PrivateDevices = true;
1506 + PrivateMounts = true;
1507 + PrivateNetwork = mkDefault false;
1508 + PrivateTmp = true;
1509 + # Tor cannot currently bind privileged port when PrivateUsers=true,
1510 + # see https://gitlab.torproject.org/legacy/trac/-/issues/20930
1511 + PrivateUsers = !bindsPrivilegedPort;
1512 + ProtectClock = true;
1513 + ProtectControlGroups = true;
1514 + ProtectHome = true;
1515 + ProtectHostname = true;
1516 + ProtectKernelLogs = true;
1517 + ProtectKernelModules = true;
1518 + ProtectKernelTunables = true;
1519 + ProtectSystem = "strict";
1521 + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
1522 + RestrictNamespaces = true;
1523 + RestrictRealtime = true;
1524 + RestrictSUIDSGID = true;
1525 + # See also the finer but experimental option settings.Sandbox
1526 + SystemCallFilter = [
1528 + # Groups in @system-service which do not contain a syscall listed by:
1529 + # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' tor
1530 + # in tests, and seem likely not necessary for tor.
1531 + "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer"
1533 + SystemCallArchitectures = "native";
1534 + SystemCallErrorNumber = "EPERM";
1538 environment.systemPackages = [ cfg.package ];
1541 + meta.maintainers = with lib.maintainers; [ julm ];
1543 diff --git a/nixos/tests/tor.nix b/nixos/tests/tor.nix
1544 index ad07231557c..c061f59226c 100644
1545 --- a/nixos/tests/tor.nix
1546 +++ b/nixos/tests/tor.nix
1547 @@ -17,7 +17,7 @@ rec {
1548 environment.systemPackages = with pkgs; [ netcat ];
1549 services.tor.enable = true;
1550 services.tor.client.enable = true;
1551 - services.tor.controlPort = 9051;
1552 + services.tor.settings.ControlPort = 9051;
1556 diff --git a/pkgs/tools/security/tor/default.nix b/pkgs/tools/security/tor/default.nix
1557 index 04bf598d132..e46fd4790a3 100644
1558 --- a/pkgs/tools/security/tor/default.nix
1559 +++ b/pkgs/tools/security/tor/default.nix
1561 { stdenv, fetchurl, pkgconfig, libevent, openssl, zlib, torsocks
1562 , libseccomp, systemd, libcap, lzma, zstd, scrypt, nixosTests
1572 + tor-client-auth-gen = writeShellScript "tor-client-auth-gen" ''
1573 + PATH="${stdenv.lib.makeBinPath [coreutils gnugrep openssl]}"
1574 + pem="$(openssl genpkey -algorithm x25519)"
1576 + printf private_key=descriptor:x25519:
1577 + echo "$pem" | grep -v " PRIVATE KEY" |
1578 + base64 -d | tail --bytes=32 | base32 | tr -d =
1580 + printf public_key=descriptor:x25519:
1581 + echo "$pem" | openssl pkey -in /dev/stdin -pubout |
1582 + grep -v " PUBLIC KEY" |
1583 + base64 -d | tail --bytes=32 | base32 | tr -d =
1586 stdenv.mkDerivation rec {
1588 version = "0.4.4.6";
1589 @@ -52,6 +67,7 @@ stdenv.mkDerivation rec {
1590 mkdir -p $geoip/share/tor
1591 mv $out/share/tor/geoip{,6} $geoip/share/tor
1592 rm -rf $out/share/tor
1593 + ln -s ${tor-client-auth-gen} $out/bin/tor-client-auth-gen