# Session 12.09.2026 (Teil 2): Traefik auf pinix, öffentlicher IPv6-Eingang ## Übergabe für die nächste Session Gearbeitet wurde auf **tnix** (nicht auf blunix), Branch `dev`, alles gepusht. Vorgänger dieser Session: [IPv6-Mobilfunkzugang und Plattformplanung](session-2026-09-12-ipv6-plattform.md). Auftrag war der [Intent](intent/pinix-ipv6-traefik-webserver.md), dessen Schritte 3 bis 5 jetzt umgesetzt sind. **Stand:** wird von **pinix** beantwortet, über Traefik auf pinix, mit eigenem Let's-Encrypt-Zertifikat. Kein VPS, kein Tailnet, kein Cloudflare-Proxy im Datenpfad — Cloudflare hält nur den AAAA-Eintrag, der unverändert blieb. Der nginx-Test auf tnix ist entfernt. **Nächster sinnvoller Schritt:** Schritt 6 des Intents — Reboot- und Reconnect-Probe. Die ist ausdrücklich **nicht** gelaufen. Das öffentliche Präfix rotiert nachweislich (auf Mangos `usb0` liegt neben der aktiven Adresse noch `2a01:599:106:e90d:94dd:42ff:fe9a:627f` als `deprecated`). Vor der Probe einen lokalen Rückweg sichern und den Zeitpunkt abstimmen. ## Arbeitsweise, auf die sich Nutzer und Agent geeinigt haben Editiert wird auf tnix im Repo, gebaut und aktiviert wird **lokal auf pinix**, committet wird erst, wenn es auf der Zielhardware läuft: ```bash rsync -a --delete --exclude .git ./ pinix:/home/drone/nixos-config-build/ ssh pinix 'cd /home/drone/nixos-config-build && \ sudo nixos-rebuild switch --flake path:.#pinix --no-reexec' ``` Begründung: kein Commit pro Zwischenstand, kein Closure-Transfer, kein Umweg über Forgejo; der `rsync` kostet etwa 1,5 s. Der nixbuild.net-Store bleibt für große Kompilierläufe. `/home/drone/nixos-config` auf pinix ist der Recovery-Checkout, kein Arbeitsplatz. ## Gesicherte Ergebnisse - **pinix kann wieder von Forgejo fetchen.** `/etc/ssh/ssh_known_hosts` war leer; der Pin für Port 2222 lag früher im auskommentierten `localServices/forgejo.nix`. Seit `355b42e` steht er global in `hosts/globalServices/forgejo-known-host.nix`. Henne-Ei: der Pin liegt selbst im Repo, ein Pull konnte ihn also nicht holen — der rsync-Bauweg war der Ausweg. Recovery-Checkout per `--ff-only` nachgezogen. - **Traefik auf pinix**, `hosts/pinix/localServices/traefik.nix`. Abgeleitet von blunix, aber bewusst ohne Cloudflare-DNS-Challenge (kein API-Token, kein Secret, dafür keine Wildcard), ohne api/Dashboard, ohne `ip_nonlocal_bind`, `log.level` INFO statt TRACE. - **Statische Testseite**, `hosts/pinix/localServices/ipv6-test.nix`, nach dem Muster von blunix' `launcher.nix`: `static-web-server` auf `127.0.0.1:8001`, Route im Dienstmodul statt in einer zentralen Liste. Die Seite nennt pinix beim Namen, damit ein alter tnix-Pfad auffällt. - **Mango-DNAT umgelegt**, `scripts/configure-mango-ipv6-web.sh` ist jetzt parametrisiert (`TARGET_IP`, `PUBLIC_IP`, `WAN_DEV`, `PORT_MAP`, `RULE_PREFIX`, `STALE_PREFIXES`), räumt Regeln früherer Ziele vorher weg und ermittelt die öffentliche Adresse zur Laufzeit statt sie einzutragen. Aufruf vom LAN aus: `ssh root@192.168.8.1 'sh -s' < scripts/configure-mango-ipv6-web.sh`. Backup je Lauf unter `/root/ipv6-web-firewall-backup.*` auf dem Mango. - **SNAT-Rückwegregeln entfallen ersatzlos.** Sie waren nötig, weil tnix seine Default-Route auf dem iPhone-Hotspot behielt; pinix' Default-Route zeigt ohnehin auf den Mango. Sechs Regeln wurden zu drei, und die echte Client-Adresse steht wieder im Traefik-Log statt `fdee:3c40:44f0::1`. - **Extern 8080 → intern 80**, weil auf pinix nur Traefik mit 80/443 lauscht. Der alte Link bleibt gültig und läuft über die HTTPS-Weiterleitung des Proxys. - **Zertifikat** per ACME HTTP-01, Resolver `le`, Speicher `/var/lib/traefik/acme.json`. Traefik-Log: `Validations succeeded`, also hat Let's Encrypt pinix von außen über IPv6 erreicht. - **tnix-Testserver entfernt.** Damit fiel die statische ULA auf `enp0s31f6` weg, die dort ein schwarzes Loch erzeugte: das Interface ist DOWN, lieferte aber trotzdem die On-Link-Route für `fdee:3c40:44f0::/64`, sodass tnix das gesamte LAN-Präfix in ein totes Interface schickte. Über IPv4 lief derselbe Weg einwandfrei — eine hübsche Falle, falls sie anderswo nochmal auftaucht. ### Gemessene Zustände (12.09.2026, vor weiterer Aktivierung neu prüfen) | Prüfung | Ergebnis | |---|---| | `curl` von tnix, HTTPS mit `--resolve` auf pinix' ULA | 200, „Hallo von pinix." | | Port 80 auf pinix | 301 auf HTTPS | | unbekannter Hostname an Traefik | 404, kein Catch-all | | `openssl s_client` gegen pinix:443 | vollständige Kette, `Verify return code: 0 (ok)` | | pinix IPv6-Uplink | RA-Default-Route via `fe80::9683:c4ff:fe12:ab4d`, `github.com` 200 | Adressen: pinix LAN-ULA `fdee:3c40:44f0:0:dea6:32ff:fe48:c6c8` (SLAAC aus MAC `dc:a6:32:48:c6:c8`), Mango öffentlich `2a01:598:ec1e:ce00:94dd:42ff:fe9a:627f`, Mango LAN `fdee:3c40:44f0::1` bzw. `192.168.8.1`. ## Fallstrick, der Zeit gekostet hat Der Intent verlangt eine „stabile LAN-Zieladresse". Der naheliegende Weg — `networking.interfaces.end0.ipv6.addresses` wie in tnix' `ipv6-web-test.nix` — **nimmt dem Host das Internet**: sobald ein Interface eine statische IPv6-Adresse bekommt, schreibt das NixOS-dhcpcd-Modul für dieses Interface `noipv6rs` in die generierte `dhcpcd.conf`. Keine Router-Solicitation, keine RA-Default-Route — und pinix' Uplink ist IPv6-only. Sichtbar wird das erst nach dem `switch`, weil dabei `dhcpcd` neu startet und die vorhandenen Adressen mitnimmt. Zurückgebaut. Ziel ist jetzt die SLAAC-Adresse, die aus MAC und ULA-Präfix ohnehin deterministisch entsteht — dieselbe Adresse, nur ohne den Nebeneffekt. Im Modul kommentiert. Wer eine wirklich unabhängige statische Adresse will, muss die Default-Route mit lösen; ein erster Versuch damit war laut `eb2022e` schon auf tnix gescheitert. ## Offene Punkte - **Zertifikatskette.** Ein externer Abruf über einen Client mit älterem CA-Bundle scheitert reproduzierbar an `unable to verify the first certificate`; die Kette läuft über `ISRG Root YR`. pinix' eigenes Bundle und `openssl` akzeptieren sie (`Verify return code: 0`). Verdacht: das Root ist noch nicht überall im Trust Store, ältere Android-Geräte dürften dasselbe Problem haben. Möglicher Fix: `preferredChain` im ACME-Resolver. Das erzwingt eine Neuausstellung, also vorher den Eintrag aus `acme.json` räumen. **Vom Nutzer noch nicht entschieden.** - **Reboot-/Reconnect-Probe** (Intent Schritt 6) steht aus, siehe oben. - **Wildcard über selbst gehostetes BIND** wurde besprochen und **zurückgestellt**, nicht verworfen. Begründung: ein autoritativer NS braucht einen zuverlässig erreichbaren Host, und genau diese Eigenschaft ist für pinix noch nicht nachgewiesen. Die Fehlerfolgen sind asymmetrisch — HTTP-01 kaputt heißt eine Seite ohne Zertifikat, autoritativer DNS kaputt heißt `0ff.space` löst nirgends mehr auf, samt Mail, XMPP-Föderation und ActivityPub. Ein nur über IPv6 erreichbarer NS schließt zudem alle IPv4-only-Resolver aus. Vorgeschlagene Reihenfolge: erst Eingang stabilisieren, dann als Zwischenstufe `_acme-challenge` delegieren (nimmt Cloudflare den API-Token weg, ohne die Auflösung der Domain an den Mobilfunkanschluss zu hängen), vollautoritatives BIND als eigene Entscheidung mit Sekundären. Siehe `docs/roadmap.md`. - **Subdomains wie auf blunix** sind erklärtes Ziel. Die Routing-Ebene trägt das bereits: DNAT arbeitet auf Adresse und Port, jede weitere Subdomain ist nur ein Traefik-Router. Nicht mitskalieren: ein Zertifikat pro Hostname bei HTTP-01, ein AAAA je Subdomain auf eine rotierende öffentliche Adresse, und IPv4-only-Besucher erreichen einen IPv6-only-Eingang gar nicht — dafür bliebe blunix als zweiter Eingang. ## Dateien und Arbeitsstand Commits auf `dev`, in dieser Reihenfolge: | Commit | Inhalt | |---|---| | `54ea39e` | `docs/pinix-minimalbetrieb.md`: Forgejo-Fetch behoben, Henne-Ei festgehalten | | `31a8e0f` | pinix: minimales Traefik plus Testseite, noch ohne TLS | | `61b4d1f` | TLS über ACME HTTP-01, Mango-Skript parametrisiert, DNAT umgelegt | | `4558f86` | tnix: IPv6-Testserver entfernt | Aktive Generationen nach der Session: pinix `w9dr3gl14dxwm5hb6l0rv7fxs0h83ahg-nixos-system-pinix`, tnix `vjmsfk6kdgynnzdc0pz53cf5knbi02qr-nixos-system-tnix`. Verworfen wurde eine uncommittete Änderung in `hosts/tnix/configuration.nix`, die das WLAN zurück auf den iPhone-Hotspot gestellt hätte — ein `switch` damit hätte tnix aus dem Netz geworfen, in dem es gerade hängt. Rückbau auf den vorherigen Stand: `dest_ip` in den Mango-Regeln zurück auf tnix' ULA `fdee:3c40:44f0:0:ca5b:76ff:fe96:e24` (oder das Skript mit `TARGET_IP=...` erneut laufen lassen) und auf tnix die Generation vor `4558f86` booten, die den nginx-Test noch enthält.