Ausgangslage
Der Internetanschluss läuft hinter CGNAT – die öffentliche IP-Adresse wird also nicht exklusiv vergeben, sondern mit anderen Kundinnen und Kunden des Providers geteilt. Klassisches Port-Forwarding von aussen ist dadurch nicht möglich, und die öffentliche IP ändert sich zudem potenziell noch häufiger als bei einem klassischen dynamischen IPv4-Anschluss.
Der konkrete Anlass für ein Dynamic-DNS-Setup war der Zugriff auf externe Systeme, die eine IP- oder DNS-basierte Zugriffsbeschränkung (Allowlisting) verwenden. Bei einer sich ändernden WAN-IP lässt sich eine feste IP-Adresse dort nicht dauerhaft hinterlegen – ein Hostname hingegen schon, sofern dieser zuverlässig auf die jeweils aktuelle IP zeigt. Genau das leistet Dynamic DNS: Ein DNS-Eintrag, der sich automatisch aktualisiert, sobald sich die WAN-IP ändert, lässt sich bei entsprechenden Systemen einmalig als Allowlist-Ziel hinterlegen und bleibt danach ohne weiteres Zutun gültig.
Problem
pfSense bringt für genau diesen Zweck einen nativen Cloudflare-DDNS-Client mit. Die Einrichtung schien zunächst unkompliziert: Ein Cloudflare API-Token mit eingeschränkten Rechten (nur DNS-Bearbeitung für die eigene Zone) erstellen, in pfSense eintragen, fertig. Die Realität sah anders aus.
Jeder Update-Versuch scheiterte mit derselben Fehlermeldung im Log: „Zone or Host ID was not found, check the hostname.“ Die Cloudflare-API-Antwort dahinter zeigte, dass die Zonen-Suche schlicht leer zurückkam – obwohl die Zone unbestreitbar existierte und der Token die richtigen Berechtigungen hatte (Zone/DNS/Bearbeiten sowie nachträglich ergänzt Zone/Zone/Lesen).
Eine Recherche in einschlägigen Foren und Community-Blogs brachte Klarheit: Es handelt sich um einen seit Jahren bekannten, nie vollständig behobenen Fehler in pfSenses Cloudflare-DDNS-Implementierung. Mehrere unabhängige Quellen bestätigten, dass scoped API-Tokens dort schlicht nicht zuverlässig funktionieren – als Workaround wird durchgängig empfohlen, stattdessen den Cloudflare Global API Key zu verwenden, der jedoch vollen Zugriff auf das gesamte Cloudflare-Konto gewährt statt nur auf eine einzelne Zone.
Das war aus Sicherheitssicht keine akzeptable Lösung für einen Homelab-Baustein, der eigentlich nach dem Prinzip der minimalen Rechtevergabe aufgebaut sein sollte.
Auflösung
Die Entscheidung fiel auf einen eigenständigen, containerisierten DDNS-Client statt der pfSense-eigenen Lösung. Ein schlankes, gut gepflegtes Docker-Image übernimmt die Aktualisierung des Cloudflare-Records – mit demselben scoped Token, der in pfSense nicht funktionieren wollte, hier aber ohne Probleme akzeptiert wird.
Damit dieser Container nicht einfach im bestehenden Docker-Netzwerk mitläuft und potenziell auf interne Netzsegmente zugreifen könnte, wurde ein dediziertes VLAN dafür eingerichtet – analog zum bestehenden Muster für andere Dienste im Homelab. Die Isolation baut auf mehreren Ebenen auf:
Auf Proxmox-Ebene bekam die betreffende VM ein zusätzliches virtuelles Netzwerkgerät mit dem entsprechenden VLAN-Tag, sodass das Tagging bereits auf Hypervisor-Ebene erfolgt und die virtuelle Maschine ein sauber getrenntes Interface für dieses Segment vorfindet. Innerhalb der VM wird dieses Interface bewusst ohne eigene IP-Adresse konfiguriert – es dient ausschliesslich als sogenannter Parent für ein ipvlan-Netzwerk auf Docker-Ebene. Der Container selbst erhält darüber eine feste IP-Adresse aus dem neuen Subnetz, direkt im Compose-File hinterlegt; ein DHCP-Dienst für dieses Segment wurde bewusst nicht eingerichtet, da ohnehin kein Gerät darin jemals eine dynamische Adresse beziehen soll.
Die eigentliche Zugriffsbeschränkung erfolgt zentral über die Firewall des Routers, nicht verteilt über mehrere Systeme hinweg. Für das neue Segment wurde eine kleine, bewusst restriktive Regelkette definiert:
- Eine Regel erlaubt ausschliesslich DNS-Auflösung Richtung des internen Resolvers.
- Eine weitere Regel lässt Ping (ICMP) zu Diagnosezwecken zu.
- Eine Regel blockiert sämtlichen Datenverkehr Richtung privater Netzbereiche (also aller anderen internen Segmente), über einen zentral gepflegten Adress-Alias.
- Eine letzte, allgemeinere Regel erlaubt ausgehenden Internetverkehr für alles, was nicht bereits durch die vorherigen Regeln behandelt wurde.
Die Reihenfolge dieser Regeln ist entscheidend: Die Blockade privater Netze muss vor der allgemeinen Freigabe stehen, sonst würde Letztere sie nie erreichen. Im Ergebnis kann der Container ausschliesslich mit dem Internet kommunizieren (konkret: DNS-Auflösung und die Cloudflare-API über HTTPS) – jeglicher Zugriff auf andere Geräte oder Dienste im Homelab ist ihm technisch verwehrt, selbst falls der Container selbst einmal kompromittiert werden sollte.
Die Inbetriebnahme brachte einen weiteren Stolperstein zutage: Der Container meldete zunächst „Host is unreachable“, sobald er versuchte, den pfSense-Gateway oder die Cloudflare-API zu erreichen – trotz augenscheinlich passender Firewall-Regeln für das neue Segment. Ein naheliegender, aber letztlich falscher Verdacht fiel zwischenzeitlich auf die Proxmox-VM-Firewall, da das neu angelegte virtuelle Netzwerkgerät mit firewall=1 markiert war. Dieses Flag aktiviert die Firewall-Filterung aber nur dann tatsächlich, wenn die VM-Firewall als Ganzes eingeschaltet ist (ein separater Schalter unter den VM-Firewall-Optionen) – und das war hier gar nicht der Fall. Der Verdacht erwies sich also als Sackgasse.
Der tatsächliche Übeltäter fand sich stattdessen direkt in pfSense: Ein Packet Capture zeigte, dass ICMP-Anfragen zwar ankamen, aber unbeantwortet blieben. Die bestehenden Firewall-Regeln für das neue Segment deckten zwar TCP/UDP und den allgemeinen ausgehenden Verkehr ab, aber keine explizite ICMP-Erlaubnis – eine zusätzliche Regel dafür löste den Blocker vollständig, sowohl für Ping als auch für die eigentlich benötigte TCP-Verbindung auf Port 443.
Lessons Learned
- pfSenses Cloudflare-DDNS-Client hat einen seit Jahren bekannten Bug mit scoped API-Tokens. Die GUI suggeriert Unterstützung, in der Praxis funktioniert zuverlässig nur der Global API Key – was aus Sicherheitssicht nicht wünschenswert ist. Ein dedizierter, containerisierter DDNS-Client mit demselben Token funktioniert dagegen problemlos.
- Das Flag
firewall=1auf einem Proxmox-Netzwerkgerät bedeutet nicht automatisch, dass dort auch gefiltert wird. Es macht das Interface lediglich firewall-fähig, sofern die VM-Firewall als Ganzes aktiviert ist – ein separater Schalter, der leicht übersehen wird und bei der Fehlersuche in die falsche Richtung führen kann. - Eine allgemeine „Allow all outgoing“-Regel deckt nicht zwangsläufig ICMP zuverlässig ab. Für Diagnosezwecke (Ping) lohnt sich eine explizite, separate Regel weiter oben in der Reihenfolge, unabhängig davon, ob eine breitere Regel eigentlich alles erlauben sollte.
- ipvlan-Container teilen sich die MAC-Adresse des Parent-Interfaces. Das ist normales, erwartetes Verhalten (im Gegensatz zu macvlan) und keine Fehlkonfiguration, auch wenn es auf den ersten Blick verwirrend wirken kann.
- Ein zufällig gewählter, nicht offensichtlicher DDNS-Hostname reduziert die Auffindbarkeit deutlich, unter anderem weil ohne zugehöriges TLS-Zertifikat kein Eintrag in Certificate-Transparency-Logs entsteht – einer der häufigsten Wege, wie Subdomains sonst öffentlich entdeckt werden.
Fazit
Was als einfache „IP-Adresse überwachen“-Anforderung begann, entpuppte sich als mehrschichtige Fehlersuche über drei verschiedene Systeme hinweg: pfSense, Proxmox und Docker-Networking. Am Ende steht eine Lösung, die nicht nur funktioniert, sondern auch besser zum Sicherheitsanspruch des Homelabs passt als der ursprünglich vorgesehene Weg – ein eigenständiger, netzwerktechnisch isolierter Container mit einem eng begrenzten API-Token statt eines Tools mit Zugriff auf das gesamte Cloudflare-Konto.
Der Fall zeigt einmal mehr, dass bei mehrschichtigen Infrastrukturen (Hypervisor, Firewall, Container-Networking) ein Fehler selten nur eine Ursache hat – und dass sich die Fehlersuche lohnt, Schicht für Schicht durchzugehen, statt vorschnell bei der erstbesten funktionierenden, aber unsicheren Lösung stehenzubleiben.