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 (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ]) 26 (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ]) 27 (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ]) 28 (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ]) 29 30 (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ]) ··· 195 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") 196 (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "") 197 (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "") 198 (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ] 199 "See the 16.09 release notes for more information.") 200 (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
··· 25 (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ]) 26 (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ]) 27 (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ]) 28 + (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ]) 29 (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ]) 30 31 (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ]) ··· 196 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") 197 (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "") 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.") 201 (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ] 202 "See the 16.09 release notes for more information.") 203 (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
+291 -127
nixos/modules/services/security/tor.nix
··· 7 torDirectory = "/var/lib/tor"; 8 9 opt = name: value: optionalString (value != null) "${name} ${value}"; 10 - optint = name: value: optionalString (value != 0) "${name} ${toString value}"; 11 12 torRc = '' 13 User tor ··· 17 GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6 18 ''} 19 20 - ${optint "ControlPort" cfg.controlPort} 21 '' 22 # Client connection config 23 + optionalString cfg.client.enable '' ··· 27 '' 28 # Relay config 29 + optionalString cfg.relay.enable '' 30 - ORPort ${cfg.relay.portSpec} 31 ${opt "Nickname" cfg.relay.nickname} 32 ${opt "ContactInfo" cfg.relay.contactInfo} 33 ··· 36 ${opt "AccountingMax" cfg.relay.accountingMax} 37 ${opt "AccountingStart" cfg.relay.accountingStart} 38 39 - ${if cfg.relay.isExit then 40 opt "ExitPolicy" cfg.relay.exitPolicy 41 else 42 "ExitPolicy reject *:*"} 43 44 - ${optionalString cfg.relay.isBridge '' 45 BridgeRelay 1 46 ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed 47 ''} 48 '' 49 - + hiddenServices 50 + cfg.extraConfig; 51 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); 58 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 in 65 { 66 options = { ··· 96 }; 97 98 controlPort = mkOption { 99 - type = types.int; 100 - default = 0; 101 example = 9051; 102 description = '' 103 If set, Tor will accept connections on the specified port ··· 133 example = "192.168.0.1:9101"; 134 description = '' 135 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. 139 ''; 140 }; 141 ··· 145 example = "accept 192.168.0.0/16, reject *"; 146 description = '' 147 Entry policies to allow/deny SOCKS requests based on IP 148 - address. First entry that matches wins. If no SocksPolicy 149 is set, we accept all (and only) requests from 150 - SocksListenAddress. 151 ''; 152 }; 153 ··· 176 description = '' 177 Whether to enable relaying TOR traffic for others. 178 179 - See https://www.torproject.org/docs/tor-doc-relay for details. 180 ''; 181 }; 182 183 - isBridge = mkOption { 184 - type = types.bool; 185 - default = false; 186 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. 192 193 - A bridge relay can't be an exit relay. 194 195 - You need to set relay.enable to true for this option to 196 - take effect. 197 198 - The bridge is set up with an obfuscated transport proxy. 199 200 - See https://www.torproject.org/bridges.html.en for more info. 201 - ''; 202 - }; 203 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. 210 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. 215 216 - You can specify which services Tor users may access via 217 - your exit relay using exitPolicy option. 218 ''; 219 }; 220 ··· 268 }; 269 270 bandwidthRate = mkOption { 271 - type = types.int; 272 - default = 0; 273 example = 100; 274 description = '' 275 Specify this to limit the bandwidth usage of relayed (server) ··· 278 }; 279 280 bandwidthBurst = mkOption { 281 - type = types.int; 282 default = cfg.relay.bandwidthRate; 283 example = 200; 284 description = '' ··· 288 ''; 289 }; 290 291 - portSpec = mkOption { 292 - type = types.str; 293 - example = "143"; 294 description = '' 295 What port to advertise for Tor connections. This corresponds to the 296 <literal>ORPort</literal> section in the Tor manual; see ··· 313 considered first to last, and the first match wins. If you 314 want to _replace_ the default exit policy, end this with 315 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 318 available in the man page or at 319 - https://www.torproject.org/documentation.html 320 321 - Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses 322 - for issues you might encounter if you use the default exit policy. 323 324 If certain IPs and ports are blocked externally, e.g. by 325 your firewall, you should update your exit policy to ··· 330 }; 331 332 hiddenServices = mkOption { 333 - type = types.attrsOf (types.submodule ({ 334 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. 349 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). 365 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; } ]; 386 }; 387 - }; 388 - description = '' 389 - Configure hidden services. 390 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 - ''; 396 }; 397 }; 398 }; 399 400 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 - }; 406 407 users.extraGroups.tor.gid = config.ids.gids.tor; 408 users.extraUsers.tor = ··· 422 restartTriggers = [ torRcFile ]; 423 424 # Translated from the upstream contrib/dist/tor.service.in 425 serviceConfig = 426 { Type = "simple"; 427 - ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config"; 428 ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; 429 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 430 KillSignal = "SIGINT";
··· 7 torDirectory = "/var/lib/tor"; 8 9 opt = name: value: optionalString (value != null) "${name} ${value}"; 10 + optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}"; 11 12 torRc = '' 13 User tor ··· 17 GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6 18 ''} 19 20 + ${optint "ControlPort" (toString cfg.controlPort)} 21 '' 22 # Client connection config 23 + optionalString cfg.client.enable '' ··· 27 '' 28 # Relay config 29 + optionalString cfg.relay.enable '' 30 + ORPort ${toString cfg.relay.port} 31 + ${opt "Address" cfg.relay.address} 32 ${opt "Nickname" cfg.relay.nickname} 33 ${opt "ContactInfo" cfg.relay.contactInfo} 34 ··· 37 ${opt "AccountingMax" cfg.relay.accountingMax} 38 ${opt "AccountingStart" cfg.relay.accountingStart} 39 40 + ${if (cfg.relay.role == "exit") then 41 opt "ExitPolicy" cfg.relay.exitPolicy 42 else 43 "ExitPolicy reject *:*"} 44 45 + ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) '' 46 BridgeRelay 1 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 + ''} 53 ''} 54 '' 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 + '')) 62 + cfg.extraConfig; 63 64 + torRcFile = pkgs.writeText "torrc" torRc; 65 66 in 67 { 68 options = { ··· 98 }; 99 100 controlPort = mkOption { 101 + type = types.nullOr (types.either types.int types.str); 102 + default = null; 103 example = 9051; 104 description = '' 105 If set, Tor will accept connections on the specified port ··· 135 example = "192.168.0.1:9101"; 136 description = '' 137 Bind to this address to listen for connections from 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. 142 ''; 143 }; 144 ··· 148 example = "accept 192.168.0.0/16, reject *"; 149 description = '' 150 Entry policies to allow/deny SOCKS requests based on IP 151 + address. First entry that matches wins. If no SocksPolicy 152 is set, we accept all (and only) requests from 153 + <option>socksListenAddress</option>. 154 ''; 155 }; 156 ··· 179 description = '' 180 Whether to enable relaying TOR traffic for others. 181 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. 190 ''; 191 }; 192 193 + role = mkOption { 194 + type = types.enum [ "exit" "relay" "bridge" "private-bridge" ]; 195 description = '' 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. 236 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> 242 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> 250 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> 259 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> 265 266 + <important> 267 + <para> 268 + WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVISE. 269 + Consult with your lawer when in doubt. 270 + </para> 271 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> 280 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> 323 ''; 324 }; 325 ··· 373 }; 374 375 bandwidthRate = mkOption { 376 + type = types.nullOr types.int; 377 + default = null; 378 example = 100; 379 description = '' 380 Specify this to limit the bandwidth usage of relayed (server) ··· 383 }; 384 385 bandwidthBurst = mkOption { 386 + type = types.nullOr types.int; 387 default = cfg.relay.bandwidthRate; 388 example = 200; 389 description = '' ··· 393 ''; 394 }; 395 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; 409 description = '' 410 What port to advertise for Tor connections. This corresponds to the 411 <literal>ORPort</literal> section in the Tor manual; see ··· 428 considered first to last, and the first match wins. If you 429 want to _replace_ the default exit policy, end this with 430 either a reject *:* or an accept *:*. Otherwise, you're 431 + _augmenting_ (prepending to) the default exit policy. 432 + Leave commented to just use the default, which is 433 available in the man page or at 434 + <link xlink:href="https://www.torproject.org/documentation.html" />. 435 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. 440 441 If certain IPs and ports are blocked externally, e.g. by 442 your firewall, you should update your exit policy to ··· 447 }; 448 449 hiddenServices = mkOption { 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, ...}: { 487 options = { 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 + }; 521 522 + toHost = mkOption { 523 + type = types.str; 524 + default = "127.0.0.1"; 525 + description = "Mapping destination host."; 526 + }; 527 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 + 543 }; 544 545 + config = { 546 + name = mkDefault name; 547 + }; 548 + })); 549 }; 550 }; 551 }; 552 553 config = mkIf cfg.enable { 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 + ''; 566 567 users.extraGroups.tor.gid = config.ids.gids.tor; 568 users.extraUsers.tor = ··· 582 restartTriggers = [ torRcFile ]; 583 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 + 590 serviceConfig = 591 { Type = "simple"; 592 ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; 593 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 594 KillSignal = "SIGINT";