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