294 lines
13 KiB
Nix
294 lines
13 KiB
Nix
|
{ config, lib, pkgs, ... }:
|
||
|
|
||
|
with lib;
|
||
|
let
|
||
|
cfg = config.services.tahoe;
|
||
|
format = pkgs.formats.ini { };
|
||
|
in
|
||
|
{
|
||
|
options.services.tahoe = {
|
||
|
introducers = mkOption {
|
||
|
default = {};
|
||
|
type = with types; attrsOf (submodule {
|
||
|
options = {
|
||
|
settings = mkOption {
|
||
|
type = types.submodule {
|
||
|
freeformType = format.type;
|
||
|
options = {
|
||
|
node.nickname = mkOption {
|
||
|
type = types.str;
|
||
|
description = "The nickname of this Tahoe introducer.";
|
||
|
};
|
||
|
node."tub.port" = mkOption {
|
||
|
default = 3458;
|
||
|
type = types.port;
|
||
|
description = "The port on which the introducer will listen.";
|
||
|
};
|
||
|
node."tub.location" = mkOption {
|
||
|
type = types.nullOr types.str;
|
||
|
description = ''
|
||
|
The external location that the introducer should listen on.
|
||
|
If specified, the port should be included.
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
description = "Freeform settings for the introducer";
|
||
|
};
|
||
|
package = mkOption {
|
||
|
default = pkgs.tahoe-lafs;
|
||
|
defaultText = literalExpression "pkgs.tahoe-lafs";
|
||
|
type = types.package;
|
||
|
description = "The package to use for the Tahoe LAFS daemon.";
|
||
|
};
|
||
|
};
|
||
|
});
|
||
|
description = lib.mdDoc "The Tahoe introducers.";
|
||
|
};
|
||
|
nodes = mkOption {
|
||
|
default = {};
|
||
|
type = with types; attrsOf (submodule ({name, config, ...}: {
|
||
|
options = {
|
||
|
settings = mkOption {
|
||
|
type = types.submodule {
|
||
|
freeformType = format.type;
|
||
|
options = {
|
||
|
node.nickname = mkOption {
|
||
|
type = types.str;
|
||
|
description = "Value to display in management tools.";
|
||
|
default = name;
|
||
|
};
|
||
|
node."tub.port" = mkOption {
|
||
|
type = types.oneOf [ types.str types.port (types.enum [ "disabled" null ]) ];
|
||
|
description = "A twisted server endpoint specification for receiving connections from other nodes.";
|
||
|
example = "tcp:12345:interface=127.0.0.1";
|
||
|
default = 3457;
|
||
|
};
|
||
|
node."tub.location" = mkOption {
|
||
|
type = types.either types.str (types.enum [ "disabled" null ]);
|
||
|
description = "comma separated connection strings that can be reached publically.";
|
||
|
example = "tcp:mynode.example.com:3457,AUTO";
|
||
|
default = "AUTO";
|
||
|
};
|
||
|
node."web.port" = mkOption {
|
||
|
type = types.nullOr (types.either types.str types.port);
|
||
|
description = "Twisted strport specification for webui and REST-api.";
|
||
|
example = "tcp:3456:interface=127.0.0.1";
|
||
|
default = 3456;
|
||
|
};
|
||
|
client."shares.needed" = mkOption {
|
||
|
type = types.ints.between 1 256;
|
||
|
description = "Default amount of shares needed to reconstruct an uploaded file.";
|
||
|
default = 3;
|
||
|
};
|
||
|
client."shares.total" = mkOption {
|
||
|
type = types.ints.between 1 256;
|
||
|
description = "Default amount of shares a file is split into.";
|
||
|
default = 10;
|
||
|
};
|
||
|
client."shares.happy" = mkOption {
|
||
|
type = types.ints.positive;
|
||
|
description = ''
|
||
|
How spread out should your shares be.
|
||
|
Can be smaller than needed, but not more than amount of servers available.";
|
||
|
'';
|
||
|
default = 7;
|
||
|
};
|
||
|
client."mutable.format" = mkOption {
|
||
|
type = types.enum [ "sdmf" "mdmf" ];
|
||
|
description = ''
|
||
|
What format to save mutable files in.
|
||
|
SDMF is useful when some nodes on your network run an older version of Tahoe-LAFS.
|
||
|
MDMF supports inplace modification and streaming downloads.
|
||
|
'';
|
||
|
default = "sdmf";
|
||
|
};
|
||
|
storage.enabled = mkEnableOption "storage service";
|
||
|
storage.anonymous = mkOption {
|
||
|
type = types.bool;
|
||
|
description = "Whether to expose storage with just the FURL and no other authentication.";
|
||
|
default = true;
|
||
|
};
|
||
|
storage.reserved_space = mkOption {
|
||
|
type = types.str;
|
||
|
description = "The minimum amount of free disk space to keep.";
|
||
|
default = "1G";
|
||
|
};
|
||
|
helper.enabled = mkEnableOption "helper service";
|
||
|
sftpd.enabled = mkEnableOption "sftpd service";
|
||
|
sftpd.port = mkOption {
|
||
|
type = types.nullOr types.str;
|
||
|
description = "A twisted connection string to listen on for the sftpd service.";
|
||
|
example = "tcp:8022:interface=127.0.0.1";
|
||
|
default = null;
|
||
|
};
|
||
|
sftpd.host_pubkey_file = mkOption {
|
||
|
type = types.nullOr types.path;
|
||
|
description = "Path to ssh public key to use for the service.";
|
||
|
default = null;
|
||
|
};
|
||
|
sftpd.host_privkey_file = mkOption {
|
||
|
type = types.nullOr types.path;
|
||
|
description = "Path to ssh private key to use for the service.";
|
||
|
default = null;
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
description = "freeform options for a normal tahoe-lafs node";
|
||
|
};
|
||
|
client.introducersFile = mkOption {
|
||
|
type = types.nullOr types.path;
|
||
|
description = "Path to a secret file containing introducers, will be placed in private/introducers.yaml";
|
||
|
default = null;
|
||
|
};
|
||
|
client.helperFile = mkOption {
|
||
|
type = types.nullOr types.path;
|
||
|
description = "Secret file containing a furl to use as a helper.";
|
||
|
default = null;
|
||
|
};
|
||
|
sftpd.accountsFile = mkOption {
|
||
|
type = types.nullOr types.path;
|
||
|
description = "Path to the accounts file. Will be copied to private/accounts";
|
||
|
default = null;
|
||
|
};
|
||
|
package = mkOption {
|
||
|
default = pkgs.tahoe-lafs;
|
||
|
defaultText = literalExpression "pkgs.tahoelafs";
|
||
|
type = types.package;
|
||
|
description = lib.mdDoc ''
|
||
|
The package to use for the Tahoe LAFS daemon.
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
}));
|
||
|
description = "The Tahoe nodes.";
|
||
|
};
|
||
|
};
|
||
|
config = mkMerge [
|
||
|
(mkIf (cfg.introducers != {}) {
|
||
|
environment = {
|
||
|
etc = flip mapAttrs' cfg.introducers (node: settings:
|
||
|
nameValuePair "tahoe-lafs/introducer-${node}.cfg" {
|
||
|
mode = "0444";
|
||
|
source = format.generate "tahoe-lafs-introducer" settings.settings;
|
||
|
});
|
||
|
# Actually require Tahoe, so that we will have it installed.
|
||
|
systemPackages = flip mapAttrsToList cfg.introducers (node: settings:
|
||
|
settings.package
|
||
|
);
|
||
|
};
|
||
|
systemd.services = flip mapAttrs' cfg.introducers (node: settings:
|
||
|
let
|
||
|
pidfile = "/run/tahoe.introducer-${node}.pid";
|
||
|
# This is a directory, but it has no trailing slash. Tahoe commands
|
||
|
# get antsy when there's a trailing slash.
|
||
|
nodedir = "/var/db/tahoe-lafs/introducer-${node}";
|
||
|
in nameValuePair "tahoe.introducer-${node}" {
|
||
|
description = "Tahoe LAFS node ${node}";
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
path = [ settings.package ];
|
||
|
restartTriggers = [
|
||
|
config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ];
|
||
|
serviceConfig = {
|
||
|
Type = "simple";
|
||
|
PIDFile = pidfile;
|
||
|
# Believe it or not, Tahoe is very brittle about the order of
|
||
|
# arguments to $(tahoe run). The node directory must come first,
|
||
|
# and arguments which alter Twisted's behavior come afterwards.
|
||
|
ExecStart = ''
|
||
|
${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} --pidfile=${lib.escapeShellArg pidfile}
|
||
|
'';
|
||
|
};
|
||
|
preStart = ''
|
||
|
if [ ! -d ${lib.escapeShellArg nodedir} ]; then
|
||
|
mkdir -p /var/db/tahoe-lafs
|
||
|
# See https://github.com/NixOS/nixpkgs/issues/25273
|
||
|
tahoe create-introducer \
|
||
|
--hostname="${config.networking.hostName}" \
|
||
|
${lib.escapeShellArg nodedir}
|
||
|
fi
|
||
|
|
||
|
# Tahoe has created a predefined tahoe.cfg which we must now
|
||
|
# scribble over.
|
||
|
# XXX I thought that a symlink would work here, but it doesn't, so
|
||
|
# we must do this on every prestart. Fixes welcome.
|
||
|
# rm ${nodedir}/tahoe.cfg
|
||
|
# ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
|
||
|
cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
|
||
|
'';
|
||
|
});
|
||
|
users.users = flip mapAttrs' cfg.introducers (node: _:
|
||
|
nameValuePair "tahoe.introducer-${node}" {
|
||
|
description = "Tahoe node user for introducer ${node}";
|
||
|
isSystemUser = true;
|
||
|
group = "tahoe.introducer-${node}";
|
||
|
});
|
||
|
users.groups = flip mapAttrs' cfg.nodes (node: _:
|
||
|
nameValuePair "tahoe.introducer-${node}" { });
|
||
|
})
|
||
|
(mkIf (cfg.nodes != {}) {
|
||
|
environment = {
|
||
|
etc = flip mapAttrs' cfg.nodes (node: settings:
|
||
|
nameValuePair "tahoe-lafs/${node}.cfg" {
|
||
|
mode = "0444";
|
||
|
source = let placeholderFile = lib.pipe settings.settings [
|
||
|
(s: lib.recursiveUpdate
|
||
|
(lib.optionalAttrs (settings.client.helperFile != null) { client."helper.furl" = "@CLIENT_HELPER_FURL@"; })
|
||
|
s)
|
||
|
];
|
||
|
in format.generate "tahoe-lafs-node" placeholderFile;
|
||
|
});
|
||
|
# Actually require Tahoe, so that we will have it installed.
|
||
|
# systemPackages = flip mapAttrsToList cfg.nodes (node: settings:
|
||
|
# settings.package
|
||
|
# );
|
||
|
};
|
||
|
systemd.services = flip mapAttrs' cfg.nodes (node: settings:
|
||
|
let
|
||
|
pidfile = "/run/tahoe.${node}.pid";
|
||
|
# This is a directory, but it has no trailing slash. Tahoe commands
|
||
|
# get antsy when there's a trailing slash.
|
||
|
nodedir = "/var/db/tahoe-lafs/${node}";
|
||
|
in nameValuePair "tahoe.${node}" {
|
||
|
description = "Tahoe LAFS node ${node}";
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
path = [ settings.package ];
|
||
|
restartTriggers = [
|
||
|
config.environment.etc."tahoe-lafs/${node}.cfg".source ];
|
||
|
serviceConfig = {
|
||
|
Type = "simple";
|
||
|
PIDFile = pidfile;
|
||
|
# Believe it or not, Tahoe is very brittle about the order of
|
||
|
# arguments to $(tahoe run). The node directory must come first,
|
||
|
# and arguments which alter Twisted's behavior come afterwards.
|
||
|
ExecStart = ''
|
||
|
${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} --pidfile=${lib.escapeShellArg pidfile}
|
||
|
'';
|
||
|
};
|
||
|
preStart = ''
|
||
|
if [ ! -d ${lib.escapeShellArg nodedir} ]; then
|
||
|
mkdir -p /var/db/tahoe-lafs
|
||
|
tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir}
|
||
|
fi
|
||
|
|
||
|
cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
|
||
|
'' + lib.optionalString (settings.client.helperFile != null) ''
|
||
|
${pkgs.replace-secret}/bin/replace-secret '@CLIENT_HELPER_FURL@' ${settings.client.helperFile} ${lib.escapeShellArg nodedir}/tahoe.cfg
|
||
|
'' + lib.optionalString (settings.client.introducersFile != null) ''
|
||
|
cp "${config.settings.client.introducersFile}" ${lib.escapeShellArg nodedir}/private/introducers.yaml
|
||
|
'' + lib.optionalString (settings.sftpd.accountsFile != null) ''
|
||
|
cp "${config.settings.client.introducersFile}" ${lib.escapeShellArg nodedir}/private/accounts
|
||
|
'';
|
||
|
});
|
||
|
users.users = flip mapAttrs' cfg.nodes (node: _:
|
||
|
nameValuePair "tahoe.${node}" {
|
||
|
description = "Tahoe node user for node ${node}";
|
||
|
isSystemUser = true;
|
||
|
group = "tahoe.${node}";
|
||
|
});
|
||
|
users.groups = flip mapAttrs' cfg.nodes (node: _:
|
||
|
nameValuePair "tahoe.${node}" { });
|
||
|
})
|
||
|
];
|
||
|
}
|