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