forked from cid-chan/peerix
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodule.nix
More file actions
389 lines (346 loc) · 12 KB
/
Copy pathmodule.nix
File metadata and controls
389 lines (346 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
{ lib, config, pkgs, ... }:
let
cfg = config.services.peerix;
tcfg = config.services.peerix-tracker;
# Auto-detect mode based on tracker URL:
# - trackerUrl set → Iroh mode (P2P with NAT traversal via tracker)
# - trackerUrl null → LAN mode (UDP broadcast, local network only)
useIrohMode = cfg.trackerUrl != null;
in
{
options = with lib; {
services.peerix = {
enable = lib.mkEnableOption "peerix";
openFirewall = lib.mkOption {
type = types.bool;
default = true;
description = ''
Defines whether or not firewall ports should be opened for it.
'';
};
privateKeyFile = lib.mkOption {
type = types.nullOr types.path;
default = null;
description = ''
File containing the private key to sign the derivations with.
'';
};
publicKeyFile = lib.mkOption {
type = types.nullOr types.path;
default = null;
description = ''
File containing the public key to sign the derivations with.
'';
};
publicKey = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The public key to sign the derivations with.
'';
};
user = lib.mkOption {
type = with types; oneOf [ str int ];
default = "nobody";
description = ''
The user the service will use.
'';
};
group = lib.mkOption {
type = with types; oneOf [ str int ];
default = "nobody";
description = ''
The user the service will use.
'';
};
globalCacheTTL = lib.mkOption {
type = types.nullOr types.int;
default = null;
description = ''
How long should nix store narinfo files.
If not defined, the module will not reconfigure the entry.
If it is defined, this will define how many seconds a cache entry will
be stored.
By default not given, as it affects the UX of the nix installation.
'';
};
package = mkOption {
type = types.package;
default = pkgs.peerix;
defaultText = literalExpression "pkgs.peerix";
description = ''
The package to use for peerix.
'';
};
trackerUrl = lib.mkOption {
type = types.nullOr types.str;
default = "https://sophronesis.dev/peerix";
description = ''
URL of the peerix tracker server for peer discovery.
If set: Uses Iroh mode (P2P with NAT traversal via tracker).
If null: Uses LAN mode (UDP broadcast, local network only).
Default: "https://sophronesis.dev/peerix" (Iroh mode).
'';
};
filterMode = lib.mkOption {
type = types.enum [ "nixpkgs" "rules" ];
default = "nixpkgs";
description = ''
Package filter mode:
- nixpkgs: Only serve packages that exist in cache.nixos.org (default)
- rules: Use heuristic pattern rules to filter system-specific packages
'';
};
filterConcurrency = lib.mkOption {
type = types.int;
default = 10;
description = ''
Maximum concurrent HTTP requests when filtering hashes against cache.nixos.org.
Lower values are gentler on your network but slower.
Default: 10.
'';
};
allowInsecureHttp = lib.mkOption {
type = types.bool;
default = false;
description = ''
Allow HTTP (non-TLS) connections to tracker and upstream cache.
WARNING: This is insecure and should only be used for testing.
By default, peerix requires HTTPS for all external connections.
'';
};
scanInterval = lib.mkOption {
type = types.int;
default = 3600;
description = ''
Interval in seconds for periodic nix store scanning.
Set to 0 to disable periodic scanning.
Default: 3600 (1 hour).
'';
};
noFilter = lib.mkOption {
type = types.bool;
default = false;
description = ''
Disable package filtering entirely.
By default, only packages that exist in cache.nixos.org are served.
'';
};
noVerify = lib.mkOption {
type = types.bool;
default = false;
description = ''
Disable hash verification against upstream cache.
WARNING: This reduces security - only use if you trust all peers.
'';
};
upstreamCache = lib.mkOption {
type = types.str;
default = "https://cache.nixos.org";
description = ''
Upstream cache URL for hash verification and filtering.
Default: https://cache.nixos.org
'';
};
timeout = lib.mkOption {
type = types.float;
default = 10.0;
description = ''
Connection timeout in seconds for peer connections.
Default: 10.0
'';
};
priority = lib.mkOption {
type = types.int;
default = 5;
description = ''
Cache priority for nix substituters.
Lower number = higher priority.
Default: 5 (higher priority than cache.nixos.org which is 10).
'';
};
lanDiscovery = lib.mkOption {
type = types.bool;
default = false;
description = ''
Enable LAN peer discovery via UDP broadcast.
This supplements Iroh mode by also checking local network peers.
Useful for mixed environments where some peers are on the same LAN.
'';
};
};
# Tracker service (separate top-level for clarity)
services.peerix-tracker = {
enable = lib.mkEnableOption "peerix tracker server for CID registry";
port = lib.mkOption {
type = types.int;
default = 12305;
description = "Port for the tracker HTTP server.";
};
dbPath = lib.mkOption {
type = types.str;
default = "/var/lib/peerix-tracker/tracker.db";
description = "Path to the tracker SQLite database.";
};
openFirewall = lib.mkOption {
type = types.bool;
default = true;
description = "Whether to open the firewall for the tracker port.";
};
package = lib.mkOption {
type = types.package;
default = pkgs.peerix;
defaultText = lib.literalExpression "pkgs.peerix";
description = "The peerix package to use for the tracker.";
};
};
};
config = lib.mkMerge [
(lib.mkIf (cfg.enable) {
systemd.services.peerix = {
enable = true;
description = "Local p2p nix caching daemon (${if useIrohMode then "Iroh" else "LAN"} mode)";
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
# State directory for persistent data
StateDirectory = "peerix";
StateDirectoryMode = "0700";
PrivateMounts = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateIPC = true;
ProtectSystem = "strict";
ProtectHome = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictNamespaces = "";
NoNewPrivileges = true;
ReadOnlyPaths = lib.mkMerge [
([
"/nix/var"
"/nix/store"
])
(lib.mkIf (cfg.privateKeyFile != null) [
cfg.privateKeyFile
])
];
ReadWritePaths = [ "/var/lib/peerix" ];
ExecPaths = [
"/nix/store"
];
Environment = lib.mkIf (cfg.privateKeyFile != null) [
"NIX_SECRET_KEY_FILE=${cfg.privateKeyFile}"
];
# Support reload to trigger manual cache rescan
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
};
# Enable reload support
reloadIfChanged = true;
script = if useIrohMode then ''
# Iroh mode: P2P with NAT traversal via tracker
export PATH="${pkgs.nix}/bin:${pkgs.nix-serve}/bin:$PATH"
exec ${cfg.package}/bin/peerix-iroh \
--port 12304 \
--tracker ${cfg.trackerUrl} \
--priority ${toString cfg.priority} \
--timeout ${toString cfg.timeout} \
--scan-interval ${toString cfg.scanInterval} \
--filter-mode ${cfg.filterMode} \
--filter-concurrency ${toString cfg.filterConcurrency} \
--upstream-cache ${cfg.upstreamCache} \
--state-dir /var/lib/peerix \
${lib.optionalString cfg.noFilter "--no-filter"} \
${lib.optionalString cfg.noVerify "--no-verify"} \
${lib.optionalString cfg.allowInsecureHttp "--allow-insecure-http"} \
${lib.optionalString cfg.lanDiscovery "--lan-discovery"}
'' else ''
# LAN mode: UDP broadcast, local network only
export PATH="${pkgs.nix}/bin:${pkgs.nix-serve}/bin:$PATH"
exec ${cfg.package}/bin/peerix \
--mode lan \
--port 12304 \
--priority ${toString cfg.priority} \
--timeout ${toString (builtins.floor (cfg.timeout * 1000))} \
--scan-interval ${toString cfg.scanInterval} \
--filter-mode ${cfg.filterMode}
'';
};
# Path unit to watch for system rebuilds and trigger peerix rescan
systemd.paths.peerix-rescan = {
description = "Watch for NixOS rebuild to trigger peerix cache rescan";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "/run/current-system";
Unit = "peerix-rescan.service";
};
};
systemd.services.peerix-rescan = {
description = "Trigger peerix cache rescan after NixOS rebuild";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.systemd}/bin/systemctl reload peerix.service";
};
};
nix = {
settings = {
extra-substituters = [
"http://127.0.0.1:12304/"
];
extra-trusted-public-keys = [
(lib.mkIf (cfg.publicKeyFile != null) (builtins.readFile cfg.publicKeyFile))
(lib.mkIf (cfg.publicKey != null) cfg.publicKey)
];
# Fail fast if peerix is down, fall back to other caches
connect-timeout = 2;
fallback = true;
};
extraOptions = lib.mkIf (cfg.globalCacheTTL != null) ''
narinfo-cache-negative-ttl = ${toString cfg.globalCacheTTL}
narinfo-cache-positive-ttl = ${toString cfg.globalCacheTTL}
'';
};
networking.firewall = lib.mkIf (cfg.openFirewall) {
allowedTCPPorts = [ 12304 ];
allowedUDPPorts = [ 12304 ];
};
})
(lib.mkIf (tcfg.enable) {
systemd.services.peerix-tracker = {
enable = true;
description = "Peerix tracker server for CID registry";
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "simple";
StateDirectory = "peerix-tracker";
DynamicUser = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
NoNewPrivileges = true;
ReadWritePaths = [ (builtins.dirOf tcfg.dbPath) ];
ExecPaths = [ "/nix/store" ];
};
script = ''
exec ${tcfg.package}/bin/peerix-tracker --port ${toString tcfg.port} --db-path ${tcfg.dbPath}
'';
};
networking.firewall = lib.mkIf (tcfg.openFirewall) {
allowedTCPPorts = [ tcfg.port ];
};
})
];
}