{ pkgs, lib, ... }: let host = "git.oxapentane.com"; gitHome = "/var/lib/git"; cgitPort = 8080; defaultBranch = "main"; mkCreateRepoCommand = { command, visibility, }: pkgs.writeShellApplication { name = command; runtimeInputs = [ pkgs.coreutils pkgs.git pkgs.gnugrep ]; text = '' set -eu if [ "$#" -ne 1 ]; then printf 'usage: ${command} NAME\n' >&2 exit 2 fi name=$1 # Accept either "foo" or "foo.git". case "$name" in *.git) name="''${name%.git}" ;; esac # Deliberately only allow a single path component. # This excludes traversal, slashes, spaces and shell metacharacters. if ! printf '%s\n' "$name" \ | grep -Eq '^[A-Za-z0-9][A-Za-z0-9._-]*$' then printf '%s\n' \ 'invalid repository name: use letters, digits, ".", "_" and "-"' \ >&2 exit 2 fi base=${lib.escapeShellArg "${gitHome}/${visibility}"} repo="$base/$name.git" umask 0027 # mkdir is the existence check: never silently reinitialize a repo. if ! mkdir -- "$repo" 2>/dev/null; then printf 'repository already exists or cannot be created: %s\n' \ "$name.git" >&2 exit 1 fi if ! git init \ --bare \ --initial-branch=${lib.escapeShellArg defaultBranch} \ "$repo" \ >/dev/null then printf 'failed to initialize repository: %s\n' "$name.git" >&2 exit 1 fi # Avoid Git's default "Unnamed repository..." text appearing in cgit. : > "$repo/description" # This is the only successful stdout output, making $() safe. printf '%s\n' "git@${host}:${visibility}/$name.git" ''; }; createPrivate = mkCreateRepoCommand { command = "create"; visibility = "private"; }; createPublic = mkCreateRepoCommand { command = "create-pub"; visibility = "public"; }; noInteractiveLogin = pkgs.writeShellScript "no-interactive-login" '' printf '%s\n' "Interactive shell access is disabled." >&2 exit 128 ''; restrictedGitShell = (pkgs.writeShellApplication { name = "restricted-git-shell"; text = '' umask 0027 exec ${pkgs.git}/bin/git-shell "$@" ''; }).overrideAttrs (old: { passthru = (old.passthru or { }) // { shellPath = "/bin/restricted-git-shell"; }; }); in { # public repos in cgit services.cgit."${host}" = { enable = true; user = "cgit"; group = "cgit"; scanPath = "${gitHome}/public"; gitHttpBackend = { enable = true; checkExportOkFiles = false; # /public is unconditionally public anyway... }; settings = { root-title = "0xa's compilable shitposts"; root-desc = "Public directory of my finest spaghetti"; remove-suffix = true; clone-url = "https://${host}/$CGIT_REPO_URL.git " + "git@${host}:public/$CGIT_REPO_URL.git"; enable-http-clone = false; enable-filter-overrides = false; enable-git-config = false; enable-http-serving = false; max-blob-size = 2048; noplainemail = true; snapshots = ""; }; nginx = { virtualHost = host; location = "/"; }; }; # just bind to proxy interface, everything's handeled by caddy services.nginx.virtualHosts.${host}.listen = [ { addr = "10.89.88.19"; port = cgitPort; } ]; # private - bare repos behind separate user users.groups.git = {}; users.users.git = { isSystemUser = true; group = "git"; home = gitHome; createHome = false; shell = restrictedGitShell; hashedPassword = "!"; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXrwZsBChUhVuF5gFEfj8GwQlnrqEttAytS5jVCfuE4 0xa@frituurpan" ]; }; # enable and cofigure ssh services.openssh = { enable = true; extraConfig = '' Match User git AuthenticationMethods publickey PasswordAuthentication no KbdInteractiveAuthentication no DisableForwarding yes PermitTTY no PermitUserRC no ''; }; # ensure directories and rights systemd.tmpfiles.rules = [ "d ${gitHome} 0711 git git -" "d ${gitHome}/public 2750 git cgit -" "d ${gitHome}/private 0711 git git -" "d ${gitHome}/git-shell-commands 0700 git git -" "L+ ${gitHome}/git-shell-commands/create - - - - ${createPrivate}/bin/create" "L+ ${gitHome}/git-shell-commands/create-pub - - - - ${createPublic}/bin/create-pub" "L+ ${gitHome}/git-shell-commands/no-interactive-login - - - - ${noInteractiveLogin}" ]; # dubious ownership error systemd.services."fcgiwrap-cgit-${host}".environment = { GIT_CONFIG_COUNT = "1"; GIT_CONFIG_KEY_0 = "safe.directory"; GIT_CONFIG_VALUE_0 = "${gitHome}/public/*"; }; }