at 25.11-pre 8.2 kB view raw
1{ 2 lib, 3 pkgs, 4 config, 5 ... 6}: 7with lib; 8let 9 cfg = config.services.rustus; 10in 11{ 12 meta.maintainers = with maintainers; [ happysalada ]; 13 14 options.services.rustus = { 15 16 enable = mkEnableOption "TUS protocol implementation in Rust"; 17 18 host = mkOption { 19 type = types.str; 20 description = '' 21 The host that rustus will connect to. 22 ''; 23 default = "127.0.0.1"; 24 example = "127.0.0.1"; 25 }; 26 27 port = mkOption { 28 type = types.port; 29 description = '' 30 The port that rustus will connect to. 31 ''; 32 default = 1081; 33 example = 1081; 34 }; 35 36 log_level = mkOption { 37 type = types.enum [ 38 "DEBUG" 39 "INFO" 40 "ERROR" 41 ]; 42 description = '' 43 Desired log level 44 ''; 45 default = "INFO"; 46 example = "ERROR"; 47 }; 48 49 max_body_size = mkOption { 50 type = types.str; 51 description = '' 52 Maximum body size in bytes 53 ''; 54 default = "10000000"; # 10 mb 55 example = "100000000"; 56 }; 57 58 url = mkOption { 59 type = types.str; 60 description = '' 61 url path for uploads 62 ''; 63 default = "/files"; 64 }; 65 66 disable_health_access_logs = mkOption { 67 type = types.bool; 68 description = '' 69 disable access log for /health endpoint 70 ''; 71 default = false; 72 }; 73 74 cors = mkOption { 75 type = types.listOf types.str; 76 description = '' 77 list of origins allowed to upload 78 ''; 79 default = [ "*" ]; 80 example = [ 81 "*.staging.domain" 82 "*.prod.domain" 83 ]; 84 }; 85 86 tus_extensions = mkOption { 87 type = types.listOf ( 88 types.enum [ 89 "getting" 90 "creation" 91 "termination" 92 "creation-with-upload" 93 "creation-defer-length" 94 "concatenation" 95 "checksum" 96 ] 97 ); 98 description = '' 99 Since TUS protocol offers extensibility you can turn off some protocol extensions. 100 ''; 101 default = [ 102 "getting" 103 "creation" 104 "termination" 105 "creation-with-upload" 106 "creation-defer-length" 107 "concatenation" 108 "checksum" 109 ]; 110 }; 111 112 remove_parts = mkOption { 113 type = types.bool; 114 description = '' 115 remove parts files after successful concatenation 116 ''; 117 default = true; 118 example = false; 119 }; 120 121 storage = lib.mkOption { 122 description = '' 123 Storages are used to actually store your files. You can configure where you want to store files. 124 ''; 125 default = { }; 126 example = lib.literalExpression '' 127 { 128 type = "hybrid-s3" 129 s3_access_key_file = konfig.age.secrets.R2_ACCESS_KEY.path; 130 s3_secret_key_file = konfig.age.secrets.R2_SECRET_KEY.path; 131 s3_bucket = "my_bucket"; 132 s3_url = "https://s3.example.com"; 133 } 134 ''; 135 type = lib.types.submodule { 136 options = { 137 type = lib.mkOption { 138 type = lib.types.enum [ 139 "file-storage" 140 "hybrid-s3" 141 ]; 142 description = "Type of storage to use"; 143 }; 144 s3_access_key_file = lib.mkOption { 145 type = lib.types.str; 146 description = "File path that contains the S3 access key."; 147 }; 148 s3_secret_key_file = lib.mkOption { 149 type = lib.types.path; 150 description = "File path that contains the S3 secret key."; 151 }; 152 s3_region = lib.mkOption { 153 type = lib.types.str; 154 default = "us-east-1"; 155 description = "S3 region name."; 156 }; 157 s3_bucket = lib.mkOption { 158 type = lib.types.str; 159 description = "S3 bucket."; 160 }; 161 s3_url = lib.mkOption { 162 type = lib.types.str; 163 description = "S3 url."; 164 }; 165 166 force_sync = lib.mkOption { 167 type = lib.types.bool; 168 description = "calls fsync system call after every write to disk in local storage"; 169 default = true; 170 }; 171 data_dir = lib.mkOption { 172 type = lib.types.str; 173 description = "path to the local directory where all files are stored"; 174 default = "/var/lib/rustus"; 175 }; 176 dir_structure = lib.mkOption { 177 type = lib.types.str; 178 description = "pattern of a directory structure locally and on s3"; 179 default = "{year}/{month}/{day}"; 180 }; 181 }; 182 }; 183 }; 184 185 info_storage = lib.mkOption { 186 description = '' 187 Info storages are used to store information about file uploads. These storages must be persistent, because every time chunk is uploaded rustus updates information about upload. And when someone wants to download file, information about it requested from storage to get actual path of an upload. 188 ''; 189 default = { }; 190 type = lib.types.submodule { 191 options = { 192 type = lib.mkOption { 193 type = lib.types.enum [ "file-info-storage" ]; 194 description = "Type of info storage to use"; 195 default = "file-info-storage"; 196 }; 197 dir = lib.mkOption { 198 type = lib.types.str; 199 description = "directory to store info about uploads"; 200 default = "/var/lib/rustus"; 201 }; 202 }; 203 }; 204 }; 205 }; 206 207 config = lib.mkIf cfg.enable { 208 209 systemd.services.rustus = 210 let 211 isHybridS3 = cfg.storage.type == "hybrid-s3"; 212 in 213 { 214 description = "Rustus server"; 215 documentation = [ "https://s3rius.github.io/rustus/" ]; 216 217 wantedBy = [ "multi-user.target" ]; 218 after = [ "network.target" ]; 219 220 environment = { 221 RUSTUS_SERVER_HOST = cfg.host; 222 RUSTUS_SERVER_PORT = toString cfg.port; 223 RUSTUS_LOG_LEVEL = cfg.log_level; 224 RUSTUS_MAX_BODY_SIZE = cfg.max_body_size; 225 RUSTUS_URL = cfg.url; 226 RUSTUS_DISABLE_HEALTH_ACCESS_LOG = lib.mkIf cfg.disable_health_access_logs "true"; 227 RUSTUS_CORS = lib.concatStringsSep "," cfg.cors; 228 RUSTUS_TUS_EXTENSIONS = lib.concatStringsSep "," cfg.tus_extensions; 229 RUSTUS_REMOVE_PARTS = if cfg.remove_parts then "true" else "false"; 230 RUSTUS_STORAGE = cfg.storage.type; 231 RUSTUS_DATA_DIR = cfg.storage.data_dir; 232 RUSTUS_DIR_STRUCTURE = cfg.storage.dir_structure; 233 RUSTUS_FORCE_FSYNC = if cfg.storage.force_sync then "true" else "false"; 234 RUSTUS_S3_URL = mkIf isHybridS3 cfg.storage.s3_url; 235 RUSTUS_S3_BUCKET = mkIf isHybridS3 cfg.storage.s3_bucket; 236 RUSTUS_S3_REGION = mkIf isHybridS3 cfg.storage.s3_region; 237 RUSTUS_S3_ACCESS_KEY_PATH = mkIf isHybridS3 "%d/S3_ACCESS_KEY_PATH"; 238 RUSTUS_S3_SECRET_KEY_PATH = mkIf isHybridS3 "%d/S3_SECRET_KEY_PATH"; 239 RUSTUS_INFO_STORAGE = cfg.info_storage.type; 240 RUSTUS_INFO_DIR = cfg.info_storage.dir; 241 }; 242 243 serviceConfig = { 244 ExecStart = "${pkgs.rustus}/bin/rustus"; 245 StateDirectory = "rustus"; 246 # User name is defined here to enable restoring a backup for example 247 # You will run the backup restore command as sudo -u rustus in order 248 # to have write permissions to /var/lib 249 User = "rustus"; 250 DynamicUser = true; 251 LoadCredential = lib.optionals isHybridS3 [ 252 "S3_ACCESS_KEY_PATH:${cfg.storage.s3_access_key_file}" 253 "S3_SECRET_KEY_PATH:${cfg.storage.s3_secret_key_file}" 254 ]; 255 # hardening 256 RestrictRealtime = true; 257 RestrictNamespaces = true; 258 LockPersonality = true; 259 ProtectKernelModules = true; 260 ProtectKernelTunables = true; 261 ProtectKernelLogs = true; 262 ProtectControlGroups = true; 263 ProtectHostUserNamespaces = true; 264 ProtectClock = true; 265 RestrictSUIDSGID = true; 266 SystemCallArchitectures = "native"; 267 CapabilityBoundingSet = ""; 268 ProtectProc = "invisible"; 269 # TODO consider SystemCallFilter LimitAS ProcSubset 270 }; 271 }; 272 }; 273}