summaryrefslogtreecommitdiffstats
path: root/nixos
diff options
context:
space:
mode:
authorJörg Thalheim <Mic92@users.noreply.github.com>2021-01-05 16:00:40 +0000
committerGitHub <noreply@github.com>2021-01-05 16:00:40 +0000
commita14ea3aeccc83f2545bbce12cf47638c8b87d8c4 (patch)
tree7758386057a645767b32869f2dcfb309d8b40001 /nixos
parent99bfa4bb601bfc94a72296f0b2395a4f888e5d84 (diff)
parent0ccdd6f2b043e5123ffd1f76cd2187c39ce19b94 (diff)
Merge pull request #97740 from ju1m/tor
nixos/tor: improve type-checking and hardening
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2103.xml10
-rw-r--r--nixos/modules/services/networking/privoxy.nix7
-rw-r--r--nixos/modules/services/security/tor.nix1390
-rw-r--r--nixos/tests/tor.nix2
4 files changed, 844 insertions, 565 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml
index dbc4ecd6930e..05daca1d710c 100644
--- a/nixos/doc/manual/release-notes/rl-2103.xml
+++ b/nixos/doc/manual/release-notes/rl-2103.xml
@@ -287,6 +287,16 @@
</programlisting>
</listitem>
<listitem>
+ <para>
+ The <literal>services.tor</literal> module has a new exhaustively typed <xref linkend="opt-services.tor.settings" /> option following RFC 0042; backward compatibility with old options has been preserved when aliasing was possible.
+ The corresponding systemd service has been hardened,
+ but there is a chance that the service still requires more permissions,
+ so please report any related trouble on the bugtracker.
+ Onion services v3 are now supported in <xref linkend="opt-services.tor.relay.onionServices" />.
+ A new <xref linkend="opt-services.tor.openFirewall" /> option as been introduced for allowing connections on all the TCP ports configured.
+ </para>
+ </listitem>
+ <listitem>
<para>
The options <literal>services.slurm.dbdserver.storagePass</literal>
and <literal>services.slurm.dbdserver.configFile</literal> have been removed.
diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
index e3b34cb0c616..7caae3282032 100644
--- a/nixos/modules/services/networking/privoxy.nix
+++ b/nixos/modules/services/networking/privoxy.nix
@@ -16,7 +16,7 @@ let
${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles}
${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles}
'' + optionalString cfg.enableTor ''
- forward-socks4a / ${config.services.tor.client.socksListenAddressFaster} .
+ forward-socks5t / 127.0.0.1:9063 .
toggle 1
enable-remote-toggle 0
enable-edit-actions 0
@@ -123,6 +123,11 @@ in
serviceConfig.ProtectSystem = "full";
};
+ services.tor.settings.SOCKSPort = mkIf cfg.enableTor [
+ # Route HTTP traffic over a faster port (without IsolateDestAddr).
+ { addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; }
+ ];
+
};
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 1cceee065b1b..1002dacc7f25 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -1,297 +1,300 @@
{ config, lib, pkgs, ... }:
+with builtins;
with lib;
let
cfg = config.services.tor;
- torDirectory = "/var/lib/tor";
- torRunDirectory = "/run/tor";
-
- opt = name: value: optionalString (value != null) "${name} ${value}";
- optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}";
-
- isolationOptions = {
- type = types.listOf (types.enum [
- "IsolateClientAddr"
- "IsolateSOCKSAuth"
- "IsolateClientProtocol"
- "IsolateDestPort"
- "IsolateDestAddr"
+ stateDir = "/var/lib/tor";
+ runDir = "/run/tor";
+ descriptionGeneric = option: ''
+ See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en#${option}">torrc manual</link>.
+ '';
+ bindsPrivilegedPort =
+ any (p0:
+ let p1 = if p0 ? "port" then p0.port else p0; in
+ if p1 == "auto" then false
+ else let p2 = if isInt p1 then p1 else toInt p1; in
+ p1 != null && 0 < p2 && p2 < 1024)
+ (flatten [
+ cfg.settings.ORPort
+ cfg.settings.DirPort
+ cfg.settings.DNSPort
+ cfg.settings.ExtORPort
+ cfg.settings.HTTPTunnelPort
+ cfg.settings.NATDPort
+ cfg.settings.SOCKSPort
+ cfg.settings.TransPort
]);
+ optionBool = optionName: mkOption {
+ type = with types; nullOr bool;
+ default = null;
+ description = descriptionGeneric optionName;
+ };
+ optionInt = optionName: mkOption {
+ type = with types; nullOr int;
+ default = null;
+ description = descriptionGeneric optionName;
+ };
+ optionString = optionName: mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = descriptionGeneric optionName;
+ };
+ optionStrings = optionName: mkOption {
+ type = with types; listOf str;
default = [];
- example = [
- "IsolateClientAddr"
- "IsolateSOCKSAuth"
- "IsolateClientProtocol"
- "IsolateDestPort"
- "IsolateDestAddr"
+ description = descriptionGeneric optionName;
+ };
+ optionAddress = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "0.0.0.0";
+ description = ''
+ IPv4 or IPv6 (if between brackets) address.
+ '';
+ };
+ optionUnix = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = ''
+ Unix domain socket path to use.
+ '';
+ };
+ optionPort = mkOption {
+ type = with types; nullOr (oneOf [port (enum ["auto"])]);
+ default = null;
+ };
+ optionPorts = optionName: mkOption {
+ type = with types; listOf port;
+ default = [];
+ description = descriptionGeneric optionName;
+ };
+ optionIsolablePort = with types; oneOf [
+ port (enum ["auto"])
+ (submodule ({config, ...}: {
+ options = {
+ addr = optionAddress;
+ port = optionPort;
+ flags = optionFlags;
+ SessionGroup = mkOption { type = nullOr int; default = null; };
+ } // genAttrs isolateFlags (name: mkOption { type = types.bool; default = false; });
+ config = {
+ flags = filter (name: config.${name} == true) isolateFlags ++
+ optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
+ };
+ }))
+ ];
+ optionIsolablePorts = optionName: mkOption {
+ default = [];
+ type = with types; either optionIsolablePort (listOf optionIsolablePort);
+ description = descriptionGeneric optionName;
+ };
+ isolateFlags = [
+ "IsolateClientAddr"
+ "IsolateClientProtocol"
+ "IsolateDestAddr"
+ "IsolateDestPort"
+ "IsolateSOCKSAuth"
+ "KeepAliveIsolateSOCKSAuth"
+ ];
+ optionSOCKSPort = doConfig: let
+ flags = [
+ "CacheDNS" "CacheIPv4DNS" "CacheIPv6DNS" "GroupWritable" "IPv6Traffic"
+ "NoDNSRequest" "NoIPv4Traffic" "NoOnionTraffic" "OnionTrafficOnly"
+ "PreferIPv6" "PreferIPv6Automap" "PreferSOCKSNoAuth" "UseDNSCache"
+ "UseIPv4Cache" "UseIPv6Cache" "WorldWritable"
+ ] ++ isolateFlags;
+ in with types; oneOf [
+ port (submodule ({config, ...}: {
+ options = {
+ unix = optionUnix;
+ addr = optionAddress;
+ port = optionPort;
+ flags = optionFlags;
+ SessionGroup = mkOption { type = nullOr int; default = null; };
+ } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
+ config = mkIf doConfig { # Only add flags in SOCKSPort to avoid duplicates
+ flags = filter (name: config.${name} == true) flags ++
+ optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
+ };
+ }))
];
- description = "Tor isolation options";
+ optionFlags = mkOption {
+ type = with types; listOf str;
+ default = [];
+ };
+ optionORPort = optionName: mkOption {
+ default = [];
+ example = 443;
+ type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [
+ port
+ (enum ["auto"])
+ (submodule ({config, ...}:
+ let flags = [ "IPv4Only" "IPv6Only" "NoAdvertise" "NoListen" ];
+ in {
+ options = {
+ addr = optionAddress;
+ port = optionPort;
+ flags = optionFlags;
+ } // genAttrs flags (name: mkOption { type = types.bool; default = false; });
+ config = {
+ flags = filter (name: config.${name} == true) flags;
+ };
+ }))
+ ]))];
+ description = descriptionGeneric optionName;
+ };
+ optionBandwith = optionName: mkOption {
+ type = with types; nullOr (either int str);
+ default = null;
+ description = descriptionGeneric optionName;
+ };
+ optionPath = optionName: mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = descriptionGeneric optionName;
};
-
- torRc = ''
- User tor
- DataDirectory ${torDirectory}
- ${optionalString cfg.enableGeoIP ''
- GeoIPFile ${cfg.package.geoip}/share/tor/geoip
- GeoIPv6File ${cfg.package.geoip}/share/tor/geoip6
- ''}
-
- ${optint "ControlPort" cfg.controlPort}
- ${optionalString cfg.controlSocket.enable "ControlPort unix:${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
- ''
- # Client connection config
- + optionalString cfg.client.enable ''
- SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
- SOCKSPort ${cfg.client.socksListenAddressFaster}
- ${opt "SocksPolicy" cfg.client.socksPolicy}
-
- ${optionalString cfg.client.transparentProxy.enable ''
- TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
- ''}
-
- ${optionalString cfg.client.dns.enable ''
- DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
- AutomapHostsOnResolve 1
- AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
- ''}
- ''
- # Explicitly disable the SOCKS server if the client is disabled. In
- # particular, this makes non-anonymous hidden services possible.
- + optionalString (! cfg.client.enable) ''
- SOCKSPort 0
- ''
- # Relay config
- + optionalString cfg.relay.enable ''
- ORPort ${toString cfg.relay.port}
- ${opt "Address" cfg.relay.address}
- ${opt "Nickname" cfg.relay.nickname}
- ${opt "ContactInfo" cfg.relay.contactInfo}
-
- ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
- ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
- ${opt "AccountingMax" cfg.relay.accountingMax}
- ${opt "AccountingStart" cfg.relay.accountingStart}
-
- ${if (cfg.relay.role == "exit") then
- opt "ExitPolicy" cfg.relay.exitPolicy
- else
- "ExitPolicy reject *:*"}
-
- ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
- BridgeRelay 1
- ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed
- ExtORPort auto
- ${optionalString (cfg.relay.role == "private-bridge") ''
- ExtraInfoStatistics 0
- PublishServerDescriptor 0
- ''}
- ''}
- ''
- # Hidden services
- + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
- HiddenServiceDir ${torDirectory}/onion/${v.name}
- ${optionalString (v.version != null) "HiddenServiceVersion ${toString v.version}"}
- ${flip concatMapStrings v.map (p: ''
- HiddenServicePort ${toString p.port} ${p.destination}
- '')}
- ${optionalString (v.authorizeClient != null) ''
- HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
- ''}
- ''))
- + cfg.extraConfig;
-
- torRcFile = pkgs.writeText "torrc" torRc;
-
+ mkValueString = k: v:
+ if v == null then ""
+ else if isBool v then
+ (if v then "1" else "0")
+ else if v ? "unix" && v.unix != null then
+ "unix:"+v.unix +
+ optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
+ else if v ? "port" && v.port != null then
+ optionalString (v ? "addr" && v.addr != null) "${v.addr}:" +
+ toString v.port +
+ optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
+ else if k == "ServerTransportPlugin" then
+ optionalString (v.transports != []) "${concatStringsSep "," v.transports} exec ${v.exec}"
+ else if k == "HidServAuth" then
+ concatMapStringsSep "\n${k} " (settings: settings.onion + " " settings.auth) v
+ else generators.mkValueStringDefault {} v;
+ genTorrc = settings:
+ generators.toKeyValue {
+ listsAsDuplicateKeys = true;
+ mkKeyValue = k: generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
+ }
+ (lib.mapAttrs (k: v:
+ # Not necesssary, but prettier rendering
+ if elem k [ "AutomapHostsSuffixes" "DirPolicy" "ExitPolicy" "SocksPolicy" ]
+ && v != []
+ then concatStringsSep "," v
+ else v)
+ (lib.filterAttrs (k: v: !(v == null || v == ""))
+ settings));
+ torrc = pkgs.writeText "torrc" (
+ genTorrc cfg.settings +
+ concatStrings (mapAttrsToList (name: onion:
+ "HiddenServiceDir ${onion.path}\n" +
+ genTorrc onion.settings) cfg.relay.onionServices)
+ );
in
{
imports = [
- (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] ''
- Use services.privoxy.enable and services.privoxy.enableTor instead.
- '')
- (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
+ (mkRenamedOptionModule [ "services" "tor" "client" "dns" "automapHostsSuffixes" ] [ "services" "tor" "settings" "AutomapHostsSuffixes" ])
+ (mkRemovedOptionModule [ "services" "tor" "client" "dns" "isolationOptions" ] "Use services.tor.settings.DNSPort instead.")
+ (mkRemovedOptionModule [ "services" "tor" "client" "dns" "listenAddress" ] "Use services.tor.settings.DNSPort instead.")
+ (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] "Use services.privoxy.enable and services.privoxy.enableTor instead.")
+ (mkRemovedOptionModule [ "services" "tor" "client" "socksIsolationOptions" ] "Use services.tor.settings.SOCKSPort instead.")
+ (mkRemovedOptionModule [ "services" "tor" "client" "socksListenAddressFaster" ] "Use services.tor.settings.SOCKSPort instead.")
+ (mkRenamedOptionModule [ "services" "tor" "client" "socksPolicy" ] [ "services" "tor" "settings" "SocksPolicy" ])
+ (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "isolationOptions" ] "Use services.tor.settings.TransPort instead.")
+ (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "listenAddress" ] "Use services.tor.settings.TransPort instead.")
+ (mkRenamedOptionModule [ "services" "tor" "controlPort" ] [ "services" "tor" "settings" "ControlPort" ])
+ (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Plese use services.tor.settings instead.")
+ (mkRenamedOptionModule [ "services" "tor" "hiddenServices" ] [ "services" "tor" "relay" "onionServices" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "accountingMax" ] [ "services" "tor" "settings" "AccountingMax" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "accountingStart" ] [ "services" "tor" "settings" "AccountingStart" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "address" ] [ "services" "tor" "settings" "Address" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthBurst" ] [ "services" "tor" "settings" "BandwidthBurst" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthRate" ] [ "services" "tor" "settings" "BandwidthRate" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "bridgeTransports" ] [ "services" "tor" "settings" "ServerTransportPlugin" "transports" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "contactInfo" ] [ "services" "tor" "settings" "ContactInfo" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "exitPolicy" ] [ "services" "tor" "settings" "ExitPolicy" ])
(mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
(mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
+ (mkRenamedOptionModule [ "services" "tor" "relay" "nickname" ] [ "services" "tor" "settings" "Nickname" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "port" ] [ "services" "tor" "settings" "ORPort" ])
+ (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "settings" "ORPort" ])
];
options = {
services.tor = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Enable the Tor daemon. By default, the daemon is run without
- relay, exit, bridge or client connectivity.
- '';
- };
+ enable = mkEnableOption ''Tor daemon.
+ By default, the daemon is run without
+ relay, exit, bridge or client connectivity'';
+
+ openFirewall = mkEnableOption "opening of the relay port(s) in the firewall";
package = mkOption {
type = types.package;
default = pkgs.tor;
defaultText = "pkgs.tor";
example = literalExample "pkgs.tor";
- description = ''
- Tor package to use
- '';
+ description = "Tor package to use.";
};
- enableGeoIP = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Whenever to configure Tor daemon to use GeoIP databases.
+ enableGeoIP = mkEnableOption ''use of GeoIP databases.
+ Disabling this will disable by-country statistics for bridges and relays
+ and some client and third-party software functionality'' // { default = true; };
- Disabling this will disable by-country statistics for
- bridges and relays and some client and third-party software
- functionality.
- '';
- };
-
- extraConfig = mkOption {
- type = types.lines;
- default = "";
- description = ''
- Extra configuration. Contents will be added verbatim to the
- configuration file at the end.
- '';
- };
-
- controlPort = mkOption {
- type = types.nullOr (types.either types.int types.str);
- default = null;
- example = 9051;
- description = ''
- If set, Tor will accept connections on the specified port
- and allow them to control the tor process.
- '';
- };
-
- controlSocket = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable Tor control socket. Control socket is created
- in <literal>${torRunDirectory}/control</literal>
- '';
- };
- };
+ controlSocket.enable = mkEnableOption ''control socket,
+ created in <literal>${runDir}/control</literal>'';
client = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable Tor daemon to route application
- connections. You might want to disable this if you plan
- running a dedicated Tor relay.
- '';
- };
+ enable = mkEnableOption ''the routing of application connections.
+ You might want to disable this if you plan running a dedicated Tor relay'';
- socksListenAddress = mkOption {
- type = types.str;
- default = "127.0.0.1:9050";
- example = "192.168.0.1:9100";
- description = ''
- Bind to this address to listen for connections from
- Socks-speaking applications. Provides strong circuit
- isolation, separate circuit per IP address.
- '';
- };
+ transparentProxy.enable = mkEnableOption "transparent proxy";
+ dns.enable = mkEnableOption "DNS resolver";
- socksListenAddressFaster = mkOption {
- type = types.str;
- default = "127.0.0.1:9063";
- example = "192.168.0.1:9101";
+ socksListenAddress = mkOption {
+ type = optionSOCKSPort false;
+ default = {addr = "127.0.0.1"; port = 9050; IsolateDestAddr = true;};
+ example = {addr = "192.168.0.1"; port = 9090; IsolateDestAddr = true;};
description = ''
Bind to this address to listen for connections from
- Socks-speaking applications. Same as
- <option>socksListenAddress</option> but uses weaker
- circuit isolation to provide performance suitable for a
- web browser.
- '';
- };
-
- socksPolicy = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "accept 192.168.0.0/16, reject *";
- description = ''
- Entry policies to allow/deny SOCKS requests based on IP
- address. First entry that matches wins. If no SocksPolicy
- is set, we accept all (and only) requests from
- <option>socksListenAddress</option>.
+ Socks-speaking applications.
'';
};
- socksIsolationOptions = mkOption (isolationOptions // {
- default = ["IsolateDestAddr"];
- });
-
- transparentProxy = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = "Whether to enable tor transparent proxy";
- };
-
- listenAddress = mkOption {
- type = types.str;
- default = "127.0.0.1:9040";
- example = "192.168.0.1:9040";
- description = ''
- Bind transparent proxy to this address.
- '';
- };
-
- isolationOptions = mkOption isolationOptions;
- };
-
- dns = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = "Whether to enable tor dns resolver";
- };
-
- listenAddress = mkOption {
- type = types.str;
- default = "127.0.0.1:9053";
- example = "192.168.0.1:9053";
- description = ''
- Bind tor dns to this address.
- '';
- };
-
- isolationOptions = mkOption isolationOptions;
-
- automapHostsSuffixes = mkOption {
- type = types.listOf types.str;
- default = [".onion" ".exit"];
- example = [".onion"];
- description = "List of suffixes to use with automapHostsOnResolve";
+ onionServices = mkOption {
+ description = descriptionGeneric "HiddenServiceDir";
+ default = {};
+ example = {
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" = {
+ clientAuthorizations = ["/run/keys/tor/alice.prv.x25519"];
+ };
};
+ type = types.attrsOf (types.submodule ({name, config, ...}: {
+ options.clientAuthorizations = mkOption {
+ description = ''
+ Clients' authorizations for a v3 onion service,
+ as a list of files containing each one private key, in the format:
+ <screen>descriptor:x25519:&lt;base32-private-key&gt;</screen>
+ '' + descriptionGeneric "_client_authorization";
+ type = with types; listOf path;
+ default = [];
+ example = ["/run/keys/tor/alice.prv.x25519"];
+ };
+ }));
};
};
relay = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable relaying TOR traffic for others.
+ enable = mkEnableOption ''relaying of Tor traffic for others.
- See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
- for details.
+ See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
+ for details.
- Setting this to true requires setting
- <option>services.tor.relay.role</option>
- and
- <option>services.tor.relay.port</option>
- options.
- '';
- };
+ Setting this to true requires setting
+ <option>services.tor.relay.role</option>
+ and
+ <option>services.tor.settings.ORPort</option>
+ options'';
role = mkOption {
type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
@@ -310,13 +313,13 @@ in
<important><para>
Running an exit relay may expose you to abuse
complaints. See
- <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
+ <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies"/>
for more info.
</para></important>
<para>
You can specify which services Tor users may access via
- your exit relay using <option>exitPolicy</option> option.
+ your exit relay using <option>settings.ExitPolicy</option> option.
</para>
</listitem>
</varlistentry>
@@ -369,15 +372,14 @@ in
<important>
<para>
WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE.
- Consult with your lawer when in doubt.
+ Consult with your lawyer when in doubt.
</para>
<para>
This role should be safe to use in most situations
(unless the act of forwarding traffic for others is
a punishable offence under your local laws, which
- would be pretty insane as it would make ISP
- illegal).
+ would be pretty insane as it would make ISP illegal).
</para>
</important>
@@ -404,7 +406,7 @@ in
<para>
Use this if you want to run a private bridge, for
- example because you'll give out your bridge address
+ example because you'll give out your bridge addr
manually to your friends.
</para>
@@ -426,269 +428,393 @@ in
'';
};
- bridgeTransports = mkOption {
- type = types.listOf types.str;
- default = ["obfs4"];
- example = ["obfs2" "obfs3" "obfs4" "scramblesuit"];
- description = "List of pluggable transports";
- };
-
- nickname = mkOption {
- type = types.str;
- default = "anonymous";
- description = ''
- A unique handle for your TOR relay.
- '';
- };
-
- contactInfo = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "admin@relay.com";
- description = ''
- Contact information for the relay owner (e.g. a mail
- address and GPG key ID).
- '';
- };
-
- accountingMax = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "450 GBytes";
- description = ''
- Specify maximum bandwidth allowed during an accounting period. This
- allows you to limit overall tor bandwidth over some time period.
- See the <literal>AccountingMax</literal> option by looking at the
- tor manual <citerefentry><refentrytitle>tor</refentrytitle>
- <manvolnum>1</manvolnum></citerefentry> for more.
-
- Note this limit applies individually to upload and
- download; if you specify <literal>"500 GBytes"</literal>
- here, then you may transfer up to 1 TBytes of overall
- bandwidth (500 GB upload, 500 GB download).
- '';
- };
-
- accountingStart = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "month 1 1:00";
- description = ''
- Specify length of an accounting period. This allows you to limit
- overall tor bandwidth over some time period. See the
- <literal>AccountingStart</literal> option by looking at the tor
- manual <citerefentry><refentrytitle>tor</refentrytitle>
- <manvolnum>1</manvolnum></citerefentry> for more.
- '';
- };
-
- bandwidthRate = mkOption {
- type = types.nullOr types.int;
- default = null;
- example = 100;
- description = ''
- Specify this to limit the bandwidth usage of relayed (server)
- traffic. Your own traffic is still unthrottled. Units: bytes/second.
- '';
- };
-
- bandwidthBurst = mkOption {
- type = types.nullOr types.int;
- default = cfg.relay.bandwidthRate;
- example = 200;
- description = ''
- Specify this to allow bursts of the bandwidth usage of relayed (server)
- traffic. The average usage will still be as specified in relayBandwidthRate.
- Your own traffic is still unthrottled. Units: bytes/second.
- '';
- };
-
- address = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "noname.example.com";
- description = ''
- The IP address or full DNS name for advertised address of your relay.
- Leave unset and Tor will guess.
- '';
- };
-
- port = mkOption {
- type = types.either types.int types.str;
- example = 143;
- description = ''
- What port to advertise for Tor connections. This corresponds to the
- <literal>ORPort</literal> section in the Tor manual; see
- <citerefentry><refentrytitle>tor</refentrytitle>
- <manvolnum>1</manvolnum></citerefentry> for more details.
-
- At a minimum, you should just specify the port for the
- relay to listen on; a common one like 143, 22, 80, or 443
- to help Tor users who may have very restrictive port-based
- firewalls.
- '';
- };
-
- exitPolicy = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "accept *:6660-6667,reject *:*";
- description = ''
- A comma-separated list of exit policies. They're
- considered first to last, and the first match wins. If you
- want to _replace_ the default exit policy, end this with
- either a reject *:* or an accept *:*. Otherwise, you're
- _augmenting_ (prepending to) the default exit policy.
- Leave commented to just use the default, which is
- available in the man page or at
- <link xlink:href="https://www.torproject.org/documentation.html" />.
-
- Look at
- <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" />
- for issues you might encounter if you use the default
- exit policy.
-
- If certain IPs and ports are blocked externally, e.g. by
- your firewall, you should update your exit policy to
- reflect this -- otherwise Tor users will be told that
- those destinations are down.
- '';
+ onionServices = mkOption {
+ description = descriptionGeneric "HiddenServiceDir";
+ default = {};
+ example = {
+ "example.org/www" = {
+ map = [ 80 ];
+ authorizedClients = [
+ "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ ];
+ };
+ };
+ type = types.attrsOf (types.submodule ({name, config, ...}: {
+ options.path = mkOption {
+ type = types.path;
+ description = ''
+ Path where to store the data files of the hidden service.
+ If the <option>secretKey</option> is null
+ this defaults to <literal>${stateDir}/onion/$onion</literal>,
+ otherwise to <literal>${runDir}/onion/$onion</literal>.
+ '';
+ };
+ options.secretKey = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ example = "/run/keys/tor/onion/expyuzz4wqqyqhjn/hs_ed25519_secret_key";
+ description = ''
+ Secret key of the onion service.
+ If null, Tor reuses any preexisting secret key (in <option>path</option>)
+ or generates a new one.
+ The associated public key and hostname are deterministically regenerated
+ from this file if they do not exist.
+ '';
+ };
+ options.authorizeClient = mkOption {
+ description = descriptionGeneric "HiddenServiceAuthorizeClient";
+ default = null;
+ type = types.nullOr (types.submodule ({...}: {
+ options = {
+ authType = mkOption {
+ type = types.enum [ "basic" "stealth" ];
+ description = ''
+ Either <literal>"basic"</literal> for a general-purpose authorization protocol
+ or <literal>"stealth"</literal> for a less scalable protocol
+ that also hides service activity from unauthorized clients.
+ '';
+ };
+ clientNames = mkOption {
+ type = with types; nonEmptyListOf (strMatching "[A-Za-z0-9+-_]+");
+ description = ''
+ Only clients that are listed here are authorized to access the hidden service.
+ Generated authorization data can be found in <filename>${stateDir}/onion/$name/hostname</filename>.
+ Clients need to put this authorization data in their configuration file using
+ <xref linkend="opt-services.tor.settings.HidServAuth"/>.
+ '';
+ };
+ };
+ }));
+ };
+ options.authorizedClients = mkOption {
+ description = ''
+ Authorized clients for a v3 onion service,
+ as a list of public key, in the format:
+ <screen>descriptor:x25519:&lt;base32-public-key&gt;</screen>
+ '' + descriptionGeneric "_client_authorization";
+ type = with types; listOf str;
+ default = [];
+ example = ["descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"];
+ };
+ options.map = mkOption {
+ description = descriptionGeneric "HiddenServicePort";