{ config, lib, pkgs, ... }: let # Persoenlicher Gitserver, nur im eigenen pinix-Tailnet erreichbar # (docs/intent/gitserver-pinix.md). Weg einer Anfrage: # # Client im pinix-Tailnet # -> fd7a:115c:a1e0:96::1 (Namespace pinix-dns, tailnet-dns.nix) # -> DNAT 443 -> [fd42:7069:6e69:78::1]:8443 (Traefik web-internal) # -> nginx auf 127.0.0.1 -> fcgiwrap -> cgit # # DNS dazu: local-data in unbound-pinix plus Split-DNS in Headscale, beides # in tailnet-dns.nix -- das Gegenstueck zu AdGuard-Rewrite und # traefik/routes-internal.nix auf blunix. host = "git.${config.domain2}"; repoRoot = "/var/lib/git"; nginxPort = 8090; # Aus dem Tailnet-Namespace kommt jede Verbindung mit dieser Quelle an, # weil tailnet-dns.nix beim Weiterleiten auf sie umschreibt. Der git-Key # gilt nur von dort -- ohne eine Zeile an sshd, also ohne Aussperr-Risiko # fuer die uebrigen Nutzer. tailnetPeer = "fd42:7069:6e69:78::2"; # cgit aus nixpkgs ohne seine Python-Filter. Das Paket wickelt # syntax-highlighting.py und md2html mit Python, Pygments und Markdown ein # (und rst2html mit docutils) -- rund 4 Python-Pakete in der Closure, obwohl # hier kein Filter davon benutzt wird. Nur der Build wird angepasst, der # C-Code bleibt unberuehrt. Am 14.09.2026 per `nix-store -qR` gemessen. cgitPackage = pkgs.cgit.overrideAttrs (_: { nativeBuildInputs = [ pkgs.buildPackages.pkg-config pkgs.buildPackages.asciidoc pkgs.buildPackages.makeWrapper ]; pythonPath = [ ]; postInstall = '' make install-man $makeFlags rm $out/lib/cgit/filters/*.py \ $out/lib/cgit/filters/html-converters/md2html \ $out/lib/cgit/filters/html-converters/rst2html for script in $out/lib/cgit/filters/*.sh $out/lib/cgit/filters/html-converters/txt2html; do wrapProgram $script --prefix PATH : '${lib.makeBinPath [ pkgs.coreutils pkgs.gnused ]}' done ''; }); # README als vorformatierter Text statt Markdown-Rendering. readmeFilter = pkgs.writeShellScript "cgit-readme-pre" '' printf '
'
    ${pkgs.gnused}/bin/sed -e 's/&/\&/g' -e 's//\>/g'
    printf '
\n' ''; in { users.users.git = { isSystemUser = true; group = "git"; home = repoRoot; createHome = true; # cgit liest ueber die Gruppe, schreiben kann nur git selbst. homeMode = "750"; shell = "${pkgs.git}/bin/git-shell"; # Kein Passwort, aber nicht gesperrt: sshd laeuft hier ohne PAM und # wertet das Default-"!" als gesperrtes Konto ("User git not allowed # because account is locked", gemessen 14.09.2026). "*" erlaubt keinen # Passwort-Login und laesst den Schluessel-Login zu. hashedPassword = "*"; openssh.authorizedKeys.keys = [ # lukas (builder@tnix) ''from="${tailnetPeer}",no-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrZETtzrSfkomL9H31SuyQJ8O39ujMIjsCkbjL4DgxM'' ]; }; users.groups.git = { }; services.cgit.${host} = { enable = true; package = cgitPackage; scanPath = repoRoot; group = "git"; # Klonen laeuft ueber SSH; Herunterladen ueber Snapshots. gitHttpBackend.enable = false; settings = { root-title = host; root-desc = "Code lesen und herunterladen"; logo = ""; head-include = "${./head.html}"; noplainemail = true; enable-index-owner = false; # Muss vor scan-path stehen, das Modul schreibt settings davor. remove-suffix = true; readme = [ ":README.md" ":README" ]; about-filter = "${readmeFilter}"; enable-http-clone = false; clone-url = "git@${host}:$CGIT_REPO_URL.git"; snapshots = "tar.gz zip"; enable-blame = false; enable-commit-graph = false; enable-log-filecount = false; enable-log-linecount = false; side-by-side-diffs = false; }; }; # Das Upstream-Modul haengt nginx sonst an Port 80, den Traefik haelt. services.nginx.virtualHosts.${host} = { listen = [ { addr = "127.0.0.1"; port = nginxPort; } ]; locations."= /pinix.css".alias = "${./cgit.css}"; }; services.traefik.dynamicConfigOptions.http = { routers.cgit-pinix = { rule = "Host(`${host}`)"; entryPoints = [ "web-internal" ]; service = "cgit-pinix"; tls = { }; }; services.cgit-pinix.loadBalancer.servers = [ { url = "http://127.0.0.1:${toString nginxPort}"; } ]; }; }