1 { lib, pkgs, config, ... }:
4 cfg = config.services.rustus;
7 meta.maintainers = with maintainers; [ happysalada ];
9 options.services.rustus = {
11 enable = mkEnableOption "TUS protocol implementation in Rust";
16 The host that rustus will connect to.
18 default = "127.0.0.1";
19 example = "127.0.0.1";
25 The port that rustus will connect to.
31 log_level = mkOption {
32 type = types.enum [ "DEBUG" "INFO" "ERROR" ];
40 max_body_size = mkOption {
43 Maximum body size in bytes
45 default = "10000000"; # 10 mb
46 example = "100000000";
57 disable_health_access_logs = mkOption {
60 disable access log for /health endpoint
66 type = types.listOf types.str;
68 list of origins allowed to upload
71 example = ["*.staging.domain" "*.prod.domain"];
74 tus_extensions = mkOption {
75 type = types.listOf (types.enum [
79 "creation-with-upload"
80 "creation-defer-length"
85 Since TUS protocol offers extensibility you can turn off some protocol extensions.
91 "creation-with-upload"
92 "creation-defer-length"
98 remove_parts = mkOption {
101 remove parts files after successful concatenation
107 storage = lib.mkOption {
109 Storages are used to actually store your files. You can configure where you want to store files.
112 example = lib.literalExpression ''
115 s3_access_key_file = konfig.age.secrets.R2_ACCESS_KEY.path;
116 s3_secret_key_file = konfig.age.secrets.R2_SECRET_KEY.path;
117 s3_bucket = "my_bucket";
118 s3_url = "https://s3.example.com";
121 type = lib.types.submodule {
123 type = lib.mkOption {
124 type = lib.types.enum ["file-storage" "hybrid-s3"];
125 description = "Type of storage to use";
127 s3_access_key_file = lib.mkOption {
128 type = lib.types.str;
129 description = "File path that contains the S3 access key.";
131 s3_secret_key_file = lib.mkOption {
132 type = lib.types.path;
133 description = "File path that contains the S3 secret key.";
135 s3_region = lib.mkOption {
136 type = lib.types.str;
137 default = "us-east-1";
138 description = "S3 region name.";
140 s3_bucket = lib.mkOption {
141 type = lib.types.str;
142 description = "S3 bucket.";
144 s3_url = lib.mkOption {
145 type = lib.types.str;
146 description = "S3 url.";
149 force_sync = lib.mkOption {
150 type = lib.types.bool;
151 description = "calls fsync system call after every write to disk in local storage";
154 data_dir = lib.mkOption {
155 type = lib.types.str;
156 description = "path to the local directory where all files are stored";
157 default = "/var/lib/rustus";
159 dir_structure = lib.mkOption {
160 type = lib.types.str;
161 description = "pattern of a directory structure locally and on s3";
162 default = "{year}/{month}/{day}";
168 info_storage = lib.mkOption {
170 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.
173 type = lib.types.submodule {
175 type = lib.mkOption {
176 type = lib.types.enum ["file-info-storage"];
177 description = "Type of info storage to use";
178 default = "file-info-storage";
181 type = lib.types.str;
182 description = "directory to store info about uploads";
183 default = "/var/lib/rustus";
190 config = lib.mkIf cfg.enable {
192 systemd.services.rustus =
194 isHybridS3 = cfg.storage.type == "hybrid-s3";
197 description = "Rustus server";
198 documentation = [ "https://s3rius.github.io/rustus/" ];
200 wantedBy = [ "multi-user.target" ];
201 after = [ "network.target" ];
204 RUSTUS_SERVER_HOST = cfg.host;
205 RUSTUS_SERVER_PORT = toString cfg.port;
206 RUSTUS_LOG_LEVEL = cfg.log_level;
207 RUSTUS_MAX_BODY_SIZE = cfg.max_body_size;
208 RUSTUS_URL = cfg.url;
209 RUSTUS_DISABLE_HEALTH_ACCESS_LOG = lib.mkIf cfg.disable_health_access_logs "true";
210 RUSTUS_CORS = lib.concatStringsSep "," cfg.cors;
211 RUSTUS_TUS_EXTENSIONS = lib.concatStringsSep "," cfg.tus_extensions;
212 RUSTUS_REMOVE_PARTS= if cfg.remove_parts then "true" else "false";
213 RUSTUS_STORAGE = cfg.storage.type;
214 RUSTUS_DATA_DIR = cfg.storage.data_dir;
215 RUSTUS_DIR_STRUCTURE = cfg.storage.dir_structure;
216 RUSTUS_FORCE_FSYNC = if cfg.storage.force_sync then "true" else "false";
217 RUSTUS_S3_URL = mkIf isHybridS3 cfg.storage.s3_url;
218 RUSTUS_S3_BUCKET = mkIf isHybridS3 cfg.storage.s3_bucket;
219 RUSTUS_S3_REGION = mkIf isHybridS3 cfg.storage.s3_region;
220 RUSTUS_S3_ACCESS_KEY_PATH = mkIf isHybridS3 "%d/S3_ACCESS_KEY_PATH";
221 RUSTUS_S3_SECRET_KEY_PATH = mkIf isHybridS3 "%d/S3_SECRET_KEY_PATH";
222 RUSTUS_INFO_STORAGE = cfg.info_storage.type;
223 RUSTUS_INFO_DIR = cfg.info_storage.dir;
227 ExecStart = "${pkgs.rustus}/bin/rustus";
228 StateDirectory = "rustus";
229 # User name is defined here to enable restoring a backup for example
230 # You will run the backup restore command as sudo -u rustus in order
231 # to have write permissions to /var/lib
234 LoadCredential = lib.optionals isHybridS3 [
235 "S3_ACCESS_KEY_PATH:${cfg.storage.s3_access_key_file}"
236 "S3_SECRET_KEY_PATH:${cfg.storage.s3_secret_key_file}"
239 RestrictRealtime=true;
240 RestrictNamespaces=true;
241 LockPersonality=true;
242 ProtectKernelModules=true;
243 ProtectKernelTunables=true;
244 ProtectKernelLogs=true;
245 ProtectControlGroups=true;
246 ProtectHostUserNamespaces=true;
248 RestrictSUIDSGID=true;
249 SystemCallArchitectures="native";
250 CapabilityBoundingSet="";
251 ProtectProc = "invisible";
252 # TODO consider SystemCallFilter LimitAS ProcSubset