at 24.11-pre 4.9 kB view raw
1{ config, lib, pkgs, utils, ... }: 2let 3 cfg = config.services.pyload; 4 5 stateDir = "/var/lib/pyload"; 6in 7{ 8 meta.maintainers = with lib.maintainers; [ ambroisie ]; 9 10 options = with lib; { 11 services.pyload = { 12 enable = mkEnableOption "pyLoad download manager"; 13 14 package = mkPackageOption pkgs "pyLoad" { default = [ "pyload-ng" ]; }; 15 16 listenAddress = mkOption { 17 type = types.str; 18 default = "localhost"; 19 example = "0.0.0.0"; 20 description = "Address to listen on for the web UI."; 21 }; 22 23 port = mkOption { 24 type = types.port; 25 default = 8000; 26 example = 9876; 27 description = "Port to listen on for the web UI."; 28 }; 29 30 downloadDirectory = mkOption { 31 type = types.path; 32 default = "${stateDir}/downloads"; 33 example = "/mnt/downloads"; 34 description = "Directory to store downloads."; 35 }; 36 37 user = mkOption { 38 type = types.str; 39 default = "pyload"; 40 description = "User under which pyLoad runs, and which owns the download directory."; 41 }; 42 43 group = mkOption { 44 type = types.str; 45 default = "pyload"; 46 description = "Group under which pyLoad runs, and which owns the download directory."; 47 }; 48 49 credentialsFile = mkOption { 50 type = with types; nullOr path; 51 default = null; 52 example = "/run/secrets/pyload-credentials.env"; 53 description = '' 54 File containing {env}`PYLOAD_DEFAULT_USERNAME` and 55 {env}`PYLOAD_DEFAULT_PASSWORD` in the format of an `EnvironmentFile=`, 56 as described by {manpage}`systemd.exec(5)`. 57 58 If not given, they default to the username/password combo of 59 pyload/pyload. 60 ''; 61 }; 62 }; 63 }; 64 65 config = lib.mkIf cfg.enable { 66 systemd.tmpfiles.settings.pyload = { 67 ${cfg.downloadDirectory}.d = { inherit (cfg) user group; }; 68 }; 69 70 systemd.services.pyload = { 71 description = "pyLoad download manager"; 72 wantedBy = [ "multi-user.target" ]; 73 after = [ "network.target" ]; 74 75 # NOTE: unlike what the documentation says, it looks like `HOME` is not 76 # defined with this service definition... 77 # Since pyload tries to do the equivalent of `cd ~`, it needs to be able 78 # to resolve $HOME, which fails when `RootDirectory` is set. 79 # FIXME: check if `SetLoginEnvironment` fixes this issue in version 255 80 environment = { 81 HOME = stateDir; 82 PYLOAD__WEBUI__HOST = cfg.listenAddress; 83 PYLOAD__WEBUI__PORT = builtins.toString cfg.port; 84 }; 85 86 serviceConfig = { 87 ExecStart = utils.escapeSystemdExecArgs [ 88 (lib.getExe cfg.package) 89 "--userdir" 90 "${stateDir}/config" 91 "--storagedir" 92 cfg.downloadDirectory 93 ]; 94 95 User = cfg.user; 96 Group = cfg.group; 97 98 EnvironmentFile = lib.optional (cfg.credentialsFile != null) cfg.credentialsFile; 99 100 StateDirectory = "pyload"; 101 WorkingDirectory = stateDir; 102 RuntimeDirectory = "pyload"; 103 RuntimeDirectoryMode = "0700"; 104 RootDirectory = "/run/pyload"; 105 BindReadOnlyPaths = [ 106 builtins.storeDir # Needed to run the python interpreter 107 ]; 108 BindPaths = [ 109 cfg.downloadDirectory 110 ]; 111 112 # Hardening options 113 LockPersonality = true; 114 NoNewPrivileges = true; 115 PrivateDevices = true; 116 PrivateMounts = true; 117 PrivateTmp = true; 118 PrivateUsers = true; 119 ProcSubset = "pid"; 120 ProtectClock = true; 121 ProtectControlGroups = true; 122 ProtectHome = true; 123 ProtectHostname = true; 124 ProtectKernelLogs = true; 125 ProtectKernelModules = true; 126 ProtectKernelTunables = true; 127 ProtectProc = "invisible"; 128 ProtectSystem = "strict"; 129 RemoveIPC = true; 130 RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; 131 RestrictNamespaces = true; 132 RestrictRealtime = true; 133 RestrictSUIDSGID = true; 134 SystemCallArchitectures = "native"; 135 SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; 136 UMask = "0002"; 137 CapabilityBoundingSet = [ 138 "~CAP_BLOCK_SUSPEND" 139 "~CAP_BPF" 140 "~CAP_CHOWN" 141 "~CAP_IPC_LOCK" 142 "~CAP_KILL" 143 "~CAP_LEASE" 144 "~CAP_LINUX_IMMUTABLE" 145 "~CAP_NET_ADMIN" 146 "~CAP_SYS_ADMIN" 147 "~CAP_SYS_BOOT" 148 "~CAP_SYS_CHROOT" 149 "~CAP_SYS_NICE" 150 "~CAP_SYS_PACCT" 151 "~CAP_SYS_PTRACE" 152 "~CAP_SYS_RESOURCE" 153 "~CAP_SYS_TTY_CONFIG" 154 ]; 155 }; 156 }; 157 158 users.users.pyload = lib.mkIf (cfg.user == "pyload") { 159 isSystemUser = true; 160 group = cfg.group; 161 home = stateDir; 162 }; 163 164 users.groups.pyload = lib.mkIf (cfg.group == "pyload") { }; 165 }; 166}