{ config, lib, pkgs, ... }: with lib; let cfg = config.services.cgit; mkCgitrc = cfg: pkgs.writeText "cgitrc" ( let cgitConfig = { css = "/cgit.css"; logo = "/cgit.png"; favicon = "/favicon.ico"; about-filter = "${cfg.package}/lib/cgit/filters/about-formatting.sh"; source-filter = "${cfg.package}/lib/cgit/filters/syntax-highlighting.py"; enable-git-config = 1; enable-http-clone = 1; remove-suffix = 1; clone-url = "https://${cfg.virtualHost}/$CGIT_REPO_URL"; scan-path = cfg.scanPath; }; in generators.toKeyValue { } (cfg.settings // cgitConfig) ); mkCgitAssets = pkg: files: strings.concatStringsSep "\n" ( builtins.map (f: '' handle_path /${f} { root * ${pkg}/cgit/${f} file_server } '') files ); in { disabledModules = [ "services/networking/cgit.nix" ]; options = { services.cgit = { enable = mkEnableOption "cgit"; package = mkPackageOption pkgs "cgit" { }; user = mkOption { default = "git"; type = types.str; description = "User to run cgit service as."; }; group = mkOption { default = "git"; type = types.str; description = "Group to run cgit service as."; }; scanPath = mkOption { default = "/var/lib/cgit"; type = types.path; description = "A path which will be scanned for repositories."; }; virtualHost = mkOption { default = null; type = types.str; description = "Virtual host to serve cgit on."; }; authorizedKeys = mkOption { default = [ ]; type = types.listOf types.str; description = "SSH keys for authorized git users."; }; settings = mkOption { default = { }; type = with types; let settingType = oneOf [ bool int str ]; in attrsOf (oneOf [ settingType (listOf settingType) ]); description = "Additional cgit configuration. See cgitrc(5)."; }; }; }; config = mkIf cfg.enable { programs.git = { enable = true; config.init.defaultBranch = "main"; }; # Setup git user with git-shell for authenticated pushes. users.users.${cfg.user} = { isSystemUser = true; home = cfg.scanPath; group = cfg.group; shell = "${pkgs.git}/bin/git-shell"; openssh.authorizedKeys.keys = cfg.authorizedKeys; }; users.groups.${cfg.group} = { }; # Harden git user to prevent SSH port forwarding to other servers. services.openssh = { enable = true; settings.AllowUsers = [ cfg.user ]; extraConfig = '' Match user ${cfg.user} AllowTCPForwarding no AllowAgentForwarding no PasswordAuthentication no PermitTTY no X11Forwarding no ''; }; # Configure fcgiwrap instance for caddy to properly serve cgi scripts. # Reference: https://github.com/NixOS/nixpkgs/blob/nixos-24.11/nixos/modules/services/networking/cgit.nix services.fcgiwrap.instances.cgit = { process = { inherit (cfg) user group; }; socket = { inherit (config.services.caddy) user group; }; }; services.caddy.virtualHosts.${cfg.virtualHost}.extraConfig = let socket = config.services.fcgiwrap.instances.cgit.socket.address; in '' encode zstd gzip reverse_proxy unix/${socket} { transport fastcgi { env SCRIPT_FILENAME ${cfg.package}/cgit/cgit.cgi env CGIT_CONFIG ${mkCgitrc cfg} } } ${mkCgitAssets cfg.package [ "cgit.css" "cgit.png" "favicon.ico" "robots.txt" ]} ''; }; }