Files
bf-infra/docs/handoff-20260825-agh-dns-cutover.md
Christoph Emsenhuber 5b043f1032 Uebergabe Schritt 4 neu gefasst: drei Funde kippen den alten Plan
Die erste Fassung entstand vor der Caddy-Abnahme. Live erhoben, dagegen:

- adguard_bf1 (.20.16) ist ein Archiv, keine Vorlage: 24 Rewrites, davon 8
  ins tote VLAN1, 9 auf den heute zerstoerten nginx-proxy-manager (LXC 128)
  und 2 FritzBox-Reste. Uebernommen werden Upstreams/Filter, die Rewrites
  entstehen neu aus caddy/Caddyfile.
- .20.17 ist nicht "bf2", sondern adguard_fm2 (FM-Standort) und antwortet
  fuer gitea mit 10.40.41.123. BF hat heute EINEN AGH.
- Die TP5-Vorratsregeln erlauben LAN->DNS hart auf .20.16. Nach dem
  DHCP-Flip wird DNS ein forward-Fluss ueber VLAN-Grenzen; ohne erweiterte
  Regeln verlieren IOT/MEDIA/UNTRUSTED/GUEST/MGMT die Aufloesung, waehrend
  VLAN20 weiterlaeuft und den Fehler tarnt.

Dazu neu: lease-time=1d macht Flip und Rollback traege (erst zur
Erneuerung wirksam), expliziter Rollback-Abschnitt, .36/.37 als frei
geprueft, und der Core bleibt bewusst Rueckfallebene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 23:25:34 +02:00

11 KiB
Raw Blame History

Übergabe — nächste Session: AdGuard als Resolver + Split-Horizon (TP23 Stufe 2 ∥ TP31 Schritt 4)

Einstieg: diese Datei, dann teilprojekte.md → TP31 und TP32. Zustand am Netz: stabil. Caddy läuft produktiv und ist abgenommen, keine Config-Transaktion offen.

🔄 Zweite Fassung (2026-08-25, spät). Die erste entstand vor der Caddy-Abnahme und enthielt drei Annahmen, die live nicht halten — sie stehen unten unter „Was den Plan ändert". Wer die alte Fassung im Kopf hat: Punkt 2 des Auftrags („Konfiguration übernehmen") ist so falsch.

Warum eine eigene Session

Der Schritt verschiebt die DNS-Auflösung des ganzen Standorts vom Core in einen Container. Heute liefert der DHCP in allen sieben VLANs dns-server = <VLAN>.1 (live geprüft) — AdGuard bekommt niemand. Ein Fehler trifft sofort jedes Gerät und ist schlecht zu diagnostizieren, wenn die Auflösung selbst das Kaputte ist. Nicht ans Ende einer langen Sitzung legen (Lehre aus TP28).

Ausgangslage (Stand 2026-08-25 23:30, live geprüft)

  • ✅ Caddy produktiv und abgenommen: LXC 130 caddy-bf1, 10.40.20.40, onboot=1, startup order=10, im Backup-Job. Caddy v2.11.4, 16 Sites. Abnahme über den Host-Header von einem zweiten Host: 11× 200, nautobot 302, 0,02–0,11 s. Config im Repo caddy/Caddyfile, Ausrollen mit caddy/deploy.sh.
    • ℹ️ pve-mcp 400 / ha-mcp 403 / gitea-mcp 404 sind keine Proxy-Fehler — dieselben Codes kommen direkt vom Origin. Nicht als Baustelle behandeln.
    • 🧹 Der Zertifikatsspeicher hält 17 Zertifikate bei 16 Sites: grafana ist verwaist (harmlos).
  • 🟡 hass liefert weiter 400 — ungelöst, gehört VOR die CNAME-Umstellung (nicht vor diesen Schritt). http:-Block steht in /homeassistant/configuration.yaml (use_x_forwarded_for: true, trusted_proxies: 10.40.20.0/24 + .10 + .167), Sicherung configuration.yaml.bak-20260825-tp31. Die Änderung ist seit 23:18 drin, HA läuft aber noch mit dem Stand von 22:40 — der Neustart fehlt. ⚠️ Sowohl der Schreibzugriff als auch der Restart wurden zuletzt vom Auto-Mode-Klassifikator blockiert; entweder freigeben oder selbst ausführen: ha core restart bzw. homeassistant.restart, danach curl -o /dev/null -w "%{http_code}\n" --resolve hass.emsenhuber.net:443:10.40.20.40 https://hass.emsenhuber.net/
    • 🔎 Der Logtext ist der Befund, nicht der Statuscode: Received X-Forwarded-For header from an untrusted proxy 10.40.20.40 ≠ „not set-up for reverse proxies" ⇒ die Option greift, die Liste nicht.
  • ✅ Aufgeräumt (TP31 Schritt 1): LXC 120/121/128/129/130(alt)/131 und 104 zerstört, 72/72 PBS-Snapshots weg. Die 14 Snapshots von ct/104 stehen bewusst (einzige Grafana-Kopie).
  • ✅ Startreihenfolge auf pve repariert (TP32): HA startet nach MariaDB, Docker, MQTT, z2m25.

🔴 Was den Plan ändert — drei Funde vom 2026-08-25 (spät), alle live erhoben

1. Der Bestands-AdGuard ist ein Archiv, keine Vorlage

adguard_bf1 (10.40.20.16, Docker in LXC 400) läuft seit 7 Tagen und hält 24 Rewrites — davon ist der überwiegende Teil tot:

Klasse Anzahl Beispiel Warum tot
Ziel im toten VLAN1 8 nas.bf…→10.40.41.10, mfc.bf…→10.40.41.4, macmini.bf…→10.40.41.167 VLAN1 ist seit 2026-08-18 leer (TP19)
Ziel nginx-bf.emsenhuber.net 9 portainer, z2m25, nodered, grafana, vw, speedtest, … der NPM (LXC 128) ist heute zerstört worden
FritzBox-Reste 2 *.myfritz.net → 100.72.62.31 / 10.40.41.254 FB6660 seit 2026-07-01 ausgebaut
FM-Standort 3 nas.fm…→10.101.0.10 anderer Standort, hier nicht anfassen

⇒ Übernehmen: upstream_dns, bootstrap_dns, Filterlisten. NICHT die Rewrites. Die 16 Rewrites werden aus caddy/Caddyfile neu erzeugt (jeder Site-Name → 10.40.20.40). Die Altlasten oben sind eine eigene, bewusste Entscheidung je Name — nicht mitschleppen.

Was dort richtig steht und mitmuss:

upstream_dns:
  - https://px35ynlol3.cloudflare-gateway.com/dns-query
  - '[/bf.lan/]10.40.20.1'
  - '[/40.10.in-addr.arpa/]10.40.20.1'

🔑 Die zweite Zeile ist nicht optional: der Core vergibt domain=bf.lan und legt aus jeder Lease einen *.lan-Eintrag an (add-dns-entries-suffix=lan). Ohne diesen conditional upstream sterben alle Lease-Namen. Ebenso bleibt mDNS Sache des Core (mdns-repeat-ifaces = vlan10/20/30/40/90).

2. 10.40.20.17 ist nicht „bf2"

Dort läuft adguard_fm2 — die zweite Instanz des FM-Standorts, hier nur mitgehostet. Sie beantwortet gitea.emsenhuber.net mit 10.40.41.123, also ebenfalls ins tote VLAN1. ⇒ Nicht als Vorlage, nicht als „vorhandene Redundanz" verbuchen. BF hat heute einen AGH. (Dass FM stale Rewrites hat, ist ein eigener Punkt — nicht in dieser Session.)

3. Die Firewall-Vorratsregeln zeigen auf die falsche IP

TP5 Etappe 2 hat vorgesorgt, aber hart auf den heutigen Container:

TP5-E2: LAN->AGH dns      accept udp dst-address=10.40.20.16 dst-port=53
TP5-E2: LAN->AGH dns-tcp  accept tcp dst-address=10.40.20.16 dst-port=53

🔴 Das ist der Punkt, an dem dieser Cutover sonst scheitert: heute fragt jeder Client seinen eigenen Gateway (input-Kette, keine Regel nötig). Nach dem DHCP-Flip wird DNS zu einem forward-Fluss über VLAN-Grenzen — und die Firewall ist seit 2026-08-18 deny-by-default. Ohne passende Regeln verlieren IOT, MEDIA, UNTRUSTED, GUEST und MGMT schlagartig die Auflösung, während VLAN20 (wo der Resolver steht) unauffällig weiterläuft. ⇒ Die beiden Regeln auf die neuen AGH-Adressen erweitern, bevor irgendein DHCP angefasst wird — und zwar über Nautobot Golden Config, nicht ad hoc: Core-Config ist GC-only.

Der Auftrag

User-Entscheid 2026-08-25: Split-Horizon nicht auf dem Core, sondern in AdGuard, zusammen mit TP23 Stufe 2 — „nichts zweimal bauen".

  1. LXC 110 agh-bf1 + 111 agh-bf2 bauen, Muster exakt wie LXC 130 (Debian 13, tag=20, onboot=1, startup order=10, in den Backup-Job backup-6dcf5c10-610f). Als Paar — ein einzelner AGH wäre ein DNS-SPOF auf pve. Adressen 10.40.20.36 / .37 — TP32 will DNS auf .3x, .30/.31/.32 sind Nautobot; .36/.37 sind frei geprüft (kein ARP, keine Lease, DHCP-Pool beginnt erst bei .100). ⚠️ Nebenbefund: TP32 nennt als Pool .200–.249, live läuft .100–.200 — Konflikt mit „.100–.199 App-Services". Bestand bleibt; nur beim Vergeben aufpassen.
  2. Basis-Config übernehmen (Upstreams/Bootstrap/Filter) aus adguard_bf1, /opt/docker/adguard_bf1/conf/AdGuardHome.yaml (Backup daneben: …bak-tp23-20260817). Rewrites NEU bauen — s. Fund 1.
  3. Die 16 Rewrites setzen: jeder Site-Name aus caddy/Caddyfile → 10.40.20.40.
  4. Firewall zuerst (Fund 3): TP5-E2-Regeln auf .36/.37 erweitern, per GC deployen, --refresh-compliance fahren.
  5. Testen, bevor irgendein DHCP angefasst wird: dig @10.40.20.36 vw.emsenhuber.net (erwartet 10.40.20.40), dig @10.40.20.36 <irgendein-lease>.lan (erwartet die Lease-IP), und dasselbe gegen .37. Zusätzlich aus einem anderen VLAN — sonst prüft man nur VLAN20 und übersieht genau den Firewall-Fall.
  6. VLAN für VLAN umstellen, nicht alle sieben: VLAN20 → VLAN10 → Rest, VLAN30 (IoT, 26 Geräte) zuletzt; dort ist ein Reconnect am teuersten.

⏱️ Die Falle, die den Zeitplan bestimmt: lease-time=1d

Alle sieben Server stehen auf 1 Tag (GUEST 12 h). Ein geändertes dns-server erreicht die Clients erst zur Erneuerung (T1 ≈ halbe Laufzeit) — der Cutover wirkt also verzögert und ungleichzeitig, und ein Rollback wirkt genauso langsam. Zwei Konsequenzen:

  • Vor dem Flip die lease-time im betroffenen VLAN auf ~10 min senken (GC-Deploy), umstellen, verifizieren, danach zurücksetzen. Sonst zieht sich die Umstellung über Stunden.
  • „Es geht noch" beweist nach dem Flip gar nichts — das Gerät hat schlicht die alte Lease. Abnahme ist eine frische Lease + scutil --dns / resolvectl status, nicht ein ping.

🔙 Rollback (vorher lesen, nicht danach)

  1. dns-server im betroffenen /ip dhcp-server network zurück auf <VLAN>.1 — der Core ist unverändert ein vollwertiger Resolver (DoH zum CF-Gateway, verify-doh-cert=yes, allow-remote-requests=yes); er wird in diesem TP nicht abgeschaltet.
  2. Clients erneuern lassen (s. lease-time oben) oder gezielt bouncen.
  3. Firewall-Regeln dürfen stehen bleiben — sie schaden nicht. ⇒ Der Core bleibt bis zum Ende der Session unangetastet als Rückfallebene. Erst wenn alle sieben VLANs sauber über AGH laufen, wird über sein Abschalten überhaupt geredet (eigener Entscheid).

Fallen, die schon bezahlt sind

  • 🔴 Ein Statuscode vom öffentlichen FQDN beweist nichts. grafana.emsenhuber.net lieferte monatelang 302 — das war der Access-Login, während der Dienst gar nicht existierte. Immer gegen das Origin messen. Umgekehrt gilt dasselbe: die 400/403/404 der MCP-Endpunkte sind echte Serverantworten, kein Proxy-Fehler.
  • 🔴 pvesm free scheitert an geschützten PBS-Snapshots stillschweigend. Entschützen: pvesh set /nodes/pve/storage/pbs/content/<volid> --protected 0.
  • 🔴 Startreihenfolge auf pve hängt NICHT an der VMID, solange ein Gast startup: order= trägt.
  • ⚠️ Core-Config läuft über Nautobot Golden Config — DHCP- und Firewall-Änderung brauchen Template und ComplianceRule, sonst Drift. Letzter geprüfter Stand: 104/104, 0 Drifts (08-23); vor dem Cutover einmal --refresh-compliance fahren, damit die Ausgangslage sauber ist.
  • ⚠️ Caddy in LXC 130 hat einen eigenen Netz-Namespace und ist deshalb nicht von der macvlan/ipvlan-Blindheit betroffen — ein AGH im Docker-LXC 400 wäre es. Ein weiterer Grund für eigene LXCs statt „Container dazu".
  • ⚠️ Mit Split-Horizon greift CF Access innerhalb des Netzes nicht mehr. Bewusster Preis (User 2026-08-25); innen tragen TP5-Segmentierung und die App-eigene Auth.

Danach offen (TP31 Rest)

Tunnel bf-public + cloudflared auf ap1/ap2 (arm64, 95 MB Flash frei — ⚠️ je ein physischer device-mode-Wechsel am Gerät nötig), CNAMEs umhängen (n8n → gitea → hass → vw; hass erst nach dem 400er-Fix), dann bf-nas/bf-pve/bf-mm löschen und die alten Container entfernen. Danach: adguard_bf1 im Docker-LXC 400 abbauen — sonst stehen zwei AdGuards mit widersprüchlichen Rewrites im selben Netz.