at 16.09-beta 6.9 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 extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')"; 12 13 toYesNo = b: if b then "yes" else "no"; 14 15 mkEndpointOpt = name: addr: port: { 16 enable = mkEnableOption name; 17 name = mkOption { 18 type = types.str; 19 default = name; 20 description = "The endpoint name."; 21 }; 22 address = mkOption { 23 type = types.str; 24 default = addr; 25 description = "Bind address for ${name} endpoint. Default: " + addr; 26 }; 27 port = mkOption { 28 type = types.int; 29 default = port; 30 description = "Bind port for ${name} endoint. Default: " + toString port; 31 }; 32 }; 33 34 commonTunOpts = let 35 i2cpOpts = { 36 length = mkOption { 37 type = types.int; 38 description = "Guaranteed minimum hops."; 39 default = 3; 40 }; 41 quantity = mkOption { 42 type = types.int; 43 description = "Number of simultaneous tunnels."; 44 default = 5; 45 }; 46 }; 47 in name: { 48 outbound = i2cpOpts; 49 inbound = i2cpOpts; 50 crypto.tagsToSend = mkOption { 51 type = types.int; 52 description = "Number of ElGamal/AES tags to send."; 53 default = 40; 54 }; 55 destination = mkOption { 56 type = types.str; 57 description = "Remote endpoint, I2P hostname or b32.i2p address."; 58 }; 59 keys = mkOption { 60 type = types.str; 61 default = name + "-keys.dat"; 62 description = "Keyset used for tunnel identity."; 63 }; 64 } // mkEndpointOpt name "127.0.0.1" 0; 65 66 i2pdConf = pkgs.writeText "i2pd.conf" '' 67 ipv6 = ${toYesNo cfg.enableIPv6} 68 notransit = ${toYesNo cfg.notransit} 69 floodfill = ${toYesNo cfg.floodfill} 70 ${if isNull cfg.port then "" else "port = ${toString cfg.port}"} 71 ${flip concatMapStrings 72 (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) 73 (proto: let portStr = toString proto.port; in '' 74 [${proto.name}] 75 address = ${proto.address} 76 port = ${toString proto.port} 77 enabled = ${toYesNo proto.enable} 78 '') 79 } 80 ''; 81 82 i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" '' 83 ${flip concatMapStrings 84 (collect (tun: tun ? port && tun ? destination) cfg.outTunnels) 85 (tun: let portStr = toString tun.port; in '' 86 [${tun.name}] 87 type = client 88 destination = ${tun.destination} 89 keys = ${tun.keys} 90 address = ${tun.address} 91 port = ${toString tun.port} 92 inbound.length = ${toString tun.inbound.length} 93 outbound.length = ${toString tun.outbound.length} 94 inbound.quantity = ${toString tun.inbound.quantity} 95 outbound.quantity = ${toString tun.outbound.quantity} 96 crypto.tagsToSend = ${toString tun.crypto.tagsToSend} 97 '') 98 } 99 ${flip concatMapStrings 100 (collect (tun: tun ? port && tun ? host) cfg.inTunnels) 101 (tun: let portStr = toString tun.port; in '' 102 [${tun.name}] 103 type = server 104 destination = ${tun.destination} 105 keys = ${tun.keys} 106 host = ${tun.address} 107 port = ${tun.port} 108 inport = ${tun.inPort} 109 accesslist = ${concatStringSep "," tun.accessList} 110 '') 111 } 112 ''; 113 114 i2pdSh = pkgs.writeScriptBin "i2pd" '' 115 #!/bin/sh 116 ${if isNull cfg.extIp then extip else ""} 117 ${pkgs.i2pd}/bin/i2pd --log=1 \ 118 --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \ 119 --conf=${i2pdConf} \ 120 --tunconf=${i2pdTunnelConf} 121 ''; 122 123in 124 125{ 126 127 ###### interface 128 129 options = { 130 131 services.i2pd = { 132 133 enable = mkOption { 134 type = types.bool; 135 default = false; 136 description = '' 137 Enables I2Pd as a running service upon activation. 138 ''; 139 }; 140 141 extIp = mkOption { 142 type = with types; nullOr str; 143 default = null; 144 description = '' 145 Your external IP. 146 ''; 147 }; 148 149 notransit = mkOption { 150 type = types.bool; 151 default = false; 152 description = '' 153 Tells the router to not accept transit tunnels during startup. 154 ''; 155 }; 156 157 floodfill = mkOption { 158 type = types.bool; 159 default = false; 160 description = '' 161 If the router is declared to be unreachable and needs introduction nodes. 162 ''; 163 }; 164 165 port = mkOption { 166 type = with types; nullOr int; 167 default = null; 168 description = '' 169 I2P listen port. If no one is given the router will pick between 9111 and 30777. 170 ''; 171 }; 172 173 enableIPv6 = mkOption { 174 type = types.bool; 175 default = false; 176 description = '' 177 Enables IPv6 connectivity. Disabled by default. 178 ''; 179 }; 180 181 proto.http = mkEndpointOpt "http" "127.0.0.1" 7070; 182 proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656; 183 proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827; 184 proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650; 185 proto.httpProxy = mkEndpointOpt "httpproxy" "127.0.0.1" 4446; 186 proto.socksProxy = mkEndpointOpt "socksproxy" "127.0.0.1" 4447; 187 188 outTunnels = mkOption { 189 default = {}; 190 type = with types; loaOf optionSet; 191 description = '' 192 Connect to someone as a client and establish a local accept endpoint 193 ''; 194 options = [ ({ name, config, ... }: { 195 options = commonTunOpts name; 196 config = { 197 name = mkDefault name; 198 }; 199 }) ]; 200 }; 201 202 inTunnels = mkOption { 203 default = {}; 204 type = with types; loaOf optionSet; 205 description = '' 206 Serve something on I2P network at port and delegate requests to address inPort. 207 ''; 208 options = [ ({ name, config, ... }: { 209 210 options = { 211 inPort = mkOption { 212 type = types.int; 213 default = 0; 214 description = "Service port. Default to the tunnel's listen port."; 215 }; 216 accessList = mkOption { 217 type = with types; listOf str; 218 default = []; 219 description = "I2P nodes that are allowed to connect to this service."; 220 }; 221 } // commonTunOpts name; 222 223 config = { 224 name = mkDefault name; 225 }; 226 227 }) ]; 228 }; 229 }; 230 }; 231 232 233 ###### implementation 234 235 config = mkIf cfg.enable { 236 237 users.extraUsers.i2pd = { 238 group = "i2pd"; 239 description = "I2Pd User"; 240 home = homeDir; 241 createHome = true; 242 uid = config.ids.uids.i2pd; 243 }; 244 245 users.extraGroups.i2pd.gid = config.ids.gids.i2pd; 246 247 systemd.services.i2pd = { 248 description = "Minimal I2P router"; 249 after = [ "network.target" ]; 250 wantedBy = [ "multi-user.target" ]; 251 serviceConfig = 252 { 253 User = "i2pd"; 254 WorkingDirectory = homeDir; 255 Restart = "on-abort"; 256 ExecStart = "${i2pdSh}/bin/i2pd"; 257 }; 258 }; 259 }; 260}