1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.resilio;
7
8 resilioSync = pkgs.resilio-sync;
9
10 sharedFoldersRecord = map (entry: {
11 secret = entry.secret;
12 dir = entry.directory;
13
14 use_relay_server = entry.useRelayServer;
15 use_tracker = entry.useTracker;
16 use_dht = entry.useDHT;
17
18 search_lan = entry.searchLAN;
19 use_sync_trash = entry.useSyncTrash;
20 known_hosts = entry.knownHosts;
21 }) cfg.sharedFolders;
22
23 configFile = pkgs.writeText "config.json" (builtins.toJSON ({
24 device_name = cfg.deviceName;
25 storage_path = cfg.storagePath;
26 listening_port = cfg.listeningPort;
27 use_gui = false;
28 check_for_updates = cfg.checkForUpdates;
29 use_upnp = cfg.useUpnp;
30 download_limit = cfg.downloadLimit;
31 upload_limit = cfg.uploadLimit;
32 lan_encrypt_data = cfg.encryptLAN;
33 } // optionalAttrs (cfg.directoryRoot != "") { directory_root = cfg.directoryRoot; }
34 // optionalAttrs cfg.enableWebUI {
35 webui = { listen = "${cfg.httpListenAddr}:${toString cfg.httpListenPort}"; } //
36 (optionalAttrs (cfg.httpLogin != "") { login = cfg.httpLogin; }) //
37 (optionalAttrs (cfg.httpPass != "") { password = cfg.httpPass; }) //
38 (optionalAttrs (cfg.apiKey != "") { api_key = cfg.apiKey; });
39 } // optionalAttrs (sharedFoldersRecord != []) {
40 shared_folders = sharedFoldersRecord;
41 }));
42
43in
44{
45 options = {
46 services.resilio = {
47 enable = mkOption {
48 type = types.bool;
49 default = false;
50 description = lib.mdDoc ''
51 If enabled, start the Resilio Sync daemon. Once enabled, you can
52 interact with the service through the Web UI, or configure it in your
53 NixOS configuration.
54 '';
55 };
56
57 deviceName = mkOption {
58 type = types.str;
59 example = "Voltron";
60 default = config.networking.hostName;
61 defaultText = literalExpression "config.networking.hostName";
62 description = lib.mdDoc ''
63 Name of the Resilio Sync device.
64 '';
65 };
66
67 listeningPort = mkOption {
68 type = types.int;
69 default = 0;
70 example = 44444;
71 description = lib.mdDoc ''
72 Listening port. Defaults to 0 which randomizes the port.
73 '';
74 };
75
76 checkForUpdates = mkOption {
77 type = types.bool;
78 default = true;
79 description = lib.mdDoc ''
80 Determines whether to check for updates and alert the user
81 about them in the UI.
82 '';
83 };
84
85 useUpnp = mkOption {
86 type = types.bool;
87 default = true;
88 description = lib.mdDoc ''
89 Use Universal Plug-n-Play (UPnP)
90 '';
91 };
92
93 downloadLimit = mkOption {
94 type = types.int;
95 default = 0;
96 example = 1024;
97 description = lib.mdDoc ''
98 Download speed limit. 0 is unlimited (default).
99 '';
100 };
101
102 uploadLimit = mkOption {
103 type = types.int;
104 default = 0;
105 example = 1024;
106 description = lib.mdDoc ''
107 Upload speed limit. 0 is unlimited (default).
108 '';
109 };
110
111 httpListenAddr = mkOption {
112 type = types.str;
113 default = "[::1]";
114 example = "0.0.0.0";
115 description = lib.mdDoc ''
116 HTTP address to bind to.
117 '';
118 };
119
120 httpListenPort = mkOption {
121 type = types.int;
122 default = 9000;
123 description = lib.mdDoc ''
124 HTTP port to bind on.
125 '';
126 };
127
128 httpLogin = mkOption {
129 type = types.str;
130 example = "allyourbase";
131 default = "";
132 description = lib.mdDoc ''
133 HTTP web login username.
134 '';
135 };
136
137 httpPass = mkOption {
138 type = types.str;
139 example = "arebelongtous";
140 default = "";
141 description = lib.mdDoc ''
142 HTTP web login password.
143 '';
144 };
145
146 encryptLAN = mkOption {
147 type = types.bool;
148 default = true;
149 description = lib.mdDoc "Encrypt LAN data.";
150 };
151
152 enableWebUI = mkOption {
153 type = types.bool;
154 default = false;
155 description = lib.mdDoc ''
156 Enable Web UI for administration. Bound to the specified
157 `httpListenAddress` and
158 `httpListenPort`.
159 '';
160 };
161
162 storagePath = mkOption {
163 type = types.path;
164 default = "/var/lib/resilio-sync/";
165 description = lib.mdDoc ''
166 Where BitTorrent Sync will store it's database files (containing
167 things like username info and licenses). Generally, you should not
168 need to ever change this.
169 '';
170 };
171
172 apiKey = mkOption {
173 type = types.str;
174 default = "";
175 description = lib.mdDoc "API key, which enables the developer API.";
176 };
177
178 directoryRoot = mkOption {
179 type = types.str;
180 default = "";
181 example = "/media";
182 description = lib.mdDoc "Default directory to add folders in the web UI.";
183 };
184
185 sharedFolders = mkOption {
186 default = [];
187 type = types.listOf (types.attrsOf types.anything);
188 example =
189 [ { secret = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y";
190 directory = "/home/user/sync_test";
191 useRelayServer = true;
192 useTracker = true;
193 useDHT = false;
194 searchLAN = true;
195 useSyncTrash = true;
196 knownHosts = [
197 "192.168.1.2:4444"
198 "192.168.1.3:4444"
199 ];
200 }
201 ];
202 description = lib.mdDoc ''
203 Shared folder list. If enabled, web UI must be
204 disabled. Secrets can be generated using `rslsync --generate-secret`.
205 Note that this secret will be
206 put inside the Nix store, so it is realistically not very
207 secret.
208
209 If you would like to be able to modify the contents of this
210 directories, it is recommended that you make your user a
211 member of the `rslsync` group.
212
213 Directories in this list should be in the
214 `rslsync` group, and that group must have
215 write access to the directory. It is also recommended that
216 `chmod g+s` is applied to the directory
217 so that any sub directories created will also belong to
218 the `rslsync` group. Also,
219 `setfacl -d -m group:rslsync:rwx` and
220 `setfacl -m group:rslsync:rwx` should also
221 be applied so that the sub directories are writable by
222 the group.
223 '';
224 };
225 };
226 };
227
228 config = mkIf cfg.enable {
229 assertions =
230 [ { assertion = cfg.deviceName != "";
231 message = "Device name cannot be empty.";
232 }
233 { assertion = cfg.enableWebUI -> cfg.sharedFolders == [];
234 message = "If using shared folders, the web UI cannot be enabled.";
235 }
236 { assertion = cfg.apiKey != "" -> cfg.enableWebUI;
237 message = "If you're using an API key, you must enable the web server.";
238 }
239 ];
240
241 users.users.rslsync = {
242 description = "Resilio Sync Service user";
243 home = cfg.storagePath;
244 createHome = true;
245 uid = config.ids.uids.rslsync;
246 group = "rslsync";
247 };
248
249 users.groups.rslsync = {};
250
251 systemd.services.resilio = with pkgs; {
252 description = "Resilio Sync Service";
253 wantedBy = [ "multi-user.target" ];
254 after = [ "network.target" ];
255 serviceConfig = {
256 Restart = "on-abort";
257 UMask = "0002";
258 User = "rslsync";
259 ExecStart = ''
260 ${resilioSync}/bin/rslsync --nodaemon --config ${configFile}
261 '';
262 };
263 };
264 };
265}