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
100 lines
4.5 KiB
Nix
100 lines
4.5 KiB
Nix
{ config, ... }:
|
|
{
|
|
# Minimaler öffentlicher Eingang auf pinix, abgeleitet von
|
|
# hosts/blunix/localServices/traefik/default.nix. Dort ist Traefik über
|
|
# Jahre gewachsen (Cloudflare-DNS-Challenge samt Wildcard, Dashboard mit
|
|
# BasicAuth, fail2ban-Accesslog, zweiter Entrypoint fürs Tailnet). Hier
|
|
# bleibt bewusst nur, was der IPv6-Eingang braucht --
|
|
# docs/intent/pinix-ipv6-traefik-webserver.md, Schritt 3.
|
|
#
|
|
# Was absichtlich fehlt und warum:
|
|
#
|
|
# - DNS-Challenge über Cloudflare. Stattdessen HTTP-01: kein API-Token,
|
|
# kein agenix-Secret, eine externe Abhängigkeit weniger -- und genau
|
|
# das Verfahren, das der tnix-Test schon bewiesen hat. Der Preis ist
|
|
# der Verzicht auf Wildcards, also ein Zertifikat pro Hostname.
|
|
# - api/Dashboard. Der Intent schließt ein öffentliches Dashboard aus.
|
|
# - log.level = "TRACE" von blunix. Auf einer SD-Karte ist das
|
|
# Schreiblast ohne Gegenwert; INFO reicht, bis etwas zu suchen ist.
|
|
|
|
# Ziel von Mangos IPv6-DNAT ist pinix' SLAAC-Adresse auf end0,
|
|
# fdee:3c40:44f0:0:dea6:32ff:fe48:c6c8 -- deterministisch aus dem
|
|
# ULA-Praefix des Mango und der MAC dc:a6:32:48:c6:c8 gebildet, also
|
|
# stabil, solange beide es sind.
|
|
#
|
|
# Sie ist hier bewusst NICHT als networking.interfaces.end0.ipv6.addresses
|
|
# deklariert, obwohl hosts/tnix/localServices/ipv6-web-test.nix genau das
|
|
# tut. Am 12.09.2026 gemessen: sobald ein Interface eine statische
|
|
# IPv6-Adresse bekommt, schreibt das NixOS-dhcpcd-Modul fuer dieses
|
|
# Interface `noipv6rs` in die generierte dhcpcd.conf. Damit entfaellt die
|
|
# Router-Solicitation und mit ihr die RA-Default-Route -- auf einem Host,
|
|
# dessen Uplink IPv6-only ist, heisst das schlicht: kein Internet mehr.
|
|
# Die Adresse doppelt zu deklarieren haette hier ohnehin nichts
|
|
# gewonnen, weil SLAAC exakt dieselbe bildet.
|
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
|
|
|
# Dasselbe Henne-Ei-Problem wie auf blunix (dort IPv4): web-internal
|
|
# bindet an die veth-Adresse, die erst pinix-dns-netns.service anlegt.
|
|
# Mit nonlocal_bind startet Traefik trotzdem, der Listener wird aktiv,
|
|
# sobald die Adresse existiert.
|
|
boot.kernel.sysctl."net.ipv6.ip_nonlocal_bind" = 1;
|
|
|
|
services.traefik = {
|
|
enable = true;
|
|
|
|
staticConfigOptions = {
|
|
log.level = "INFO";
|
|
|
|
# ":80"/":443" nimmt IPv4 und IPv6 an. Öffentlich erreichbar ist
|
|
# davon nur, was Mangos DNAT weiterreicht; im LAN hängt pinix
|
|
# zusätzlich unter seiner ULA-Adresse daran.
|
|
entryPoints = {
|
|
web = {
|
|
address = ":80";
|
|
# Weiterleitung auf HTTPS. Die ACME-HTTP-01-Validierung laeuft
|
|
# trotzdem ueber diesen Entrypoint: Traefik legt dafuer einen
|
|
# eigenen Router mit hoeherer Prioritaet an, der vor der
|
|
# Weiterleitung greift.
|
|
http.redirections.entryPoint = {
|
|
to = "websecure";
|
|
scheme = "https";
|
|
};
|
|
};
|
|
websecure.address = ":443";
|
|
|
|
# Tailnet-only-Routen, wie web-internal auf blunix -- aber fuer das
|
|
# eigene pinix-Tailnet. Dessen Adresse liegt im Namespace pinix-dns;
|
|
# der reicht 443 auf die Host-Seite des veth-Paars an diesen Port
|
|
# weiter (tailnet-dns.nix). Nur von dort erreichbar. Eigener Port,
|
|
# damit websecure auf ":443" unangetastet bleibt.
|
|
web-internal.address = "[fd42:7069:6e69:78::1]:8443";
|
|
};
|
|
|
|
# Scharf geschaltet am 12.09.2026, gemeinsam mit dem Umlegen von
|
|
# Mangos DNAT auf pinix (scripts/configure-mango-ipv6-web.sh).
|
|
# Vorher waere hier keine Validierung angekommen: 80/443 zeigten
|
|
# noch auf tnix, das fuer denselben Hostnamen ein eigenes
|
|
# Zertifikat hielt. Zwei Hosts gleichzeitig ausstellen zu lassen
|
|
# bringt nur fehlgeschlagene Validierungen gegen Let's Encrypts
|
|
# Rate Limit -- deshalb war die Reihenfolge hier wichtig.
|
|
certificatesResolvers.le.acme = {
|
|
email = config.adminEmail;
|
|
storage = "/var/lib/traefik/acme.json";
|
|
httpChallenge.entryPoint = "web";
|
|
};
|
|
|
|
};
|
|
|
|
# Das Wildcard-Zertifikat fuer domain2 holt nicht Traefik, sondern
|
|
# security.acme per DNS-01 gegen das eigene BIND (acme.nix). Hier wird es
|
|
# nur eingehaengt, damit Router unter domain2 kein eigenes Zertifikat
|
|
# anfordern muessen. Erneuerung loest dort reloadServices aus.
|
|
dynamicConfigOptions.tls.certificates = [
|
|
{
|
|
certFile = "/var/lib/acme/${config.domain2}/fullchain.pem";
|
|
keyFile = "/var/lib/acme/${config.domain2}/key.pem";
|
|
}
|
|
];
|
|
};
|
|
|
|
}
|