From a722a999d1af37d9f3760680dc50fc598fce01a6 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Mon, 29 Dec 2025 18:43:03 +0100 Subject: [PATCH] nixos/cgit: add gitHttpBackend options --- .../manual/release-notes/rl-2605.section.md | 2 + nixos/modules/services/networking/cgit.nix | 62 +++++++++++++++++-- nixos/tests/cgit.nix | 37 +++++++++++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index 9d4f926dc718..ed4c83085e79 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -63,6 +63,8 @@ of pulling the upstream container image from Docker Hub. If you want the old beh - Ethercalc and its associated module have been removed, as the package is unmaintained and cannot be installed from source with npm now. +- `services.cgit` before always had the git-http-backend and its "export all" setting enabled, which sidestepped any access control configured in cgit's settings. Now you have to make a decision and either enable or disable `services.cgit.gitHttpBackend.checkExportOkFiles` (or disable the git-http-backend). + - The Bash implementation of the `nixos-rebuild` program is removed. All switchable systems now use the Python rewrite. Any prior usage of `system.rebuild.enableNg` must now be removed. If you have any outstanding issues with the new implementation, please open an issue on GitHub. - The `networking.wireless` module has been security hardened: the `wpa_supplicant` daemon now runs under an unprivileged user with restricted access to the system. diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index c9abf022932f..63d247a9e068 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -193,6 +193,32 @@ in type = lib.types.str; default = "cgit"; }; + + gitHttpBackend.enable = lib.mkOption { + description = '' + Whether to bypass cgit and use git-http-backend for HTTP clones. + While this enables HTTP clones to use the more efficient smart protocol, + it does not support access control via cgit's settings (e.g. the `ignore` repository setting). + + If you want to disallow access to some repositories with this backend, + enable `checkExportOkFiles` and set `strict-export = "git-daemon-export-ok"` in `settings`. + ''; + type = lib.types.bool; + default = true; + }; + + gitHttpBackend.checkExportOkFiles = lib.mkOption { + description = '' + Whether git-http-backend should only export repositories that contain a `git-daemon-export-ok` file. + + When the backend is enabled and the check is disabled all repositories can be cloned + irrespective of cgit's settings (e.g. the `ignore` repository setting). + + When enabled you must also configure `strict-export = "git-daemon-export-ok"` + in `settings` to make cgit check for the same files. + ''; + type = lib.types.bool; + }; }; } ) @@ -201,10 +227,30 @@ in }; config = lib.mkIf (lib.any (cfg: cfg.enable) (lib.attrValues cfgs)) { - assertions = lib.mapAttrsToList (vhost: cfg: { - assertion = !cfg.enable || (cfg.scanPath == null) != (cfg.repos == { }); - message = "Exactly one of services.cgit.${vhost}.scanPath or services.cgit.${vhost}.repos must be set."; - }) cfgs; + assertions = lib.flatten ( + lib.mapAttrsToList (vhost: cfg: [ + { + assertion = !cfg.enable || (cfg.scanPath == null) != (cfg.repos == { }); + message = "Misconfigured services.cgit.${vhost}: Exactly one of scanPath or repos must be set."; + } + { + assertion = + !cfg.enable + || !cfg.gitHttpBackend.enable + || !cfg.gitHttpBackend.checkExportOkFiles + || cfg.settings.strict-export == "git-daemon-export-ok"; + message = "Misconfigured services.cgit.${vhost}: When gitHttpBackend.checkExportOkFiles is true then settings.strict-export must be \"git-daemon-export-ok\"."; + } + { + assertion = + !cfg.enable + || !cfg.gitHttpBackend.enable + || cfg.settings.strict-export == null + || cfg.gitHttpBackend.checkExportOkFiles; + message = "Misconfigured services.cgit.${vhost}: settings.strict-export is set but the gitHttpBackend is enabled and checkExportOkFiles is false."; + } + ]) cfgs + ); users = lib.mkMerge ( lib.flip lib.mapAttrsToList cfgs ( @@ -259,16 +305,20 @@ in alias = lib.mkDefault "${cfg.package}/cgit/${fileName}"; } )) - // { + // lib.optionalAttrs cfg.gitHttpBackend.enable { "~ ${regexLocation cfg}/.+/(info/refs|git-upload-pack)" = { fastcgiParams = rec { SCRIPT_FILENAME = "${pkgs.git}/libexec/git-core/git-http-backend"; - GIT_HTTP_EXPORT_ALL = "1"; GIT_PROJECT_ROOT = gitProjectRoot name cfg; HOME = GIT_PROJECT_ROOT; + } + // lib.optionalAttrs (!cfg.gitHttpBackend.checkExportOkFiles) { + GIT_HTTP_EXPORT_ALL = "1"; }; extraConfig = mkFastcgiPass name cfg; }; + } + // { "${stripLocation cfg}/" = { fastcgiParams = { SCRIPT_FILENAME = "${cfg.package}/cgit/cgit.cgi"; diff --git a/nixos/tests/cgit.nix b/nixos/tests/cgit.nix index 2c48e6bde47d..a7839e24c4a4 100644 --- a/nixos/tests/cgit.nix +++ b/nixos/tests/cgit.nix @@ -39,6 +39,23 @@ in ":date.txt" ]; }; + gitHttpBackend.checkExportOkFiles = false; + }; + services.cgit."check.localhost" = { + enable = true; + scanPath = "/tmp/git"; + settings = { + strict-export = "git-daemon-export-ok"; + }; + gitHttpBackend.checkExportOkFiles = true; + }; + services.cgit."no-git-http-backend.localhost" = { + enable = true; + scanPath = "/tmp/git"; + settings = { + strict-export = "git-daemon-export-ok"; + }; + gitHttpBackend.enable = false; }; environment.systemPackages = [ pkgs.git ]; @@ -107,5 +124,25 @@ in server.fail( "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F 'cgit NixOS Test at'" ) + + # EXPORT_ALL is not set with checkExportOkFiles = true + server.succeed("touch /tmp/git/some-repo/git-daemon-export-ok") + server.succeed( + "git clone http://check.localhost/some-repo $(mktemp -d)" + ) + server.succeed("rm /tmp/git/some-repo/git-daemon-export-ok") + server.fail( + "git clone http://check.localhost/some-repo $(mktemp -d)" + ) + + # Disabling the git-http-backend-works + server.succeed("touch /tmp/git/some-repo/git-daemon-export-ok") + server.succeed( + "git clone http://no-git-http-backend.localhost/some-repo $(mktemp -d)" + ) + server.succeed("rm /tmp/git/some-repo/git-daemon-export-ok") + server.fail( + "git clone http://no-git-http-backend.localhost/some-repo $(mktemp -d)" + ) ''; }