{ config, ... }: { # Wildcard-Zertifikat fuer domain2 ueber DNS-01 gegen das *eigene* BIND. # # Bewusst ueber das NixOS-Modul `security.acme` und nicht ueber Traefiks # eigenen certificatesResolver, obwohl blunix es dort so macht: # # - Der Zustand liegt unter /var/lib/acme pro Zertifikat statt in Traefiks # undurchsichtiger acme.json, in der alle Zertifikate zusammen stecken. # - Andere Dienste auf diesem Host koennen dasselbe Zertifikat lesen, # ohne Traefik zu sein -- Prosody oder ein Mailserver spaeter genauso. # - Erneuerungs-Timer, Units und Reload-Hooks kommen vom Modul, das ist # der Upstream-Weg statt eigener Mechanik (CLAUDE.md: Upstream-Module # gegenueber eigenen Patches bevorzugen). # # Der TSIG-Schluessel ist dasselbe agenix-Secret, das bind.nix zu BINDs # key-Syntax rendert. Es liegt in Environment-Form vor, genau was # `environmentFile` erwartet (RFC2136_NAMESERVER, _TSIG_KEY, # _TSIG_ALGORITHM, _TSIG_SECRET) -- ein Secret, kein zweites Format. security.acme = { acceptTerms = true; defaults.email = config.adminEmail; certs."${config.domain2}" = { domain = config.domain2; # Der eigentliche Zweck: HTTP-01 kann keine Wildcards, DNS-01 kann es. extraDomainNames = [ "*.${config.domain2}" ]; dnsProvider = "rfc2136"; environmentFile = config.age.secrets.bindAcmeTsig.path; # Aus: die Pruefung ist von diesem Host aus strukturell nicht # durchfuehrbar. Am 12.09.2026 in drei Schritten durchgemessen, jeder # mit eigenem Fehlerbild: # # 1. Systemresolver (Mango): liefert die SOA, auf eine NS-Abfrage aber # gar nichts -- sein Forwarder bedient NS-Eintraege nicht. lego # meldet "could not determine authoritative nameservers". # 2. Eigenes unbound: SERVFAIL. Es rekursiert korrekt bis zur # Delegation, die auf die *oeffentliche* Adresse des Mango zeigt, # und die ist von innen nicht erreichbar -- die DNAT-Regeln tragen # `reflection=0`. # 3. Fremder Resolver (Quad9): findet die NS korrekt, aber lego fragt # den autoritativen Server danach *direkt* ab. Von innen landet das # wieder auf der oeffentlichen Mango-Adresse, wo derselbe Hairpin # greift und Mangos eigener Forwarder antwortet statt unseres BIND # -- "did not return the expected TXT record". # # Kern: jeder Weg von innen endet an derselben Hairpin-Grenze. Der # Verzicht auf die Vorpruefung ist deshalb kein Nachlassen der # Sorgfalt, sondern die Anerkennung, dass nur Let's Encrypt selbst von # aussen pruefen kann -- und genau das tut es. Dass die Delegation von # aussen funktioniert, ist unabhaengig belegt: Quad9 und Googles # Resolver liefern beide NS, SOA und TXT aus unserem BIND. # # Wieder einschaltbar, sobald am Mango Reflection/Hairpin fuer Port 53 # aktiv ist; dann ist "[2620:fe::fe]:53" als dnsResolver der Weg. dnsPropagationCheck = false; # Traefik liest die Dateien aus /var/lib/acme; ueber die Gruppe bekommt # es Leserecht, ohne dass die Schluesseldatei weltlesbar wird. group = "traefik"; reloadServices = [ "traefik.service" ]; }; }; }