at 23.11-pre 8.7 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 145 HTTP address to bind to. 146 ''; 147 }; 148 149 httpListenPort = mkOption { 150 type = types.int; 151 default = 9000; 152 description = lib.mdDoc '' 153 HTTP port to bind on. 154 ''; 155 }; 156 157 httpLogin = mkOption { 158 type = types.str; 159 example = "allyourbase"; 160 default = ""; 161 description = lib.mdDoc '' 162 HTTP web login username. 163 ''; 164 }; 165 166 httpPass = mkOption { 167 type = types.str; 168 example = "arebelongtous"; 169 default = ""; 170 description = lib.mdDoc '' 171 HTTP web login password. 172 ''; 173 }; 174 175 encryptLAN = mkOption { 176 type = types.bool; 177 default = true; 178 description = lib.mdDoc "Encrypt LAN data."; 179 }; 180 181 enableWebUI = mkOption { 182 type = types.bool; 183 default = false; 184 description = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc "API key, which enables the developer API."; 205 }; 206 207 directoryRoot = mkOption { 208 type = types.str; 209 default = ""; 210 example = "/media"; 211 description = lib.mdDoc "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 = lib.mdDoc '' 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}