nixpkgs/nixos/modules/services/networking/i2pd.nix

337 lines
8.5 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.i2pd;
homeDir = "/var/lib/i2pd";
extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')";
toOneZero = b: if b then "1" else "0";
i2pdConf = pkgs.writeText "i2pd.conf" ''
v6 = ${toOneZero cfg.enableIPv6}
unreachable = ${toOneZero cfg.unreachable}
floodfill = ${toOneZero cfg.floodfill}
${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
httpproxyport = ${toString cfg.proxy.httpPort}
socksproxyport = ${toString cfg.proxy.socksPort}
ircaddress = ${cfg.irc.host}
ircport = ${toString cfg.irc.port}
ircdest = ${cfg.irc.dest}
irckeys = ${cfg.irc.keyFile}
eepport = ${toString cfg.eep.port}
${if isNull cfg.sam.port then "" else "--samport=${toString cfg.sam.port}"}
eephost = ${cfg.eep.host}
eepkeys = ${cfg.eep.keyFile}
'';
i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" ''
${flip concatMapStrings
(collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
(tun: let portStr = toString tun.port; in ''
[${tun.name}]
type = client
destination = ${tun.destination}
keys = ${tun.keys}
address = ${tun.address}
port = ${toString tun.port}
'')
}
${flip concatMapStrings
(collect (tun: tun ? port && tun ? host) cfg.outTunnels)
(tun: let portStr = toString tun.port; in ''
[${tun.name}]
type = server
destination = ${tun.destination}
keys = ${tun.keys}
host = ${tun.address}
port = ${tun.port}
inport = ${tun.inPort}
accesslist = ${concatStringSep "," tun.accessList}
'')
}
'';
i2pdSh = pkgs.writeScriptBin "i2pd" ''
#!/bin/sh
${if isNull cfg.extIp then extip else ""}
${pkgs.i2pd}/bin/i2pd --log=1 --daemon=0 --service=0 \
--host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \
--conf=${i2pdConf} \
--tunnelscfg=${i2pdTunnelConf}
'';
in
{
###### interface
options = {
services.i2pd = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enables I2Pd as a running service upon activation.
'';
};
extIp = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Your external IP.
'';
};
unreachable = mkOption {
type = types.bool;
default = false;
description = ''
If the router is declared to be unreachable and needs introduction nodes.
'';
};
floodfill = mkOption {
type = types.bool;
default = false;
description = ''
If the router is declared to be unreachable and needs introduction nodes.
'';
};
port = mkOption {
type = with types; nullOr int;
default = null;
description = ''
I2P listen port. If no one is given the router will pick between 9111 and 30777.
'';
};
enableIPv6 = mkOption {
type = types.bool;
default = false;
description = ''
Enables IPv6 connectivity. Disabled by default.
'';
};
http = {
port = mkOption {
type = types.int;
default = 7070;
description = ''
HTTP listen port.
'';
};
};
proxy = {
httpPort = mkOption {
type = types.int;
default = 4446;
description = ''
HTTP proxy listen port.
'';
};
socksPort = mkOption {
type = types.int;
default = 4447;
description = ''
SOCKS proxy listen port.
'';
};
};
irc = {
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address to forward incoming traffic to. 127.0.0.1 by default.
'';
};
dest = mkOption {
type = types.str;
default = "irc.postman.i2p";
description = ''
Destination I2P tunnel endpoint address of IRC server. irc.postman.i2p by default.
'';
};
port = mkOption {
type = types.int;
default = 6668;
description = ''
Local IRC tunnel endoint port to listen on. 6668 by default.
'';
};
keyFile = mkOption {
type = types.str;
default = "privKeys.dat";
description = ''
File name containing destination keys. privKeys.dat by default.
'';
};
};
eep = {
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address to forward incoming traffic to. 127.0.0.1 by default.
'';
};
port = mkOption {
type = types.int;
default = 80;
description = ''
Port to forward incoming traffic to. 80 by default.
'';
};
keyFile = mkOption {
type = types.str;
default = "privKeys.dat";
description = ''
File name containing destination keys. privKeys.dat by default.
'';
};
};
sam = {
port = mkOption {
type = with types; nullOr int;
default = null;
description = ''
Local SAM tunnel endpoint. Usually 7656. SAM is disabled if not specified.
'';
};
};
outTunnels = mkOption {
default = {};
type = with types; loaOf optionSet;
description = ''
'';
options = [ ({ name, config, ... }: {
options = {
name = mkOption {
type = types.str;
description = "The name of the tunnel.";
};
destination = mkOption {
type = types.str;
description = "Remote endpoint, I2P hostname or b32.i2p address.";
};
keys = mkOption {
type = types.str;
default = name + "-keys.dat";
description = "Keyset used for tunnel identity.";
};
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Local bind address for tunnel.";
};
port = mkOption {
type = types.int;
default = 0;
description = "Local tunnel listen port.";
};
};
config = {
name = mkDefault name;
};
}) ];
};
inTunnels = mkOption {
default = {};
type = with types; loaOf optionSet;
description = ''
'';
options = [ ({ name, config, ... }: {
options = {
name = mkOption {
type = types.str;
description = "The name of the tunnel.";
};
keys = mkOption {
type = types.path;
default = name + "-keys.dat";
description = "Keyset used for tunnel identity.";
};
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Local service IP address.";
};
port = mkOption {
type = types.int;
default = 0;
description = "Local tunnel listen port.";
};
inPort = mkOption {
type = types.int;
default = 0;
description = "I2P service port. Default to the tunnel's listen port.";
};
accessList = mkOption {
type = with types; listOf str;
default = [];
description = "I2P nodes that are allowed to connect to this service.";
};
};
config = {
name = mkDefault name;
};
}) ];
};
};
};
###### implementation
config = mkIf cfg.enable {
users.extraUsers.i2pd = {
group = "i2pd";
description = "I2Pd User";
home = homeDir;
createHome = true;
uid = config.ids.uids.i2pd;
};
users.extraGroups.i2pd.gid = config.ids.gids.i2pd;
systemd.services.i2pd = {
description = "Minimal I2P router";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig =
{
User = "i2pd";
WorkingDirectory = homeDir;
Restart = "on-abort";
ExecStart = "${i2pdSh}/bin/i2pd";
};
};
};
}