nixos/i2pd: Update options to encompass recent additions to the daemon

Also:
  * switch to flat sysdir
  * remove nixos default reseeds, rely on program defaults
  * refactor config expressions
This commit is contained in:
Edward Tjörnhammar 2018-09-09 18:47:26 +02:00
parent 201ee19758
commit 9dc661aa72
No known key found for this signature in database
GPG Key ID: 577898458385603E

View File

@ -8,6 +8,17 @@ let
homeDir = "/var/lib/i2pd"; homeDir = "/var/lib/i2pd";
strOpt = k: v: k + " = " + v;
boolOpt = k: v: k + " = " + boolToString v;
intOpt = k: v: k + " = " + toString v;
lstOpt = k: xs: k + " = " + concatStringsSep "," xs;
optionalNullString = o: s: optional (! isNull s) (strOpt o s);
optionalNullBool = o: b: optional (! isNull b) (boolOpt o b);
optionalNullInt = o: i: optional (! isNull i) (intOpt o i);
optionalEmptyList = o: l: optional ([] != l) (lstOpt o l);
mkEnableTrueOption = name: mkEnableOption name // { default = true; };
mkEndpointOpt = name: addr: port: { mkEndpointOpt = name: addr: port: {
enable = mkEnableOption name; enable = mkEnableOption name;
name = mkOption { name = mkOption {
@ -18,42 +29,54 @@ let
address = mkOption { address = mkOption {
type = types.str; type = types.str;
default = addr; default = addr;
description = "Bind address for ${name} endpoint. Default: " + addr; description = "Bind address for ${name} endpoint.";
}; };
port = mkOption { port = mkOption {
type = types.int; type = types.int;
default = port; default = port;
description = "Bind port for ${name} endoint. Default: " + toString port; description = "Bind port for ${name} endoint.";
}; };
}; };
mkKeyedEndpointOpt = name: addr: port: keyFile: i2cpOpts = name: {
(mkEndpointOpt name addr port) // {
keys = mkOption {
type = types.str;
default = "";
description = ''
File to persist ${lib.toUpper name} keys.
'';
};
};
commonTunOpts = let
i2cpOpts = {
length = mkOption { length = mkOption {
type = types.int; type = types.int;
description = "Guaranteed minimum hops."; description = "Guaranteed minimum hops for ${name} tunnels.";
default = 3; default = 3;
}; };
quantity = mkOption { quantity = mkOption {
type = types.int; type = types.int;
description = "Number of simultaneous tunnels."; description = "Number of simultaneous ${name} tunnels.";
default = 5; default = 5;
}; };
}; };
in name: {
outbound = i2cpOpts; mkKeyedEndpointOpt = name: addr: port: keyloc:
inbound = i2cpOpts; (mkEndpointOpt name addr port) // {
keys = mkOption {
type = with types; nullOr str;
default = keyloc;
description = ''
File to persist ${lib.toUpper name} keys.
'';
};
inbound = i2cpOpts name;
outbound = i2cpOpts name;
latency.min = mkOption {
type = with types; nullOr int;
description = "Min latency for tunnels.";
default = null;
};
latency.max = mkOption {
type = with types; nullOr int;
description = "Max latency for tunnels.";
default = null;
};
};
commonTunOpts = name: {
outbound = i2cpOpts name;
inbound = i2cpOpts name;
crypto.tagsToSend = mkOption { crypto.tagsToSend = mkOption {
type = types.int; type = types.int;
description = "Number of ElGamal/AES tags to send."; description = "Number of ElGamal/AES tags to send.";
@ -70,94 +93,142 @@ let
}; };
} // mkEndpointOpt name "127.0.0.1" 0; } // mkEndpointOpt name "127.0.0.1" 0;
i2pdConf = pkgs.writeText "i2pd.conf" '' sec = name: "\n[" + name + "]";
# DO NOT EDIT -- this file has been generated automatically. notice = "# DO NOT EDIT -- this file has been generated automatically.";
loglevel = ${cfg.logLevel} i2pdConf = let
opts = [
ipv4 = ${boolToString cfg.enableIPv4} notice
ipv6 = ${boolToString cfg.enableIPv6} (strOpt "loglevel" cfg.logLevel)
notransit = ${boolToString cfg.notransit} (boolOpt "logclftime" cfg.logCLFTime)
floodfill = ${boolToString cfg.floodfill} (boolOpt "ipv4" cfg.enableIPv4)
netid = ${toString cfg.netid} (boolOpt "ipv6" cfg.enableIPv6)
${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" } (boolOpt "notransit" cfg.notransit)
${if isNull cfg.port then "" else "port = ${toString cfg.port}"} (boolOpt "floodfill" cfg.floodfill)
(intOpt "netid" cfg.netid)
[limits] ] ++ (optionalNullInt "bandwidth" cfg.bandwidth)
transittunnels = ${toString cfg.limits.transittunnels} ++ (optionalNullInt "port" cfg.port)
++ (optionalNullString "family" cfg.family)
[upnp] ++ (optionalNullString "datadir" cfg.dataDir)
enabled = ${boolToString cfg.upnp.enable} ++ (optionalNullInt "share" cfg.share)
name = ${cfg.upnp.name} ++ (optionalNullBool "ssu" cfg.ssu)
++ (optionalNullBool "ntcp" cfg.ntcp)
[precomputation] ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy)
elgamal = ${boolToString cfg.precomputation.elgamal} ++ (optionalNullString "ifname" cfg.ifname)
++ (optionalNullString "ifname4" cfg.ifname4)
[reseed] ++ (optionalNullString "ifname6" cfg.ifname6)
verify = ${boolToString cfg.reseed.verify} ++ [
file = ${cfg.reseed.file} (sec "limits")
urls = ${builtins.concatStringsSep "," cfg.reseed.urls} (intOpt "transittunnels" cfg.limits.transittunnels)
(intOpt "coresize" cfg.limits.coreSize)
[addressbook] (intOpt "openfiles" cfg.limits.openFiles)
defaulturl = ${cfg.addressbook.defaulturl} (intOpt "ntcphard" cfg.limits.ntcpHard)
subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions} (intOpt "ntcpsoft" cfg.limits.ntcpSoft)
(intOpt "ntcpthreads" cfg.limits.ntcpThreads)
${flip concatMapStrings (sec "upnp")
(boolOpt "enabled" cfg.upnp.enable)
(sec "precomputation")
(boolOpt "elgamal" cfg.precomputation.elgamal)
(sec "reseed")
(boolOpt "verify" cfg.reseed.verify)
] ++ (optionalNullString "file" cfg.reseed.file)
++ (optionalEmptyList "urls" cfg.reseed.urls)
++ (optionalNullString "floodfill" cfg.reseed.floodfill)
++ (optionalNullString "zipfile" cfg.reseed.zipfile)
++ (optionalNullString "proxy" cfg.reseed.proxy)
++ [
(sec "trust")
(boolOpt "enabled" cfg.trust.enable)
(boolOpt "hidden" cfg.trust.hidden)
] ++ (optionalEmptyList "routers" cfg.trust.routers)
++ (optionalNullString "family" cfg.trust.family)
++ [
(sec "websockets")
(boolOpt "enabled" cfg.websocket.enable)
(strOpt "address" cfg.websocket.address)
(intOpt "port" cfg.websocket.port)
(sec "exploratory")
(intOpt "inbound.length" cfg.exploratory.inbound.length)
(intOpt "inbound.quantity" cfg.exploratory.inbound.quantity)
(intOpt "outbound.length" cfg.exploratory.outbound.length)
(intOpt "outbound.quantity" cfg.exploratory.outbound.quantity)
(sec "ntcp2")
(boolOpt "enabled" cfg.ntcp2.enable)
(boolOpt "published" cfg.ntcp2.published)
(intOpt "port" cfg.ntcp2.port)
(sec "addressbook")
(strOpt "defaulturl" cfg.addressbook.defaulturl)
] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
++ (flip map
(collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
(proto: '' (proto: let protoOpts = [
[${proto.name}] (sec proto.name)
enabled = ${boolToString proto.enable} (boolOpt "enabled" proto.enable)
address = ${proto.address} (strOpt "address" proto.address)
port = ${toString proto.port} (intOpt "port" proto.port)
${if proto ? keys then "keys = ${proto.keys}" else ""} ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
${if proto ? auth then "auth = ${boolToString proto.auth}" else ""} ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
${if proto ? user then "user = ${proto.user}" else ""} ++ (if proto ? user then optionalNullString "user" proto.user else [])
${if proto ? pass then "pass = ${proto.pass}" else ""} ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""} ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""} ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
'') ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
} ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
''; ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
in (concatStringsSep "\n" protoOpts)
));
in
pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts);
i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" '' tunnelConf = let opts = [
# DO NOT EDIT -- this file has been generated automatically. notice
${flip concatMapStrings (flip map
(collect (tun: tun ? port && tun ? destination) cfg.outTunnels) (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
(tun: '' (tun: let outTunOpts = [
[${tun.name}] (sec tun.name)
type = client "type = client"
destination = ${tun.destination} (intOpt "port" tun.port)
destinationport = ${toString tun.destinationPort} (strOpt "destination" tun.destination)
keys = ${tun.keys} ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
address = ${tun.address} ++ (if tun ? keys then
port = ${toString tun.port} optionalNullString "keys" tun.keys else [])
inbound.length = ${toString tun.inbound.length} ++ (if tun ? address then
outbound.length = ${toString tun.outbound.length} optionalNullString "address" tun.address else [])
inbound.quantity = ${toString tun.inbound.quantity} ++ (if tun ? inbound.length then
outbound.quantity = ${toString tun.outbound.quantity} optionalNullInt "inbound.length" tun.inbound.length else [])
crypto.tagsToSend = ${toString tun.crypto.tagsToSend} ++ (if tun ? inbound.quantity then
'') optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
} ++ (if tun ? outbound.length then
${flip concatMapStrings optionalNullInt "outbound.length" tun.outbound.length else [])
++ (if tun ? outbound.quantity then
optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
++ (if tun ? crypto.tagsToSend then
optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
in concatStringsSep "\n" outTunOpts))
(flip map
(collect (tun: tun ? port && tun ? address) cfg.inTunnels) (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
(tun: '' (tun: let inTunOpts = [
[${tun.name}] (sec tun.name)
type = server "type = server"
destination = ${tun.destination} (intOpt "port" tun.port)
keys = ${tun.keys} (strOpt "host" tun.address)
host = ${tun.address} ] ++ (if tun ? destination then
port = ${toString tun.port} optionalNullString "destination" tun.destination else [])
inport = ${toString tun.inPort} ++ (if tun ? keys then
accesslist = ${builtins.concatStringsSep "," tun.accessList} optionalNullString "keys" tun.keys else [])
'') ++ (if tun ? inPort then
} optionalNullInt "inport" tun.inPort else [])
''; ++ (if tun ? accessList then
optionalEmptyList "accesslist" tun.accessList else []);
in concatStringsSep "\n" inTunOpts))];
in pkgs.writeText "i2pd-tunnels.conf" opts;
i2pdSh = pkgs.writeScriptBin "i2pd" '' i2pdSh = pkgs.writeScriptBin "i2pd" ''
#!/bin/sh #!/bin/sh
exec ${pkgs.i2pd}/bin/i2pd \ exec ${pkgs.i2pd}/bin/i2pd \
${if isNull cfg.address then "" else "--host="+cfg.address} \ ${if isNull cfg.address then "" else "--host="+cfg.address} \
--service \
--conf=${i2pdConf} \ --conf=${i2pdConf} \
--tunconf=${i2pdTunnelConf} --tunconf=${tunnelConf}
''; '';
in in
@ -170,9 +241,7 @@ in
services.i2pd = { services.i2pd = {
enable = mkOption { enable = mkEnableOption "I2Pd daemon" // {
type = types.bool;
default = false;
description = '' description = ''
Enables I2Pd as a running service upon activation. Enables I2Pd as a running service upon activation.
Please read http://i2pd.readthedocs.io/en/latest/ for further Please read http://i2pd.readthedocs.io/en/latest/ for further
@ -192,6 +261,8 @@ in
''; '';
}; };
logCLFTime = mkEnableOption "Full CLF-formatted date and time to log";
address = mkOption { address = mkOption {
type = with types; nullOr str; type = with types; nullOr str;
default = null; default = null;
@ -200,17 +271,72 @@ in
''; '';
}; };
notransit = mkOption { family = mkOption {
type = types.bool; type = with types; nullOr str;
default = false; default = null;
description = ''
Specify a family the router belongs to.
'';
};
dataDir = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Alternative path to storage of i2pd data (RI, keys, peer profiles, ...)
'';
};
share = mkOption {
type = types.int;
default = 100;
description = ''
Limit of transit traffic from max bandwidth in percents.
'';
};
ifname = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Network interface to bind to.
'';
};
ifname4 = mkOption {
type = with types; nullOr str;
default = null;
description = ''
IPv4 interface to bind to.
'';
};
ifname6 = mkOption {
type = with types; nullOr str;
default = null;
description = ''
IPv6 interface to bind to.
'';
};
ntcpProxy = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Proxy URL for NTCP transport.
'';
};
ntcp = mkEnableTrueOption "ntcp";
ssu = mkEnableTrueOption "ssu";
notransit = mkEnableOption "notransit" // {
description = '' description = ''
Tells the router to not accept transit tunnels during startup. Tells the router to not accept transit tunnels during startup.
''; '';
}; };
floodfill = mkOption { floodfill = mkEnableOption "floodfill" // {
type = types.bool;
default = false;
description = '' description = ''
If the router is declared to be unreachable and needs introduction nodes. If the router is declared to be unreachable and needs introduction nodes.
''; '';
@ -241,51 +367,20 @@ in
''; '';
}; };
enableIPv4 = mkOption { enableIPv4 = mkEnableTrueOption "IPv4 connectivity";
type = types.bool; enableIPv6 = mkEnableOption "IPv6 connectivity";
default = true; nat = mkEnableTrueOption "NAT bypass";
description = ''
Enables IPv4 connectivity. Enabled by default.
'';
};
enableIPv6 = mkOption { upnp.enable = mkEnableOption "UPnP service discovery";
type = types.bool; upnp.name = mkOption {
default = false;
description = ''
Enables IPv6 connectivity. Disabled by default.
'';
};
nat = mkOption {
type = types.bool;
default = true;
description = ''
Assume router is NATed. Enabled by default.
'';
};
upnp = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enables UPnP.
'';
};
name = mkOption {
type = types.str; type = types.str;
default = "I2Pd"; default = "I2Pd";
description = '' description = ''
Name i2pd appears in UPnP forwardings list. Name i2pd appears in UPnP forwardings list.
''; '';
}; };
};
precomputation.elgamal = mkOption { precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // {
type = types.bool;
default = true;
description = '' description = ''
Whenever to use precomputated tables for ElGamal. Whenever to use precomputated tables for ElGamal.
<command>i2pd</command> defaults to <literal>false</literal> <command>i2pd</command> defaults to <literal>false</literal>
@ -296,48 +391,56 @@ in
''; '';
}; };
reseed = { reseed.verify = mkEnableOption "SU3 signature verification";
verify = mkOption {
type = types.bool; reseed.file = mkOption {
default = false; type = with types; nullOr str;
default = null;
description = '' description = ''
Request SU3 signature verification Full path to SU3 file to reseed from.
''; '';
}; };
file = mkOption { reseed.urls = mkOption {
type = types.str;
default = "";
description = ''
Full path to SU3 file to reseed from
'';
};
urls = mkOption {
type = with types; listOf str; type = with types; listOf str;
default = [ default = [];
"https://reseed.i2p-project.de/"
"https://i2p.mooo.com/netDb/"
"https://netdb.i2p2.no/"
"https://us.reseed.i2p2.no:444/"
"https://uk.reseed.i2p2.no:444/"
"https://i2p.manas.ca:8443/"
];
description = '' description = ''
Reseed URLs Reseed URLs.
''; '';
}; };
reseed.floodfill = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Path to router info of floodfill to reseed from.
'';
}; };
addressbook = { reseed.zipfile = mkOption {
defaulturl = mkOption { type = with types; nullOr str;
default = null;
description = ''
Path to local .zip file to reseed from.
'';
};
reseed.proxy = mkOption {
type = with types; nullOr str;
default = null;
description = ''
URL for reseed proxy, supports http/socks.
'';
};
addressbook.defaulturl = mkOption {
type = types.str; type = types.str;
default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
description = '' description = ''
AddressBook subscription URL for initial setup AddressBook subscription URL for initial setup
''; '';
}; };
subscriptions = mkOption { addressbook.subscriptions = mkOption {
type = with types; listOf str; type = with types; listOf str;
default = [ default = [
"http://inr.i2p/export/alive-hosts.txt" "http://inr.i2p/export/alive-hosts.txt"
@ -348,24 +451,94 @@ in
AddressBook subscription URLs AddressBook subscription URLs
''; '';
}; };
trust.enable = mkEnableOption "Explicit trust options";
trust.family = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Router Familiy to trust for first hops.
'';
};
trust.routers = mkOption {
type = with types; listOf str;
default = [];
description = ''
Only connect to the listed routers.
'';
};
trust.hidden = mkEnableOption "Router concealment.";
websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
exploratory.inbound = i2cpOpts "exploratory";
exploratory.outbound = i2cpOpts "exploratory";
ntcp2.enable = mkEnableTrueOption "NTCP2.";
ntcp2.published = mkEnableOption "NTCP2 publication.";
ntcp2.port = mkOption {
type = types.int;
default = 0;
description = ''
Port to listen for incoming NTCP2 connections (0=auto).
'';
}; };
limits.transittunnels = mkOption { limits.transittunnels = mkOption {
type = types.int; type = types.int;
default = 2500; default = 2500;
description = '' description = ''
Maximum number of active transit sessions Maximum number of active transit sessions.
'';
};
limits.coreSize = mkOption {
type = types.int;
default = 0;
description = ''
Maximum size of corefile in Kb (0 - use system limit).
'';
};
limits.openFiles = mkOption {
type = types.int;
default = 0;
description = ''
Maximum number of open files (0 - use system default).
'';
};
limits.ntcpHard = mkOption {
type = types.int;
default = 0;
description = ''
Maximum number of active transit sessions.
'';
};
limits.ntcpSoft = mkOption {
type = types.int;
default = 0;
description = ''
Threshold to start probabalistic backoff with ntcp sessions (default: use system limit).
'';
};
limits.ntcpThreads = mkOption {
type = types.int;
default = 1;
description = ''
Maximum number of threads used by NTCP DH worker.
''; '';
}; };
proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // { proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
auth = mkOption {
type = types.bool; auth = mkEnableOption "Webconsole authentication";
default = false;
description = ''
Enable authentication for webconsole.
'';
};
user = mkOption { user = mkOption {
type = types.str; type = types.str;
default = "i2pd"; default = "i2pd";
@ -373,6 +546,7 @@ in
Username for webconsole access Username for webconsole access
''; '';
}; };
pass = mkOption { pass = mkOption {
type = types.str; type = types.str;
default = "i2pd"; default = "i2pd";
@ -380,11 +554,35 @@ in
Password for webconsole access. Password for webconsole access.
''; '';
}; };
strictHeaders = mkOption {
type = with types; nullOr bool;
default = null;
description = ''
Enable strict host checking on WebUI.
'';
}; };
proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 ""; hostname = mkOption {
proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "") type = with types; nullOr str;
default = null;
description = ''
Expected hostname for WebUI.
'';
};
};
proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat")
// { // {
outproxy = mkOption {
type = with types; nullOr str;
default = null;
description = "Upstream outproxy bind address.";
};
};
proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat")
// {
outproxyEnable = mkEnableOption "SOCKS outproxy";
outproxy = mkOption { outproxy = mkOption {
type = types.str; type = types.str;
default = "127.0.0.1"; default = "127.0.0.1";
@ -408,8 +606,8 @@ in
{ name, ... }: { { name, ... }: {
options = { options = {
destinationPort = mkOption { destinationPort = mkOption {
type = types.int; type = with types; nullOr int;
default = 0; default = null;
description = "Connect to particular port at destination."; description = "Connect to particular port at destination.";
}; };
} // commonTunOpts name; } // commonTunOpts name;