nixpkgs/nixos/modules/services/networking/resilio.nix
2024-10-04 15:13:04 +02:00

296 lines
8.3 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.resilio;
sharedFoldersRecord = map (entry: {
dir = entry.directory;
use_relay_server = entry.useRelayServer;
use_tracker = entry.useTracker;
use_dht = entry.useDHT;
search_lan = entry.searchLAN;
use_sync_trash = entry.useSyncTrash;
known_hosts = entry.knownHosts;
}) cfg.sharedFolders;
configFile = pkgs.writeText "config.json" (builtins.toJSON ({
device_name = cfg.deviceName;
storage_path = cfg.storagePath;
listening_port = cfg.listeningPort;
use_gui = false;
check_for_updates = cfg.checkForUpdates;
use_upnp = cfg.useUpnp;
download_limit = cfg.downloadLimit;
upload_limit = cfg.uploadLimit;
lan_encrypt_data = cfg.encryptLAN;
} // optionalAttrs (cfg.directoryRoot != "") { directory_root = cfg.directoryRoot; }
// optionalAttrs cfg.enableWebUI {
webui = { listen = "${cfg.httpListenAddr}:${toString cfg.httpListenPort}"; } //
(optionalAttrs (cfg.httpLogin != "") { login = cfg.httpLogin; }) //
(optionalAttrs (cfg.httpPass != "") { password = cfg.httpPass; }) //
(optionalAttrs (cfg.apiKey != "") { api_key = cfg.apiKey; });
} // optionalAttrs (sharedFoldersRecord != []) {
shared_folders = sharedFoldersRecord;
}));
sharedFoldersSecretFiles = map (entry: {
dir = entry.directory;
secretFile = if builtins.hasAttr "secret" entry then
toString (pkgs.writeTextFile {
name = "secret-file";
text = entry.secret;
})
else
entry.secretFile;
}) cfg.sharedFolders;
runConfigPath = "/run/rslsync/config.json";
createConfig = pkgs.writeShellScriptBin "create-resilio-config" (
if cfg.sharedFolders != [ ] then ''
${pkgs.jq}/bin/jq \
'.shared_folders |= map(.secret = $ARGS.named[.dir])' \
${
lib.concatMapStringsSep " \\\n "
(entry: ''--arg '${entry.dir}' "$(cat '${entry.secretFile}')"'')
sharedFoldersSecretFiles
} \
<${configFile} \
>${runConfigPath}
'' else ''
# no secrets, passing through config
cp ${configFile} ${runConfigPath};
''
);
in
{
options = {
services.resilio = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
If enabled, start the Resilio Sync daemon. Once enabled, you can
interact with the service through the Web UI, or configure it in your
NixOS configuration.
'';
};
package = mkPackageOption pkgs "resilio-sync" { };
deviceName = mkOption {
type = types.str;
example = "Voltron";
default = config.networking.hostName;
defaultText = literalExpression "config.networking.hostName";
description = ''
Name of the Resilio Sync device.
'';
};
listeningPort = mkOption {
type = types.int;
default = 0;
example = 44444;
description = ''
Listening port. Defaults to 0 which randomizes the port.
'';
};
checkForUpdates = mkOption {
type = types.bool;
default = true;
description = ''
Determines whether to check for updates and alert the user
about them in the UI.
'';
};
useUpnp = mkOption {
type = types.bool;
default = true;
description = ''
Use Universal Plug-n-Play (UPnP)
'';
};
downloadLimit = mkOption {
type = types.int;
default = 0;
example = 1024;
description = ''
Download speed limit. 0 is unlimited (default).
'';
};
uploadLimit = mkOption {
type = types.int;
default = 0;
example = 1024;
description = ''
Upload speed limit. 0 is unlimited (default).
'';
};
httpListenAddr = mkOption {
type = types.str;
default = "[::1]";
example = "0.0.0.0";
description = ''
HTTP address to bind to.
'';
};
httpListenPort = mkOption {
type = types.int;
default = 9000;
description = ''
HTTP port to bind on.
'';
};
httpLogin = mkOption {
type = types.str;
example = "allyourbase";
default = "";
description = ''
HTTP web login username.
'';
};
httpPass = mkOption {
type = types.str;
example = "arebelongtous";
default = "";
description = ''
HTTP web login password.
'';
};
encryptLAN = mkOption {
type = types.bool;
default = true;
description = "Encrypt LAN data.";
};
enableWebUI = mkOption {
type = types.bool;
default = false;
description = ''
Enable Web UI for administration. Bound to the specified
`httpListenAddress` and
`httpListenPort`.
'';
};
storagePath = mkOption {
type = types.path;
default = "/var/lib/resilio-sync/";
description = ''
Where BitTorrent Sync will store it's database files (containing
things like username info and licenses). Generally, you should not
need to ever change this.
'';
};
apiKey = mkOption {
type = types.str;
default = "";
description = "API key, which enables the developer API.";
};
directoryRoot = mkOption {
type = types.str;
default = "";
example = "/media";
description = "Default directory to add folders in the web UI.";
};
sharedFolders = mkOption {
default = [];
type = types.listOf (types.attrsOf types.anything);
example =
[ { secretFile = "/run/resilio-secret";
directory = "/home/user/sync_test";
useRelayServer = true;
useTracker = true;
useDHT = false;
searchLAN = true;
useSyncTrash = true;
knownHosts = [
"192.168.1.2:4444"
"192.168.1.3:4444"
];
}
];
description = ''
Shared folder list. If enabled, web UI must be
disabled. Secrets can be generated using `rslsync --generate-secret`.
If you would like to be able to modify the contents of this
directories, it is recommended that you make your user a
member of the `rslsync` group.
Directories in this list should be in the
`rslsync` group, and that group must have
write access to the directory. It is also recommended that
`chmod g+s` is applied to the directory
so that any sub directories created will also belong to
the `rslsync` group. Also,
`setfacl -d -m group:rslsync:rwx` and
`setfacl -m group:rslsync:rwx` should also
be applied so that the sub directories are writable by
the group.
'';
};
};
};
config = mkIf cfg.enable {
assertions =
[ { assertion = cfg.deviceName != "";
message = "Device name cannot be empty.";
}
{ assertion = cfg.enableWebUI -> cfg.sharedFolders == [];
message = "If using shared folders, the web UI cannot be enabled.";
}
{ assertion = cfg.apiKey != "" -> cfg.enableWebUI;
message = "If you're using an API key, you must enable the web server.";
}
];
users.users.rslsync = {
description = "Resilio Sync Service user";
home = cfg.storagePath;
createHome = true;
uid = config.ids.uids.rslsync;
group = "rslsync";
};
users.groups.rslsync = {};
systemd.services.resilio = with pkgs; {
description = "Resilio Sync Service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Restart = "on-abort";
UMask = "0002";
User = "rslsync";
RuntimeDirectory = "rslsync";
ExecStartPre = "${createConfig}/bin/create-resilio-config";
ExecStart = ''
${lib.getExe cfg.package} --nodaemon --config ${runConfigPath}
'';
};
};
};
meta.maintainers = [ ];
}