nixos/preload: init
[NixPkgs.git] / nixos / modules / services / network-filesystems / tahoe.nix
blob14c0a3d4725f1e5e1f3bd598975388597bae2c06
1 { config, lib, pkgs, ... }:
3 with lib;
4 let
5   cfg = config.services.tahoe;
6 in
7   {
8     options.services.tahoe = {
9       introducers = mkOption {
10         default = {};
11         type = with types; attrsOf (submodule {
12           options = {
13             nickname = mkOption {
14               type = types.str;
15               description = lib.mdDoc ''
16                 The nickname of this Tahoe introducer.
17               '';
18             };
19             tub.port = mkOption {
20               default = 3458;
21               type = types.port;
22               description = lib.mdDoc ''
23                 The port on which the introducer will listen.
24               '';
25             };
26             tub.location = mkOption {
27               default = null;
28               type = types.nullOr types.str;
29               description = lib.mdDoc ''
30                 The external location that the introducer should listen on.
32                 If specified, the port should be included.
33               '';
34             };
35             package = mkOption {
36               default = pkgs.tahoelafs;
37               defaultText = literalExpression "pkgs.tahoelafs";
38               type = types.package;
39               description = lib.mdDoc ''
40                 The package to use for the Tahoe LAFS daemon.
41               '';
42             };
43           };
44         });
45         description = lib.mdDoc ''
46           The Tahoe introducers.
47         '';
48       };
49       nodes = mkOption {
50         default = {};
51         type = with types; attrsOf (submodule {
52           options = {
53             nickname = mkOption {
54               type = types.str;
55               description = lib.mdDoc ''
56                 The nickname of this Tahoe node.
57               '';
58             };
59             tub.port = mkOption {
60               default = 3457;
61               type = types.port;
62               description = lib.mdDoc ''
63                 The port on which the tub will listen.
65                 This is the correct setting to tweak if you want Tahoe's storage
66                 system to listen on a different port.
67               '';
68             };
69             tub.location = mkOption {
70               default = null;
71               type = types.nullOr types.str;
72               description = lib.mdDoc ''
73                 The external location that the node should listen on.
75                 This is the setting to tweak if there are multiple interfaces
76                 and you want to alter which interface Tahoe is advertising.
78                 If specified, the port should be included.
79               '';
80             };
81             web.port = mkOption {
82               default = 3456;
83               type = types.port;
84               description = lib.mdDoc ''
85                 The port on which the Web server will listen.
87                 This is the correct setting to tweak if you want Tahoe's WUI to
88                 listen on a different port.
89               '';
90             };
91             client.introducer = mkOption {
92               default = null;
93               type = types.nullOr types.str;
94               description = lib.mdDoc ''
95                 The furl for a Tahoe introducer node.
97                 Like all furls, keep this safe and don't share it.
98               '';
99             };
100             client.helper = mkOption {
101               default = null;
102               type = types.nullOr types.str;
103               description = lib.mdDoc ''
104                 The furl for a Tahoe helper node.
106                 Like all furls, keep this safe and don't share it.
107               '';
108             };
109             client.shares.needed = mkOption {
110               default = 3;
111               type = types.int;
112               description = lib.mdDoc ''
113                 The number of shares required to reconstitute a file.
114               '';
115             };
116             client.shares.happy = mkOption {
117               default = 7;
118               type = types.int;
119               description = lib.mdDoc ''
120                 The number of distinct storage nodes required to store
121                 a file.
122               '';
123             };
124             client.shares.total = mkOption {
125               default = 10;
126               type = types.int;
127               description = lib.mdDoc ''
128                 The number of shares required to store a file.
129               '';
130             };
131             storage.enable = mkEnableOption (lib.mdDoc "storage service");
132             storage.reservedSpace = mkOption {
133               default = "1G";
134               type = types.str;
135               description = lib.mdDoc ''
136                 The amount of filesystem space to not use for storage.
137               '';
138             };
139             helper.enable = mkEnableOption (lib.mdDoc "helper service");
140             sftpd.enable = mkEnableOption (lib.mdDoc "SFTP service");
141             sftpd.port = mkOption {
142               default = null;
143               type = types.nullOr types.int;
144               description = lib.mdDoc ''
145                 The port on which the SFTP server will listen.
147                 This is the correct setting to tweak if you want Tahoe's SFTP
148                 daemon to listen on a different port.
149               '';
150             };
151             sftpd.hostPublicKeyFile = mkOption {
152               default = null;
153               type = types.nullOr types.path;
154               description = lib.mdDoc ''
155                 Path to the SSH host public key.
156               '';
157             };
158             sftpd.hostPrivateKeyFile = mkOption {
159               default = null;
160               type = types.nullOr types.path;
161               description = lib.mdDoc ''
162                 Path to the SSH host private key.
163               '';
164             };
165             sftpd.accounts.file = mkOption {
166               default = null;
167               type = types.nullOr types.path;
168               description = lib.mdDoc ''
169                 Path to the accounts file.
170               '';
171             };
172             sftpd.accounts.url = mkOption {
173               default = null;
174               type = types.nullOr types.str;
175               description = lib.mdDoc ''
176                 URL of the accounts server.
177               '';
178             };
179             package = mkOption {
180               default = pkgs.tahoelafs;
181               defaultText = literalExpression "pkgs.tahoelafs";
182               type = types.package;
183               description = lib.mdDoc ''
184                 The package to use for the Tahoe LAFS daemon.
185               '';
186             };
187           };
188         });
189         description = lib.mdDoc ''
190           The Tahoe nodes.
191         '';
192       };
193     };
194     config = mkMerge [
195       (mkIf (cfg.introducers != {}) {
196         environment = {
197           etc = flip mapAttrs' cfg.introducers (node: settings:
198             nameValuePair "tahoe-lafs/introducer-${node}.cfg" {
199               mode = "0444";
200               text = ''
201                 # This configuration is generated by Nix. Edit at your own
202                 # peril; here be dragons.
204                 [node]
205                 nickname = ${settings.nickname}
206                 tub.port = ${toString settings.tub.port}
207                 ${optionalString (settings.tub.location != null)
208                   "tub.location = ${settings.tub.location}"}
209               '';
210             });
211           # Actually require Tahoe, so that we will have it installed.
212           systemPackages = flip mapAttrsToList cfg.introducers (node: settings:
213             settings.package
214           );
215         };
216         # Open up the firewall.
217         # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers
218         #   (node: settings: settings.tub.port);
219         systemd.services = flip mapAttrs' cfg.introducers (node: settings:
220           let
221             pidfile = "/run/tahoe.introducer-${node}.pid";
222             # This is a directory, but it has no trailing slash. Tahoe commands
223             # get antsy when there's a trailing slash.
224             nodedir = "/var/db/tahoe-lafs/introducer-${node}";
225           in nameValuePair "tahoe.introducer-${node}" {
226             description = "Tahoe LAFS node ${node}";
227             wantedBy = [ "multi-user.target" ];
228             path = [ settings.package ];
229             restartTriggers = [
230               config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ];
231             serviceConfig = {
232               Type = "simple";
233               PIDFile = pidfile;
234               # Believe it or not, Tahoe is very brittle about the order of
235               # arguments to $(tahoe run). The node directory must come first,
236               # and arguments which alter Twisted's behavior come afterwards.
237               ExecStart = ''
238                 ${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} --pidfile=${lib.escapeShellArg pidfile}
239               '';
240             };
241             preStart = ''
242               if [ ! -d ${lib.escapeShellArg nodedir} ]; then
243                 mkdir -p /var/db/tahoe-lafs
244                 # See https://github.com/NixOS/nixpkgs/issues/25273
245                 tahoe create-introducer \
246                   --hostname="${config.networking.hostName}" \
247                   ${lib.escapeShellArg nodedir}
248               fi
250               # Tahoe has created a predefined tahoe.cfg which we must now
251               # scribble over.
252               # XXX I thought that a symlink would work here, but it doesn't, so
253               # we must do this on every prestart. Fixes welcome.
254               # rm ${nodedir}/tahoe.cfg
255               # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
256               cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
257             '';
258           });
259         users.users = flip mapAttrs' cfg.introducers (node: _:
260           nameValuePair "tahoe.introducer-${node}" {
261             description = "Tahoe node user for introducer ${node}";
262             isSystemUser = true;
263           });
264       })
265       (mkIf (cfg.nodes != {}) {
266         environment = {
267           etc = flip mapAttrs' cfg.nodes (node: settings:
268             nameValuePair "tahoe-lafs/${node}.cfg" {
269               mode = "0444";
270               text = ''
271                 # This configuration is generated by Nix. Edit at your own
272                 # peril; here be dragons.
274                 [node]
275                 nickname = ${settings.nickname}
276                 tub.port = ${toString settings.tub.port}
277                 ${optionalString (settings.tub.location != null)
278                   "tub.location = ${settings.tub.location}"}
279                 # This is a Twisted endpoint. Twisted Web doesn't work on
280                 # non-TCP. ~ C.
281                 web.port = tcp:${toString settings.web.port}
283                 [client]
284                 ${optionalString (settings.client.introducer != null)
285                   "introducer.furl = ${settings.client.introducer}"}
286                 ${optionalString (settings.client.helper != null)
287                   "helper.furl = ${settings.client.helper}"}
289                 shares.needed = ${toString settings.client.shares.needed}
290                 shares.happy = ${toString settings.client.shares.happy}
291                 shares.total = ${toString settings.client.shares.total}
293                 [storage]
294                 enabled = ${boolToString settings.storage.enable}
295                 reserved_space = ${settings.storage.reservedSpace}
297                 [helper]
298                 enabled = ${boolToString settings.helper.enable}
300                 [sftpd]
301                 enabled = ${boolToString settings.sftpd.enable}
302                 ${optionalString (settings.sftpd.port != null)
303                   "port = ${toString settings.sftpd.port}"}
304                 ${optionalString (settings.sftpd.hostPublicKeyFile != null)
305                   "host_pubkey_file = ${settings.sftpd.hostPublicKeyFile}"}
306                 ${optionalString (settings.sftpd.hostPrivateKeyFile != null)
307                   "host_privkey_file = ${settings.sftpd.hostPrivateKeyFile}"}
308                 ${optionalString (settings.sftpd.accounts.file != null)
309                   "accounts.file = ${settings.sftpd.accounts.file}"}
310                 ${optionalString (settings.sftpd.accounts.url != null)
311                   "accounts.url = ${settings.sftpd.accounts.url}"}
312               '';
313             });
314           # Actually require Tahoe, so that we will have it installed.
315           systemPackages = flip mapAttrsToList cfg.nodes (node: settings:
316             settings.package
317           );
318         };
319         # Open up the firewall.
320         # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes
321         #   (node: settings: settings.tub.port);
322         systemd.services = flip mapAttrs' cfg.nodes (node: settings:
323           let
324             pidfile = "/run/tahoe.${node}.pid";
325             # This is a directory, but it has no trailing slash. Tahoe commands
326             # get antsy when there's a trailing slash.
327             nodedir = "/var/db/tahoe-lafs/${node}";
328           in nameValuePair "tahoe.${node}" {
329             description = "Tahoe LAFS node ${node}";
330             wantedBy = [ "multi-user.target" ];
331             path = [ settings.package ];
332             restartTriggers = [
333               config.environment.etc."tahoe-lafs/${node}.cfg".source ];
334             serviceConfig = {
335               Type = "simple";
336               PIDFile = pidfile;
337               # Believe it or not, Tahoe is very brittle about the order of
338               # arguments to $(tahoe run). The node directory must come first,
339               # and arguments which alter Twisted's behavior come afterwards.
340               ExecStart = ''
341                 ${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} --pidfile=${lib.escapeShellArg pidfile}
342               '';
343             };
344             preStart = ''
345               if [ ! -d ${lib.escapeShellArg nodedir} ]; then
346                 mkdir -p /var/db/tahoe-lafs
347                 tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir}
348               fi
350               # Tahoe has created a predefined tahoe.cfg which we must now
351               # scribble over.
352               # XXX I thought that a symlink would work here, but it doesn't, so
353               # we must do this on every prestart. Fixes welcome.
354               # rm ${nodedir}/tahoe.cfg
355               # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg
356               cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
357             '';
358           });
359         users.users = flip mapAttrs' cfg.nodes (node: _:
360           nameValuePair "tahoe.${node}" {
361             description = "Tahoe node user for node ${node}";
362             isSystemUser = true;
363           });
364       })
365     ];
366   }