python312Packages.dissect-extfs: 3.11 -> 3.12
[NixPkgs.git] / nixos / modules / services / web-servers / rustus.nix
blobb356133df085db89c4085f7e6246268803ba9f7c
1 { lib, pkgs, config, ... }:
2 with lib;
3 let
4   cfg = config.services.rustus;
5 in
7   meta.maintainers = with maintainers; [ happysalada ];
9   options.services.rustus = {
11     enable = mkEnableOption "TUS protocol implementation in Rust";
13     host = mkOption {
14       type = types.str;
15       description = ''
16         The host that rustus will connect to.
17       '';
18       default = "127.0.0.1";
19       example = "127.0.0.1";
20     };
22     port = mkOption {
23       type = types.port;
24       description = ''
25         The port that rustus will connect to.
26       '';
27       default = 1081;
28       example = 1081;
29     };
31     log_level = mkOption {
32       type = types.enum [ "DEBUG" "INFO" "ERROR" ];
33       description = ''
34         Desired log level
35       '';
36       default = "INFO";
37       example = "ERROR";
38     };
40     max_body_size = mkOption {
41       type = types.str;
42       description = ''
43         Maximum body size in bytes
44       '';
45       default = "10000000"; # 10 mb
46       example = "100000000";
47     };
49     url = mkOption {
50       type = types.str;
51       description = ''
52         url path for uploads
53       '';
54       default = "/files";
55     };
57     disable_health_access_logs = mkOption {
58       type = types.bool;
59       description = ''
60         disable access log for /health endpoint
61       '';
62       default = false;
63     };
65     cors = mkOption {
66       type = types.listOf types.str;
67       description = ''
68         list of origins allowed to upload
69       '';
70       default = ["*"];
71       example = ["*.staging.domain" "*.prod.domain"];
72     };
74     tus_extensions = mkOption {
75       type = types.listOf (types.enum [
76         "getting"
77         "creation"
78         "termination"
79         "creation-with-upload"
80         "creation-defer-length"
81         "concatenation"
82         "checksum"
83       ]);
84       description = ''
85         Since TUS protocol offers extensibility you can turn off some protocol extensions.
86       '';
87       default = [
88         "getting"
89         "creation"
90         "termination"
91         "creation-with-upload"
92         "creation-defer-length"
93         "concatenation"
94         "checksum"
95       ];
96     };
98     remove_parts = mkOption {
99       type = types.bool;
100       description = ''
101         remove parts files after successful concatenation
102       '';
103       default = true;
104       example = false;
105     };
107     storage = lib.mkOption {
108       description = ''
109         Storages are used to actually store your files. You can configure where you want to store files.
110       '';
111       default = {};
112       example = lib.literalExpression ''
113         {
114           type = "hybrid-s3"
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";
119         }
120       '';
121       type = lib.types.submodule {
122         options = {
123           type = lib.mkOption {
124             type = lib.types.enum ["file-storage" "hybrid-s3"];
125             description = "Type of storage to use";
126           };
127           s3_access_key_file = lib.mkOption {
128             type = lib.types.str;
129             description = "File path that contains the S3 access key.";
130           };
131           s3_secret_key_file = lib.mkOption {
132             type = lib.types.path;
133             description = "File path that contains the S3 secret key.";
134           };
135           s3_region = lib.mkOption {
136             type = lib.types.str;
137             default = "us-east-1";
138             description = "S3 region name.";
139           };
140           s3_bucket = lib.mkOption {
141             type = lib.types.str;
142             description = "S3 bucket.";
143           };
144           s3_url = lib.mkOption {
145             type = lib.types.str;
146             description = "S3 url.";
147           };
149           force_sync = lib.mkOption {
150             type = lib.types.bool;
151             description = "calls fsync system call after every write to disk in local storage";
152             default = true;
153           };
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";
158           };
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}";
163           };
164         };
165       };
166     };
168     info_storage = lib.mkOption {
169       description = ''
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.
171       '';
172       default = {};
173       type = lib.types.submodule {
174         options = {
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";
179           };
180           dir = lib.mkOption {
181             type = lib.types.str;
182             description = "directory to store info about uploads";
183             default = "/var/lib/rustus";
184           };
185         };
186       };
187     };
188   };
190   config = lib.mkIf cfg.enable {
192     systemd.services.rustus =
193       let
194         isHybridS3 = cfg.storage.type == "hybrid-s3";
195       in
196     {
197       description = "Rustus server";
198       documentation = [ "https://s3rius.github.io/rustus/" ];
200       wantedBy = [ "multi-user.target" ];
201       after = [ "network.target" ];
203       environment = {
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;
224       };
226       serviceConfig = {
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
232         User = "rustus";
233         DynamicUser = true;
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}"
237         ];
238         # hardening
239         RestrictRealtime=true;
240         RestrictNamespaces=true;
241         LockPersonality=true;
242         ProtectKernelModules=true;
243         ProtectKernelTunables=true;
244         ProtectKernelLogs=true;
245         ProtectControlGroups=true;
246         ProtectHostUserNamespaces=true;
247         ProtectClock=true;
248         RestrictSUIDSGID=true;
249         SystemCallArchitectures="native";
250         CapabilityBoundingSet="";
251         ProtectProc = "invisible";
252         # TODO consider SystemCallFilter LimitAS ProcSubset
253       };
254     };
255   };