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