at 21.11-pre 21 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.i2pd; 8 9 homeDir = "/var/lib/i2pd"; 10 11 strOpt = k: v: k + " = " + v; 12 boolOpt = k: v: k + " = " + boolToString v; 13 intOpt = k: v: k + " = " + toString v; 14 lstOpt = k: xs: k + " = " + concatStringsSep "," xs; 15 optionalNullString = o: s: optional (s != null) (strOpt o s); 16 optionalNullBool = o: b: optional (b != null) (boolOpt o b); 17 optionalNullInt = o: i: optional (i != null) (intOpt o i); 18 optionalEmptyList = o: l: optional ([] != l) (lstOpt o l); 19 20 mkEnableTrueOption = name: mkEnableOption name // { default = true; }; 21 22 mkEndpointOpt = name: addr: port: { 23 enable = mkEnableOption name; 24 name = mkOption { 25 type = types.str; 26 default = name; 27 description = "The endpoint name."; 28 }; 29 address = mkOption { 30 type = types.str; 31 default = addr; 32 description = "Bind address for ${name} endpoint."; 33 }; 34 port = mkOption { 35 type = types.int; 36 default = port; 37 description = "Bind port for ${name} endoint."; 38 }; 39 }; 40 41 i2cpOpts = name: { 42 length = mkOption { 43 type = types.int; 44 description = "Guaranteed minimum hops for ${name} tunnels."; 45 default = 3; 46 }; 47 quantity = mkOption { 48 type = types.int; 49 description = "Number of simultaneous ${name} tunnels."; 50 default = 5; 51 }; 52 }; 53 54 mkKeyedEndpointOpt = name: addr: port: keyloc: 55 (mkEndpointOpt name addr port) // { 56 keys = mkOption { 57 type = with types; nullOr str; 58 default = keyloc; 59 description = '' 60 File to persist ${lib.toUpper name} keys. 61 ''; 62 }; 63 inbound = i2cpOpts name; 64 outbound = i2cpOpts name; 65 latency.min = mkOption { 66 type = with types; nullOr int; 67 description = "Min latency for tunnels."; 68 default = null; 69 }; 70 latency.max = mkOption { 71 type = with types; nullOr int; 72 description = "Max latency for tunnels."; 73 default = null; 74 }; 75 }; 76 77 commonTunOpts = name: { 78 outbound = i2cpOpts name; 79 inbound = i2cpOpts name; 80 crypto.tagsToSend = mkOption { 81 type = types.int; 82 description = "Number of ElGamal/AES tags to send."; 83 default = 40; 84 }; 85 destination = mkOption { 86 type = types.str; 87 description = "Remote endpoint, I2P hostname or b32.i2p address."; 88 }; 89 keys = mkOption { 90 type = types.str; 91 default = name + "-keys.dat"; 92 description = "Keyset used for tunnel identity."; 93 }; 94 } // mkEndpointOpt name "127.0.0.1" 0; 95 96 sec = name: "\n[" + name + "]"; 97 notice = "# DO NOT EDIT -- this file has been generated automatically."; 98 i2pdConf = let 99 opts = [ 100 notice 101 (strOpt "loglevel" cfg.logLevel) 102 (boolOpt "logclftime" cfg.logCLFTime) 103 (boolOpt "ipv4" cfg.enableIPv4) 104 (boolOpt "ipv6" cfg.enableIPv6) 105 (boolOpt "notransit" cfg.notransit) 106 (boolOpt "floodfill" cfg.floodfill) 107 (intOpt "netid" cfg.netid) 108 ] ++ (optionalNullInt "bandwidth" cfg.bandwidth) 109 ++ (optionalNullInt "port" cfg.port) 110 ++ (optionalNullString "family" cfg.family) 111 ++ (optionalNullString "datadir" cfg.dataDir) 112 ++ (optionalNullInt "share" cfg.share) 113 ++ (optionalNullBool "ssu" cfg.ssu) 114 ++ (optionalNullBool "ntcp" cfg.ntcp) 115 ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy) 116 ++ (optionalNullString "ifname" cfg.ifname) 117 ++ (optionalNullString "ifname4" cfg.ifname4) 118 ++ (optionalNullString "ifname6" cfg.ifname6) 119 ++ [ 120 (sec "limits") 121 (intOpt "transittunnels" cfg.limits.transittunnels) 122 (intOpt "coresize" cfg.limits.coreSize) 123 (intOpt "openfiles" cfg.limits.openFiles) 124 (intOpt "ntcphard" cfg.limits.ntcpHard) 125 (intOpt "ntcpsoft" cfg.limits.ntcpSoft) 126 (intOpt "ntcpthreads" cfg.limits.ntcpThreads) 127 (sec "upnp") 128 (boolOpt "enabled" cfg.upnp.enable) 129 (sec "precomputation") 130 (boolOpt "elgamal" cfg.precomputation.elgamal) 131 (sec "reseed") 132 (boolOpt "verify" cfg.reseed.verify) 133 ] ++ (optionalNullString "file" cfg.reseed.file) 134 ++ (optionalEmptyList "urls" cfg.reseed.urls) 135 ++ (optionalNullString "floodfill" cfg.reseed.floodfill) 136 ++ (optionalNullString "zipfile" cfg.reseed.zipfile) 137 ++ (optionalNullString "proxy" cfg.reseed.proxy) 138 ++ [ 139 (sec "trust") 140 (boolOpt "enabled" cfg.trust.enable) 141 (boolOpt "hidden" cfg.trust.hidden) 142 ] ++ (optionalEmptyList "routers" cfg.trust.routers) 143 ++ (optionalNullString "family" cfg.trust.family) 144 ++ [ 145 (sec "websockets") 146 (boolOpt "enabled" cfg.websocket.enable) 147 (strOpt "address" cfg.websocket.address) 148 (intOpt "port" cfg.websocket.port) 149 (sec "exploratory") 150 (intOpt "inbound.length" cfg.exploratory.inbound.length) 151 (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity) 152 (intOpt "outbound.length" cfg.exploratory.outbound.length) 153 (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity) 154 (sec "ntcp2") 155 (boolOpt "enabled" cfg.ntcp2.enable) 156 (boolOpt "published" cfg.ntcp2.published) 157 (intOpt "port" cfg.ntcp2.port) 158 (sec "addressbook") 159 (strOpt "defaulturl" cfg.addressbook.defaulturl) 160 ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions) 161 ++ (flip map 162 (collect (proto: proto ? port && proto ? address) cfg.proto) 163 (proto: let protoOpts = [ 164 (sec proto.name) 165 (boolOpt "enabled" proto.enable) 166 (strOpt "address" proto.address) 167 (intOpt "port" proto.port) 168 ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else []) 169 ++ (if proto ? auth then optionalNullBool "auth" proto.auth else []) 170 ++ (if proto ? user then optionalNullString "user" proto.user else []) 171 ++ (if proto ? pass then optionalNullString "pass" proto.pass else []) 172 ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else []) 173 ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else []) 174 ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else []) 175 ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else []) 176 ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []); 177 in (concatStringsSep "\n" protoOpts) 178 )); 179 in 180 pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts); 181 182 tunnelConf = let opts = [ 183 notice 184 (flip map 185 (collect (tun: tun ? port && tun ? destination) cfg.outTunnels) 186 (tun: let outTunOpts = [ 187 (sec tun.name) 188 "type = client" 189 (intOpt "port" tun.port) 190 (strOpt "destination" tun.destination) 191 ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else []) 192 ++ (if tun ? keys then 193 optionalNullString "keys" tun.keys else []) 194 ++ (if tun ? address then 195 optionalNullString "address" tun.address else []) 196 ++ (if tun ? inbound.length then 197 optionalNullInt "inbound.length" tun.inbound.length else []) 198 ++ (if tun ? inbound.quantity then 199 optionalNullInt "inbound.quantity" tun.inbound.quantity else []) 200 ++ (if tun ? outbound.length then 201 optionalNullInt "outbound.length" tun.outbound.length else []) 202 ++ (if tun ? outbound.quantity then 203 optionalNullInt "outbound.quantity" tun.outbound.quantity else []) 204 ++ (if tun ? crypto.tagsToSend then 205 optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []); 206 in concatStringsSep "\n" outTunOpts)) 207 (flip map 208 (collect (tun: tun ? port && tun ? address) cfg.inTunnels) 209 (tun: let inTunOpts = [ 210 (sec tun.name) 211 "type = server" 212 (intOpt "port" tun.port) 213 (strOpt "host" tun.address) 214 ] ++ (if tun ? destination then 215 optionalNullString "destination" tun.destination else []) 216 ++ (if tun ? keys then 217 optionalNullString "keys" tun.keys else []) 218 ++ (if tun ? inPort then 219 optionalNullInt "inport" tun.inPort else []) 220 ++ (if tun ? accessList then 221 optionalEmptyList "accesslist" tun.accessList else []); 222 in concatStringsSep "\n" inTunOpts))]; 223 in pkgs.writeText "i2pd-tunnels.conf" opts; 224 225 i2pdSh = pkgs.writeScriptBin "i2pd" '' 226 #!/bin/sh 227 exec ${pkgs.i2pd}/bin/i2pd \ 228 ${if cfg.address == null then "" else "--host="+cfg.address} \ 229 --service \ 230 --conf=${i2pdConf} \ 231 --tunconf=${tunnelConf} 232 ''; 233 234in 235 236{ 237 238 imports = [ 239 (mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ]) 240 ]; 241 242 ###### interface 243 244 options = { 245 246 services.i2pd = { 247 248 enable = mkEnableOption "I2Pd daemon" // { 249 description = '' 250 Enables I2Pd as a running service upon activation. 251 Please read http://i2pd.readthedocs.io/en/latest/ for further 252 configuration help. 253 ''; 254 }; 255 256 logLevel = mkOption { 257 type = types.enum ["debug" "info" "warn" "error"]; 258 default = "error"; 259 description = '' 260 The log level. <command>i2pd</command> defaults to "info" 261 but that generates copious amounts of log messages. 262 263 We default to "error" which is similar to the default log 264 level of <command>tor</command>. 265 ''; 266 }; 267 268 logCLFTime = mkEnableOption "Full CLF-formatted date and time to log"; 269 270 address = mkOption { 271 type = with types; nullOr str; 272 default = null; 273 description = '' 274 Your external IP or hostname. 275 ''; 276 }; 277 278 family = mkOption { 279 type = with types; nullOr str; 280 default = null; 281 description = '' 282 Specify a family the router belongs to. 283 ''; 284 }; 285 286 dataDir = mkOption { 287 type = with types; nullOr str; 288 default = null; 289 description = '' 290 Alternative path to storage of i2pd data (RI, keys, peer profiles, ...) 291 ''; 292 }; 293 294 share = mkOption { 295 type = types.int; 296 default = 100; 297 description = '' 298 Limit of transit traffic from max bandwidth in percents. 299 ''; 300 }; 301 302 ifname = mkOption { 303 type = with types; nullOr str; 304 default = null; 305 description = '' 306 Network interface to bind to. 307 ''; 308 }; 309 310 ifname4 = mkOption { 311 type = with types; nullOr str; 312 default = null; 313 description = '' 314 IPv4 interface to bind to. 315 ''; 316 }; 317 318 ifname6 = mkOption { 319 type = with types; nullOr str; 320 default = null; 321 description = '' 322 IPv6 interface to bind to. 323 ''; 324 }; 325 326 ntcpProxy = mkOption { 327 type = with types; nullOr str; 328 default = null; 329 description = '' 330 Proxy URL for NTCP transport. 331 ''; 332 }; 333 334 ntcp = mkEnableTrueOption "ntcp"; 335 ssu = mkEnableTrueOption "ssu"; 336 337 notransit = mkEnableOption "notransit" // { 338 description = '' 339 Tells the router to not accept transit tunnels during startup. 340 ''; 341 }; 342 343 floodfill = mkEnableOption "floodfill" // { 344 description = '' 345 If the router is declared to be unreachable and needs introduction nodes. 346 ''; 347 }; 348 349 netid = mkOption { 350 type = types.int; 351 default = 2; 352 description = '' 353 I2P overlay netid. 354 ''; 355 }; 356 357 bandwidth = mkOption { 358 type = with types; nullOr int; 359 default = null; 360 description = '' 361 Set a router bandwidth limit integer in KBps. 362 If not set, <command>i2pd</command> defaults to 32KBps. 363 ''; 364 }; 365 366 port = mkOption { 367 type = with types; nullOr int; 368 default = null; 369 description = '' 370 I2P listen port. If no one is given the router will pick between 9111 and 30777. 371 ''; 372 }; 373 374 enableIPv4 = mkEnableTrueOption "IPv4 connectivity"; 375 enableIPv6 = mkEnableOption "IPv6 connectivity"; 376 nat = mkEnableTrueOption "NAT bypass"; 377 378 upnp.enable = mkEnableOption "UPnP service discovery"; 379 upnp.name = mkOption { 380 type = types.str; 381 default = "I2Pd"; 382 description = '' 383 Name i2pd appears in UPnP forwardings list. 384 ''; 385 }; 386 387 precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // { 388 description = '' 389 Whenever to use precomputated tables for ElGamal. 390 <command>i2pd</command> defaults to <literal>false</literal> 391 to save 64M of memory (and looses some performance). 392 393 We default to <literal>true</literal> as that is what most 394 users want anyway. 395 ''; 396 }; 397 398 reseed.verify = mkEnableOption "SU3 signature verification"; 399 400 reseed.file = mkOption { 401 type = with types; nullOr str; 402 default = null; 403 description = '' 404 Full path to SU3 file to reseed from. 405 ''; 406 }; 407 408 reseed.urls = mkOption { 409 type = with types; listOf str; 410 default = []; 411 description = '' 412 Reseed URLs. 413 ''; 414 }; 415 416 reseed.floodfill = mkOption { 417 type = with types; nullOr str; 418 default = null; 419 description = '' 420 Path to router info of floodfill to reseed from. 421 ''; 422 }; 423 424 reseed.zipfile = mkOption { 425 type = with types; nullOr str; 426 default = null; 427 description = '' 428 Path to local .zip file to reseed from. 429 ''; 430 }; 431 432 reseed.proxy = mkOption { 433 type = with types; nullOr str; 434 default = null; 435 description = '' 436 URL for reseed proxy, supports http/socks. 437 ''; 438 }; 439 440 addressbook.defaulturl = mkOption { 441 type = types.str; 442 default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; 443 description = '' 444 AddressBook subscription URL for initial setup 445 ''; 446 }; 447 addressbook.subscriptions = mkOption { 448 type = with types; listOf str; 449 default = [ 450 "http://inr.i2p/export/alive-hosts.txt" 451 "http://i2p-projekt.i2p/hosts.txt" 452 "http://stats.i2p/cgi-bin/newhosts.txt" 453 ]; 454 description = '' 455 AddressBook subscription URLs 456 ''; 457 }; 458 459 trust.enable = mkEnableOption "Explicit trust options"; 460 461 trust.family = mkOption { 462 type = with types; nullOr str; 463 default = null; 464 description = '' 465 Router Familiy to trust for first hops. 466 ''; 467 }; 468 469 trust.routers = mkOption { 470 type = with types; listOf str; 471 default = []; 472 description = '' 473 Only connect to the listed routers. 474 ''; 475 }; 476 477 trust.hidden = mkEnableOption "Router concealment"; 478 479 websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666; 480 481 exploratory.inbound = i2cpOpts "exploratory"; 482 exploratory.outbound = i2cpOpts "exploratory"; 483 484 ntcp2.enable = mkEnableTrueOption "NTCP2."; 485 ntcp2.published = mkEnableOption "NTCP2 publication"; 486 ntcp2.port = mkOption { 487 type = types.int; 488 default = 0; 489 description = '' 490 Port to listen for incoming NTCP2 connections (0=auto). 491 ''; 492 }; 493 494 limits.transittunnels = mkOption { 495 type = types.int; 496 default = 2500; 497 description = '' 498 Maximum number of active transit sessions. 499 ''; 500 }; 501 502 limits.coreSize = mkOption { 503 type = types.int; 504 default = 0; 505 description = '' 506 Maximum size of corefile in Kb (0 - use system limit). 507 ''; 508 }; 509 510 limits.openFiles = mkOption { 511 type = types.int; 512 default = 0; 513 description = '' 514 Maximum number of open files (0 - use system default). 515 ''; 516 }; 517 518 limits.ntcpHard = mkOption { 519 type = types.int; 520 default = 0; 521 description = '' 522 Maximum number of active transit sessions. 523 ''; 524 }; 525 526 limits.ntcpSoft = mkOption { 527 type = types.int; 528 default = 0; 529 description = '' 530 Threshold to start probabalistic backoff with ntcp sessions (default: use system limit). 531 ''; 532 }; 533 534 limits.ntcpThreads = mkOption { 535 type = types.int; 536 default = 1; 537 description = '' 538 Maximum number of threads used by NTCP DH worker. 539 ''; 540 }; 541 542 proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // { 543 544 auth = mkEnableOption "Webconsole authentication"; 545 546 user = mkOption { 547 type = types.str; 548 default = "i2pd"; 549 description = '' 550 Username for webconsole access 551 ''; 552 }; 553 554 pass = mkOption { 555 type = types.str; 556 default = "i2pd"; 557 description = '' 558 Password for webconsole access. 559 ''; 560 }; 561 562 strictHeaders = mkOption { 563 type = with types; nullOr bool; 564 default = null; 565 description = '' 566 Enable strict host checking on WebUI. 567 ''; 568 }; 569 570 hostname = mkOption { 571 type = with types; nullOr str; 572 default = null; 573 description = '' 574 Expected hostname for WebUI. 575 ''; 576 }; 577 }; 578 579 proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat") 580 // { 581 outproxy = mkOption { 582 type = with types; nullOr str; 583 default = null; 584 description = "Upstream outproxy bind address."; 585 }; 586 }; 587 proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat") 588 // { 589 outproxyEnable = mkEnableOption "SOCKS outproxy"; 590 outproxy = mkOption { 591 type = types.str; 592 default = "127.0.0.1"; 593 description = "Upstream outproxy bind address."; 594 }; 595 outproxyPort = mkOption { 596 type = types.int; 597 default = 4444; 598 description = "Upstream outproxy bind port."; 599 }; 600 }; 601 602 proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656; 603 proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827; 604 proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654; 605 proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650; 606 607 outTunnels = mkOption { 608 default = {}; 609 type = with types; attrsOf (submodule ( 610 { name, ... }: { 611 options = { 612 destinationPort = mkOption { 613 type = with types; nullOr int; 614 default = null; 615 description = "Connect to particular port at destination."; 616 }; 617 } // commonTunOpts name; 618 config = { 619 name = mkDefault name; 620 }; 621 } 622 )); 623 description = '' 624 Connect to someone as a client and establish a local accept endpoint 625 ''; 626 }; 627 628 inTunnels = mkOption { 629 default = {}; 630 type = with types; attrsOf (submodule ( 631 { name, ... }: { 632 options = { 633 inPort = mkOption { 634 type = types.int; 635 default = 0; 636 description = "Service port. Default to the tunnel's listen port."; 637 }; 638 accessList = mkOption { 639 type = with types; listOf str; 640 default = []; 641 description = "I2P nodes that are allowed to connect to this service."; 642 }; 643 } // commonTunOpts name; 644 config = { 645 name = mkDefault name; 646 }; 647 } 648 )); 649 description = '' 650 Serve something on I2P network at port and delegate requests to address inPort. 651 ''; 652 }; 653 }; 654 }; 655 656 657 ###### implementation 658 659 config = mkIf cfg.enable { 660 661 users.users.i2pd = { 662 group = "i2pd"; 663 description = "I2Pd User"; 664 home = homeDir; 665 createHome = true; 666 uid = config.ids.uids.i2pd; 667 }; 668 669 users.groups.i2pd.gid = config.ids.gids.i2pd; 670 671 systemd.services.i2pd = { 672 description = "Minimal I2P router"; 673 after = [ "network.target" ]; 674 wantedBy = [ "multi-user.target" ]; 675 serviceConfig = 676 { 677 User = "i2pd"; 678 WorkingDirectory = homeDir; 679 Restart = "on-abort"; 680 ExecStart = "${i2pdSh}/bin/i2pd"; 681 }; 682 }; 683 }; 684}