1 { config, lib, pkgs, ... }:
4 inherit (lib) mkIf mkOption mkDefault mkEnableOption types optional optionals;
5 inherit (lib.types) nullOr bool listOf str attrsOf submodule;
7 cfg = config.services.i2pd;
9 homeDir = "/var/lib/i2pd";
11 strOpt = k: v: k + " = " + v;
12 boolOpt = k: v: k + " = " + lib.boolToString v;
13 intOpt = k: v: k + " = " + toString v;
14 lstOpt = k: xs: k + " = " + lib.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);
20 mkEnableTrueOption = name: mkEnableOption name // { default = true; };
22 mkEndpointOpt = name: addr: port: {
23 enable = mkEnableOption name;
27 description = "The endpoint name.";
32 description = "Bind address for ${name} endpoint.";
37 description = "Bind port for ${name} endpoint.";
44 description = "Guaranteed minimum hops for ${name} tunnels.";
49 description = "Number of simultaneous ${name} tunnels.";
54 mkKeyedEndpointOpt = name: addr: port: keyloc:
55 (mkEndpointOpt name addr port) // {
60 File to persist ${lib.toUpper name} keys.
63 inbound = i2cpOpts name;
64 outbound = i2cpOpts name;
65 latency.min = mkOption {
66 type = with types; nullOr int;
67 description = "Min latency for tunnels.";
70 latency.max = mkOption {
71 type = with types; nullOr int;
72 description = "Max latency for tunnels.";
77 commonTunOpts = name: {
78 outbound = i2cpOpts name;
79 inbound = i2cpOpts name;
80 crypto.tagsToSend = mkOption {
82 description = "Number of ElGamal/AES tags to send.";
85 destination = mkOption {
87 description = "Remote endpoint, I2P hostname or b32.i2p address.";
91 default = name + "-keys.dat";
92 description = "Keyset used for tunnel identity.";
94 } // mkEndpointOpt name "127.0.0.1" 0;
96 sec = name: "\n[" + name + "]";
97 notice = "# DO NOT EDIT -- this file has been generated automatically.";
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)
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)
128 (boolOpt "enabled" cfg.upnp.enable)
129 (sec "precomputation")
130 (boolOpt "elgamal" cfg.precomputation.elgamal)
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)
140 (boolOpt "enabled" cfg.trust.enable)
141 (boolOpt "hidden" cfg.trust.hidden)
142 ] ++ (optionalEmptyList "routers" cfg.trust.routers)
143 ++ (optionalNullString "family" cfg.trust.family)
146 (boolOpt "enabled" cfg.websocket.enable)
147 (strOpt "address" cfg.websocket.address)
148 (intOpt "port" cfg.websocket.port)
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)
155 (boolOpt "enabled" cfg.ntcp2.enable)
156 (boolOpt "published" cfg.ntcp2.published)
157 (intOpt "port" cfg.ntcp2.port)
159 (strOpt "defaulturl" cfg.addressbook.defaulturl)
160 ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
163 (boolOpt "yggdrasil" cfg.yggdrasil.enable)
164 ] ++ (optionalNullString "yggaddress" cfg.yggdrasil.address)
166 (lib.collect (proto: proto ? port && proto ? address) cfg.proto)
167 (proto: let protoOpts = [
169 (boolOpt "enabled" proto.enable)
170 (strOpt "address" proto.address)
171 (intOpt "port" proto.port)
172 ] ++ (optionals (proto ? keys) (optionalNullString "keys" proto.keys))
173 ++ (optionals (proto ? auth) (optionalNullBool "auth" proto.auth))
174 ++ (optionals (proto ? user) (optionalNullString "user" proto.user))
175 ++ (optionals (proto ? pass) (optionalNullString "pass" proto.pass))
176 ++ (optionals (proto ? strictHeaders) (optionalNullBool "strictheaders" proto.strictHeaders))
177 ++ (optionals (proto ? hostname) (optionalNullString "hostname" proto.hostname))
178 ++ (optionals (proto ? outproxy) (optionalNullString "outproxy" proto.outproxy))
179 ++ (optionals (proto ? outproxyPort) (optionalNullInt "outproxyport" proto.outproxyPort))
180 ++ (optionals (proto ? outproxyEnable) (optionalNullBool "outproxy.enabled" proto.outproxyEnable));
181 in (lib.concatStringsSep "\n" protoOpts)
184 pkgs.writeText "i2pd.conf" (lib.concatStringsSep "\n" opts);
192 (intOpt "port" tun.port)
193 (strOpt "destination" tun.destination)
194 ] ++ (optionals (tun ? destinationPort) (optionalNullInt "destinationport" tun.destinationPort))
195 ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
196 ++ (optionals (tun ? address) (optionalNullString "address" tun.address))
197 ++ (optionals (tun ? inbound.length) (optionalNullInt "inbound.length" tun.inbound.length))
198 ++ (optionals (tun ? inbound.quantity) (optionalNullInt "inbound.quantity" tun.inbound.quantity))
199 ++ (optionals (tun ? outbound.length) (optionalNullInt "outbound.length" tun.outbound.length))
200 ++ (optionals (tun ? outbound.quantity) (optionalNullInt "outbound.quantity" tun.outbound.quantity))
201 ++ (optionals (tun ? crypto.tagsToSend) (optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend));
203 lib.concatStringsSep "\n" outTunOpts;
210 (intOpt "port" tun.port)
211 (strOpt "host" tun.address)
212 ] ++ (optionals (tun ? destination) (optionalNullString "destination" tun.destination))
213 ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
214 ++ (optionals (tun ? inPort) (optionalNullInt "inport" tun.inPort))
215 ++ (optionals (tun ? accessList) (optionalEmptyList "accesslist" tun.accessList));
217 lib.concatStringsSep "\n" inTunOpts;
219 allOutTunnels = lib.collect (tun: tun ? port && tun ? destination) cfg.outTunnels;
220 allInTunnels = lib.collect (tun: tun ? port && tun ? address) cfg.inTunnels;
222 opts = [ notice ] ++ (map mkOutTunnel allOutTunnels) ++ (map mkInTunnel allInTunnels);
224 pkgs.writeText "i2pd-tunnels.conf" (lib.concatStringsSep "\n" opts);
226 i2pdFlags = lib.concatStringsSep " " (
227 optional (cfg.address != null) ("--host=" + cfg.address) ++ [
229 ("--conf=" + i2pdConf)
230 ("--tunconf=" + tunnelConf)
238 (lib.mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ])
247 enable = mkEnableOption "I2Pd daemon" // {
249 Enables I2Pd as a running service upon activation.
250 Please read <https://i2pd.readthedocs.io/en/latest/> for further
255 package = lib.mkPackageOption pkgs "i2pd" { };
257 logLevel = mkOption {
258 type = types.enum ["debug" "info" "warn" "error"];
261 The log level. {command}`i2pd` defaults to "info"
262 but that generates copious amounts of log messages.
264 We default to "error" which is similar to the default log
265 level of {command}`tor`.
269 logCLFTime = mkEnableOption "full CLF-formatted date and time to log";
275 Your external IP or hostname.
283 Specify a family the router belongs to.
291 Alternative path to storage of i2pd data (RI, keys, peer profiles, ...)
299 Limit of transit traffic from max bandwidth in percents.
307 Network interface to bind to.
315 IPv4 interface to bind to.
323 IPv6 interface to bind to.
327 ntcpProxy = mkOption {
331 Proxy URL for NTCP transport.
335 ntcp = mkEnableTrueOption "ntcp";
336 ssu = mkEnableTrueOption "ssu";
338 notransit = mkEnableOption "notransit" // {
340 Tells the router to not accept transit tunnels during startup.
344 floodfill = mkEnableOption "floodfill" // {
346 If the router is declared to be unreachable and needs introduction nodes.
358 bandwidth = mkOption {
359 type = with types; nullOr int;
362 Set a router bandwidth limit integer in KBps.
363 If not set, {command}`i2pd` defaults to 32KBps.
368 type = with types; nullOr int;
371 I2P listen port. If no one is given the router will pick between 9111 and 30777.
375 enableIPv4 = mkEnableTrueOption "IPv4 connectivity";
376 enableIPv6 = mkEnableOption "IPv6 connectivity";
377 nat = mkEnableTrueOption "NAT bypass";
379 upnp.enable = mkEnableOption "UPnP service discovery";
380 upnp.name = mkOption {
384 Name i2pd appears in UPnP forwardings list.
388 precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // {
390 Whenever to use precomputated tables for ElGamal.
391 {command}`i2pd` defaults to `false`
392 to save 64M of memory (and looses some performance).
394 We default to `true` as that is what most
399 reseed.verify = mkEnableOption "SU3 signature verification";
401 reseed.file = mkOption {
405 Full path to SU3 file to reseed from.
409 reseed.urls = mkOption {
417 reseed.floodfill = mkOption {
421 Path to router info of floodfill to reseed from.
425 reseed.zipfile = mkOption {
429 Path to local .zip file to reseed from.
433 reseed.proxy = mkOption {
437 URL for reseed proxy, supports http/socks.
441 addressbook.defaulturl = mkOption {
443 default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
445 AddressBook subscription URL for initial setup
448 addressbook.subscriptions = mkOption {
451 "http://inr.i2p/export/alive-hosts.txt"
452 "http://i2p-projekt.i2p/hosts.txt"
453 "http://stats.i2p/cgi-bin/newhosts.txt"
456 AddressBook subscription URLs
460 trust.enable = mkEnableOption "explicit trust options";
462 trust.family = mkOption {
466 Router Family to trust for first hops.
470 trust.routers = mkOption {
474 Only connect to the listed routers.
478 trust.hidden = mkEnableOption "router concealment";
480 websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
482 exploratory.inbound = i2cpOpts "exploratory";
483 exploratory.outbound = i2cpOpts "exploratory";
485 ntcp2.enable = mkEnableTrueOption "NTCP2";
486 ntcp2.published = mkEnableOption "NTCP2 publication";
487 ntcp2.port = mkOption {
491 Port to listen for incoming NTCP2 connections (0=auto).
495 limits.transittunnels = mkOption {
499 Maximum number of active transit sessions.
503 limits.coreSize = mkOption {
507 Maximum size of corefile in Kb (0 - use system limit).
511 limits.openFiles = mkOption {
515 Maximum number of open files (0 - use system default).
519 limits.ntcpHard = mkOption {
523 Maximum number of active transit sessions.
527 limits.ntcpSoft = mkOption {
531 Threshold to start probabalistic backoff with ntcp sessions (default: use system limit).
535 limits.ntcpThreads = mkOption {
539 Maximum number of threads used by NTCP DH worker.
543 yggdrasil.enable = mkEnableOption "Yggdrasil";
545 yggdrasil.address = mkOption {
549 Your local yggdrasil address. Specify it if you want to bind your router to a
554 proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
556 auth = mkEnableOption "webconsole authentication";
562 Username for webconsole access
570 Password for webconsole access.
574 strictHeaders = mkOption {
578 Enable strict host checking on WebUI.
582 hostname = mkOption {
586 Expected hostname for WebUI.
591 proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat")
593 outproxy = mkOption {
596 description = "Upstream outproxy bind address.";
599 proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat")
601 outproxyEnable = mkEnableOption "SOCKS outproxy";
602 outproxy = mkOption {
604 default = "127.0.0.1";
605 description = "Upstream outproxy bind address.";
607 outproxyPort = mkOption {
610 description = "Upstream outproxy bind port.";
614 proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656;
615 proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827;
616 proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654;
617 proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650;
619 outTunnels = mkOption {
621 type = attrsOf (submodule (
624 destinationPort = mkOption {
625 type = with types; nullOr int;
627 description = "Connect to particular port at destination.";
629 } // commonTunOpts name;
631 name = mkDefault name;
636 Connect to someone as a client and establish a local accept endpoint
640 inTunnels = mkOption {
642 type = attrsOf (submodule (
648 description = "Service port. Default to the tunnel's listen port.";
650 accessList = mkOption {
653 description = "I2P nodes that are allowed to connect to this service.";
655 } // commonTunOpts name;
657 name = mkDefault name;
662 Serve something on I2P network at port and delegate requests to address inPort.
669 ###### implementation
671 config = mkIf cfg.enable {
675 description = "I2Pd User";
678 uid = config.ids.uids.i2pd;
681 users.groups.i2pd.gid = config.ids.gids.i2pd;
683 systemd.services.i2pd = {
684 description = "Minimal I2P router";
685 after = [ "network.target" ];
686 wantedBy = [ "multi-user.target" ];
690 WorkingDirectory = homeDir;
691 Restart = "on-abort";
692 ExecStart = "${cfg.package}/bin/i2pd ${i2pdFlags}";