This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/docs/session-2026-09-12-pinix-traefik.md
pijukka d326031f38 docs: Session-Uebergabe zu Traefik auf pinix, Intent-Status nachgezogen
Format wie docs/session-2026-09-12-ipv6-plattform.md, an das diese Session
anschliesst. Haelt fest, was verifiziert ist, was ausdruecklich nicht
getestet wurde (Reboot-/Reconnect-Probe) und welche Entscheidungen offen
beim Nutzer liegen (Zertifikatskette, Wildcard via BIND).

Der Intent sagte noch "noch nicht ausgefuehrt"; Schritte 1 bis 5 sind
erledigt, Schritt 6 nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019jNMZkpvA9QuNEPg9BA6oY
2026-09-12 15:28:26 +02:00

163 lines
8.7 KiB
Markdown

# 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:** <https://ipv6-test.0ff.space/> 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.