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