A Python port of the Invisible Internet Project (I2P)
1# NixOS module for the I2P Python router.
2#
3# Usage in configuration.nix or flake-based config:
4#
5# services.i2p-python = {
6# enable = true;
7# ntcp2.port = 9700;
8# ssu2.port = 9700;
9# console.enable = true;
10# console.port = 7657;
11# openFirewall = true;
12# };
13
14flake:
15{ config, lib, pkgs, ... }:
16
17let
18 cfg = config.services.i2p-python;
19in
20{
21 options.services.i2p-python = {
22 enable = lib.mkEnableOption "I2P Python router";
23
24 package = lib.mkPackageOption flake.packages.${pkgs.system} "default" { };
25
26 ntcp2 = {
27 port = lib.mkOption {
28 type = lib.types.port;
29 default = 9700;
30 description = "NTCP2 transport listen port.";
31 };
32 address = lib.mkOption {
33 type = lib.types.str;
34 default = "0.0.0.0";
35 description = "NTCP2 bind address.";
36 };
37 };
38
39 ssu2 = {
40 port = lib.mkOption {
41 type = lib.types.port;
42 default = 9700;
43 description = "SSU2 transport listen port (UDP).";
44 };
45 };
46
47 console = {
48 enable = lib.mkEnableOption "web console";
49 port = lib.mkOption {
50 type = lib.types.port;
51 default = 7657;
52 description = "Web console listen port.";
53 };
54 address = lib.mkOption {
55 type = lib.types.str;
56 default = "127.0.0.1";
57 description = "Web console bind address.";
58 };
59 };
60
61 dataDir = lib.mkOption {
62 type = lib.types.path;
63 default = "/var/lib/i2p-python";
64 description = "Directory for router state and data.";
65 };
66
67 logLevel = lib.mkOption {
68 type = lib.types.enum [ "DEBUG" "INFO" "WARNING" "ERROR" ];
69 default = "INFO";
70 description = "Log verbosity level.";
71 };
72
73 openFirewall = lib.mkOption {
74 type = lib.types.bool;
75 default = false;
76 description = "Whether to open transport ports in the firewall.";
77 };
78 };
79
80 config = lib.mkIf cfg.enable {
81 users.users.i2p-python = {
82 isSystemUser = true;
83 group = "i2p-python";
84 home = cfg.dataDir;
85 createHome = true;
86 description = "I2P Python router daemon";
87 };
88
89 users.groups.i2p-python = { };
90
91 systemd.services.i2p-python = {
92 description = "I2P Python Router";
93 after = [ "network-online.target" ];
94 wants = [ "network-online.target" ];
95 wantedBy = [ "multi-user.target" ];
96
97 environment = {
98 I2P_DATA_DIR = cfg.dataDir;
99 };
100
101 serviceConfig = {
102 Type = "simple";
103 User = "i2p-python";
104 Group = "i2p-python";
105 ExecStart = lib.concatStringsSep " " [
106 (lib.getExe cfg.package)
107 "--host ${cfg.ntcp2.address}"
108 "--port ${toString cfg.ntcp2.port}"
109 "--data-dir ${cfg.dataDir}"
110 "--log-level ${cfg.logLevel}"
111 ];
112 Restart = "on-failure";
113 RestartSec = 10;
114
115 # Hardening
116 StateDirectory = "i2p-python";
117 StateDirectoryMode = "0700";
118 ProtectSystem = "strict";
119 ProtectHome = true;
120 PrivateTmp = true;
121 NoNewPrivileges = true;
122 ReadWritePaths = [ cfg.dataDir ];
123 LimitNOFILE = 65536;
124 };
125 };
126
127 networking.firewall = lib.mkIf cfg.openFirewall {
128 allowedTCPPorts = [ cfg.ntcp2.port ];
129 allowedUDPPorts = [ cfg.ssu2.port ];
130 };
131 };
132}