···
8
+
cfg = config.services.reposilite;
9
+
format = pkgs.formats.cdn { };
10
+
configFile = format.generate "reposilite.cdn" cfg.settings;
12
+
useEmbeddedDb = cfg.database.type == "sqlite" || cfg.database.type == "h2";
13
+
useMySQL = cfg.database.type == "mariadb" || cfg.database.type == "mysql";
14
+
usePostgres = cfg.database.type == "postgresql";
16
+
# db password is appended at runtime by the service script (if needed)
18
+
if useEmbeddedDb then
19
+
"${cfg.database.type} ${cfg.database.path}"
21
+
"${cfg.database.type} ${cfg.database.host}:${builtins.toString cfg.database.port} ${cfg.database.dbname} ${cfg.database.user} $(<${cfg.database.passwordFile})";
23
+
certDir = config.security.acme.certs.${cfg.useACMEHost}.directory;
27
+
type = lib.mkOption {
28
+
type = lib.types.enum [
36
+
Database engine to use.
41
+
path = lib.mkOption {
42
+
type = lib.types.str;
44
+
Path to the embedded database file. Set to `--temporary` to use an in-memory database.
46
+
default = "reposilite.db";
49
+
host = lib.mkOption {
50
+
type = lib.types.str;
52
+
Database host address.
54
+
default = "127.0.0.1";
57
+
port = lib.mkOption {
58
+
type = lib.types.port;
62
+
defaultText = lib.literalExpression ''
63
+
if type == "postgresql" then 5432 else 3306
65
+
default = if usePostgres then config.services.postgresql.settings.port else 3306;
68
+
dbname = lib.mkOption {
69
+
type = lib.types.str;
73
+
default = "reposilite";
76
+
user = lib.mkOption {
77
+
type = lib.types.str;
81
+
default = "reposilite";
84
+
passwordFile = lib.mkOption {
85
+
type = lib.types.nullOr lib.types.path;
87
+
Path to the file containing the password for the database connection.
88
+
This file must be readable by {option}`services.reposilite.user`.
96
+
freeformType = format.type;
98
+
hostname = lib.mkOption {
99
+
type = lib.types.str;
101
+
The hostname to bind to. Set to `0.0.0.0` to accept connections from everywhere, or `127.0.0.1` to restrict to localhost."
103
+
default = "0.0.0.0";
104
+
example = "127.0.0.1";
107
+
port = lib.mkOption {
108
+
type = lib.types.port;
110
+
The TCP port to bind to.
115
+
database = lib.mkOption {
116
+
type = lib.types.nullOr lib.types.str;
118
+
Database connection string. Please use {option}`services.reposilite.database` instead.
119
+
See https://reposilite.com/guide/general#local-configuration for valid values.
124
+
sslEnabled = lib.mkOption {
125
+
type = lib.types.bool;
127
+
Whether to listen for encrypted connections on {option}`settings.sslPort`.
132
+
sslPort = lib.mkOption {
133
+
type = lib.types.port; # cant be null
134
+
description = "SSL port to bind to. SSL needs to be enabled explicitly via {option}`settings.enableSsl`.";
138
+
keyPath = lib.mkOption {
139
+
type = lib.types.nullOr lib.types.str;
141
+
Path to the .jsk KeyStore or paths to the PKCS#8 certificate and private key, separated by a space (see example).
142
+
You can use `''${WORKING_DIRECTORY}` to refer to paths relative to Reposilite's working directory.
143
+
If you are using a Java KeyStore, don't forget to specify the password via the {var}`REPOSILITE_LOCAL_KEYPASSWORD` environment variable.
144
+
See https://reposilite.com/guide/ssl for more information on how to set SSL up.
147
+
example = "\${WORKING_DIRECTORY}/cert.pem \${WORKING_DIRECTORY}/key.pem";
150
+
keyPassword = lib.mkOption {
151
+
type = lib.types.nullOr lib.types.str;
153
+
Plaintext password used to unlock the Java KeyStore set in {option}`services.reposilite.settings.keyPath`.
154
+
WARNING: this option is insecure and should not be used to store the password.
155
+
Consider using {option}`services.reposilite.keyPasswordFile` instead.
160
+
enforceSsl = lib.mkOption {
161
+
type = lib.types.bool;
163
+
Whether to redirect all traffic to SSL.
168
+
webThreadPool = lib.mkOption {
169
+
type = lib.types.ints.between 5 65535;
171
+
Maximum amount of threads used by the core thread pool. (min: 5)
172
+
The web thread pool handles the first few steps of incoming HTTP connections, tasks are redirected as soon as possible to the IO thread pool.
177
+
ioThreadPool = lib.mkOption {
178
+
type = lib.types.ints.between 2 65535;
180
+
The IO thread pool handles all tasks that may benefit from non-blocking IO. (min: 2)
181
+
Because most tasks are redirected to IO thread pool, it might be a good idea to keep it at least equal to web thread pool.
186
+
databaseThreadPool = lib.mkOption {
187
+
type = lib.types.ints.positive;
189
+
Maximum amount of concurrent connections to the database. (one per thread)
190
+
Embedded databases (sqlite, h2) do not support truly concurrent connections, so the value will always be `1` if they are used.
195
+
compressionStrategy = lib.mkOption {
196
+
type = lib.types.enum [
201
+
Compression algorithm used by this instance of Reposilite.
202
+
`none` reduces usage of CPU & memory, but requires transfering more data.
207
+
idleTimeout = lib.mkOption {
208
+
type = lib.types.ints.unsigned;
210
+
Default idle timeout used by Jetty.
215
+
bypassExternalCache = lib.mkOption {
216
+
type = lib.types.bool;
218
+
Add cache bypass headers to responses from /api/* to avoid issues with proxies such as Cloudflare.
223
+
cachedLogSize = lib.mkOption {
224
+
type = lib.types.ints.unsigned;
226
+
Amount of messages stored in the cache logger.
231
+
defaultFrontend = lib.mkOption {
232
+
type = lib.types.bool;
234
+
Whether to enable the default included frontend with a dashboard.
239
+
basePath = lib.mkOption {
240
+
type = lib.types.str;
242
+
Custom base path for this Reposilite instance.
243
+
It is not recommended changing this, you should instead prioritize using a different subdomain.
248
+
debugEnabled = lib.mkOption {
249
+
type = lib.types.bool;
251
+
Whether to enable debug mode.
259
+
options.services.reposilite = {
260
+
enable = lib.mkEnableOption "Reposilite";
261
+
package = lib.mkPackageOption pkgs "reposilite" { } // {
264
+
pkg.override (old: {
265
+
plugins = (old.plugins or [ ]) ++ cfg.plugins;
269
+
plugins = lib.mkOption {
270
+
type = lib.types.listOf lib.types.package;
272
+
List of plugins to add to Reposilite.
275
+
example = "with reposilitePlugins; [ checksum groovy ]";
278
+
database = lib.mkOption {
279
+
description = "Database options.";
281
+
type = lib.types.submodule databaseModule;
284
+
keyPasswordFile = lib.mkOption {
285
+
type = lib.types.nullOr lib.types.path;
287
+
Path the the file containing the password used to unlock the Java KeyStore file specified in {option}`services.reposilite.settings.keyPath`.
288
+
This file must be readable my {option}`services.reposilite.user`.
293
+
useACMEHost = lib.mkOption {
294
+
type = lib.types.nullOr lib.types.str;
296
+
Host of an existing Let's Encrypt certificate to use for SSL.
297
+
Make sure that the certificate directory is readable by the `reposilite` user or group, for example via {option}`security.acme.certs.<cert>.group`.
298
+
*Note that this option does not create any certificates, nor it does add subdomains to existing ones – you will need to create them manually using {option}`security.acme.certs`*
303
+
settings = lib.mkOption {
304
+
description = "Configuration written to the reposilite.cdn file";
306
+
type = lib.types.submodule settingsModule;
309
+
workingDirectory = lib.mkOption {
310
+
type = lib.types.path;
312
+
Working directory for Reposilite.
314
+
default = "/var/lib/reposilite";
317
+
extraArgs = lib.mkOption {
318
+
type = lib.types.listOf lib.types.str;
320
+
Extra arguments/parameters passed to the Reposilite. Can be used for first token generation.
323
+
example = lib.literalExpression ''[ "--token" "name:tempsecrettoken" ]'';
326
+
user = lib.mkOption {
327
+
type = lib.types.str;
329
+
The user to run Reposilite under.
331
+
default = "reposilite";
334
+
group = lib.mkOption {
335
+
type = lib.types.str;
337
+
The group to run Reposilite under.
339
+
default = "reposilite";
342
+
openFirewall = lib.mkOption {
343
+
type = lib.types.bool;
345
+
Whether to open the firewall ports for Reposilite. If SSL is enabled, its port will be opened too.
351
+
config = lib.mkIf cfg.enable {
354
+
assertion = cfg.settings.sslEnabled -> cfg.settings.keyPath != null;
356
+
Reposilite was configured to enable SSL, but no valid paths to certificate files were provided via `settings.keyPath`.
357
+
Read more about SSL certificates here: https://reposilite.com/guide/ssl
361
+
assertion = cfg.settings.enforceSsl -> cfg.settings.sslEnabled;
362
+
message = "You cannot enforce SSL if SSL is not enabled.";
365
+
assertion = !useEmbeddedDb -> cfg.database.passwordFile != null;
366
+
message = "You need to set `services.reposilite.database.passwordFile` when using MySQL or Postgres.";
370
+
services.reposilite.settings.keyPath = lib.mkIf (
371
+
cfg.useACMEHost != null
372
+
) "${certDir}/fullchain.pem ${certDir}/key.pem";
374
+
environment.systemPackages = [ cfg.package ];
377
+
groups.${cfg.group} = lib.mkIf (cfg.group == "reposilite") { };
378
+
users.${cfg.user} = lib.mkIf (cfg.user == "reposilite") {
379
+
isSystemUser = true;
384
+
networking.firewall = lib.mkIf cfg.openFirewall (
387
+
allowedTCPPorts = [ cfg.settings.port ];
389
+
(lib.mkIf cfg.settings.sslEnabled {
390
+
allowedTCPPorts = [ cfg.settings.sslPort ];
395
+
systemd.services.reposilite = {
397
+
wantedBy = [ "multi-user.target" ];
399
+
[ "network.target" ]
400
+
++ (lib.optional useMySQL "mysql.service")
401
+
++ (lib.optional usePostgres "postgresql.service");
404
+
lib.optionalString (cfg.keyPasswordFile != null && cfg.settings.keyPassword == null) ''
405
+
export REPOSILITE_LOCAL_KEYPASSWORD="$(<${cfg.keyPasswordFile})"
408
+
export REPOSILITE_LOCAL_DATABASE="${dbString}"
410
+
${lib.getExe cfg.package} --local-configuration ${configFile} --local-configuration-mode none --working-directory ${cfg.workingDirectory} ${lib.escapeShellArgs cfg.extraArgs}
413
+
serviceConfig = lib.mkMerge [
414
+
(lib.mkIf (builtins.dirOf cfg.workingDirectory == "/var/lib") {
415
+
StateDirectory = builtins.baseNameOf cfg.workingDirectory;
416
+
StateDirectoryMode = "700";
420
+
Restart = "on-failure";
424
+
WorkingDirectory = cfg.workingDirectory;
426
+
# TODO better hardening
427
+
LimitNOFILE = "1048576";
429
+
PrivateDevices = true;
430
+
ProtectHome = true;
431
+
ProtectSystem = "strict";
432
+
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
438
+
meta.maintainers = [ lib.maintainers.uku3lig ];