Merge pull request #28338 from oxij/nixos/better-tor

nixos: better tor config

authored by Joachim F and committed by GitHub 9447b8b9 ab3de8ed

+294 -127
+3
nixos/modules/rename.nix
··· 25 25 (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ]) 26 26 (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ]) 27 27 (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ]) 28 + (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ]) 28 29 (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ]) 29 30 30 31 (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ]) ··· 195 196 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") 196 197 (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "") 197 198 (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "") 199 + (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.") 200 + (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.") 198 201 (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ] 199 202 "See the 16.09 release notes for more information.") 200 203 (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
+291 -127
nixos/modules/services/security/tor.nix
··· 7 7 torDirectory = "/var/lib/tor"; 8 8 9 9 opt = name: value: optionalString (value != null) "${name} ${value}"; 10 - optint = name: value: optionalString (value != 0) "${name} ${toString value}"; 10 + optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}"; 11 11 12 12 torRc = '' 13 13 User tor ··· 17 17 GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6 18 18 ''} 19 19 20 - ${optint "ControlPort" cfg.controlPort} 20 + ${optint "ControlPort" (toString cfg.controlPort)} 21 21 '' 22 22 # Client connection config 23 23 + optionalString cfg.client.enable '' ··· 27 27 '' 28 28 # Relay config 29 29 + optionalString cfg.relay.enable '' 30 - ORPort ${cfg.relay.portSpec} 30 + ORPort ${toString cfg.relay.port} 31 + ${opt "Address" cfg.relay.address} 31 32 ${opt "Nickname" cfg.relay.nickname} 32 33 ${opt "ContactInfo" cfg.relay.contactInfo} 33 34 ··· 36 37 ${opt "AccountingMax" cfg.relay.accountingMax} 37 38 ${opt "AccountingStart" cfg.relay.accountingStart} 38 39 39 - ${if cfg.relay.isExit then 40 + ${if (cfg.relay.role == "exit") then 40 41 opt "ExitPolicy" cfg.relay.exitPolicy 41 42 else 42 43 "ExitPolicy reject *:*"} 43 44 44 - ${optionalString cfg.relay.isBridge '' 45 + ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) '' 45 46 BridgeRelay 1 46 47 ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed 48 + ExtORPort auto 49 + ${optionalString (cfg.relay.role == "private-bridge") '' 50 + ExtraInfoStatistics 0 51 + PublishServerDescriptor 0 52 + ''} 47 53 ''} 48 54 '' 49 - + hiddenServices 55 + # Hidden services 56 + + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: '' 57 + HiddenServiceDir ${torDirectory}/onion/${v.name} 58 + ${flip concatMapStrings v.map (p: '' 59 + HiddenServicePort ${toString p.port} ${p.destination} 60 + '')} 61 + '')) 50 62 + cfg.extraConfig; 51 63 52 - hiddenServices = concatStrings (mapAttrsToList (hiddenServiceDir: hs: 53 - let 54 - hsports = concatStringsSep "\n" (map mkHiddenServicePort hs.hiddenServicePorts); 55 - in 56 - "HiddenServiceDir ${hiddenServiceDir}\n${hsports}\n${hs.extraConfig}\n" 57 - ) cfg.hiddenServices); 64 + torRcFile = pkgs.writeText "torrc" torRc; 58 65 59 - mkHiddenServicePort = hsport: let 60 - trgt = optionalString (hsport.target != null) (" " + hsport.target); 61 - in "HiddenServicePort ${toString hsport.virtualPort}${trgt}"; 62 - 63 - torRcFile = pkgs.writeText "torrc" torRc; 64 66 in 65 67 { 66 68 options = { ··· 96 98 }; 97 99 98 100 controlPort = mkOption { 99 - type = types.int; 100 - default = 0; 101 + type = types.nullOr (types.either types.int types.str); 102 + default = null; 101 103 example = 9051; 102 104 description = '' 103 105 If set, Tor will accept connections on the specified port ··· 133 135 example = "192.168.0.1:9101"; 134 136 description = '' 135 137 Bind to this address to listen for connections from 136 - Socks-speaking applications. Same as socksListenAddress 137 - but uses weaker circuit isolation to provide performance 138 - suitable for a web browser. 138 + Socks-speaking applications. Same as 139 + <option>socksListenAddress</option> but uses weaker 140 + circuit isolation to provide performance suitable for a 141 + web browser. 139 142 ''; 140 143 }; 141 144 ··· 145 148 example = "accept 192.168.0.0/16, reject *"; 146 149 description = '' 147 150 Entry policies to allow/deny SOCKS requests based on IP 148 - address. First entry that matches wins. If no SocksPolicy 151 + address. First entry that matches wins. If no SocksPolicy 149 152 is set, we accept all (and only) requests from 150 - SocksListenAddress. 153 + <option>socksListenAddress</option>. 151 154 ''; 152 155 }; 153 156 ··· 176 179 description = '' 177 180 Whether to enable relaying TOR traffic for others. 178 181 179 - See https://www.torproject.org/docs/tor-doc-relay for details. 182 + See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" /> 183 + for details. 184 + 185 + Setting this to true requires setting 186 + <option>services.tor.relay.role</option> 187 + and 188 + <option>services.tor.relay.port</option> 189 + options. 180 190 ''; 181 191 }; 182 192 183 - isBridge = mkOption { 184 - type = types.bool; 185 - default = false; 193 + role = mkOption { 194 + type = types.enum [ "exit" "relay" "bridge" "private-bridge" ]; 186 195 description = '' 187 - Bridge relays (or "bridges") are Tor relays that aren't 188 - listed in the main directory. Since there is no complete 189 - public list of them, even if an ISP is filtering 190 - connections to all the known Tor relays, they probably 191 - won't be able to block all the bridges. 196 + Your role in Tor network. There're several options: 197 + 198 + <variablelist> 199 + <varlistentry> 200 + <term><literal>exit</literal></term> 201 + <listitem> 202 + <para> 203 + An exit relay. This allows Tor users to access regular 204 + Internet services through your public IP. 205 + </para> 206 + 207 + <important><para> 208 + Running an exit relay may expose you to abuse 209 + complaints. See 210 + <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" /> 211 + for more info. 212 + </para></important> 213 + 214 + <para> 215 + You can specify which services Tor users may access via 216 + your exit relay using <option>exitPolicy</option> option. 217 + </para> 218 + </listitem> 219 + </varlistentry> 220 + 221 + <varlistentry> 222 + <term><literal>relay</literal></term> 223 + <listitem> 224 + <para> 225 + Regular relay. This allows Tor users to relay onion 226 + traffic to other Tor nodes, but not to public 227 + Internet. 228 + </para> 229 + 230 + <important><para> 231 + Note that some misconfigured and/or disrespectful 232 + towards privacy sites will block you even if your 233 + relay is not an exit relay. That is, just being listed 234 + in a public relay directory can have unwanted 235 + consequences. 192 236 193 - A bridge relay can't be an exit relay. 237 + Which means you might not want to use 238 + this role if you browse public Internet from the same 239 + network as your relay, unless you want to write 240 + e-mails to those sites (you should!). 241 + </para></important> 194 242 195 - You need to set relay.enable to true for this option to 196 - take effect. 243 + <para> 244 + See 245 + <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" /> 246 + for more info. 247 + </para> 248 + </listitem> 249 + </varlistentry> 197 250 198 - The bridge is set up with an obfuscated transport proxy. 251 + <varlistentry> 252 + <term><literal>bridge</literal></term> 253 + <listitem> 254 + <para> 255 + Regular bridge. Works like a regular relay, but 256 + doesn't list you in the public relay directory and 257 + hides your Tor node behind obfsproxy. 258 + </para> 199 259 200 - See https://www.torproject.org/bridges.html.en for more info. 201 - ''; 202 - }; 260 + <para> 261 + Using this option will make Tor advertise your bridge 262 + to users through various mechanisms like 263 + <link xlink:href="https://bridges.torproject.org/" />, though. 264 + </para> 203 265 204 - isExit = mkOption { 205 - type = types.bool; 206 - default = false; 207 - description = '' 208 - An exit relay allows Tor users to access regular Internet 209 - services. 266 + <important> 267 + <para> 268 + WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVISE. 269 + Consult with your lawer when in doubt. 270 + </para> 210 271 211 - Unlike running a non-exit relay, running an exit relay may 212 - expose you to abuse complaints. See 213 - https://www.torproject.org/faq.html.en#ExitPolicies for 214 - more info. 272 + <para> 273 + This role should be safe to use in most situations 274 + (unless the act of forwarding traffic for others is 275 + a punishable offence under your local laws, which 276 + would be pretty insane as it would make ISP 277 + illegal). 278 + </para> 279 + </important> 215 280 216 - You can specify which services Tor users may access via 217 - your exit relay using exitPolicy option. 281 + <para> 282 + See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" /> 283 + for more info. 284 + </para> 285 + </listitem> 286 + </varlistentry> 287 + 288 + <varlistentry> 289 + <term><literal>private-bridge</literal></term> 290 + <listitem> 291 + <para> 292 + Private bridge. Works like regular bridge, but does 293 + not advertise your node in any way. 294 + </para> 295 + 296 + <para> 297 + Using this role means that you won't contribute to Tor 298 + network in any way unless you advertise your node 299 + yourself in some way. 300 + </para> 301 + 302 + <para> 303 + Use this if you want to run a private bridge, for 304 + example because you'll give out your bridge address 305 + manually to your friends. 306 + </para> 307 + 308 + <para> 309 + Switching to this role after measurable time in 310 + "bridge" role is pretty useless as some Tor users 311 + would have learned about your node already. In the 312 + latter case you can still change 313 + <option>port</option> option. 314 + </para> 315 + 316 + <para> 317 + See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" /> 318 + for more info. 319 + </para> 320 + </listitem> 321 + </varlistentry> 322 + </variablelist> 218 323 ''; 219 324 }; 220 325 ··· 268 373 }; 269 374 270 375 bandwidthRate = mkOption { 271 - type = types.int; 272 - default = 0; 376 + type = types.nullOr types.int; 377 + default = null; 273 378 example = 100; 274 379 description = '' 275 380 Specify this to limit the bandwidth usage of relayed (server) ··· 278 383 }; 279 384 280 385 bandwidthBurst = mkOption { 281 - type = types.int; 386 + type = types.nullOr types.int; 282 387 default = cfg.relay.bandwidthRate; 283 388 example = 200; 284 389 description = '' ··· 288 393 ''; 289 394 }; 290 395 291 - portSpec = mkOption { 292 - type = types.str; 293 - example = "143"; 396 + address = mkOption { 397 + type = types.nullOr types.str; 398 + default = null; 399 + example = "noname.example.com"; 400 + description = '' 401 + The IP address or full DNS name for advertised address of your relay. 402 + Leave unset and Tor will guess. 403 + ''; 404 + }; 405 + 406 + port = mkOption { 407 + type = types.either types.int types.str; 408 + example = 143; 294 409 description = '' 295 410 What port to advertise for Tor connections. This corresponds to the 296 411 <literal>ORPort</literal> section in the Tor manual; see ··· 313 428 considered first to last, and the first match wins. If you 314 429 want to _replace_ the default exit policy, end this with 315 430 either a reject *:* or an accept *:*. Otherwise, you're 316 - _augmenting_ (prepending to) the default exit 317 - policy. Leave commented to just use the default, which is 431 + _augmenting_ (prepending to) the default exit policy. 432 + Leave commented to just use the default, which is 318 433 available in the man page or at 319 - https://www.torproject.org/documentation.html 434 + <link xlink:href="https://www.torproject.org/documentation.html" />. 320 435 321 - Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses 322 - for issues you might encounter if you use the default exit policy. 436 + Look at 437 + <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" /> 438 + for issues you might encounter if you use the default 439 + exit policy. 323 440 324 441 If certain IPs and ports are blocked externally, e.g. by 325 442 your firewall, you should update your exit policy to ··· 330 447 }; 331 448 332 449 hiddenServices = mkOption { 333 - type = types.attrsOf (types.submodule ({ 450 + description = '' 451 + A set of static hidden services that terminate their Tor 452 + circuits at this node. 453 + 454 + Every element in this set declares a virtual onion host. 455 + 456 + You can specify your onion address by putting corresponding 457 + private key to an appropriate place in ${torDirectory}. 458 + 459 + For services without private keys in ${torDirectory} Tor 460 + daemon will generate random key pairs (which implies random 461 + onion addresses) on restart. The latter could take a while, 462 + please be patient. 463 + 464 + <note><para> 465 + Hidden services can be useful even if you don't intend to 466 + actually <emphasis>hide</emphasis> them, since they can 467 + also be seen as a kind of NAT traversal mechanism. 468 + 469 + E.g. the example will make your sshd, whatever runs on 470 + "8080" and your mail server available from anywhere where 471 + the Tor network is available (which, with the help from 472 + bridges, is pretty much everywhere), even if both client 473 + and server machines are behind NAT you have no control 474 + over. 475 + </para></note> 476 + ''; 477 + default = {}; 478 + example = literalExample '' 479 + { "my-hidden-service-example".map = [ 480 + { port = 22; } # map ssh port to this machine's ssh 481 + { port = 80; toPort = 8080; } # map http port to whatever runs on 8080 482 + { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can 483 + ]; 484 + } 485 + ''; 486 + type = types.loaOf (types.submodule ({name, config, ...}: { 334 487 options = { 335 - hiddenServicePorts = mkOption { 336 - type = types.listOf (types.submodule { 337 - options = { 338 - virtualPort = mkOption { 339 - type = types.int; 340 - example = 80; 341 - description = "Virtual port."; 342 - }; 343 - target = mkOption { 344 - type = types.nullOr types.str; 345 - default = null; 346 - example = "127.0.0.1:8080"; 347 - description = '' 348 - Target virtual Port shall be mapped to. 488 + 489 + name = mkOption { 490 + type = types.str; 491 + description = '' 492 + Name of this tor hidden service. 493 + 494 + This is purely descriptive. 495 + 496 + After restarting Tor daemon you should be able to 497 + find your .onion address in 498 + <literal>${torDirectory}/onion/$name/hostname</literal>. 499 + ''; 500 + }; 501 + 502 + map = mkOption { 503 + default = []; 504 + description = "Port mapping for this hidden service."; 505 + type = types.listOf (types.submodule ({config, ...}: { 506 + options = { 507 + 508 + port = mkOption { 509 + type = types.either types.int types.str; 510 + example = 80; 511 + description = '' 512 + Hidden service port to "bind to". 513 + ''; 514 + }; 515 + 516 + destination = mkOption { 517 + internal = true; 518 + type = types.str; 519 + description = "Forward these connections where?"; 520 + }; 349 521 350 - You may override the target port, address, or both by 351 - specifying a target of addr, port, addr:port, or 352 - unix:path. (You can specify an IPv6 target as 353 - [addr]:port. Unix paths may be quoted, and may use 354 - standard C escapes.) 355 - ''; 356 - }; 357 - }; 358 - }); 359 - example = [ { virtualPort = 80; target = "127.0.0.1:8080"; } { virtualPort = 6667; } ]; 360 - description = '' 361 - If target is <literal>null</literal> the virtual port is mapped 362 - to the same port on 127.0.0.1 over TCP. You may use 363 - <literal>target</literal> to overwrite this behaviour (see 364 - description of target). 522 + toHost = mkOption { 523 + type = types.str; 524 + default = "127.0.0.1"; 525 + description = "Mapping destination host."; 526 + }; 365 527 366 - This corresponds to the <literal>HiddenServicePort VIRTPORT 367 - [TARGET]</literal> option by looking at the tor manual 368 - <citerefentry><refentrytitle>tor</refentrytitle> 369 - <manvolnum>1</manvolnum></citerefentry> for more information. 370 - ''; 371 - }; 372 - extraConfig = mkOption { 373 - type = types.str; 374 - default = ""; 375 - description = '' 376 - Extra configuration. Contents will be added in the current 377 - hidden service context. 378 - ''; 379 - }; 380 - }; 381 - })); 382 - default = {}; 383 - example = { 384 - "/var/lib/tor/webserver" = { 385 - hiddenServicePorts = [ { virtualPort = 80; } ]; 528 + toPort = mkOption { 529 + type = types.either types.int types.str; 530 + example = 8080; 531 + description = "Mapping destination port."; 532 + }; 533 + 534 + }; 535 + 536 + config = { 537 + toPort = mkDefault config.port; 538 + destination = mkDefault "${config.toHost}:${toString config.toPort}"; 539 + }; 540 + })); 541 + }; 542 + 386 543 }; 387 - }; 388 - description = '' 389 - Configure hidden services. 390 544 391 - Please consult the tor manual 392 - <citerefentry><refentrytitle>tor</refentrytitle> 393 - <manvolnum>1</manvolnum></citerefentry> for a more detailed 394 - explanation. (search for 'HIDDEN'). 395 - ''; 545 + config = { 546 + name = mkDefault name; 547 + }; 548 + })); 396 549 }; 397 550 }; 398 551 }; 399 552 400 553 config = mkIf cfg.enable { 401 - assertions = singleton 402 - { message = "Can't be both an exit and a bridge relay at the same time"; 403 - assertion = 404 - cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit); 405 - }; 554 + # Not sure if `cfg.relay.role == "private-bridge"` helps as tor 555 + # sends a lot of stats 556 + warnings = optional (cfg.relay.enable && cfg.hiddenServices != {}) 557 + '' 558 + Running Tor hidden services on a public relay makes the 559 + presence of hidden services visible through simple statistical 560 + analysis of publicly available data. 561 + 562 + You can safely ignore this warning if you don't intend to 563 + actually hide your hidden services. In either case, you can 564 + always create a container/VM with a separate Tor daemon instance. 565 + ''; 406 566 407 567 users.extraGroups.tor.gid = config.ids.gids.tor; 408 568 users.extraUsers.tor = ··· 422 582 restartTriggers = [ torRcFile ]; 423 583 424 584 # Translated from the upstream contrib/dist/tor.service.in 585 + preStart = '' 586 + install -o tor -g tor -d ${torDirectory}/onion 587 + ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config 588 + ''; 589 + 425 590 serviceConfig = 426 591 { Type = "simple"; 427 - ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config"; 428 592 ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; 429 593 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 430 594 KillSignal = "SIGINT";