{ 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"; } ]; }; }