{ config, pkgs, lib, ... }: # cgit - fast web interface for git repositories. # # Repositories live in /srv/git/.git (bare repos). # Access model: # - Web browsing: public, no auth (https://git.karanj.com) # - git clone/pull over HTTPS: public, read-only via git-http-backend # - git push: SSH only, using the "git" user + your authorized keys # # The NixOS cgit module serves both cgit browsing and git-http-backend # (clone/pull, via gitHttpBackend.enable which defaults to true) on the # *same* nginx vhost/location - nginx tells them apart by matching the # request path against a regex (.../info/refs|git-upload-pack for the # smart-HTTP protocol), not by a separate port. That combined vhost is # bound to localhost:8086 below, and Caddy reverse-proxies everything # for git.karanj.com straight to it. Only git-upload-pack is wired up, so # push over HTTP is impossible regardless - push stays SSH-only. { # Dedicated git user for SSH push access users.users.git = { isSystemUser = true; group = "git"; home = "/srv/git"; shell = pkgs.git; # Allow pushing from both your devices openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwAgL0o4NVonSG07Xu4Eai84ns4AjoZj2V7dGC9nXit karanjayachandra@Einstein.local" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+qnLTnorv+I2rSSfGjNiCuX/W5AxoNgAdu+cTOyKzW Galileo" ]; }; users.groups.git = {}; # Repository root # # The git-shell-commands lines below add Git LFS support. git-shell's own # hardcoded allowlist is exactly three commands - git-receive-pack, # git-upload-pack, git-upload-archive (confirmed from git's own shell.c) - # so a plain LFS-tracked push fails with "unrecognized command # 'git-lfs-authenticate ...'" otherwise. The documented extension point is # an executable file matching the command name in ~/git-shell-commands # (~ being /srv/git, the git user's home above). Symlinking git-lfs-transfer # there - nixpkgs' server-side implementation of Git LFS's pure-SSH # protocol - is enough: git-lfs clients (v3.0+) already try this command # automatically for SSH remotes before falling back to the HTTP-based # git-lfs-authenticate flow, so nothing needs configuring client-side. This # keeps LFS on the same SSH-only path as normal pushes, with no HTTP LFS # server or tokens involved. systemd.tmpfiles.rules = [ "d /srv/git 0755 git git -" "d /srv/git/git-shell-commands 0750 git git -" "L+ /srv/git/git-shell-commands/git-lfs-transfer - - - - ${pkgs.git-lfs-transfer}/bin/git-lfs-transfer" ]; # cgit web interface served via nginx + fcgiwrap services.cgit."git.karanj.com" = { enable = true; scanPath = "/srv/git"; gitHttpBackend.checkExportOkFiles = false; settings = { # Site branding root-title = "karan's git"; root-desc = "personal git repositories"; # Enable common features enable-index-links = 1; enable-commit-graph = 1; enable-log-filecount = 1; enable-log-linecount = 1; enable-blame = 1; enable-http-clone = 1; # show clone URL in UI # Public clone URL prefix shown in the cgit UI clone-url = "https://git.karanj.com/$CGIT_REPO_URL"; # Syntax highlighting source-filter = "${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py"; about-filter = "${pkgs.cgit}/lib/cgit/filters/about-formatting.sh"; }; }; services.nginx.virtualHosts."git.karanj.com" = { forceSSL = false; enableACME = false; listen = [ { addr = "127.0.0.1"; port = 8086; } ]; }; }