pinix's Headscale now allocates its own range (100.96.0.0/24, fd7a:115c:a1e0:96::/64). The pinix-dns namespace node forwards exactly TCP 443 to a new Traefik web-internal entrypoint on the veth host side (:8443, websecure untouched) and TCP 22 to sshd. unbound-pinix answers git.lukasrosier.com (AAAA only), distributed via split DNS for that name only. gitserver/: cgit via services.cgit, reduced to reading and snapshot downloads, built without its Python filters; git@ with git-shell and a from= restricted key. Verified from tnix's pinixnet namespace (DNS, HTTPS, snapshot, ls-remote, push) and refused from the public IPv6, the blunix tailnet and the LAN. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JGodmgtRqahbT52gApsM8N
130 lines
4.4 KiB
Nix
130 lines
4.4 KiB
Nix
{ 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 '<pre class="readme">'
|
|
${pkgs.gnused}/bin/sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g'
|
|
printf '</pre>\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}"; }
|
|
];
|
|
};
|
|
}
|