diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 428dbdcc54c295..ca06388f4fc71e 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -1,4 +1,13 @@ { + "module-services-reposilite": [ + "index.html#module-services-reposilite" + ], + "module-services-reposilite-generating-a-token": [ + "index.html#module-services-reposilite-generating-a-token" + ], + "module-services-reposilite-quickstart": [ + "index.html#module-services-reposilite-quickstart" + ], "book-nixos-manual": [ "index.html#book-nixos-manual" ], diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 209655cc9925ac..e4d6cee7c61a1b 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -189,6 +189,8 @@ - [Rathole](https://github.com/rapiz1/rathole), a lightweight and high-performance reverse proxy for NAT traversal. Available as [services.rathole](#opt-services.rathole.enable). +- [Reposilite](https://reposilite.com), a lightweight and easy-to-use repository manager for Maven based artifacts in JVM ecosystem. Available as [services.reposilite](#opt-services.reposilite.enable). + - [Proton Mail bridge](https://proton.me/mail/bridge), a desktop application that runs in the background, encrypting and decrypting messages as they enter and leave your computer. Available as [services.protonmail-bridge](#opt-services.protonmail-bridge.enable). - [chromadb](https://www.trychroma.com/), an open-source AI application database with batteries included. Available as [services.chromadb](options.html#opt-services.chromadb.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 2bfb963c19dfc9..9ba7ac3ae5226e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1539,6 +1539,7 @@ ./services/web-apps/pretix.nix ./services/web-apps/privatebin.nix ./services/web-apps/prosody-filer.nix + ./services/web-apps/reposilite.nix ./services/web-apps/rimgo.nix ./services/web-apps/rutorrent.nix ./services/web-apps/screego.nix diff --git a/nixos/modules/services/web-apps/reposilite.md b/nixos/modules/services/web-apps/reposilite.md new file mode 100644 index 00000000000000..32374cf6383eeb --- /dev/null +++ b/nixos/modules/services/web-apps/reposilite.md @@ -0,0 +1,45 @@ +# Reposilite {#module-services-reposilite} + +Reposilite is a self-hosted repository manager for Maven based artifacts in JVM ecosystem. + +Visit [the Reposilite project page](https://reposilite.com) to learn +more about it. + +## Quickstart {#module-services-reposilite-quickstart} + +Use the following configuration to start a public instance of Reposilite locally: + +```nix +{ + services.reposilite = { + enable = true; + settings = { + sslEnabled = true; + }; + openFirewall = true; + }; +} +``` + +## Generating a Token {#module-services-reposilite-generating-a-token} + +You'll need to generate your first access token on the CLI (after which you can generate them in the web UI): + +``` +sudo systemctl stop reposilite.service +reposilite --working-directory /var/lib/reposilite --port 8084 --database "sqlite reposilite.db" --token +``` + +If you have modified the `database` option, update the flag in the command above as such. + +This creates a temporary token. With the server still running, access the web dashboard in a browser at `localhost:8084`, and create a permanent token by navigating to the `Console` tab and typing: + +``` +token-generate +``` + +This will output a secret which you should save. You can then Ctrl-C to kill the Reposilite process and run the following command to restart the systemd service: + +``` +sudo systemctl start reposilite.service +``` diff --git a/nixos/modules/services/web-apps/reposilite.nix b/nixos/modules/services/web-apps/reposilite.nix new file mode 100644 index 00000000000000..4ad9b70379e705 --- /dev/null +++ b/nixos/modules/services/web-apps/reposilite.nix @@ -0,0 +1,293 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.reposilite; + + inherit (lib) + mkEnableOption + mkPackageOption + mkOption + mkIf + mkDefault + getExe + types + ; + + # Reposilite actually uses https://github.com/dzikoysk/cdn, but its format is similar enough to YAMLs to use pkgs.formats.yaml. + settingsFormat = pkgs.formats.yaml { }; +in +{ + options.services.reposilite = { + enable = mkEnableOption "reposilite"; + package = mkPackageOption pkgs "reposilite" { }; + + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; + options = { + hostname = mkOption { + description = '' + Hostname + The hostname can be used to limit which connections are accepted. + Use 0.0.0.0 to accept connections from anywhere. + 127.0.0.1 will only allow connections from localhost. + ''; + default = "0.0.0.0"; + example = "127.0.0.1"; + type = types.str; + }; + + port = mkOption { + description = "Port to bind"; + default = 8080; + example = 5678; + type = types.port; + }; + + database = mkOption { + description = '' + Database configuration. Supported storage providers: + - mysql localhost:3306 database user password + - sqlite reposilite.db + - sqlite --temporary + Experimental providers (not covered with tests): + - postgresql localhost:5432 database user password + - h2 reposilite + ''; + default = "sqlite reposilite.db"; + example = "mysql localhost:3306 database user password"; + type = types.str; + }; + + webThreadPool = mkOption { + description = '' + Max amount of threads used by core thread pool (min: 5) + The web thread pool handles first few steps of incoming http connections, as soon as possible all tasks are redirected to IO thread pool. + ''; + default = 16; + example = 32; + type = types.int; + }; + + ioThreadPool = mkOption { + description = '' + IO thread pool handles all tasks that may benefit from non-blocking IO (min: 2) + Because most of tasks are redirected to IO thread pool, it might be a good idea to keep it at least equal to web thread pool. + ''; + default = 8; + example = 12; + type = types.int; + }; + + databaseThreadPool = mkOption { + description = '' + Database thread pool manages open connections to database (min: 1) + Embedded databases such as SQLite or H2 don't support truly concurrent connections, so the value will be always 1 for them if selected. + ''; + default = 1; + example = 3; + type = types.int; + }; + + compressionStrategy = mkOption { + description = '' + Select compression strategy used by this instance. + Using 'none' reduces usage of CPU & memory, but ends up with higher transfer usage. + GZIP is better option if you're not limiting resources that much to increase overall request times. + Available strategies: none, gzip + ''; + default = "none"; + example = "gzip"; + type = types.enum [ + "none" + "gzip" + ]; + }; + + idleTimeout = mkOption { + description = "Default idle timeout used by Jetty"; + default = 30000; + example = 120000; + type = types.int; + }; + + bypassExternalCache = mkOption { + description = '' + Adds cache bypass headers to each request from /api/* scope served by this instance. + Helps to avoid various random issues caused by proxy provides (e.g. Cloudflare) and browsers. + ''; + default = true; + type = types.bool; + }; + + cachedLogSize = mkOption { + description = "Amount of messages stored in cached logger."; + default = 32; + example = 64; + type = types.ints.unsigned; + }; + + defaultFrontend = mkOption { + description = "Enable default frontend with dashboard."; + default = true; + type = types.bool; + }; + + basePath = mkOption { + description = '' + Set custom base path for Reposilite instance. + It's not recommended to mount Reposilite under custom base path and you should always prioritize subdomain over this option. + ''; + default = "/"; + example = "/maven"; + type = types.str; + }; + + debugEnabled = mkOption { + description = "Debug mode"; + default = false; + type = types.bool; + }; + + }; + }; + description = "Configuration written to the config file for Reposilite."; + default = { }; + }; + + openFirewall = mkOption { + description = '' + Whether to open the firewall for Reposilite. + This adds `services.reposilite.settings.port` to `networking.firewall.allowedTCPPorts`. + ''; + default = false; + type = types.bool; + }; + + nginx = mkOption { + type = types.submodule { + options = { + enable = mkEnableOption "the nginx reverse proxy virtual host"; + + hostname = mkOption { + description = '' + The hostname for the nginx virtual host. + ''; + example = "reposilite.example.com"; + type = types.str; + }; + }; + }; + + description = "nginx config TODO"; + default = { }; + }; + + user = mkOption { + type = types.nonEmptyStr; + default = "reposilite"; + description = "User account under which Reposilite runs."; + }; + + group = mkOption { + type = types.nonEmptyStr; + default = "reposilite"; + description = "Group account under which Reposilite runs."; + }; + }; + + config = mkIf cfg.enable ( + let + config-cdn = settingsFormat.generate "reposilite.cdn" cfg.settings; + in + { + users = { + users."${cfg.user}" = { + description = "Reposilite user"; + group = cfg.group; + isSystemUser = true; + }; + groups."${cfg.group}" = { }; + }; + + # This is done so that the reposilite command is available in PATH so users can setup their first token (see reposilite.md). + environment.systemPackages = [ cfg.package ]; + + systemd.services.reposilite = { + description = "Reposilite server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "exec"; + User = cfg.user; + Group = cfg.group; + UMask = "0077"; + ExecStart = "${getExe cfg.package} --local-configuration ${config-cdn} --local-configuration-mode none --working-directory /var/lib/reposilite"; + WorkingDirectory = "/var/lib/reposilite"; + StateDirectory = "reposilite"; + + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateUsers = true; + PrivateIPC = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + LockPersonality = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + + CapabilityBoundingSet = [ "" ]; + + NoNewPrivileges = true; + SystemCallFilter = [ "@system-service" ]; + SystemCallErrorNumber = "EPERM"; + SystemCallArchitectures = "native"; + + DevicePolicy = "closed"; + + IPAddressDeny = "any"; + IPAddressAllow = cfg.settings.hostname; + }; + }; + + services.reposilite = mkIf cfg.nginx.enable { + settings.hostname = mkDefault "localhost"; + openFirewall = mkDefault false; + }; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.server.port ]; + + services.nginx = mkIf cfg.nginx.enable { + enable = true; + virtualHosts.${cfg.nginx.hostname} = { + locations."/" = { + proxyPass = "http://${cfg.settings.hostname}:${toString cfg.settings.port}/"; + }; + }; + }; + } + ); + + meta.doc = ./reposilite.md; + meta.maintainers = [ lib.maintainers.jamalam ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 516ab9f84e1263..d9e0488250c414 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -891,6 +891,7 @@ in { redmine = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./redmine.nix {}; renovate = handleTest ./renovate.nix {}; replace-dependencies = handleTest ./replace-dependencies {}; + reposilite = runTest ./reposilite.nix; restartByActivationScript = handleTest ./restart-by-activation-script.nix {}; restic-rest-server = handleTest ./restic-rest-server.nix {}; restic = handleTest ./restic.nix {}; diff --git a/nixos/tests/reposilite.nix b/nixos/tests/reposilite.nix new file mode 100644 index 00000000000000..ec9e2ac562156f --- /dev/null +++ b/nixos/tests/reposilite.nix @@ -0,0 +1,23 @@ +{ lib, ... }: + +{ + name = "reposilite"; + + nodes = { + machine_default = + { pkgs, ... }: + { + services.reposilite = { + enable = true; + }; + }; + }; + + testScript = '' + machine_default.start() + machine_default.wait_for_unit("reposilite.service") + machine_default.wait_for_open_port(8080) + ''; + + meta.maintainers = [ lib.maintainers.jamalam ]; +} diff --git a/pkgs/by-name/re/reposilite/package.nix b/pkgs/by-name/re/reposilite/package.nix index 426da3db55eb23..11e4a91d6559de 100644 --- a/pkgs/by-name/re/reposilite/package.nix +++ b/pkgs/by-name/re/reposilite/package.nix @@ -4,6 +4,7 @@ fetchurl, makeWrapper, jre_headless, + nixosTests, }: stdenv.mkDerivation (finalAttrs: { @@ -30,6 +31,10 @@ stdenv.mkDerivation (finalAttrs: { runHook postInstall ''; + passthru.tests = { + reposilite = nixosTests.reposilite; + }; + meta = { description = "Lightweight and easy-to-use repository management software dedicated for the Maven based artifacts in the JVM ecosystem"; homepage = "https://github.com/dzikoysk/reposilite";