Vom Fritzbox-Mesh-WLAN zum eigenen UniFi-Controller

Wie ich mein WLAN von der Fritzbox auf zwei Ubiquiti Access Points mit selbst gehostetem Controller umgezogen habe — mit einer Mongo-Authentifizierungs-Herausforderung, einem stummen Firewall-Rätsel, das erst meine Temperaturstationen verraten haben, und einem Roaming-Problem, das mich bis zum Schluss beschäftigt hat.

Ausgangslage

Mit meiner Fritzbox als eigenständigem Access Point war ich lange Zeit durchaus zufrieden. Sie lief zuverlässig, hatte sogar schon ein funktionierendes Mesh mit einem zweiten Gerät, und hat mir über Jahre hinweg gute Dienste geleistet. Der eigentliche Grund für den Wechsel war simpel: Für mein Fritzbox-Modell gab es schlicht keine Firmware-Updates mehr. Ohne Sicherheitsupdates wollte ich das WLAN aber nicht dauerhaft weiterbetreiben, gerade weil inzwischen auch deutlich mehr Geräte auf einem einzigen, undifferenzierten Netz liefen als ursprünglich geplant.

Zeit für eine sauberere Lösung: zwei Ubiquiti Access Points, verwaltet über einen selbst gehosteten UniFi-Controller, vollständig in die bestehende VLAN-Struktur integriert statt als Insellösung nebenher zu laufen.

Der Plan klang überschaubar: ein Docker-Container für den Controller, zwei dedizierte VLANs für Controller-Management und AP-Management, und eine schrittweise Migration der bestehenden WLAN-Geräte weg vom alten, undifferenzierten Trusted-Netz hin zu sauber getrennten Netzen für vertrauenswürdige Geräte, IoT-Geräte und ausgemusterte Altgeräte. Wie so oft war der Plan der einfache Teil.

Problem 1: Die Datenbank, die nicht wollte

Die Grundinstallation lief zunächst überraschend glatt: Docker-Compose- Stack mit dem Controller-Image und einer MongoDB als Datenbank, eigenes VLAN für den Controller, ipvlan-Networking passend zu meinem bestehenden Muster. Doch beim ersten Start scheiterte die Verbindung zwischen Controller und Datenbank wiederholt an Authentifizierungsfehlern.

Die Ursache erwies sich als vielschichtiger als gedacht. MongoDB behandelt Benutzer als an eine bestimmte Datenbank gebundene Identität. Ein Benutzer, der in mehreren Datenbanken separat angelegt wird, erzeugt mehrere unabhängige Identitäten — auch bei identischem Namen und Passwort. Der Controller meldet sich aber nur einmal an und versucht danach, mit dieser einen Sitzung auf mehrere interne Teildatenbanken zuzugreifen, was prompt mit Berechtigungsfehlern quittiert wurde.

Erschwerend kam hinzu, dass eine Umgebungsvariable, die laut Dokumentation die Authentifizierungs-Datenbank festlegen sollte, vom Controller schlicht ignoriert wurde — unabhängig vom gesetzten Wert verband er sich immer mit derselben, fest verdrahteten internen Datenbank. Ich habe mehrere Anläufe gebraucht und bin dabei zwischenzeitlich sogar in die falsche Richtung gelaufen, weil die Dokumentation genau das Gegenteil suggerierte. Erst ein Blick in ein GitHub-Issue des Projekts brachte Klarheit über die tatsächlich funktionierende Kombination aus Datenbank-Zuordnung und Rollen-Vergabe: ein einziger Benutzer, angelegt in genau der Datenbank, die der Controller intern fest erwartet, aber ausgestattet mit Rollen für sämtliche vier internen Teildatenbanken der Anwendung in einem einzigen Vorgang statt mehrerer getrennter. Nach dieser Korrektur und einem vollständigen Reset der Datenbank lief die Verbindung sauber durch.

Problem 2: Der Access Point, der nicht senden durfte

Die Einbindung der Access Points selbst verlief danach zunächst unkompliziert. Der kabelgebundene Access Point bekam am Switch einen sogenannten Trunk-Port, der sowohl das Management-Netz unverändert als auch die später konfigurierten Client-Netze getaggt durchlässt. Der zweite Access Point wurde komplett kabellos als Mesh-Gerät eingebunden — einfach in Reichweite des ersten mit Strom versorgt, meldete er sich automatisch über ein verstecktes Kopplungs-Netzwerk beim Controller.

Als ich danach versuchte, WLAN-Netze auf verschiedene VLANs zu verteilen, merkte ich schnell: Der Switch-Port, der bis dahin nur das reine Management-Netz ungetaggt durchliess, blockierte jeglichen getaggten Verkehr für die eigentlichen Client-Netze. Ein Trunk-Port muss beides gleichzeitig können — ungetaggten Verkehr für ein natives Netz und getaggten Verkehr für mehrere weitere. Nach etwas Suchen in der Dokumentation meines Switches fand sich die richtige Einstellung: ein Modus, der beide Frame-Typen gleichzeitig akzeptiert, kombiniert mit einer separaten Mitgliedschaftstabelle, in der jedes VLAN einzeln als getaggt oder ungetaggt pro Port eingetragen wird.

Problem 3: Der Switch, der sich versteckte

Kurz nachdem der Trunk-Port stand, wollte ich testweise über das neue WLAN auf die Verwaltungsoberfläche meines Switches zugreifen — und kam nicht durch. Ein kurzer Schreckmoment, bis klar wurde: Das war keine Fehlkonfiguration, sondern eine bewusste Sicherheitseinstellung meines Switches, die den Verwaltungszugriff auf bestimmte, dafür vorgesehene Ports beschränkt. Der Datenverkehr von WLAN-Clients erreicht den Switch jetzt über einen anderen physischen Port als vorher, als das alte WLAN-Netz noch komplett unabhängig über die Fritzbox lief — und dieser neue Pfad war schlicht nicht in der Liste der erlaubten Zugriffs-Ports enthalten. Über mein gewohntes kabelgebundenes Gerät funktionierte der Zugriff die ganze Zeit über völlig normal weiter. Ein Nicht-Problem also, nur ein Moment der Verwirrung.

Problem 4: Die Temperaturstationen, die plötzlich schwiegen

Ein zentraler Teil der Migration war, alte Geräte von meinem Haupt-WLAN-Netz auf ein separates, isoliertes Netz umzuziehen. Der Trick dafür war überraschend elegant: Die neue WLAN-Kennung erhielt exakt denselben Namen und dasselbe Passwort wie das alte Netz. Geräte erkennen WLAN-Netze anhand des Namens, nicht der dahinterliegenden Technik — dadurch verbanden sich sämtliche Bestandsgeräte automatisch mit dem neuen Setup, ganz ohne manuelles Eingreifen an jedem einzelnen Gerät. Ein kleiner Moment der Freude, nachdem vorher so vieles kompliziert war.

Die Freude hielt allerdings nicht lange an. Kurz nach der Isolation dieses neuen Netzes — bewusst so eingerichtet, dass Altgeräte nicht mehr auf andere interne Netze zugreifen können — hörten meine Temperaturstationen auf, Daten zu schreiben. Kein Fehler an den Geräten selbst, sie liefen einfach weiter, als wäre nichts. Nur die Daten kamen nirgends mehr an.

Die Suche nach der Ursache führte über einen Umweg, der mich zusätzlich stutzig machte: Die Firewall-Protokollierung selbst zeigte seit Wochen keine neuen Einträge mehr an, obwohl aktiv Verkehr blockiert wurde. Ein Infrastruktur-Ausfall einige Tage zuvor hatte offenbar die Verbindung zwischen der Firewall und meinem zentralen Log-System in einen toten Zustand versetzt, aus dem sie sich nicht von selbst erholte — kein automatisches Reconnect, einfach Stille. Erst ein erneutes Speichern der Protokollierungs-Einstellungen brachte den Log-Fluss wieder in Gang. Im Anschluss zeigte sich sofort, was los war: Die frisch isolierten Altgeräte versuchten, Daten an einen internen Datenbank-Server zu senden — genau das Ziel, das die neue Isolationsregel des Netzes ebenso blockierte wie den ursprünglich beabsichtigten Zugriff auf andere interne Netze. Eine gezielte Ausnahme für genau dieses eine Ziel löste das Problem, ohne die grundsätzliche Isolation aufzuweichen.

Problem 5: Das Roaming, das einfach nicht wollte

Das letzte und hartnäckigste Kapitel betraf etwas, das mit der Fritzbox nie ein Thema war: Mein Smartphone wechselte nicht zuverlässig zwischen den beiden Access Points, selbst wenn ich buchstäblich direkt neben dem näheren Gerät stand. Fritzbox-Mesh-Systeme sind hier von Haus aus aggressiv und kundenfreundlich, während sich UniFi als deutlich zurückhaltender entpuppte.

Der erste Ansatz war ein Mechanismus, der Geräte aktiv vom Access Point trennt, sobald das Signal unter einen Schwellenwert fällt — verbunden mit zwei Roaming-Beschleunigungs-Standards, die den eigentlichen Wechsel schneller und für den Client transparenter machen sollen. Nach der Aktivierung zeigte das Verbindungsprotokoll aber ein neues, unerwartetes Bild: Wiederholte Authentifizierungsfehler beim Versuch, sich neu zu verbinden — über beide Access Points und alle Frequenzbänder verteilt. Eine bekannte Krux: Der schnellere der beiden Roaming-Standards hat mit bestimmten Smartphone-Chipsätzen Kompatibilitätsprobleme, die sich genau so äussern. Nach dessen Deaktivierung verschwanden die Authentifizierungs- fehler tatsächlich vollständig aus dem Protokoll.

Kurz glaubte ich, das Kapitel damit abschliessen zu können — bis das Smartphone kurz darauf minutenlang gar keine WLAN-Verbindung mehr aufbaute und stattdessen auf Mobilfunk auswich. Ein genauerer Blick ins Protokoll zeigte ein wildes Hin- und Herspringen zwischen beiden Access Points im Sekundentakt, teilweise bei Signalwerten, die praktisch unbrauchbar waren — und auffällig oft auf dem neuesten, kurzreichweitigsten der drei verfügbaren Frequenzbänder. Nach dessen Deaktivierung für dieses Netz schien sich die Lage zu beruhigen, bis nach ein paar stabilen Minuten das gleiche Verhalten erneut auftrat.

An diesem Punkt muss ich ehrlich sein: Die endgültige Ursache habe ich nicht mit letzter Sicherheit isoliert. Ich habe im Laufe des Abends mehrere Stellschrauben gleichzeitig bewegt — Roaming-Standards, Mindest-Signalstärke, Frequenzbänder, eine Sicherheitsfunktion des DHCP-Servers, dazwischen auch einen Neustart der Geräte — und irgendeine Kombination davon hat die Situation am Ende beruhigt. Welche einzelne Änderung tatsächlich den Ausschlag gegeben hat, oder ob es schlicht der Neustart selbst war, der einen verklemmten internen Zustand aufgeräumt hat, kann ich im Nachhinein nicht mehr sauber auseinanderhalten. Eine unbefriedigende Antwort, aber eine ehrlichere als eine nachträglich konstruierte Erklärung.

Lessons Learned

Bei mehrteiligen Docker-Anwendungen mit externer Datenbank lohnt sich ein Blick in die Datenbank-Dokumentation des jeweiligen Datenbank-Systems, nicht nur in die Anwendungs-Dokumentation — Konzepte wie datenbankgebundene Benutzeridentitäten sind nicht immer offensichtlich, wenn man sie zum ersten Mal trifft. Und Umgebungsvariablen tun nicht immer das, was ihr Name suggeriert: Im Zweifel hilft ein Blick in offene Probleme und Diskussionen im jeweiligen Projekt-Repository mehr als die offizielle Dokumentation allein.

Wer ein bestehendes Netz nachträglich isoliert, sollte vorher genau prüfen, welche stillen Abhängigkeiten darin existieren. Geräte, die unauffällig im Hintergrund Daten an interne Dienste senden, fallen oft erst Tage später auf — und manchmal eben nur, weil man aus einem ganz anderen Grund zufällig ins Protokoll schaut. Eine Überwachung der eigenen Protokollierungs-Kette selbst gehört inzwischen ganz oben auf meine Liste offener Punkte, denn ausgerechnet in den Tagen nach einem Infrastruktur-Vorfall hätte ich diese Protokolle am dringendsten gebraucht.

Und schliesslich: Nicht jedes Problem lässt sich in einem Abend sauber isolieren, gerade wenn mehrere Systeme gleichzeitig involviert sind — Funktechnik, Switch-Konfiguration, DHCP, Firewall. Manchmal muss man akzeptieren, dass eine Kombination aus mehreren Änderungen und schlichtem Ausprobieren zu einem funktionierenden Zustand geführt hat, auch ohne dass am Ende eine einzelne, elegante Erklärung dabei herauskommt.

Fazit

Der Umstieg von der Fritzbox auf einen selbst gehosteten Controller war deutlich aufwendiger, als ich erwartet hatte — aber ich bin bisher sehr zufrieden mit dem Ergebnis. Die Möglichkeit, WLAN-Netze sauber auf bestehende VLANs zu mappen, statt sie als separate Insellösung zu betreiben, war genau der Schritt, der gefehlt hat, und der Mesh-Verbund zwischen den beiden Access Points funktioniert im Alltag unkompliziert. Die Datenbank-Authentifizierung und das stille Firewall-Problem waren lehrreiche Herausforderungen, die sich mit etwas Recherche und Geduld lösen liessen. Das Roaming-Verhalten bleibt vorerst ein Punkt, den ich weiter beobachte — nicht jedes Problem lässt sich an einem Abend restlos verstehen, auch wenn der aktuelle Zustand für den Moment funktioniert.

Schreibe einen Kommentar