1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.tor; 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 14 DataDirectory ${torDirectory} 15 16 ${optint "ControlPort" cfg.controlPort} 17 '' 18 # Client connection config 19 + optionalString cfg.client.enable '' 20 SOCKSPort ${cfg.client.socksListenAddress} IsolateDestAddr 21 SOCKSPort ${cfg.client.socksListenAddressFaster} 22 ${opt "SocksPolicy" cfg.client.socksPolicy} 23 '' 24 # Relay config 25 + optionalString cfg.relay.enable '' 26 ORPort ${cfg.relay.portSpec} 27 ${opt "Nickname" cfg.relay.nickname} 28 ${opt "ContactInfo" cfg.relay.contactInfo} 29 30 ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate} 31 ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst} 32 ${opt "AccountingMax" cfg.relay.accountingMax} 33 ${opt "AccountingStart" cfg.relay.accountingStart} 34 35 ${if cfg.relay.isExit then 36 opt "ExitPolicy" cfg.relay.exitPolicy 37 else 38 "ExitPolicy reject *:*"} 39 40 ${optionalString cfg.relay.isBridge '' 41 BridgeRelay 1 42 ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed 43 ''} 44 '' 45 + cfg.extraConfig; 46 47 torRcFile = pkgs.writeText "torrc" torRc; 48in 49{ 50 options = { 51 services.tor = { 52 enable = mkOption { 53 type = types.bool; 54 default = false; 55 description = '' 56 Enable the Tor daemon. By default, the daemon is run without 57 relay, exit, bridge or client connectivity. 58 ''; 59 }; 60 61 extraConfig = mkOption { 62 type = types.lines; 63 default = ""; 64 description = '' 65 Extra configuration. Contents will be added verbatim to the 66 configuration file at the end. 67 ''; 68 }; 69 70 controlPort = mkOption { 71 type = types.int; 72 default = 0; 73 example = 9051; 74 description = '' 75 If set, Tor will accept connections on the specified port 76 and allow them to control the tor process. 77 ''; 78 }; 79 80 client = { 81 enable = mkOption { 82 type = types.bool; 83 default = false; 84 description = '' 85 Whether to enable Tor daemon to route application 86 connections. You might want to disable this if you plan 87 running a dedicated Tor relay. 88 ''; 89 }; 90 91 socksListenAddress = mkOption { 92 type = types.str; 93 default = "127.0.0.1:9050"; 94 example = "192.168.0.1:9100"; 95 description = '' 96 Bind to this address to listen for connections from 97 Socks-speaking applications. Provides strong circuit 98 isolation, separate circuit per IP address. 99 ''; 100 }; 101 102 socksListenAddressFaster = mkOption { 103 type = types.str; 104 default = "127.0.0.1:9063"; 105 example = "192.168.0.1:9101"; 106 description = '' 107 Bind to this address to listen for connections from 108 Socks-speaking applications. Same as socksListenAddress 109 but uses weaker circuit isolation to provide performance 110 suitable for a web browser. 111 ''; 112 }; 113 114 socksPolicy = mkOption { 115 type = types.nullOr types.str; 116 default = null; 117 example = "accept 192.168.0.0/16, reject *"; 118 description = '' 119 Entry policies to allow/deny SOCKS requests based on IP 120 address. First entry that matches wins. If no SocksPolicy 121 is set, we accept all (and only) requests from 122 SocksListenAddress. 123 ''; 124 }; 125 126 privoxy.enable = mkOption { 127 default = true; 128 description = '' 129 Whether to enable and configure the system Privoxy to use Tor's 130 faster port, suitable for HTTP. 131 132 To have anonymity, protocols need to be scrubbed of identifying 133 information, and this can be accomplished for HTTP by Privoxy. 134 135 Privoxy can also be useful for KDE torification. A good setup would be: 136 setting SOCKS proxy to the default Tor port, providing maximum 137 circuit isolation where possible; and setting HTTP proxy to Privoxy 138 to route HTTP traffic over faster, but less isolated port. 139 ''; 140 }; 141 }; 142 143 relay = { 144 enable = mkOption { 145 type = types.bool; 146 default = false; 147 description = '' 148 Whether to enable relaying TOR traffic for others. 149 150 See https://www.torproject.org/docs/tor-doc-relay for details. 151 ''; 152 }; 153 154 isBridge = mkOption { 155 type = types.bool; 156 default = false; 157 description = '' 158 Bridge relays (or "bridges") are Tor relays that aren't 159 listed in the main directory. Since there is no complete 160 public list of them, even if an ISP is filtering 161 connections to all the known Tor relays, they probably 162 won't be able to block all the bridges. 163 164 A bridge relay can't be an exit relay. 165 166 You need to set relay.enable to true for this option to 167 take effect. 168 169 The bridge is set up with an obfuscated transport proxy. 170 171 See https://www.torproject.org/bridges.html.en for more info. 172 ''; 173 }; 174 175 isExit = mkOption { 176 type = types.bool; 177 default = false; 178 description = '' 179 An exit relay allows Tor users to access regular Internet 180 services. 181 182 Unlike running a non-exit relay, running an exit relay may 183 expose you to abuse complaints. See 184 https://www.torproject.org/faq.html.en#ExitPolicies for 185 more info. 186 187 You can specify which services Tor users may access via 188 your exit relay using exitPolicy option. 189 ''; 190 }; 191 192 nickname = mkOption { 193 type = types.str; 194 default = "anonymous"; 195 description = '' 196 A unique handle for your TOR relay. 197 ''; 198 }; 199 200 contactInfo = mkOption { 201 type = types.nullOr types.str; 202 default = null; 203 example = "admin@relay.com"; 204 description = '' 205 Contact information for the relay owner (e.g. a mail 206 address and GPG key ID). 207 ''; 208 }; 209 210 accountingMax = mkOption { 211 type = types.nullOr types.str; 212 default = null; 213 example = "450 GBytes"; 214 description = '' 215 Specify maximum bandwidth allowed during an accounting 216 period. This allows you to limit overall tor bandwidth 217 over some time period. See the 218 <literal>AccountingMax</literal> option by looking at the 219 tor manual (<literal>man tor</literal>) for more. 220 221 Note this limit applies individually to upload and 222 download; if you specify <literal>"500 GBytes"</literal> 223 here, then you may transfer up to 1 TBytes of overall 224 bandwidth (500 GB upload, 500 GB download). 225 ''; 226 }; 227 228 accountingStart = mkOption { 229 type = types.nullOr types.str; 230 default = null; 231 example = "month 1 1:00"; 232 description = '' 233 Specify length of an accounting period. This allows you to 234 limit overall tor bandwidth over some time period. See the 235 <literal>AccountingStart</literal> option by looking at 236 the tor manual (<literal>man tor</literal>) for more. 237 ''; 238 }; 239 240 bandwidthRate = mkOption { 241 type = types.int; 242 default = 0; 243 example = 100; 244 description = '' 245 Specify this to limit the bandwidth usage of relayed (server) 246 traffic. Your own traffic is still unthrottled. Units: bytes/second. 247 ''; 248 }; 249 250 bandwidthBurst = mkOption { 251 type = types.int; 252 default = cfg.relay.bandwidthRate; 253 example = 200; 254 description = '' 255 Specify this to allow bursts of the bandwidth usage of relayed (server) 256 traffic. The average usage will still be as specified in relayBandwidthRate. 257 Your own traffic is still unthrottled. Units: bytes/second. 258 ''; 259 }; 260 261 portSpec = mkOption { 262 type = types.str; 263 example = "143"; 264 description = '' 265 What port to advertise for Tor connections. This corresponds 266 to the <literal>ORPort</literal> section in the Tor manual; see 267 <literal>man tor</literal> for more details. 268 269 At a minimum, you should just specify the port for the 270 relay to listen on; a common one like 143, 22, 80, or 443 271 to help Tor users who may have very restrictive port-based 272 firewalls. 273 ''; 274 }; 275 276 exitPolicy = mkOption { 277 type = types.nullOr types.str; 278 default = null; 279 example = "accept *:6660-6667,reject *:*"; 280 description = '' 281 A comma-separated list of exit policies. They're 282 considered first to last, and the first match wins. If you 283 want to _replace_ the default exit policy, end this with 284 either a reject *:* or an accept *:*. Otherwise, you're 285 _augmenting_ (prepending to) the default exit 286 policy. Leave commented to just use the default, which is 287 available in the man page or at 288 https://www.torproject.org/documentation.html 289 290 Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses 291 for issues you might encounter if you use the default exit policy. 292 293 If certain IPs and ports are blocked externally, e.g. by 294 your firewall, you should update your exit policy to 295 reflect this -- otherwise Tor users will be told that 296 those destinations are down. 297 ''; 298 }; 299 }; 300 }; 301 }; 302 303 config = mkIf cfg.enable { 304 assertions = singleton 305 { message = "Can't be both an exit and a bridge relay at the same time"; 306 assertion = 307 cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit); 308 }; 309 310 users.extraGroups.tor.gid = config.ids.gids.tor; 311 users.extraUsers.tor = 312 { description = "Tor Daemon User"; 313 createHome = true; 314 home = torDirectory; 315 group = "tor"; 316 uid = config.ids.uids.tor; 317 }; 318 319 systemd.services.tor = 320 { description = "Tor Daemon"; 321 path = [ pkgs.tor ]; 322 323 wantedBy = [ "multi-user.target" ]; 324 after = [ "network.target" ]; 325 restartTriggers = [ torRcFile ]; 326 327 # Translated from the upstream contrib/dist/tor.service.in 328 serviceConfig = 329 { Type = "simple"; 330 ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config"; 331 ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; 332 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 333 KillSignal = "SIGINT"; 334 TimeoutSec = 30; 335 Restart = "on-failure"; 336 LimitNOFILE = 32768; 337 338 # Hardening 339 # Note: DevicePolicy is set to 'closed', although the 340 # minimal permissions are really: 341 # DeviceAllow /dev/null rw 342 # DeviceAllow /dev/urandom r 343 # .. but we can't specify DeviceAllow multiple times. 'closed' 344 # is close enough. 345 PrivateTmp = "yes"; 346 DevicePolicy = "closed"; 347 InaccessibleDirectories = "/home"; 348 ReadOnlyDirectories = "/"; 349 ReadWriteDirectories = torDirectory; 350 NoNewPrivileges = "yes"; 351 }; 352 }; 353 354 environment.systemPackages = [ pkgs.tor ]; 355 356 services.privoxy = mkIf (cfg.client.enable && cfg.client.privoxy.enable) { 357 enable = true; 358 extraConfig = '' 359 forward-socks4a / ${cfg.client.socksListenAddressFaster} . 360 toggle 1 361 enable-remote-toggle 0 362 enable-edit-actions 0 363 enable-remote-http-toggle 0 364 ''; 365 }; 366 }; 367}