at 21.11-pre 48 kB view raw
1{ config, lib, pkgs, ... }: 2 3with builtins; 4with lib; 5 6let 7 cfg = config.services.tor; 8 stateDir = "/var/lib/tor"; 9 runDir = "/run/tor"; 10 descriptionGeneric = option: '' 11 See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en#${option}">torrc manual</link>. 12 ''; 13 bindsPrivilegedPort = 14 any (p0: 15 let p1 = if p0 ? "port" then p0.port else p0; in 16 if p1 == "auto" then false 17 else let p2 = if isInt p1 then p1 else toInt p1; in 18 p1 != null && 0 < p2 && p2 < 1024) 19 (flatten [ 20 cfg.settings.ORPort 21 cfg.settings.DirPort 22 cfg.settings.DNSPort 23 cfg.settings.ExtORPort 24 cfg.settings.HTTPTunnelPort 25 cfg.settings.NATDPort 26 cfg.settings.SOCKSPort 27 cfg.settings.TransPort 28 ]); 29 optionBool = optionName: mkOption { 30 type = with types; nullOr bool; 31 default = null; 32 description = descriptionGeneric optionName; 33 }; 34 optionInt = optionName: mkOption { 35 type = with types; nullOr int; 36 default = null; 37 description = descriptionGeneric optionName; 38 }; 39 optionString = optionName: mkOption { 40 type = with types; nullOr str; 41 default = null; 42 description = descriptionGeneric optionName; 43 }; 44 optionStrings = optionName: mkOption { 45 type = with types; listOf str; 46 default = []; 47 description = descriptionGeneric optionName; 48 }; 49 optionAddress = mkOption { 50 type = with types; nullOr str; 51 default = null; 52 example = "0.0.0.0"; 53 description = '' 54 IPv4 or IPv6 (if between brackets) address. 55 ''; 56 }; 57 optionUnix = mkOption { 58 type = with types; nullOr path; 59 default = null; 60 description = '' 61 Unix domain socket path to use. 62 ''; 63 }; 64 optionPort = mkOption { 65 type = with types; nullOr (oneOf [port (enum ["auto"])]); 66 default = null; 67 }; 68 optionPorts = optionName: mkOption { 69 type = with types; listOf port; 70 default = []; 71 description = descriptionGeneric optionName; 72 }; 73 optionIsolablePort = with types; oneOf [ 74 port (enum ["auto"]) 75 (submodule ({config, ...}: { 76 options = { 77 addr = optionAddress; 78 port = optionPort; 79 flags = optionFlags; 80 SessionGroup = mkOption { type = nullOr int; default = null; }; 81 } // genAttrs isolateFlags (name: mkOption { type = types.bool; default = false; }); 82 config = { 83 flags = filter (name: config.${name} == true) isolateFlags ++ 84 optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}"; 85 }; 86 })) 87 ]; 88 optionIsolablePorts = optionName: mkOption { 89 default = []; 90 type = with types; either optionIsolablePort (listOf optionIsolablePort); 91 description = descriptionGeneric optionName; 92 }; 93 isolateFlags = [ 94 "IsolateClientAddr" 95 "IsolateClientProtocol" 96 "IsolateDestAddr" 97 "IsolateDestPort" 98 "IsolateSOCKSAuth" 99 "KeepAliveIsolateSOCKSAuth" 100 ]; 101 optionSOCKSPort = doConfig: let 102 flags = [ 103 "CacheDNS" "CacheIPv4DNS" "CacheIPv6DNS" "GroupWritable" "IPv6Traffic" 104 "NoDNSRequest" "NoIPv4Traffic" "NoOnionTraffic" "OnionTrafficOnly" 105 "PreferIPv6" "PreferIPv6Automap" "PreferSOCKSNoAuth" "UseDNSCache" 106 "UseIPv4Cache" "UseIPv6Cache" "WorldWritable" 107 ] ++ isolateFlags; 108 in with types; oneOf [ 109 port (submodule ({config, ...}: { 110 options = { 111 unix = optionUnix; 112 addr = optionAddress; 113 port = optionPort; 114 flags = optionFlags; 115 SessionGroup = mkOption { type = nullOr int; default = null; }; 116 } // genAttrs flags (name: mkOption { type = types.bool; default = false; }); 117 config = mkIf doConfig { # Only add flags in SOCKSPort to avoid duplicates 118 flags = filter (name: config.${name} == true) flags ++ 119 optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}"; 120 }; 121 })) 122 ]; 123 optionFlags = mkOption { 124 type = with types; listOf str; 125 default = []; 126 }; 127 optionORPort = optionName: mkOption { 128 default = []; 129 example = 443; 130 type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [ 131 port 132 (enum ["auto"]) 133 (submodule ({config, ...}: 134 let flags = [ "IPv4Only" "IPv6Only" "NoAdvertise" "NoListen" ]; 135 in { 136 options = { 137 addr = optionAddress; 138 port = optionPort; 139 flags = optionFlags; 140 } // genAttrs flags (name: mkOption { type = types.bool; default = false; }); 141 config = { 142 flags = filter (name: config.${name} == true) flags; 143 }; 144 })) 145 ]))]; 146 description = descriptionGeneric optionName; 147 }; 148 optionBandwith = optionName: mkOption { 149 type = with types; nullOr (either int str); 150 default = null; 151 description = descriptionGeneric optionName; 152 }; 153 optionPath = optionName: mkOption { 154 type = with types; nullOr path; 155 default = null; 156 description = descriptionGeneric optionName; 157 }; 158 159 mkValueString = k: v: 160 if v == null then "" 161 else if isBool v then 162 (if v then "1" else "0") 163 else if v ? "unix" && v.unix != null then 164 "unix:"+v.unix + 165 optionalString (v ? "flags") (" " + concatStringsSep " " v.flags) 166 else if v ? "port" && v.port != null then 167 optionalString (v ? "addr" && v.addr != null) "${v.addr}:" + 168 toString v.port + 169 optionalString (v ? "flags") (" " + concatStringsSep " " v.flags) 170 else if k == "ServerTransportPlugin" then 171 optionalString (v.transports != []) "${concatStringsSep "," v.transports} exec ${v.exec}" 172 else if k == "HidServAuth" then 173 v.onion + " " + v.auth 174 else generators.mkValueStringDefault {} v; 175 genTorrc = settings: 176 generators.toKeyValue { 177 listsAsDuplicateKeys = true; 178 mkKeyValue = k: generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k; 179 } 180 (lib.mapAttrs (k: v: 181 # Not necesssary, but prettier rendering 182 if elem k [ "AutomapHostsSuffixes" "DirPolicy" "ExitPolicy" "SocksPolicy" ] 183 && v != [] 184 then concatStringsSep "," v 185 else v) 186 (lib.filterAttrs (k: v: !(v == null || v == "")) 187 settings)); 188 torrc = pkgs.writeText "torrc" ( 189 genTorrc cfg.settings + 190 concatStrings (mapAttrsToList (name: onion: 191 "HiddenServiceDir ${onion.path}\n" + 192 genTorrc onion.settings) cfg.relay.onionServices) 193 ); 194in 195{ 196 imports = [ 197 (mkRenamedOptionModule [ "services" "tor" "client" "dns" "automapHostsSuffixes" ] [ "services" "tor" "settings" "AutomapHostsSuffixes" ]) 198 (mkRemovedOptionModule [ "services" "tor" "client" "dns" "isolationOptions" ] "Use services.tor.settings.DNSPort instead.") 199 (mkRemovedOptionModule [ "services" "tor" "client" "dns" "listenAddress" ] "Use services.tor.settings.DNSPort instead.") 200 (mkRemovedOptionModule [ "services" "tor" "client" "privoxy" "enable" ] "Use services.privoxy.enable and services.privoxy.enableTor instead.") 201 (mkRemovedOptionModule [ "services" "tor" "client" "socksIsolationOptions" ] "Use services.tor.settings.SOCKSPort instead.") 202 (mkRemovedOptionModule [ "services" "tor" "client" "socksListenAddressFaster" ] "Use services.tor.settings.SOCKSPort instead.") 203 (mkRenamedOptionModule [ "services" "tor" "client" "socksPolicy" ] [ "services" "tor" "settings" "SocksPolicy" ]) 204 (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "isolationOptions" ] "Use services.tor.settings.TransPort instead.") 205 (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "listenAddress" ] "Use services.tor.settings.TransPort instead.") 206 (mkRenamedOptionModule [ "services" "tor" "controlPort" ] [ "services" "tor" "settings" "ControlPort" ]) 207 (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Plese use services.tor.settings instead.") 208 (mkRenamedOptionModule [ "services" "tor" "hiddenServices" ] [ "services" "tor" "relay" "onionServices" ]) 209 (mkRenamedOptionModule [ "services" "tor" "relay" "accountingMax" ] [ "services" "tor" "settings" "AccountingMax" ]) 210 (mkRenamedOptionModule [ "services" "tor" "relay" "accountingStart" ] [ "services" "tor" "settings" "AccountingStart" ]) 211 (mkRenamedOptionModule [ "services" "tor" "relay" "address" ] [ "services" "tor" "settings" "Address" ]) 212 (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthBurst" ] [ "services" "tor" "settings" "BandwidthBurst" ]) 213 (mkRenamedOptionModule [ "services" "tor" "relay" "bandwidthRate" ] [ "services" "tor" "settings" "BandwidthRate" ]) 214 (mkRenamedOptionModule [ "services" "tor" "relay" "bridgeTransports" ] [ "services" "tor" "settings" "ServerTransportPlugin" "transports" ]) 215 (mkRenamedOptionModule [ "services" "tor" "relay" "contactInfo" ] [ "services" "tor" "settings" "ContactInfo" ]) 216 (mkRenamedOptionModule [ "services" "tor" "relay" "exitPolicy" ] [ "services" "tor" "settings" "ExitPolicy" ]) 217 (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.") 218 (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.") 219 (mkRenamedOptionModule [ "services" "tor" "relay" "nickname" ] [ "services" "tor" "settings" "Nickname" ]) 220 (mkRenamedOptionModule [ "services" "tor" "relay" "port" ] [ "services" "tor" "settings" "ORPort" ]) 221 (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "settings" "ORPort" ]) 222 ]; 223 224 options = { 225 services.tor = { 226 enable = mkEnableOption ''Tor daemon. 227 By default, the daemon is run without 228 relay, exit, bridge or client connectivity''; 229 230 openFirewall = mkEnableOption "opening of the relay port(s) in the firewall"; 231 232 package = mkOption { 233 type = types.package; 234 default = pkgs.tor; 235 defaultText = "pkgs.tor"; 236 example = literalExample "pkgs.tor"; 237 description = "Tor package to use."; 238 }; 239 240 enableGeoIP = mkEnableOption ''use of GeoIP databases. 241 Disabling this will disable by-country statistics for bridges and relays 242 and some client and third-party software functionality'' // { default = true; }; 243 244 controlSocket.enable = mkEnableOption ''control socket, 245 created in <literal>${runDir}/control</literal>''; 246 247 client = { 248 enable = mkEnableOption ''the routing of application connections. 249 You might want to disable this if you plan running a dedicated Tor relay''; 250 251 transparentProxy.enable = mkEnableOption "transparent proxy"; 252 dns.enable = mkEnableOption "DNS resolver"; 253 254 socksListenAddress = mkOption { 255 type = optionSOCKSPort false; 256 default = {addr = "127.0.0.1"; port = 9050; IsolateDestAddr = true;}; 257 example = {addr = "192.168.0.1"; port = 9090; IsolateDestAddr = true;}; 258 description = '' 259 Bind to this address to listen for connections from 260 Socks-speaking applications. 261 ''; 262 }; 263 264 onionServices = mkOption { 265 description = descriptionGeneric "HiddenServiceDir"; 266 default = {}; 267 example = { 268 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" = { 269 clientAuthorizations = ["/run/keys/tor/alice.prv.x25519"]; 270 }; 271 }; 272 type = types.attrsOf (types.submodule ({name, config, ...}: { 273 options.clientAuthorizations = mkOption { 274 description = '' 275 Clients' authorizations for a v3 onion service, 276 as a list of files containing each one private key, in the format: 277 <screen>descriptor:x25519:&lt;base32-private-key&gt;</screen> 278 '' + descriptionGeneric "_client_authorization"; 279 type = with types; listOf path; 280 default = []; 281 example = ["/run/keys/tor/alice.prv.x25519"]; 282 }; 283 })); 284 }; 285 }; 286 287 relay = { 288 enable = mkEnableOption ''relaying of Tor traffic for others. 289 290 See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" /> 291 for details. 292 293 Setting this to true requires setting 294 <option>services.tor.relay.role</option> 295 and 296 <option>services.tor.settings.ORPort</option> 297 options''; 298 299 role = mkOption { 300 type = types.enum [ "exit" "relay" "bridge" "private-bridge" ]; 301 description = '' 302 Your role in Tor network. There're several options: 303 304 <variablelist> 305 <varlistentry> 306 <term><literal>exit</literal></term> 307 <listitem> 308 <para> 309 An exit relay. This allows Tor users to access regular 310 Internet services through your public IP. 311 </para> 312 313 <important><para> 314 Running an exit relay may expose you to abuse 315 complaints. See 316 <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies"/> 317 for more info. 318 </para></important> 319 320 <para> 321 You can specify which services Tor users may access via 322 your exit relay using <option>settings.ExitPolicy</option> option. 323 </para> 324 </listitem> 325 </varlistentry> 326 327 <varlistentry> 328 <term><literal>relay</literal></term> 329 <listitem> 330 <para> 331 Regular relay. This allows Tor users to relay onion 332 traffic to other Tor nodes, but not to public 333 Internet. 334 </para> 335 336 <important><para> 337 Note that some misconfigured and/or disrespectful 338 towards privacy sites will block you even if your 339 relay is not an exit relay. That is, just being listed 340 in a public relay directory can have unwanted 341 consequences. 342 343 Which means you might not want to use 344 this role if you browse public Internet from the same 345 network as your relay, unless you want to write 346 e-mails to those sites (you should!). 347 </para></important> 348 349 <para> 350 See 351 <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" /> 352 for more info. 353 </para> 354 </listitem> 355 </varlistentry> 356 357 <varlistentry> 358 <term><literal>bridge</literal></term> 359 <listitem> 360 <para> 361 Regular bridge. Works like a regular relay, but 362 doesn't list you in the public relay directory and 363 hides your Tor node behind obfs4proxy. 364 </para> 365 366 <para> 367 Using this option will make Tor advertise your bridge 368 to users through various mechanisms like 369 <link xlink:href="https://bridges.torproject.org/" />, though. 370 </para> 371 372 <important> 373 <para> 374 WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVICE. 375 Consult with your lawyer when in doubt. 376 </para> 377 378 <para> 379 This role should be safe to use in most situations 380 (unless the act of forwarding traffic for others is 381 a punishable offence under your local laws, which 382 would be pretty insane as it would make ISP illegal). 383 </para> 384 </important> 385 386 <para> 387 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" /> 388 for more info. 389 </para> 390 </listitem> 391 </varlistentry> 392 393 <varlistentry> 394 <term><literal>private-bridge</literal></term> 395 <listitem> 396 <para> 397 Private bridge. Works like regular bridge, but does 398 not advertise your node in any way. 399 </para> 400 401 <para> 402 Using this role means that you won't contribute to Tor 403 network in any way unless you advertise your node 404 yourself in some way. 405 </para> 406 407 <para> 408 Use this if you want to run a private bridge, for 409 example because you'll give out your bridge addr 410 manually to your friends. 411 </para> 412 413 <para> 414 Switching to this role after measurable time in 415 "bridge" role is pretty useless as some Tor users 416 would have learned about your node already. In the 417 latter case you can still change 418 <option>port</option> option. 419 </para> 420 421 <para> 422 See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" /> 423 for more info. 424 </para> 425 </listitem> 426 </varlistentry> 427 </variablelist> 428 ''; 429 }; 430 431 onionServices = mkOption { 432 description = descriptionGeneric "HiddenServiceDir"; 433 default = {}; 434 example = { 435 "example.org/www" = { 436 map = [ 80 ]; 437 authorizedClients = [ 438 "descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 439 ]; 440 }; 441 }; 442 type = types.attrsOf (types.submodule ({name, config, ...}: { 443 options.path = mkOption { 444 type = types.path; 445 description = '' 446 Path where to store the data files of the hidden service. 447 If the <option>secretKey</option> is null 448 this defaults to <literal>${stateDir}/onion/$onion</literal>, 449 otherwise to <literal>${runDir}/onion/$onion</literal>. 450 ''; 451 }; 452 options.secretKey = mkOption { 453 type = with types; nullOr path; 454 default = null; 455 example = "/run/keys/tor/onion/expyuzz4wqqyqhjn/hs_ed25519_secret_key"; 456 description = '' 457 Secret key of the onion service. 458 If null, Tor reuses any preexisting secret key (in <option>path</option>) 459 or generates a new one. 460 The associated public key and hostname are deterministically regenerated 461 from this file if they do not exist. 462 ''; 463 }; 464 options.authorizeClient = mkOption { 465 description = descriptionGeneric "HiddenServiceAuthorizeClient"; 466 default = null; 467 type = types.nullOr (types.submodule ({...}: { 468 options = { 469 authType = mkOption { 470 type = types.enum [ "basic" "stealth" ]; 471 description = '' 472 Either <literal>"basic"</literal> for a general-purpose authorization protocol 473 or <literal>"stealth"</literal> for a less scalable protocol 474 that also hides service activity from unauthorized clients. 475 ''; 476 }; 477 clientNames = mkOption { 478 type = with types; nonEmptyListOf (strMatching "[A-Za-z0-9+-_]+"); 479 description = '' 480 Only clients that are listed here are authorized to access the hidden service. 481 Generated authorization data can be found in <filename>${stateDir}/onion/$name/hostname</filename>. 482 Clients need to put this authorization data in their configuration file using 483 <xref linkend="opt-services.tor.settings.HidServAuth"/>. 484 ''; 485 }; 486 }; 487 })); 488 }; 489 options.authorizedClients = mkOption { 490 description = '' 491 Authorized clients for a v3 onion service, 492 as a list of public key, in the format: 493 <screen>descriptor:x25519:&lt;base32-public-key&gt;</screen> 494 '' + descriptionGeneric "_client_authorization"; 495 type = with types; listOf str; 496 default = []; 497 example = ["descriptor:x25519:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]; 498 }; 499 options.map = mkOption { 500 description = descriptionGeneric "HiddenServicePort"; 501 type = with types; listOf (oneOf [ 502 port (submodule ({...}: { 503 options = { 504 port = optionPort; 505 target = mkOption { 506 default = null; 507 type = nullOr (submodule ({...}: { 508 options = { 509 unix = optionUnix; 510 addr = optionAddress; 511 port = optionPort; 512 }; 513 })); 514 }; 515 }; 516 })) 517 ]); 518 apply = map (v: if isInt v then {port=v; target=null;} else v); 519 }; 520 options.version = mkOption { 521 description = descriptionGeneric "HiddenServiceVersion"; 522 type = with types; nullOr (enum [2 3]); 523 default = null; 524 }; 525 options.settings = mkOption { 526 description = '' 527 Settings of the onion service. 528 '' + descriptionGeneric "_hidden_service_options"; 529 default = {}; 530 type = types.submodule { 531 freeformType = with types; 532 (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // { 533 description = "settings option"; 534 }; 535 options.HiddenServiceAllowUnknownPorts = optionBool "HiddenServiceAllowUnknownPorts"; 536 options.HiddenServiceDirGroupReadable = optionBool "HiddenServiceDirGroupReadable"; 537 options.HiddenServiceExportCircuitID = mkOption { 538 description = descriptionGeneric "HiddenServiceExportCircuitID"; 539 type = with types; nullOr (enum ["haproxy"]); 540 default = null; 541 }; 542 options.HiddenServiceMaxStreams = mkOption { 543 description = descriptionGeneric "HiddenServiceMaxStreams"; 544 type = with types; nullOr (ints.between 0 65535); 545 default = null; 546 }; 547 options.HiddenServiceMaxStreamsCloseCircuit = optionBool "HiddenServiceMaxStreamsCloseCircuit"; 548 options.HiddenServiceNumIntroductionPoints = mkOption { 549 description = descriptionGeneric "HiddenServiceNumIntroductionPoints"; 550 type = with types; nullOr (ints.between 0 20); 551 default = null; 552 }; 553 options.HiddenServiceSingleHopMode = optionBool "HiddenServiceSingleHopMode"; 554 options.RendPostPeriod = optionString "RendPostPeriod"; 555 }; 556 }; 557 config = { 558 path = mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}"); 559 settings.HiddenServiceVersion = config.version; 560 settings.HiddenServiceAuthorizeClient = 561 if config.authorizeClient != null then 562 config.authorizeClient.authType + " " + 563 concatStringsSep "," config.authorizeClient.clientNames 564 else null; 565 settings.HiddenServicePort = map (p: mkValueString "" p.port + " " + mkValueString "" p.target) config.map; 566 }; 567 })); 568 }; 569 }; 570 571 settings = mkOption { 572 description = '' 573 See <link xlink:href="https://2019.www.torproject.org/docs/tor-manual.html.en">torrc manual</link> 574 for documentation. 575 ''; 576 default = {}; 577 type = types.submodule { 578 freeformType = with types; 579 (attrsOf (nullOr (oneOf [str int bool (listOf str)]))) // { 580 description = "settings option"; 581 }; 582 options.Address = optionString "Address"; 583 options.AssumeReachable = optionBool "AssumeReachable"; 584 options.AccountingMax = optionBandwith "AccountingMax"; 585 options.AccountingStart = optionString "AccountingStart"; 586 options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity"; 587 options.AuthDirListBadExits = optionBool "AuthDirListBadExits"; 588 options.AuthDirPinKeys = optionBool "AuthDirPinKeys"; 589 options.AuthDirSharedRandomness = optionBool "AuthDirSharedRandomness"; 590 options.AuthDirTestEd25519LinkKeys = optionBool "AuthDirTestEd25519LinkKeys"; 591 options.AuthoritativeDirectory = optionBool "AuthoritativeDirectory"; 592 options.AutomapHostsOnResolve = optionBool "AutomapHostsOnResolve"; 593 options.AutomapHostsSuffixes = optionStrings "AutomapHostsSuffixes" // { 594 default = [".onion" ".exit"]; 595 example = [".onion"]; 596 }; 597 options.BandwidthBurst = optionBandwith "BandwidthBurst"; 598 options.BandwidthRate = optionBandwith "BandwidthRate"; 599 options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir"; 600 options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry"; 601 options.BridgeRelay = optionBool "BridgeRelay" // { default = false; }; 602 options.CacheDirectory = optionPath "CacheDirectory"; 603 options.CacheDirectoryGroupReadable = optionBool "CacheDirectoryGroupReadable"; # default is null and like "auto" 604 options.CellStatistics = optionBool "CellStatistics"; 605 options.ClientAutoIPv6ORPort = optionBool "ClientAutoIPv6ORPort"; 606 options.ClientDNSRejectInternalAddresses = optionBool "ClientDNSRejectInternalAddresses"; 607 options.ClientOnionAuthDir = mkOption { 608 description = descriptionGeneric "ClientOnionAuthDir"; 609 default = null; 610 type = with types; nullOr path; 611 }; 612 options.ClientPreferIPv6DirPort = optionBool "ClientPreferIPv6DirPort"; # default is null and like "auto" 613 options.ClientPreferIPv6ORPort = optionBool "ClientPreferIPv6ORPort"; # default is null and like "auto" 614 options.ClientRejectInternalAddresses = optionBool "ClientRejectInternalAddresses"; 615 options.ClientUseIPv4 = optionBool "ClientUseIPv4"; 616 options.ClientUseIPv6 = optionBool "ClientUseIPv6"; 617 options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics"; 618 options.ConstrainedSockets = optionBool "ConstrainedSockets"; 619 options.ContactInfo = optionString "ContactInfo"; 620 options.ControlPort = mkOption rec { 621 description = descriptionGeneric "ControlPort"; 622 default = []; 623 example = [{port = 9051;}]; 624 type = with types; oneOf [port (enum ["auto"]) (listOf (oneOf [ 625 port (enum ["auto"]) (submodule ({config, ...}: let 626 flags = ["GroupWritable" "RelaxDirModeCheck" "WorldWritable"]; 627 in { 628 options = { 629 unix = optionUnix; 630 flags = optionFlags; 631 addr = optionAddress; 632 port = optionPort; 633 } // genAttrs flags (name: mkOption { type = types.bool; default = false; }); 634 config = { 635 flags = filter (name: config.${name} == true) flags; 636 }; 637 })) 638 ]))]; 639 }; 640 options.ControlPortFileGroupReadable= optionBool "ControlPortFileGroupReadable"; 641 options.ControlPortWriteToFile = optionPath "ControlPortWriteToFile"; 642 options.ControlSocket = optionPath "ControlSocket"; 643 options.ControlSocketsGroupWritable = optionBool "ControlSocketsGroupWritable"; 644 options.CookieAuthFile = optionPath "CookieAuthFile"; 645 options.CookieAuthFileGroupReadable = optionBool "CookieAuthFileGroupReadable"; 646 options.CookieAuthentication = optionBool "CookieAuthentication"; 647 options.DataDirectory = optionPath "DataDirectory" // { default = stateDir; }; 648 options.DataDirectoryGroupReadable = optionBool "DataDirectoryGroupReadable"; 649 options.DirPortFrontPage = optionPath "DirPortFrontPage"; 650 options.DirAllowPrivateAddresses = optionBool "DirAllowPrivateAddresses"; 651 options.DormantCanceledByStartup = optionBool "DormantCanceledByStartup"; 652 options.DormantOnFirstStartup = optionBool "DormantOnFirstStartup"; 653 options.DormantTimeoutDisabledByIdleStreams = optionBool "DormantTimeoutDisabledByIdleStreams"; 654 options.DirCache = optionBool "DirCache"; 655 options.DirPolicy = mkOption { 656 description = descriptionGeneric "DirPolicy"; 657 type = with types; listOf str; 658 default = []; 659 example = ["accept *:*"]; 660 }; 661 options.DirPort = optionORPort "DirPort"; 662 options.DirReqStatistics = optionBool "DirReqStatistics"; 663 options.DisableAllSwap = optionBool "DisableAllSwap"; 664 options.DisableDebuggerAttachment = optionBool "DisableDebuggerAttachment"; 665 options.DisableNetwork = optionBool "DisableNetwork"; 666 options.DisableOOSCheck = optionBool "DisableOOSCheck"; 667 options.DNSPort = optionIsolablePorts "DNSPort"; 668 options.DoSCircuitCreationEnabled = optionBool "DoSCircuitCreationEnabled"; 669 options.DoSConnectionEnabled = optionBool "DoSConnectionEnabled"; # default is null and like "auto" 670 options.DoSRefuseSingleHopClientRendezvous = optionBool "DoSRefuseSingleHopClientRendezvous"; 671 options.DownloadExtraInfo = optionBool "DownloadExtraInfo"; 672 options.EnforceDistinctSubnets = optionBool "EnforceDistinctSubnets"; 673 options.EntryStatistics = optionBool "EntryStatistics"; 674 options.ExitPolicy = optionStrings "ExitPolicy" // { 675 default = ["reject *:*"]; 676 example = ["accept *:*"]; 677 }; 678 options.ExitPolicyRejectLocalInterfaces = optionBool "ExitPolicyRejectLocalInterfaces"; 679 options.ExitPolicyRejectPrivate = optionBool "ExitPolicyRejectPrivate"; 680 options.ExitPortStatistics = optionBool "ExitPortStatistics"; 681 options.ExitRelay = optionBool "ExitRelay"; # default is null and like "auto" 682 options.ExtORPort = mkOption { 683 description = descriptionGeneric "ExtORPort"; 684 default = null; 685 type = with types; nullOr (oneOf [ 686 port (enum ["auto"]) (submodule ({...}: { 687 options = { 688 addr = optionAddress; 689 port = optionPort; 690 }; 691 })) 692 ]); 693 apply = p: if isInt p || isString p then { port = p; } else p; 694 }; 695 options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile"; 696 options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable"; 697 options.ExtendAllowPrivateAddresses = optionBool "ExtendAllowPrivateAddresses"; 698 options.ExtraInfoStatistics = optionBool "ExtraInfoStatistics"; 699 options.FascistFirewall = optionBool "FascistFirewall"; 700 options.FetchDirInfoEarly = optionBool "FetchDirInfoEarly"; 701 options.FetchDirInfoExtraEarly = optionBool "FetchDirInfoExtraEarly"; 702 options.FetchHidServDescriptors = optionBool "FetchHidServDescriptors"; 703 options.FetchServerDescriptors = optionBool "FetchServerDescriptors"; 704 options.FetchUselessDescriptors = optionBool "FetchUselessDescriptors"; 705 options.ReachableAddresses = optionStrings "ReachableAddresses"; 706 options.ReachableDirAddresses = optionStrings "ReachableDirAddresses"; 707 options.ReachableORAddresses = optionStrings "ReachableORAddresses"; 708 options.GeoIPFile = optionPath "GeoIPFile"; 709 options.GeoIPv6File = optionPath "GeoIPv6File"; 710 options.GuardfractionFile = optionPath "GuardfractionFile"; 711 options.HidServAuth = mkOption { 712 description = descriptionGeneric "HidServAuth"; 713 default = []; 714 type = with types; listOf (oneOf [ 715 (submodule { 716 options = { 717 onion = mkOption { 718 type = strMatching "[a-z2-7]{16}\\.onion"; 719 description = "Onion address."; 720 example = "xxxxxxxxxxxxxxxx.onion"; 721 }; 722 auth = mkOption { 723 type = strMatching "[A-Za-z0-9+/]{22}"; 724 description = "Authentication cookie."; 725 }; 726 }; 727 }) 728 ]); 729 example = [ 730 { 731 onion = "xxxxxxxxxxxxxxxx.onion"; 732 auth = "xxxxxxxxxxxxxxxxxxxxxx"; 733 } 734 ]; 735 }; 736 options.HiddenServiceNonAnonymousMode = optionBool "HiddenServiceNonAnonymousMode"; 737 options.HiddenServiceStatistics = optionBool "HiddenServiceStatistics"; 738 options.HSLayer2Nodes = optionStrings "HSLayer2Nodes"; 739 options.HSLayer3Nodes = optionStrings "HSLayer3Nodes"; 740 options.HTTPTunnelPort = optionIsolablePorts "HTTPTunnelPort"; 741 options.IPv6Exit = optionBool "IPv6Exit"; 742 options.KeyDirectory = optionPath "KeyDirectory"; 743 options.KeyDirectoryGroupReadable = optionBool "KeyDirectoryGroupReadable"; 744 options.LogMessageDomains = optionBool "LogMessageDomains"; 745 options.LongLivedPorts = optionPorts "LongLivedPorts"; 746 options.MainloopStats = optionBool "MainloopStats"; 747 options.MaxAdvertisedBandwidth = optionBandwith "MaxAdvertisedBandwidth"; 748 options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness"; 749 options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending"; 750 options.NATDPort = optionIsolablePorts "NATDPort"; 751 options.NewCircuitPeriod = optionInt "NewCircuitPeriod"; 752 options.Nickname = optionString "Nickname"; 753 options.ORPort = optionORPort "ORPort"; 754 options.OfflineMasterKey = optionBool "OfflineMasterKey"; 755 options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto" 756 options.PaddingStatistics = optionBool "PaddingStatistics"; 757 options.PerConnBWBurst = optionBandwith "PerConnBWBurst"; 758 options.PerConnBWRate = optionBandwith "PerConnBWRate"; 759 options.PidFile = optionPath "PidFile"; 760 options.ProtocolWarnings = optionBool "ProtocolWarnings"; 761 options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors"; 762 options.PublishServerDescriptor = mkOption { 763 description = descriptionGeneric "PublishServerDescriptor"; 764 type = with types; nullOr (enum [false true 0 1 "0" "1" "v3" "bridge"]); 765 default = null; 766 }; 767 options.ReducedExitPolicy = optionBool "ReducedExitPolicy"; 768 options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto" 769 options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts"; 770 options.RelayBandwidthBurst = optionBandwith "RelayBandwidthBurst"; 771 options.RelayBandwidthRate = optionBandwith "RelayBandwidthRate"; 772 #options.RunAsDaemon 773 options.Sandbox = optionBool "Sandbox"; 774 options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig"; 775 options.ServerDNSAllowNonRFC953Hostnames = optionBool "ServerDNSAllowNonRFC953Hostnames"; 776 options.ServerDNSDetectHijacking = optionBool "ServerDNSDetectHijacking"; 777 options.ServerDNSRandomizeCase = optionBool "ServerDNSRandomizeCase"; 778 options.ServerDNSResolvConfFile = optionPath "ServerDNSResolvConfFile"; 779 options.ServerDNSSearchDomains = optionBool "ServerDNSSearchDomains"; 780 options.ServerTransportPlugin = mkOption { 781 description = descriptionGeneric "ServerTransportPlugin"; 782 default = null; 783 type = with types; nullOr (submodule ({...}: { 784 options = { 785 transports = mkOption { 786 description = "List of pluggable transports."; 787 type = listOf str; 788 example = ["obfs2" "obfs3" "obfs4" "scramblesuit"]; 789 }; 790 exec = mkOption { 791 type = types.str; 792 description = "Command of pluggable transport."; 793 }; 794 }; 795 })); 796 }; 797 options.SocksPolicy = optionStrings "SocksPolicy" // { 798 example = ["accept *:*"]; 799 }; 800 options.SOCKSPort = mkOption { 801 description = descriptionGeneric "SOCKSPort"; 802 default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else []; 803 example = [{port = 9090;}]; 804 type = types.listOf (optionSOCKSPort true); 805 }; 806 options.TestingTorNetwork = optionBool "TestingTorNetwork"; 807 options.TransPort = optionIsolablePorts "TransPort"; 808 options.TransProxyType = mkOption { 809 description = descriptionGeneric "TransProxyType"; 810 type = with types; nullOr (enum ["default" "TPROXY" "ipfw" "pf-divert"]); 811 default = null; 812 }; 813 #options.TruncateLogFile 814 options.UnixSocksGroupWritable = optionBool "UnixSocksGroupWritable"; 815 options.UseDefaultFallbackDirs = optionBool "UseDefaultFallbackDirs"; 816 options.UseMicrodescriptors = optionBool "UseMicrodescriptors"; 817 options.V3AuthUseLegacyKey = optionBool "V3AuthUseLegacyKey"; 818 options.V3AuthoritativeDirectory = optionBool "V3AuthoritativeDirectory"; 819 options.VersioningAuthoritativeDirectory = optionBool "VersioningAuthoritativeDirectory"; 820 options.VirtualAddrNetworkIPv4 = optionString "VirtualAddrNetworkIPv4"; 821 options.VirtualAddrNetworkIPv6 = optionString "VirtualAddrNetworkIPv6"; 822 options.WarnPlaintextPorts = optionPorts "WarnPlaintextPorts"; 823 }; 824 }; 825 }; 826 }; 827 828 config = mkIf cfg.enable { 829 # Not sure if `cfg.relay.role == "private-bridge"` helps as tor 830 # sends a lot of stats 831 warnings = optional (cfg.settings.BridgeRelay && 832 flatten (mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != []) 833 '' 834 Running Tor hidden services on a public relay makes the 835 presence of hidden services visible through simple statistical 836 analysis of publicly available data. 837 See https://trac.torproject.org/projects/tor/ticket/8742 838 839 You can safely ignore this warning if you don't intend to 840 actually hide your hidden services. In either case, you can 841 always create a container/VM with a separate Tor daemon instance. 842 '' ++ 843 flatten (mapAttrsToList (n: o: 844 optional (o.settings.HiddenServiceVersion == 2) [ 845 (optional (o.settings.HiddenServiceExportCircuitID != null) '' 846 HiddenServiceExportCircuitID is used in the HiddenService: ${n} 847 but this option is only for v3 hidden services. 848 '') 849 ] ++ 850 optional (o.settings.HiddenServiceVersion != 2) [ 851 (optional (o.settings.HiddenServiceAuthorizeClient != null) '' 852 HiddenServiceAuthorizeClient is used in the HiddenService: ${n} 853 but this option is only for v2 hidden services. 854 '') 855 (optional (o.settings.RendPostPeriod != null) '' 856 RendPostPeriod is used in the HiddenService: ${n} 857 but this option is only for v2 hidden services. 858 '') 859 ] 860 ) cfg.relay.onionServices); 861 862 users.groups.tor.gid = config.ids.gids.tor; 863 users.users.tor = 864 { description = "Tor Daemon User"; 865 createHome = true; 866 home = stateDir; 867 group = "tor"; 868 uid = config.ids.uids.tor; 869 }; 870 871 services.tor.settings = mkMerge [ 872 (mkIf cfg.enableGeoIP { 873 GeoIPFile = "${cfg.package.geoip}/share/tor/geoip"; 874 GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6"; 875 }) 876 (mkIf cfg.controlSocket.enable { 877 ControlPort = [ { unix = runDir + "/control"; GroupWritable=true; RelaxDirModeCheck=true; } ]; 878 }) 879 (mkIf cfg.relay.enable ( 880 optionalAttrs (cfg.relay.role != "exit") { 881 ExitPolicy = mkForce ["reject *:*"]; 882 } // 883 optionalAttrs (elem cfg.relay.role ["bridge" "private-bridge"]) { 884 BridgeRelay = true; 885 ExtORPort.port = mkDefault "auto"; 886 ServerTransportPlugin.transports = mkDefault ["obfs4"]; 887 ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed"; 888 } // optionalAttrs (cfg.relay.role == "private-bridge") { 889 ExtraInfoStatistics = false; 890 PublishServerDescriptor = false; 891 } 892 )) 893 (mkIf (!cfg.relay.enable) { 894 # Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings, 895 # because it would still enable Tor as a relay, 896 # which can trigger all sort of problems when not carefully done, 897 # like the blocklisting of the machine's IP addresses 898 # by some hosting providers... 899 DirPort = mkForce []; 900 ORPort = mkForce []; 901 PublishServerDescriptor = mkForce false; 902 }) 903 (mkIf cfg.client.enable ( 904 { SOCKSPort = [ cfg.client.socksListenAddress ]; 905 } // optionalAttrs cfg.client.transparentProxy.enable { 906 TransPort = [{ addr = "127.0.0.1"; port = 9040; }]; 907 } // optionalAttrs cfg.client.dns.enable { 908 DNSPort = [{ addr = "127.0.0.1"; port = 9053; }]; 909 AutomapHostsOnResolve = true; 910 } // optionalAttrs (flatten (mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != []) { 911 ClientOnionAuthDir = runDir + "/ClientOnionAuthDir"; 912 } 913 )) 914 ]; 915 916 networking.firewall = mkIf cfg.openFirewall { 917 allowedTCPPorts = 918 concatMap (o: 919 if isInt o && o > 0 then [o] 920 else if o ? "port" && isInt o.port && o.port > 0 then [o.port] 921 else [] 922 ) (flatten [ 923 cfg.settings.ORPort 924 cfg.settings.DirPort 925 ]); 926 }; 927 928 systemd.services.tor = { 929 description = "Tor Daemon"; 930 path = [ pkgs.tor ]; 931 932 wantedBy = [ "multi-user.target" ]; 933 after = [ "network.target" ]; 934 restartTriggers = [ torrc ]; 935 936 serviceConfig = { 937 Type = "simple"; 938 User = "tor"; 939 Group = "tor"; 940 ExecStartPre = [ 941 "${cfg.package}/bin/tor -f ${torrc} --verify-config" 942 # DOC: Appendix G of https://spec.torproject.org/rend-spec-v3 943 ("+" + pkgs.writeShellScript "ExecStartPre" (concatStringsSep "\n" (flatten (["set -eu"] ++ 944 mapAttrsToList (name: onion: 945 optional (onion.authorizedClients != []) '' 946 rm -rf ${escapeShellArg onion.path}/authorized_clients 947 install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} ${escapeShellArg onion.path}/authorized_clients 948 '' ++ 949 imap0 (i: pubKey: '' 950 echo ${pubKey} | 951 install -o tor -g tor -m 0400 /dev/stdin ${escapeShellArg onion.path}/authorized_clients/${toString i}.auth 952 '') onion.authorizedClients ++ 953 optional (onion.secretKey != null) '' 954 install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} 955 key="$(cut -f1 -d: ${escapeShellArg onion.secretKey})" 956 case "$key" in 957 ("== ed25519v"*"-secret") 958 install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;; 959 (*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;; 960 esac 961 '' 962 ) cfg.relay.onionServices ++ 963 mapAttrsToList (name: onion: imap0 (i: prvKeyPath: 964 let hostname = removeSuffix ".onion" name; in '' 965 printf "%s:" ${escapeShellArg hostname} | cat - ${escapeShellArg prvKeyPath} | 966 install -o tor -g tor -m 0700 /dev/stdin \ 967 ${runDir}/ClientOnionAuthDir/${escapeShellArg hostname}.${toString i}.auth_private 968 '') onion.clientAuthorizations) 969 cfg.client.onionServices 970 )))) 971 ]; 972 ExecStart = "${cfg.package}/bin/tor -f ${torrc}"; 973 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 974 KillSignal = "SIGINT"; 975 TimeoutSec = 30; 976 Restart = "on-failure"; 977 LimitNOFILE = 32768; 978 RuntimeDirectory = [ 979 # g+x allows access to the control socket 980 "tor" 981 "tor/root" 982 # g+x can't be removed in ExecStart=, but will be removed by Tor 983 "tor/ClientOnionAuthDir" 984 ]; 985 RuntimeDirectoryMode = "0710"; 986 StateDirectoryMode = "0700"; 987 StateDirectory = [ 988 "tor" 989 "tor/onion" 990 ] ++ 991 flatten (mapAttrsToList (name: onion: 992 optional (onion.secretKey == null) "tor/onion/${name}" 993 ) cfg.relay.onionServices); 994 # The following options are only to optimize: 995 # systemd-analyze security tor 996 RootDirectory = runDir + "/root"; 997 RootDirectoryStartOnly = true; 998 #InaccessiblePaths = [ "-+${runDir}/root" ]; 999 UMask = "0066"; 1000 BindPaths = [ stateDir ]; 1001 BindReadOnlyPaths = [ storeDir "/etc" ]; 1002 AmbientCapabilities = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE"; 1003 CapabilityBoundingSet = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE"; 1004 # ProtectClock= adds DeviceAllow=char-rtc r 1005 DeviceAllow = ""; 1006 LockPersonality = true; 1007 MemoryDenyWriteExecute = true; 1008 NoNewPrivileges = true; 1009 PrivateDevices = true; 1010 PrivateMounts = true; 1011 PrivateNetwork = mkDefault false; 1012 PrivateTmp = true; 1013 # Tor cannot currently bind privileged port when PrivateUsers=true, 1014 # see https://gitlab.torproject.org/legacy/trac/-/issues/20930 1015 PrivateUsers = !bindsPrivilegedPort; 1016 ProtectClock = true; 1017 ProtectControlGroups = true; 1018 ProtectHome = true; 1019 ProtectHostname = true; 1020 ProtectKernelLogs = true; 1021 ProtectKernelModules = true; 1022 ProtectKernelTunables = true; 1023 ProtectSystem = "strict"; 1024 RemoveIPC = true; 1025 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 1026 RestrictNamespaces = true; 1027 RestrictRealtime = true; 1028 RestrictSUIDSGID = true; 1029 # See also the finer but experimental option settings.Sandbox 1030 SystemCallFilter = [ 1031 "@system-service" 1032 # Groups in @system-service which do not contain a syscall listed by: 1033 # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' tor 1034 # in tests, and seem likely not necessary for tor. 1035 "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer" 1036 ]; 1037 SystemCallArchitectures = "native"; 1038 SystemCallErrorNumber = "EPERM"; 1039 }; 1040 }; 1041 1042 environment.systemPackages = [ cfg.package ]; 1043 }; 1044 1045 meta.maintainers = with lib.maintainers; [ julm ]; 1046}