Files
nixpkgs/nixos/tests/i2pd.nix
2026-08-10 04:39:43 +09:00

211 lines
6.9 KiB
Nix

{ lib, ... }:
{
name = "i2pd";
meta.maintainers = with lib.maintainers; [ h7x4 ];
nodes = {
server =
{ config, pkgs, ... }:
{
virtualisation.vlans = [ 1 ];
networking = {
useDHCP = false;
interfaces.eth1.useDHCP = false;
firewall.allowedTCPPorts = [ 12345 ];
firewall.allowedUDPPorts = [ 12345 ];
};
systemd.services."test-web-server" = {
wantedBy = [ "multi-user.target" ];
before = [ "i2pd.service" ];
serviceConfig = {
ExecStart = ''
${lib.getExe' pkgs.python3 "python3"} \
-m http.server 8080 \
--bind 127.0.0.1 \
--directory ${pkgs.writeTextDir "index.html" "hello world"}
'';
DynamicUser = true;
};
};
services.i2pd = {
enable = true;
settings = {
# Needed to retrieve b32 address
loglevel = "info";
# Avoid real I2P network ID (2), just in case
# there are any hidden assumptions tied to it.
netid = 77;
host = config.networking.primaryIPAddress;
port = 12345;
# Allow use of local addresses
reservedrange = false;
# No reseed infra available, and we seed netDb manually anyway
reseed.urls = "";
reseed.yggurls = "";
# "router" is the only other node reachable, so every tunnel
# is a single hop through it.
shareddest.inbound.length = 1;
shareddest.outbound.length = 1;
exploratory.inbound.length = 1;
exploratory.outbound.length = 1;
};
serverTunnels.testserver = {
host = "127.0.0.1";
port = 8080;
keys = "testserver-keys.dat";
inbound.length = 1;
outbound.length = 1;
};
};
};
router =
{ config, ... }:
{
virtualisation.vlans = [ 1 ];
networking = {
useDHCP = false;
interfaces.eth1.useDHCP = false;
firewall.allowedTCPPorts = [ 12345 ];
firewall.allowedUDPPorts = [ 12345 ];
};
services.i2pd = {
enable = true;
settings = {
loglevel = "info";
netid = 77;
host = config.networking.primaryIPAddress;
port = 12345;
reservedrange = false;
reseed.urls = "";
reseed.yggurls = "";
floodfill = true;
# "router" has no peer to hop through for its own pools.
shareddest.inbound.length = 0;
shareddest.outbound.length = 0;
exploratory.inbound.length = 0;
exploratory.outbound.length = 0;
};
};
};
client =
{ config, ... }:
{
virtualisation.vlans = [ 1 ];
networking = {
useDHCP = false;
interfaces.eth1.useDHCP = false;
};
# i2pd asserts this exists before it starts, the test script
# overwrites it with the server's actual address once known.
systemd.tmpfiles.rules = [
"f /run/i2pd-secrets/server-destination 0400 root root - unknown.b32.i2p"
];
services.i2pd = {
enable = true;
settings = {
loglevel = "info";
netid = 77;
host = config.networking.primaryIPAddress;
reservedrange = false;
reseed.urls = "";
reseed.yggurls = "";
httpproxy.inbound.length = 1;
httpproxy.outbound.length = 1;
shareddest.inbound.length = 1;
shareddest.outbound.length = 1;
exploratory.inbound.length = 1;
exploratory.outbound.length = 1;
};
clientTunnels.toServer = {
port = 10800;
destination._secret = "/run/i2pd-secrets/server-destination";
inbound.length = 1;
outbound.length = 1;
};
};
};
};
testScript =
# python
''
import re
start_all()
server.wait_for_unit("i2pd.service")
client.wait_for_unit("i2pd.service")
router.wait_for_unit("i2pd.service")
server.wait_for_file("/var/lib/i2pd/router.info")
client.wait_for_file("/var/lib/i2pd/router.info")
router.wait_for_file("/var/lib/i2pd/router.info")
with subtest("Exchange router info"):
server.wait_until_succeeds(
"journalctl -u i2pd -o cat --grep 'Local address \\S+ created'"
)
server_log_line = server.succeed(
"journalctl -u i2pd -o cat --grep 'Local address \\S+ created' --reverse -n 1"
)
server_b32_match = re.search(r"Local address (\S+) created", server_log_line)
assert server_b32_match is not None
server_b32 = server_b32_match.group(1)
client.succeed(
"mkdir -p /run/i2pd-secrets",
f"printf '%s' '{server_b32}.b32.i2p' > /run/i2pd-secrets/server-destination",
)
# We don't have any reseed infra available, so we manually seed each
# of server/client with "router"'s identity, and vice versa.
router.copy_from_machine("/var/lib/i2pd/router.info", "router-identity")
server.copy_from_machine("/var/lib/i2pd/router.info", "server-identity")
client.copy_from_machine("/var/lib/i2pd/router.info", "client-identity")
router_identity = str(router.out_dir / "router-identity" / "router.info")
server.copy_from_host(router_identity, "/var/lib/i2pd/netDb/r0/router.dat")
client.copy_from_host(router_identity, "/var/lib/i2pd/netDb/r0/router.dat")
router.copy_from_host(
str(server.out_dir / "server-identity" / "router.info"),
"/var/lib/i2pd/netDb/r0/server.dat",
)
router.copy_from_host(
str(client.out_dir / "client-identity" / "router.info"),
"/var/lib/i2pd/netDb/r0/client.dat",
)
client.systemctl("restart i2pd.service")
server.systemctl("restart i2pd.service")
router.systemctl("restart i2pd.service")
client.wait_for_unit("i2pd.service")
server.wait_for_unit("i2pd.service")
router.wait_for_unit("i2pd.service")
client.wait_for_open_port(4444)
server.wait_for_unit("test-web-server.service")
with subtest("Request content using the HTTP proxy"):
# This is intended to keep on failing until the client eventually receives
# a LeaseSet from the server.
client.wait_until_succeeds(
f"curl --fail -x 127.0.0.1:4444 http://{server_b32}.b32.i2p/ | grep -q 'hello world'",
)
with subtest("Request content using a port-forwarding tunnel"):
client.wait_until_succeeds(
"curl --fail http://127.0.0.1:10800/ | grep -q 'hello world'",
)
'';
}