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