From e17d4b05a12fe1b567ef4d55d2f01c23ff48228d Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 15 Dec 2020 21:30:24 +0000 Subject: nixos/tor: don't do privoxy stuff by default It's very surprising that services.tor.client.enable would set services.privoxy.enable. This violates the principle of least astonishment, because it's Privoxy that can integrate with Tor, rather than the other way around. So this patch moves the Privoxy Tor integration to the Privoxy module, and it also disables it by default. This change is documented in the release notes. Reported-by: V --- nixos/modules/services/networking/privoxy.nix | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 1f41c720adf..e3b34cb0c61 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -8,15 +8,22 @@ let cfg = config.services.privoxy; - confFile = pkgs.writeText "privoxy.conf" '' + confFile = pkgs.writeText "privoxy.conf" ('' user-manual ${privoxy}/share/doc/privoxy/user-manual confdir ${privoxy}/etc/ listen-address ${cfg.listenAddress} enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"} ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles} ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles} + '' + optionalString cfg.enableTor '' + forward-socks4a / ${config.services.tor.client.socksListenAddressFaster} . + toggle 1 + enable-remote-toggle 0 + enable-edit-actions 0 + enable-remote-http-toggle 0 + '' + '' ${cfg.extraConfig} - ''; + ''); in @@ -72,6 +79,15 @@ in ''; }; + enableTor = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure Privoxy to use Tor's faster SOCKS port, + suitable for HTTP. + ''; + }; + extraConfig = mkOption { type = types.lines; default = "" ; -- cgit 1.4.1 From 0ccdd6f2b043e5123ffd1f76cd2187c39ce19b94 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Fri, 11 Sep 2020 07:46:59 +0200 Subject: nixos/tor: improve type-checking and hardening Fixes #77395. Fixes #82790. --- nixos/doc/manual/release-notes/rl-2103.xml | 10 + nixos/modules/services/networking/privoxy.nix | 7 +- nixos/modules/services/security/tor.nix | 1390 +++++++++++++++---------- nixos/tests/tor.nix | 2 +- pkgs/tools/security/tor/default.nix | 18 +- 5 files changed, 861 insertions(+), 566 deletions(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml index 38262b50899..1e3ae23b9b3 100644 --- a/nixos/doc/manual/release-notes/rl-2103.xml +++ b/nixos/doc/manual/release-notes/rl-2103.xml @@ -278,6 +278,16 @@ = true; + + + The services.tor module has a new exhaustively typed 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 . + A new option as been introduced for allowing connections on all the TCP ports configured. + + The options services.slurm.dbdserver.storagePass diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index e3b34cb0c61..7caae328203 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 1cceee065b1..1002dacc7f2 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 torrc manual. + ''; + 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 ${torRunDirectory}/control - ''; - }; - }; + controlSocket.enable = mkEnableOption ''control socket, + created in ${runDir}/control''; 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 - 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 - . + 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: + descriptor:x25519:<base32-private-key> + '' + 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 - for details. + See + for details. - Setting this to true requires setting - - and - - options. - ''; - }; + Setting this to true requires setting + + and + + options''; role = mkOption { type = types.enum [ "exit" "relay" "bridge" "private-bridge" ]; @@ -310,13 +313,13 @@ in Running an exit relay may expose you to abuse complaints. See - + for more info. You can specify which services Tor users may access via - your exit relay using option. + your exit relay using option. @@ -369,15 +372,14 @@ in WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE. - Consult with your lawer when in doubt. + Consult with your lawyer when in doubt. 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). @@ -404,7 +406,7 @@ in 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. @@ -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 AccountingMax option by looking at the - tor manual tor - 1 for more. - - Note this limit applies individually to upload and - download; if you specify "500 GBytes" - 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 - AccountingStart option by looking at the tor - manual tor - 1 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 - ORPort section in the Tor manual; see - tor - 1 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 - . - - Look at - - 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 is null + this defaults to ${stateDir}/onion/$onion, + otherwise to ${runDir}/onion/$onion. + ''; + }; + 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 ) + 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 "basic" for a general-purpose authorization protocol + or "stealth" 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 ${stateDir}/onion/$name/hostname. + Clients need to put this authorization data in their configuration file using + . + ''; + }; + }; + })); + }; + options.authorizedClients = mkOption { + description = '' + Authorized clients for a v3 onion service, + as a list of public key, in the format: + descriptor:x25519:<base32-public-key> + '' + descriptionGeneric "_client_authorization"; + type = with types; listOf str; + default = []; + example = ["descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]; + }; + options.map = mkOption { + description = descriptionGeneric "HiddenServicePort"; + type = with types; listOf (oneOf [ + port (submodule ({...}: { + options = { + port = optionPort; + target = mkOption { + default = null; + type = nullOr (submodule ({...}: { + options = { + unix = optionUnix; + addr = optionAddress; + port = optionPort; + }; + })); + }; + }; + })) + ]); + apply = map (v: if isInt v then {port=v; target=null;} else v); + }; + options.version = mkOption { + description = descriptionGeneric "HiddenServiceVersion"; + type = with types; nullOr (enum [2 3]); + default = null; + }; + options.settings = mkOption { + description = '' + Settings of the onion service. + '' + descriptionGeneric "_hidden_service_options"; + default = {}; + type = types.submodule { + freeformType = with types; + (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // { + description = "settings option"; + }; + options.HiddenServiceAllowUnknownPorts = optionBool "HiddenServiceAllowUnknownPorts"; + options.HiddenServiceDirGroupReadable = optionBool "HiddenServiceDirGroupReadable"; + options.HiddenServiceExportCircuitID = mkOption { + description = descriptionGeneric "HiddenServiceExportCircuitID"; + type = with types; nullOr (enum ["haproxy"]); + default = null; + }; + options.HiddenServiceMaxStreams = mkOption { + description = descriptionGeneric "HiddenServiceMaxStreams"; + type = with types; nullOr (ints.between 0 65535); + default = null; + }; + options.HiddenServiceMaxStreamsCloseCircuit = optionBool "HiddenServiceMaxStreamsCloseCircuit"; + options.HiddenServiceNumIntroductionPoints = mkOption { + description = descriptionGeneric "HiddenServiceNumIntroductionPoints"; + type = with types; nullOr (ints.between 0 20); + default = null; + }; + options.HiddenServiceSingleHopMode = optionBool "HiddenServiceSingleHopMode"; + options.RendPostPeriod = optionString "RendPostPeriod"; + }; + }; + config = { + path = mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}"); + settings.HiddenServiceVersion = config.version; + settings.HiddenServiceAuthorizeClient = + if config.authorizeClient != null then + config.authorizeClient.authType + " " + + concatStringsSep "," config.authorizeClient.clientNames + else null; + settings.HiddenServicePort = map (p: mkValueString "" p.port + " " + mkValueString "" p.target) config.map; + }; + })); }; }; - hiddenServices = mkOption { + settings = mkOption { description = '' - A set of static hidden services that terminate their Tor - circuits at this node. - - Every element in this set declares a virtual onion host. - - You can specify your onion address by putting corresponding - private key to an appropriate place in ${torDirectory}. - - For services without private keys in ${torDirectory} Tor - daemon will generate random key pairs (which implies random - onion addresses) on restart. The latter could take a while, - please be patient. - - - Hidden services can be useful even if you don't intend to - actually hide them, since they can - also be seen as a kind of NAT traversal mechanism. - - E.g. the example will make your sshd, whatever runs on - "8080" and your mail server available from anywhere where - the Tor network is available (which, with the help from - bridges, is pretty much everywhere), even if both client - and server machines are behind NAT you have no control - over. - + See torrc manual + for documentation. ''; default = {}; - example = literalExample '' - { "my-hidden-service-example".map = [ - { port = 22; } # map ssh port to this machine's ssh - { port = 80; toPort = 8080; } # map http port to whatever runs on 8080 - { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can - ]; - } - ''; - type = types.attrsOf (types.submodule ({name, ...}: { - options = { - - name = mkOption { - type = types.str; - description = '' - Name of this tor hidden service. - - This is purely descriptive. - - After restarting Tor daemon you should be able to - find your .onion address in - ${torDirectory}/onion/$name/hostname. - ''; - }; - - map = mkOption { - default = []; - description = "Port mapping for this hidden service."; - type = types.listOf (types.submodule ({config, ...}: { - options = { - - port = mkOption { - type = types.either types.int types.str; - example = 80; - description = '' - Hidden service port to "bind to". - ''; - }; - - destination = mkOption { - internal = true; - type = types.str; - description = "Forward these connections where?"; - }; - - toHost = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "Mapping destination host."; - }; - - toPort = mkOption { - type = types.either types.int types.str; - example = 8080; - description = "Mapping destination port."; - }; - - }; - - config = { - toPort = mkDefault config.port; - destination = mkDefault "${config.toHost}:${toString config.toPort}"; - }; - })); - }; - - authorizeClient = mkOption { - default = null; - description = "If configured, the hidden service is accessible for authorized clients only."; - type = types.nullOr (types.submodule ({...}: { - - options = { - - authType = mkOption { - type = types.enum [ "basic" "stealth" ]; - description = '' - Either "basic" for a general-purpose authorization protocol - or "stealth" for a less scalable protocol - that also hides service activity from unauthorized clients. - ''; - }; - - clientNames = mkOption { - type = types.nonEmptyListOf (types.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 ${torDirectory}/onion/$name/hostname. - Clients need to put this authorization data in their configuration file using HidServAuth. - ''; - }; - }; - })); - }; - - version = mkOption { - default = null; - description = "Rendezvous service descriptor version to publish for the hidden service. Currently, versions 2 and 3 are supported. (Default: 2)"; - type = types.nullOr (types.enum [ 2 3 ]); - }; + type = types.submodule { + freeformType = with types; + (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // { + description = "settings option"; + }; + options.Address = optionString "Address"; + options.AssumeReachable = optionBool "AssumeReachable"; + options.AccountingMax = optionBandwith "AccountingMax"; + options.AccountingStart = optionString "AccountingStart"; + options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity"; + options.AuthDirListBadExits = optionBool "AuthDirListBadExits"; + options.AuthDirPinKeys = optionBool "AuthDirPinKeys"; + options.AuthDirSharedRandomness = optionBool "AuthDirSharedRandomness"; + options.AuthDirTestEd25519LinkKeys = optionBool "AuthDirTestEd25519LinkKeys"; + options.AuthoritativeDirectory = optionBool "AuthoritativeDirectory"; + options.AutomapHostsOnResolve = optionBool "AutomapHostsOnResolve"; + options.AutomapHostsSuffixes = optionStrings "AutomapHostsSuffixes" // { + default = [".onion" ".exit"]; + example = [".onion"]; }; - - config = { - name = mkDefault name; + options.BandwidthBurst = optionBandwith "BandwidthBurst"; + options.BandwidthRate = optionBandwith "BandwidthRate"; + options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir"; + options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry"; + options.BridgeRelay = optionBool "BridgeRelay" // { default = false; }; + options.CacheDirectory = optionPath "CacheDirectory"; + options.CacheDirectoryGroupReadable = optionBool "CacheDirectoryGroupReadable"; # default is null and like "auto" + options.CellStatistics = optionBool "CellStatistics"; + options.ClientAutoIPv6ORPort = optionBool "ClientAutoIPv6ORPort"; + options.ClientDNSRejectInternalAddresses = optionBool "ClientDNSRejectInternalAddresses"; + options.ClientOnionAuthDir = mkOption { + description = descriptionGeneric "ClientOnionAuthDir"; + default = null; + type = with types; nullOr path; + }; + options.ClientPreferIPv6DirPort = optionBool "ClientPreferIPv6DirPort"; # default is null and like "auto" + options.ClientPreferIPv6ORPort = optionBool "ClientPreferIPv6ORPort"; # default is null and like "auto" + options.ClientRejectInternalAddresses = optionBool "ClientRejectInternalAddresses"; + options.ClientUseIPv4 = optionBool "ClientUseIPv4"; + options.ClientUseIPv6 = optionBool "ClientUseIPv6"; + options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics"; + options.ConstrainedSockets = optionBool "ConstrainedSockets"; + options.ContactInfo = optionString "ContactInfo"; + options.ControlPort = mkOption rec { + description = descriptionGeneric "ControlPort"; + default = []; + example = [{port = 9051;}]; + type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [ + port (enum ["auto"]) (submodule ({config, ...}: let + flags = ["GroupWritable" "RelaxDirModeCheck" "WorldWritable"]; + in { + options = { + unix = optionUnix; + flags = optionFlags; + addr = optionAddress; + port = optionPort; + } // genAttrs flags (name: mkOption { type = types.bool; default = false; }); + config = { + flags = filter (name: config.${name} == true) flags; + }; + })) + ]))]; + }; + options.ControlPortFileGroupReadable= optionBool "ControlPortFileGroupReadable"; + options.ControlPortWriteToFile = optionPath "ControlPortWriteToFile"; + options.ControlSocket = optionPath "ControlSocket"; + options.ControlSocketsGroupWritable = optionBool "ControlSocketsGroupWritable"; + options.CookieAuthFile = optionPath "CookieAuthFile"; + options.CookieAuthFileGroupReadable = optionBool "CookieAuthFileGroupReadable"; + options.CookieAuthentication = optionBool "CookieAuthentication"; + options.DataDirectory = optionPath "DataDirectory" // { default = stateDir; }; + options.DataDirectoryGroupReadable = optionBool "DataDirectoryGroupReadable"; + options.DirPortFrontPage = optionPath "DirPortFrontPage"; + options.DirAllowPrivateAddresses = optionBool "DirAllowPrivateAddresses"; + options.DormantCanceledByStartup = optionBool "DormantCanceledByStartup"; + options.DormantOnFirstStartup = optionBool "DormantOnFirstStartup"; + options.DormantTimeoutDisabledByIdleStreams = optionBool "DormantTimeoutDisabledByIdleStreams"; + options.DirCache = optionBool "DirCache"; + options.DirPolicy = mkOption { + description = descriptionGeneric "DirPolicy"; + type = with types; listOf str; + default = []; + example = ["accept *:*"]; + }; + options.DirPort = optionORPort "DirPort"; + options.DirReqStatistics = optionBool "DirReqStatistics"; + options.DisableAllSwap = optionBool "DisableAllSwap"; + options.DisableDebuggerAttachment = optionBool "DisableDebuggerAttachment"; + options.DisableNetwork = optionBool "DisableNetwork"; + options.DisableOOSCheck = optionBool "DisableOOSCheck"; + options.DNSPort = optionIsolablePorts "DNSPort"; + options.DoSCircuitCreationEnabled = optionBool "DoSCircuitCreationEnabled"; + options.DoSConnectionEnabled = optionBool "DoSConnectionEnabled"; # default is null and like "auto" + options.DoSRefuseSingleHopClientRendezvous = optionBool "DoSRefuseSingleHopClientRendezvous"; + options.DownloadExtraInfo = optionBool "DownloadExtraInfo"; + options.EnforceDistinctSubnets = optionBool "EnforceDistinctSubnets"; + options.EntryStatistics = optionBool "EntryStatistics"; + options.ExitPolicy = optionStrings "ExitPolicy" // { + default = ["reject *:*"]; + example = ["accept *:*"]; + }; + options.ExitPolicyRejectLocalInterfaces = optionBool "ExitPolicyRejectLocalInterfaces"; + options.ExitPolicyRejectPrivate = optionBool "ExitPolicyRejectPrivate"; + options.ExitPortStatistics = optionBool "ExitPortStatistics"; + options.ExitRelay = optionBool "ExitRelay"; # default is null and like "auto" + options.ExtORPort = mkOption { + description = descriptionGeneric "ExtORPort"; + default = null; + type = with types; nullOr (oneOf [ + port (enum ["auto"]) (submodule ({...}: { + options = { + addr = optionAddress; + port = optionPort; + }; + })) + ]); + apply = p: if isInt p || isString p then { port = p; } else p; }; - })); + options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile"; + options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable"; + options.ExtendAllowPrivateAddresses = optionBool "ExtendAllowPrivateAddresses"; + options.ExtraInfoStatistics = optionBool "ExtraInfoStatistics"; + options.FascistFirewall = optionBool "FascistFirewall"; + options.FetchDirInfoEarly = optionBool "FetchDirInfoEarly"; + options.FetchDirInfoExtraEarly = optionBool "FetchDirInfoExtraEarly"; + options.FetchHidServDescriptors = optionBool "FetchHidServDescriptors"; + options.FetchServerDescriptors = optionBool "FetchServerDescriptors"; + options.FetchUselessDescriptors = optionBool "FetchUselessDescriptors"; + options.ReachableAddresses = optionStrings "ReachableAddresses"; + options.ReachableDirAddresses = optionStrings "ReachableDirAddresses"; + options.ReachableORAddresses = optionStrings "ReachableORAddresses"; + options.GeoIPFile = optionPath "GeoIPFile"; + options.GeoIPv6File = optionPath "GeoIPv6File"; + options.GuardfractionFile = optionPath "GuardfractionFile"; + options.HidServAuth = mkOption { + description = descriptionGeneric "HidServAuth"; + default = []; + type = with types; listOf (oneOf [ + (submodule { + options = { + onion = mkOption { + type = strMatching "[a-z2-7]{16}(\\.onion)?"; + description = "Onion address."; + example = "xxxxxxxxxxxxxxxx.onion"; + }; + auth = mkOption { + type = strMatching "[A-Za-z0-9+/]{22}"; + description = "Authentication cookie."; + }; + }; + }) + ]); + }; + options.HiddenServiceNonAnonymousMode = optionBool "HiddenServiceNonAnonymousMode"; + options.HiddenServiceStatistics = optionBool "HiddenServiceStatistics"; + options.HSLayer2Nodes = optionStrings "HSLayer2Nodes"; + options.HSLayer3Nodes = optionStrings "HSLayer3Nodes"; + options.HTTPTunnelPort = optionIsolablePorts "HTTPTunnelPort"; + options.IPv6Exit = optionBool "IPv6Exit"; + options.KeyDirectory = optionPath "KeyDirectory"; + options.KeyDirectoryGroupReadable = optionBool "KeyDirectoryGroupReadable"; + options.LogMessageDomains = optionBool "LogMessageDomains"; + options.LongLivedPorts = optionPorts "LongLivedPorts"; + options.MainloopStats = optionBool "MainloopStats"; + options.MaxAdvertisedBandwidth = optionBandwith "MaxAdvertisedBandwidth"; + options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness"; + options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending"; + options.NATDPort = optionIsolablePorts "NATDPort"; + options.NewCircuitPeriod = optionInt "NewCircuitPeriod"; + options.Nickname = optionString "Nickname"; + options.ORPort = optionORPort "ORPort"; + options.OfflineMasterKey = optionBool "OfflineMasterKey"; + options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto" + options.PaddingStatistics = optionBool "PaddingStatistics"; + options.PerConnBWBurst = optionBandwith "PerConnBWBurst"; + options.PerConnBWRate = optionBandwith "PerConnBWRate"; + options.PidFile = optionPath "PidFile"; + options.ProtocolWarnings = optionBool "ProtocolWarnings"; + options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors"; + options.PublishServerDescriptor = mkOption { + description = descriptionGeneric "PublishServerDescriptor"; + type = with types; nullOr (enum [false true 0 1 "0" "1" "v3" "bridge"]); + default = null; + }; + options.ReducedExitPolicy = optionBool "ReducedExitPolicy"; + options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto" + options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts"; + options.RelayBandwidthBurst = optionBandwith "RelayBandwidthBurst"; + options.RelayBandwidthRate = optionBandwith "RelayBandwidthRate"; + #options.RunAsDaemon + options.Sandbox = optionBool "Sandbox"; + options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig"; + options.ServerDNSAllowNonRFC953Hostnames = optionBool "ServerDNSAllowNonRFC953Hostnames"; + options.ServerDNSDetectHijacking = optionBool "ServerDNSDetectHijacking"; + options.ServerDNSRandomizeCase = optionBool "ServerDNSRandomizeCase"; + options.ServerDNSResolvConfFile = optionPath "ServerDNSResolvConfFile"; + options.ServerDNSSearchDomains = optionBool "ServerDNSSearchDomains"; + options.ServerTransportPlugin = mkOption { + description = descriptionGeneric "ServerTransportPlugin"; + default = null; + type = with types; nullOr (submodule ({...}: { + options = { + transports = mkOption { + description = "List of pluggable transports."; + type = listOf str; + example = ["obfs2" "obfs3" "obfs4" "scramblesuit"]; + }; + exec = mkOption { + type = types.str; + description = "Command of pluggable transport."; + }; + }; + })); + }; + options.SocksPolicy = optionStrings "SocksPolicy" // { + example = ["accept *:*"]; + }; + options.SOCKSPort = mkOption { + description = descriptionGeneric "SOCKSPort"; + default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else []; + example = [{port = 9090;}]; + type = types.listOf (optionSOCKSPort true); + }; + options.TestingTorNetwork = optionBool "TestingTorNetwork"; + options.TransPort = optionIsolablePorts "TransPort"; + options.TransProxyType = mkOption { + description = descriptionGeneric "TransProxyType"; + type = with types; nullOr (enum ["default" "TPROXY" "ipfw" "pf-divert"]); + default = null; + }; + #options.TruncateLogFile + options.UnixSocksGroupWritable = optionBool "UnixSocksGroupWritable"; + options.UseDefaultFallbackDirs = optionBool "UseDefaultFallbackDirs"; + options.UseMicrodescriptors = optionBool "UseMicrodescriptors"; + options.V3AuthUseLegacyKey = optionBool "V3AuthUseLegacyKey"; + options.V3AuthoritativeDirectory = optionBool "V3AuthoritativeDirectory"; + options.VersioningAuthoritativeDirectory = optionBool "VersioningAuthoritativeDirectory"; + options.VirtualAddrNetworkIPv4 = optionString "VirtualAddrNetworkIPv4"; + options.VirtualAddrNetworkIPv6 = optionString "VirtualAddrNetworkIPv6"; + options.WarnPlaintextPorts = optionPorts "WarnPlaintextPorts"; + }; }; }; }; @@ -696,79 +822,217 @@ in config = mkIf cfg.enable { # Not sure if `cfg.relay.role == "private-bridge"` helps as tor # sends a lot of stats - warnings = optional (cfg.relay.enable && cfg.hiddenServices != {}) + warnings = optional (cfg.settings.BridgeRelay && + flatten (mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != []) '' Running Tor hidden services on a public relay makes the presence of hidden services visible through simple statistical analysis of publicly available data. + See https://trac.torproject.org/projects/tor/ticket/8742 You can safely ignore this warning if you don't intend to actually hide your hidden services. In either case, you can always create a container/VM with a separate Tor daemon instance. - ''; + '' ++ + flatten (mapAttrsToList (n: o: + optional (o.settings.HiddenServiceVersion == 2) [ + (optional (o.settings.HiddenServiceExportCircuitID != null) '' + HiddenServiceExportCircuitID is used in the HiddenService: ${n} + but this option is only for v3 hidden services. + '') + ] ++ + optional (o.settings.HiddenServiceVersion != 2) [ + (optional (o.settings.HiddenServiceAuthorizeClient != null) '' + HiddenServiceAuthorizeClient is used in the HiddenService: ${n} + but this option is only for v2 hidden services. + '') + (optional (o.settings.RendPostPeriod != null) '' + RendPostPeriod is used in the HiddenService: ${n} + but this option is only for v2 hidden services. + '') + ] + ) cfg.relay.onionServices); users.groups.tor.gid = config.ids.gids.tor; users.users.tor = { description = "Tor Daemon User"; createHome = true; - home = torDirectory; + home = stateDir; group = "tor"; uid = config.ids.uids.tor; }; - # We have to do this instead of using RuntimeDirectory option in - # the service below because systemd has no way to set owners of - # RuntimeDirectory and putting this into the service below - # requires that service to relax it's sandbox since this needs - # writable /run - systemd.services.tor-init = - { description = "Tor Daemon Init"; - wantedBy = [ "tor.service" ]; - script = '' - install -m 0700 -o tor -g tor -d ${torDirectory} ${torDirectory}/onion - install -m 0750 -o tor -g tor -d ${torRunDirectory} - ''; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - }; + services.tor.settings = mkMerge [ + (mkIf cfg.enableGeoIP { + GeoIPFile = "${cfg.package.geoip}/share/tor/geoip"; + GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6"; + }) + (mkIf cfg.controlSocket.enable { + ControlPort = [ { unix = runDir + "/control"; GroupWritable=true; RelaxDirModeCheck=true; } ]; + }) + (mkIf cfg.relay.enable ( + optionalAttrs (cfg.relay.role != "exit") { + ExitPolicy = mkForce ["reject *:*"]; + } // + optionalAttrs (elem cfg.relay.role ["bridge" "private-bridge"]) { + BridgeRelay = true; + ExtORPort.port = mkDefault "auto"; + ServerTransportPlugin.transports = mkDefault ["obfs4"]; + ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed"; + } // optionalAttrs (cfg.relay.role == "private-bridge") { + ExtraInfoStatistics = false; + PublishServerDescriptor = false; + } + )) + (mkIf (!cfg.relay.enable) { + # Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings, + # because it would still enable Tor as a relay, + # which can trigger all sort of problems when not carefully done, + # like the blocklisting of the machine's IP addresses + # by some hosting providers... + DirPort = mkForce []; + ORPort = mkForce []; + PublishServerDescriptor = mkForce false; + }) + (mkIf cfg.client.enable ( + { SOCKSPort = [ cfg.client.socksListenAddress ]; + } // optionalAttrs cfg.client.transparentProxy.enable { + TransPort = [{ addr = "127.0.0.1"; port = 9040; }]; + } // optionalAttrs cfg.client.dns.enable { + DNSPort = [{ addr = "127.0.0.1"; port = 9053; }]; + AutomapHostsOnResolve = true; + AutomapHostsSuffixes = cfg.client.dns.automapHostsSuffixes; + } // optionalAttrs (flatten (mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != []) { + ClientOnionAuthDir = runDir + "/ClientOnionAuthDir"; + } + )) + ]; - systemd.services.tor = - { description = "Tor Daemon"; - path = [ pkgs.tor ]; - - wantedBy = [ "multi-user.target" ]; - after = [ "tor-init.service" "network.target" ]; - restartTriggers = [ torRcFile ]; - - serviceConfig = - { Type = "simple"; - # Translated from the upstream contrib/dist/tor.service.in - ExecStartPre = "${cfg.package}/bin/tor -f ${torRcFile} --verify-config"; - ExecStart = "${cfg.package}/bin/tor -f ${torRcFile}"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - KillSignal = "SIGINT"; - TimeoutSec = 30; - Restart = "on-failure"; - LimitNOFILE = 32768; - - # Hardening - # this seems to unshare /run despite what systemd.exec(5) says - PrivateTmp = mkIf (!cfg.controlSocket.enable) "yes"; - PrivateDevices = "yes"; - ProtectHome = "yes"; - ProtectSystem = "strict"; - InaccessiblePaths = "/home"; - ReadOnlyPaths = "/"; - ReadWritePaths = [ torDirectory torRunDirectory ]; - NoNewPrivileges = "yes"; - - # tor.service.in has this in, but this line it fails to spawn a namespace when using hidden services - #CapabilityBoundingSet = "CAP_SETUID CAP_SETGID CAP_NET_BIND_SERVICE"; - }; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = + concatMap (o: optional (isInt o && o > 0 || o ? "port" && isInt o.port && o.port > 0) o.port) + (flatten [ + cfg.settings.ORPort + cfg.settings.DirPort + ]); + }; + + systemd.services.tor = { + description = "Tor Daemon"; + path = [ pkgs.tor ]; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + restartTriggers = [ torrc ]; + + serviceConfig = { + Type = "simple"; + User = "tor"; + Group = "tor"; + ExecStartPre = [ + "${cfg.package}/bin/tor -f ${torrc} --verify-config" + # DOC: Appendix G of https://spec.torproject.org/rend-spec-v3 + ("+" + pkgs.writeShellScript "ExecStartPre" (concatStringsSep "\n" (flatten (["set -eu"] ++ + mapAttrsToList (name: onion: + optional (onion.authorizedClients != []) '' + rm -rf ${escapeShellArg onion.path}/authorized_clients + install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} ${escapeShellArg onion.path}/authorized_clients + '' ++ + imap0 (i: pubKey: '' + echo ${pubKey} | + install -o tor -g tor -m 0400 /dev/stdin ${escapeShellArg onion.path}/authorized_clients/${toString i}.auth + '') onion.authorizedClients ++ + optional (onion.secretKey != null) '' + install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} + key="$(cut -f1 -d: ${escapeShellArg onion.secretKey})" + case "$key" in + ("== ed25519v"*"-secret") + install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;; + (*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;; + esac + '' + ) cfg.relay.onionServices ++ + mapAttrsToList (name: onion: imap0 (i: prvKeyPath: + let hostname = removeSuffix ".onion" name; in '' + printf "%s:" ${escapeShellArg hostname} | cat - ${escapeShellArg prvKeyPath} | + install -o tor -g tor -m 0700 /dev/stdin \ + ${runDir}/ClientOnionAuthDir/${escapeShellArg hostname}.${toString i}.auth_private + '') onion.clientAuthorizations) + cfg.client.onionServices + )))) + ]; + ExecStart = "${cfg.package}/bin/tor -f ${torrc}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + KillSignal = "SIGINT"; + TimeoutSec = 30; + Restart = "on-failure"; + LimitNOFILE = 32768; + RuntimeDirectory = [ + # g+x allows access to the control socket + "tor" + "tor/root" + # g+x can't be removed in ExecStart=, but will be removed by Tor + "tor/ClientOnionAuthDir" + ]; + RuntimeDirectoryMode = "0710"; + StateDirectoryMode = "0700"; + StateDirectory = [ + "tor" + "tor/onion" + ] ++ + flatten (mapAttrsToList (name: onion: + optional (onion.secretKey == null) "tor/onion/${name}" + ) cfg.relay.onionServices); + # The following options are only to optimize: + # systemd-analyze security tor + RootDirectory = runDir + "/root"; + RootDirectoryStartOnly = true; + #InaccessiblePaths = [ "-+${runDir}/root" ]; + UMask = "0066"; + BindPaths = [ stateDir ]; + BindReadOnlyPaths = [ storeDir "/etc" ]; + AmbientCapabilities = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE"; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateNetwork = mkDefault false; + PrivateTmp = true; + # Tor cannot currently bind privileged port when PrivateUsers=true, + # see https://gitlab.torproject.org/legacy/trac/-/issues/20930 + PrivateUsers = !bindsPrivilegedPort; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # See also the finer but experimental option settings.Sandbox + SystemCallFilter = [ + "@system-service" + # Groups in @system-service which do not contain a syscall listed by: + # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' tor + # in tests, and seem likely not necessary for tor. + "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer" + ]; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; }; + }; environment.systemPackages = [ cfg.package ]; }; + + meta.maintainers = with lib.maintainers; [ julm ]; } diff --git a/nixos/tests/tor.nix b/nixos/tests/tor.nix index ad07231557c..c061f59226c 100644 --- a/nixos/tests/tor.nix +++ b/nixos/tests/tor.nix @@ -17,7 +17,7 @@ rec { environment.systemPackages = with pkgs; [ netcat ]; services.tor.enable = true; services.tor.client.enable = true; - services.tor.controlPort = 9051; + services.tor.settings.ControlPort = 9051; }; testScript = '' diff --git a/pkgs/tools/security/tor/default.nix b/pkgs/tools/security/tor/default.nix index 04bf598d132..e46fd4790a3 100644 --- a/pkgs/tools/security/tor/default.nix +++ b/pkgs/tools/security/tor/default.nix @@ -1,5 +1,6 @@ { stdenv, fetchurl, pkgconfig, libevent, openssl, zlib, torsocks , libseccomp, systemd, libcap, lzma, zstd, scrypt, nixosTests +, writeShellScript # for update.nix , writeScript @@ -12,7 +13,21 @@ , gnused , nix }: - +let + tor-client-auth-gen = writeShellScript "tor-client-auth-gen" '' + PATH="${stdenv.lib.makeBinPath [coreutils gnugrep openssl]}" + pem="$(openssl genpkey -algorithm x25519)" + + printf private_key=descriptor:x25519: + echo "$pem" | grep -v " PRIVATE KEY" | + base64 -d | tail --bytes=32 | base32 | tr -d = + + printf public_key=descriptor:x25519: + echo "$pem" | openssl pkey -in /dev/stdin -pubout | + grep -v " PUBLIC KEY" | + base64 -d | tail --bytes=32 | base32 | tr -d = + ''; +in stdenv.mkDerivation rec { pname = "tor"; version = "0.4.4.6"; @@ -52,6 +67,7 @@ stdenv.mkDerivation rec { mkdir -p $geoip/share/tor mv $out/share/tor/geoip{,6} $geoip/share/tor rm -rf $out/share/tor + ln -s ${tor-client-auth-gen} $out/bin/tor-client-auth-gen ''; passthru = { -- cgit 1.4.1 From 3673ded392641ae247638376fd3b9635fc41abc9 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Wed, 3 Mar 2021 17:46:00 +0100 Subject: nixos/privoxy: add https and settings options This is a major rewrite of the Privoxy module: - As per RFC0042, remove privoxy.extraConfig and replace it with a privoxy.settings option, which maps a NixOS freeform submodule to the Privoxy configuration format. - Move all top-level options that mirrored a setting to the real ones in privoxy.settings. This still keeps the type-checking, default values and examples in places. - Add two convenience options: userActions and userFilters, which simplify the operation of creating a file with pkgs.writeText, converting it to a string and adding it to the actionsfile/ filterfile list. - Add a privoxy.inspectHttps option to automagically setup TLS decryption support. I don't know how long have been waiting for this feature: can't believe it has just happened. - Also add a privoxy.certsLifetime to control the periodical cleanup of the temporary certificates generate by Privoxy. --- nixos/modules/services/networking/privoxy.nix | 289 +++++++++++++++++++------- 1 file changed, 209 insertions(+), 80 deletions(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 7caae328203..8ab9a6171fc 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -4,26 +4,46 @@ with lib; let - inherit (pkgs) privoxy; - cfg = config.services.privoxy; - confFile = pkgs.writeText "privoxy.conf" ('' - user-manual ${privoxy}/share/doc/privoxy/user-manual - confdir ${privoxy}/etc/ - listen-address ${cfg.listenAddress} - enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"} - ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles} - ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles} - '' + optionalString cfg.enableTor '' - forward-socks5t / 127.0.0.1:9063 . - toggle 1 - enable-remote-toggle 0 - enable-edit-actions 0 - enable-remote-http-toggle 0 - '' + '' - ${cfg.extraConfig} - ''); + serialise = name: val: + if isList val then concatMapStrings (serialise name) val + else if isBool val then serialise name (if val then "1" else "0") + else "${name} ${toString val}\n"; + + configType = with types; + let atom = oneOf [ int bool string path ]; + in attrsOf (either atom (listOf atom)) + // { description = '' + privoxy configuration type. The format consists of an attribute + set of settings. Each setting can be either a value (integer, string, + boolean or path) or a list of such values. + ''; + }; + + ageType = types.str // { + check = x: + isString x && + (builtins.match "([0-9]+([smhdw]|min|ms|us)*)+" x != null); + description = "tmpfiles.d(5) age format"; + }; + + configFile = pkgs.writeText "privoxy.conf" + (concatStrings ( + # Relative paths in some options are relative to confdir. Privoxy seems + # to parse the options in order of appearance, so this must come first. + # Nix however doesn't preserve the order in attrsets, so we have to + # hardcode confdir here. + [ "confdir ${pkgs.privoxy}/etc\n" ] + ++ mapAttrsToList serialise cfg.settings + )); + + inspectAction = pkgs.writeText "inspect-all-https.action" + '' + # Enable HTTPS inspection for all requests + {+https-inspection} + / + ''; in @@ -31,70 +51,130 @@ in ###### interface - options = { + options.services.privoxy = { - services.privoxy = { + enable = mkEnableOption "Privoxy, non-caching filtering proxy"; - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to enable the Privoxy non-caching filtering proxy. - ''; - }; + enableTor = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure Privoxy to use Tor's faster SOCKS port, + suitable for HTTP. + ''; + }; - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1:8118"; - description = '' - Address the proxy server is listening to. - ''; - }; + inspectHttps = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure Privoxy to inspect HTTPS requests, meaning all + encrypted traffic will be filtered as well. This works by decrypting + and re-encrypting the requests using a per-domain generated certificate. - actionsFiles = mkOption { - type = types.listOf types.str; - example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ]; - default = [ "match-all.action" "default.action" ]; - description = '' - List of paths to Privoxy action files. - These paths may either be absolute or relative to the privoxy configuration directory. - ''; - }; + To issue per-domain certificates, Privoxy must be provided with a CA + certificate, using the ca-cert-file, + ca-key-file settings. - filterFiles = mkOption { - type = types.listOf types.str; - example = [ "default.filter" "/etc/privoxy/user.filter" ]; - default = [ "default.filter" ]; - description = '' - List of paths to Privoxy filter files. - These paths may either be absolute or relative to the privoxy configuration directory. - ''; - }; + + The CA certificate must also be added to the system trust roots, + otherwise browsers will reject all Privoxy certificates as invalid. + You can do so by using the option + . + + ''; + }; - enableEditActions = mkOption { - type = types.bool; - default = false; - description = '' - Whether or not the web-based actions file editor may be used. - ''; - }; + certsLifetime = mkOption { + type = ageType; + default = "10d"; + example = "12h"; + description = '' + If inspectHttps is enabled, the time generated HTTPS + certificates will be stored in a temporary directory for reuse. Once + the lifetime has expired the directory will cleared and the certificate + will have to be generated again, on-demand. - enableTor = mkOption { - type = types.bool; - default = false; - description = '' - Whether to configure Privoxy to use Tor's faster SOCKS port, - suitable for HTTP. - ''; - }; + Depending on the traffic, you may want to reduce the lifetime to limit + the disk usage, since Privoxy itself never deletes the certificates. - extraConfig = mkOption { - type = types.lines; - default = "" ; - description = '' - Extra configuration. Contents will be added verbatim to the configuration file. - ''; + The format is that of the tmpfiles.d(5) + Age parameter. + ''; + }; + + userActions = mkOption { + type = types.lines; + default = ""; + description = '' + Actions to be included in a user.action file. This + will have a higher priority and can be used to override all other + actions. + ''; + }; + + userFilters = mkOption { + type = types.lines; + default = ""; + description = '' + Filters to be included in a user.filter file. This + will have a higher priority and can be used to override all other + filters definitions. + ''; + }; + + settings = mkOption { + type = types.submodule { + freeformType = configType; + + options.listen-address = mkOption { + type = types.str; + default = "127.0.0.1:8118"; + description = "Pair of address:port the proxy server is listening to."; + }; + + options.enable-edit-actions = mkOption { + type = types.bool; + default = false; + description = "Whether the web-based actions file editor may be used."; + }; + + options.actionsfile = mkOption { + type = types.listOf types.str; + # This must come after all other entries, in order to override the + # other actions/filters installed by Privoxy or the user. + apply = x: x ++ optional (cfg.userActions != "") + (toString (pkgs.writeText "user.actions" cfg.userActions)); + default = [ "match-all.action" "default.action" ]; + description = '' + List of paths to Privoxy action files. These paths may either be + absolute or relative to the privoxy configuration directory. + ''; + }; + + options.filterfile = mkOption { + type = types.listOf types.str; + default = [ "default.filter" ]; + apply = x: x ++ optional (cfg.userFilters != "") + (toString (pkgs.writeText "user.filter" cfg.userFilters)); + description = '' + List of paths to Privoxy filter files. These paths may either be + absolute or relative to the privoxy configuration directory. + ''; + }; }; + default = {}; + example = literalExample '' + { listen-address = "[::]:8118"; # listen on IPv6 only + forward-socks5 = ".onion localhost:9050 ."; # forward .onion requests to Tor + } + ''; + description = '' + This option is mapped to the main Privoxy configuration file. + Check out the Privoxy user manual at + + for available settings and documentation. + ''; }; }; @@ -104,23 +184,34 @@ in config = mkIf cfg.enable { users.users.privoxy = { + description = "Privoxy daemon user"; isSystemUser = true; - home = "/var/empty"; group = "privoxy"; }; users.groups.privoxy = {}; + systemd.tmpfiles.rules = with cfg.settings; [ + "d ${certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}" + ]; + systemd.services.privoxy = { description = "Filtering web proxy"; after = [ "network.target" "nss-lookup.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}"; - - serviceConfig.PrivateDevices = true; - serviceConfig.PrivateTmp = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectSystem = "full"; + serviceConfig = { + User = "privoxy"; + Group = "privoxy"; + ExecStart = "${pkgs.privoxy}/bin/privoxy --no-daemon ${configFile}"; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "full"; + }; + unitConfig = mkIf cfg.inspectHttps { + ConditionPathExists = with cfg.settings; + [ ca-cert-file ca-key-file ]; + }; }; services.tor.settings.SOCKSPort = mkIf cfg.enableTor [ @@ -128,8 +219,46 @@ in { addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; } ]; + services.privoxy.settings = { + user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual"; + filterfile = [ "default.filter" ]; + actionsfile = + [ "match-all.action" + "default.action" + ] ++ optional cfg.inspectHttps (toString inspectAction); + } // (optionalAttrs cfg.enableTor { + forward-socks5 = "127.0.0.1:9063 ."; + toggle = true; + enable-remote-toggle = false; + enable-edit-actions = false; + enable-remote-http-toggle = false; + }) // (optionalAttrs cfg.inspectHttps { + # This allows setting absolute key/crt paths + ca-directory = "/var/empty"; + certificate-directory = "/run/privoxy/certs"; + trusted-cas-file = "/etc/ssl/certs/ca-certificates.crt"; + }); + }; + imports = + let + top = x: [ "services" "privoxy" x ]; + setting = x: [ "services" "privoxy" "settings" x ]; + in + [ (mkRenamedOptionModule (top "enableEditActions") (setting "enable-edit-actions")) + (mkRenamedOptionModule (top "listenAddress") (setting "listen-address")) + (mkRenamedOptionModule (top "actionsFiles") (setting "actionsfile")) + (mkRenamedOptionModule (top "filterFiles") (setting "filterfile")) + (mkRemovedOptionModule (top "extraConfig") + '' + Use services.privoxy.settings instead. + This is part of the general move to use structured settings instead of raw + text for config as introduced by RFC0042: + https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md + '') + ]; + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; } -- cgit 1.4.1 From 8e21a1c51bbcdddd001c60cf9ea3975b2e317bbe Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Fri, 5 Mar 2021 16:16:55 +0100 Subject: nixos/privoxy: set temporary directory This is needed for working external filters, otherwise privoxy will fail without a clear error message. --- nixos/modules/services/networking/privoxy.nix | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 8ab9a6171fc..b8d33916342 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -221,6 +221,8 @@ in services.privoxy.settings = { user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual"; + # This is needed for external filters + temporary-directory = "/tmp"; filterfile = [ "default.filter" ]; actionsfile = [ "match-all.action" -- cgit 1.4.1 From df6d7f314227bafec6fce798f4019a48d6634a8e Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Tue, 9 Mar 2021 16:59:35 +0100 Subject: nixos/privoxy: document repeated settings --- nixos/modules/services/networking/privoxy.nix | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index b8d33916342..f1a9c6029cb 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -165,15 +165,29 @@ in }; default = {}; example = literalExample '' - { listen-address = "[::]:8118"; # listen on IPv6 only - forward-socks5 = ".onion localhost:9050 ."; # forward .onion requests to Tor + { # Listen on IPv6 only + listen-address = "[::]:8118"; + + # Forward .onion requests to Tor + forward-socks5 = ".onion localhost:9050 ."; + + # Log redirects and filters + debug = [ 128 64 ]; + # This is equivalent to writing these lines + # in the Privoxy configuration file: + # debug 128 + # debug 64 } ''; description = '' This option is mapped to the main Privoxy configuration file. Check out the Privoxy user manual at - + for available settings and documentation. + + + Repeated settings can be represented by using a list. + ''; }; -- cgit 1.4.1 From 7962df46febf67976dadf6ecf9acc033804580bd Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Thu, 11 Mar 2021 08:02:09 +0100 Subject: nixos/privoxy: make certificate-directory optional The tmpfiles.d rule should only be added if inspectHttps is enabled. --- nixos/modules/services/networking/privoxy.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index f1a9c6029cb..7c22b7d09b9 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -205,9 +205,8 @@ in users.groups.privoxy = {}; - systemd.tmpfiles.rules = with cfg.settings; [ - "d ${certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}" - ]; + systemd.tmpfiles.rules = optional cfg.inspectHttps + "d ${cfg.settings.certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}"; systemd.services.privoxy = { description = "Filtering web proxy"; -- cgit 1.4.1 From 273f5c38a358ce623234c5a9da904b3b1722835d Mon Sep 17 00:00:00 2001 From: Xinglu Chen Date: Sun, 28 Mar 2021 21:16:55 +0200 Subject: nixos/privoxy: add missing "/" to "forward-socks5" option Without this, Privoxy will silently fail, meaning that no traffic would be routed through Tor, giving users a false sense of privacy. --- nixos/modules/services/networking/privoxy.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nixos/modules/services/networking/privoxy.nix') diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 7c22b7d09b9..df818baa465 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -242,7 +242,7 @@ in "default.action" ] ++ optional cfg.inspectHttps (toString inspectAction); } // (optionalAttrs cfg.enableTor { - forward-socks5 = "127.0.0.1:9063 ."; + forward-socks5 = "/ 127.0.0.1:9063 ."; toggle = true; enable-remote-toggle = false; enable-edit-actions = false; -- cgit 1.4.1