Homelab-Umbau Teil 9: Die Geräte, die kein 802.1X können

Worum es geht Link zu Überschrift

Am Ende von Teil 7 steht ein Rat: Für Geräte, die kein 802.1X können, plant man eine zweite SSID mit klassischem Schlüssel ein, die fest in einem Untrusted-VLAN endet.

Ich habe diesen Rat nicht befolgt.

Der Grund ist der, aus dem ich in Teil 1 gegen eine SSID pro VLAN argumentiert habe: Sobald die Zuordnung an der SSID klebt, brauche ich für jede weitere Vertrauensstufe eine weitere SSID. Mein AV-Receiver und meine Spielekonsole können beide kein 802.1X, sollen aber ausdrücklich nicht ins selbe Netz. Mit einer fest verdrahteten SSID hätte ich genau das Sammel-VLAN gebaut, das ich loswerden wollte.

Die Alternative heisst MAC Authentication Bypass, kurz MAB. Der Access Point fragt beim RADIUS-Server nach, bevor überhaupt eine Anmeldung stattfindet — mit der MAC-Adresse als einzigem Ausweis. Der Server antwortet wie bei 802.1X mit einer VLAN-Zuweisung. Die SSID bleibt eine, die Entscheidung bleibt zentral.

Eigentlich hatte ich für diesen Teil die Firewall-Regeln angekündigt. Die kommen im nächsten. Erst müssen die Geräte in ihren Netzen sein, sonst beschreibe ich Regeln für VLANs, in denen niemand steht.

Was MAB ist und was nicht Link zu Überschrift

Man sollte sich über eins im Klaren sein: MAC-Adressen sind kein Sicherheitsmerkmal. Sie stehen unverschlüsselt in jedem Frame, jedes Betriebssystem kann sie in einer Zeile ändern. Wer meine SSID sieht, kann die MAC meines Receivers mitlesen und sich als er ausgeben.

Das ist bekannt und ändert nichts am Nutzen. MAB ist keine Authentifizierung, es ist eine Sortierung. Die Frage lautet nicht „darf dieses Gerät ins Netz", sondern „in welches Netz gehört dieses Gerät". Der Schutz vor Fremden liegt weiterhin beim WPA2-Schlüssel der SSID.

Daraus folgt die Design-Entscheidung, die den ganzen Aufbau prägt: Eine unbekannte MAC wird nicht abgewiesen, sondern nach GUEST geschoben. Rein, aber isoliert, statt Reject. Wer meinen Schlüssel hat, kommt ins Gästenetz mit Internet und ohne Zugriff nach innen. Das ist die Vertrauensstufe, die ein geteiltes Passwort tatsächlich rechtfertigt.

Ablaufdiagramm: Ein Gerät verbindet sich mit der SSID oli, der Access Point fragt FreeRADIUS mit der MAC-Adresse. Ist ein Eintrag vorhanden, kommt das Gerät in sein eigenes VLAN 46 oder 47, sonst in VLAN 50 GUEST.

Die MAC ist der einzige Ausweis — und wer keinen hat, wird nicht abgewiesen, sondern einsortiert.

Damit habe ich zwei SSIDs statt einer, und das ist der Kompromiss:

SSID Verschlüsselung Mechanismus Geräte
oli-ent WPA2-Enterprise 802.1X, PEAP Handys, Drucker, TV, Licht
oli WPA2-PSK MAB über RADIUS Receiver, Konsole, Gäste

Zwei sind unter der Faustregel aus Teil 1 unproblematisch. Und der entscheidende Unterschied zum Rat, den ich damals gegeben habe: Auch auf der zweiten SSID entscheidet der RADIUS-Server, nicht die Konfiguration des Access Points. Der bleibt dumm, die Gerätepflege liegt an einer Stelle.

Was schiefging Link zu Überschrift

Fünf Probleme. Bei zweien stand in der Literatur schlicht etwas anderes, als mein System tut.

Der Marker, den es nicht gibt Link zu Überschrift

Jede Anleitung zu MAB mit FreeRADIUS beginnt gleich: MAC-Auth-Anfragen erkennt man daran, dass der Access Point Service-Type = Call-Check mitschickt. Damit trennt man sie von echten 802.1X-Sitzungen, die auf derselben Infrastruktur laufen.

Meine Bedingung lief auf:

ERROR: Failed retrieving values required to evaluate condition

Das Attribut existiert in der Anfrage nicht. Mein hostapd sendet es nicht, der MAB-Zweig wurde nie betreten.

Was stattdessen drinsteht, sieht man erst, wenn man den Server im Vordergrund laufen lässt und eine echte Anfrage anschaut:

User-Name          = "bc9ebb24a843"
User-Password      = "bc9ebb24a843"
Called-Station-Id  = "7A-DA-11-69-12-17:oli"
Calling-Station-Id = "BC-9E-BB-24-A8-43"
NAS-Port-Type      = Wireless-802.11
Connect-Info       = "CONNECT 11Mbps 802.11b"

Zwei brauchbare Merkmale: Es gibt keine EAP-Message, und die Called-Station-Id endet auf den SSID-Namen. Beides zusammen ist ein zuverlässiger Ersatz:

if (!&EAP-Message && &Called-Station-Id =~ /:oli$/) {
    update control {
        &Auth-Type := Accept
    }
}

Das $ ist zwingend. Ohne Anker matcht :oli-ent mit, und der MAB-Zweig greift dann auch in die laufenden Enterprise-Sitzungen. Meine beiden SSIDs unterscheiden sich um ein Suffix — eine unglückliche Namenswahl, die mir vorher nie aufgefallen ist.

Die Lehre: Bevor man auf ein Attribut prüft, schaut man nach, ob das eigene Gerät es sendet. Ein radiusd -X und eine einzige echte Anfrage hätten mir eine Stunde gespart.

Zwei MAC-Formate in derselben Anfrage Link zu Überschrift

Im Paket oben stehen zwei Schreibweisen derselben Adresse:

User-Name          = "bc9ebb24a843"       <- klein, ohne Trenner
Calling-Station-Id = "BC-9E-BB-24-A8-43"  <- gross, mit Bindestrichen

Ich hatte meine Geräteeinträge in der Bindestrich-Form angelegt, weil das die Form ist, die man in Anleitungen sieht. Gematcht hätte davon nichts. Die Einträge werden gegen den User-Name geprüft, und der ist kleingeschrieben ohne Trenner:

000678c7a0a7                     # Receiver
    Tunnel-Type = VLAN,
    Tunnel-Medium-Type = IEEE-802,
    Tunnel-Private-Group-Id = 47

bc9ebb24a843                     # Konsole
    Tunnel-Type = VLAN,
    Tunnel-Medium-Type = IEEE-802,
    Tunnel-Private-Group-Id = 46

Die Datei ist übrigens mods-config/files/authorize, nicht users — der Fehler aus Teil 7, der mich damals am längsten aufgehalten hat.

Welches Format der eigene Access Point schickt, ist nicht standardisiert. Andere Implementierungen senden Doppelpunkte oder Grossschreibung. Das rät man nicht, das liest man einmal im Debug-Log ab.

Der Weg zu macaddr_acl=2 Link zu Überschrift

Auf der OpenWrt-Seite braucht es einen Schalter: hostapd soll die MAC-Freigabe beim RADIUS-Server erfragen. Die Option heisst macaddr_acl=2. Bis sie in der richtigen Datei stand, habe ich zwei Wege ausprobiert, die beide nicht funktionieren.

option macfilter 'radius' klingt nach der offensichtlichen Lösung und steht so in mehreren Anleitungen. In meinem Build kennt der zuständige case-Block nur allow und deny. Alles andere fällt stillschweigend in den Auffangzweig — keine Fehlermeldung, keine Wirkung.

list hostapd_options 'macaddr_acl=2' ist der übliche Notausgang, wenn eine Option in UCI fehlt. Er funktioniert hier ebenfalls nicht, und der Grund ist interessanter: Netifd hängt hostapd_options an den globalen Teil der Konfigurationsdatei an. macaddr_acl ist aber eine BSS-Option. Sie landet an der falschen Stelle und wird ignoriert. Dazu kommt, dass sich meine beiden SSIDs pro Funkeinheit dieselbe Datei teilen — die Option hätte also ohnehin nicht gezielt bei einer der beiden landen können.

Der Weg, der funktioniert, ist unspektakulär. Im Zweig für PSK-Verschlüsselung genügt es, dass überhaupt ein RADIUS-Server konfiguriert ist. Dann schreibt netifd macaddr_acl=2 von allein ins richtige BSS. Es reichen also die regulären Optionen:

uci set wireless.default_radio0.auth_server='10.0.1.138'
uci set wireless.default_radio0.auth_port='1812'
uci set wireless.default_radio0.auth_secret='<shared secret>'
uci set wireless.default_radio0.dynamic_vlan='1'

Kein Image-Neubau, keine Sonderkonfiguration.

Nachprüfen lässt sich das Ergebnis in der erzeugten Datei, und genau dort sollte man auch nachschauen:

grep -E "macaddr_acl|auth_server_addr|dynamic_vlan" /var/run/hostapd-phy0.*.conf

Die Lehre: Bei OpenWrt ist die UCI-Konfiguration nur die Eingabe. Was hostapd tatsächlich liest, steht in /var/run/. Wer nur die Eingabe prüft, sieht nicht, was hinten rauskommt — dieser Satz wird gegen Ende dieses Beitrags noch wichtig.

Der Fallback, der zu viel gefangen hat Link zu Überschrift

Am Ende der Gerätedatei steht der Auffangeintrag für unbekannte Adressen:

DEFAULT Auth-Type == Accept
    Tunnel-Type = VLAN,
    Tunnel-Medium-Type = IEEE-802,
    Tunnel-Private-Group-Id = 50

Die Bedingung Auth-Type == Accept soll ihn strikt auf den MAB-Zweig begrenzen. Nur dort wird dieser Wert gesetzt, also kann er in Enterprise-Sitzungen nicht greifen — so meine Annahme.

Das Debug-Log sagt etwas anderes. Der Eintrag matcht auch in laufenden oli-ent-Sitzungen und schreibt VLAN 50 in die Zwischenantwort. Dass mir das nie aufgefallen ist, liegt daran, dass die Attribute aus dem inneren PEAP-Tunnel die äusseren am Ende überschreiben. Das Ergebnis stimmt, der Weg dahin nicht.

Der Fehler wäre erst in dem Moment sichtbar geworden, in dem eine Enterprise-Sitzung einmal kein inneres VLAN liefert. Dann landet ein vertrauenswürdiges Gerät still im Gästenetz. Nichts stürzt ab, nichts meldet sich, es funktioniert nur alles ein bisschen anders als gedacht.

Behoben mit einer zweiten Bedingung, die unabhängig von der Tunnel-Reihenfolge greift:

DEFAULT Auth-Type == Accept, Called-Station-Id =~ ":oli$"

Die Lehre: Dass am Ende das Richtige rauskommt, heisst nicht, dass die Logik stimmt. Zwei Fehler, die sich gegenseitig aufheben, sind zwei Fehler.

Eine SSID, drei Radios, eine vergessen Link zu Überschrift

Und dann der Fehler, der mich am meisten gekostet hat, obwohl er in einer Zeile zu beheben war.

Mein Access Point hat drei Funkeinheiten. Eine SSID auf drei Radios sind in OpenWrt drei getrennte Konfigurationsabschnitte, und jede Option muss in allen dreien stehen.

Bei oli-ent fehlten einem Abschnitt die VLAN-Optionen. In der erzeugten Konfiguration für dieses Radio stand deshalb keine einzige VLAN-Zeile. Das Handy bekam auf dem einen Radio sein VLAN und auf dem anderen keins — und landete dann im nativen Netz. Also mitten zwischen meinen Servern.

Im Log ist der Unterschied eindeutig. Auf dem funktionierenden Radio:

RADIUS: VLAN ID 41
AP-STA-CONNECTED ... vlanid=41

Auf dem anderen fehlen beide Zeilen.

Was die Sache unangenehm macht: Welches Radio ein Gerät nimmt, entscheidet es selbst, und es wechselt im Betrieb. Der Fehler tritt also sporadisch auf und verschwindet beim Nachprüfen wieder. Ein Gerät, das mal richtig und mal falsch einsortiert wird, sieht nach einem Problem des Geräts aus.

Erschwerend kam dazu, dass meine Abschnitte nicht durchgehend gleich heissen. Bei der einen SSID sind es die Standardnamen, bei der anderen drei frei vergebene. Wer die Namen aus dem Kopf in eine Schleife tippt, erwischt genau den einen nicht. Deshalb:

uci show wireless | grep -E "\.device=|dynamic_vlan"

Die Liste kommt aus dem Befehl, nicht aus der Erinnerung.

Die Falle, die aus dem funktionierenden Fallback entsteht Link zu Überschrift

Bevor es weitergeht, ein Punkt, der wichtiger ist als alle fünf Fehler zusammen.

Solange unbekannte Geräte abgewiesen werden, ist ein Konfigurationsfehler laut: Das Gerät kommt nicht ins WLAN, man merkt es sofort. Mit einem funktionierenden Fallback verschwindet dieses Signal.

Merksatz: Mit funktionierendem Fallback gibt es kein hartes Scheitern mehr. Eine falsch eingetragene MAC landet still im Gästenetz statt abgewiesen zu werden — das Gerät funktioniert scheinbar und ist im falschen Netz. Beim Testen nicht auf „geht" prüfen, sondern auf die tatsächlich vergebene Adresse.

Das ist der Preis für „rein, aber isoliert". Ich halte ihn für gerechtfertigt, aber er verlangt eine andere Testdisziplin. Jede Prüfung endet bei mir seitdem in der Lease-Tabelle der Firewall, nicht beim Verbindungssymbol des Geräts.

Sechs Wochen später: die Konfiguration, die nichts tat Link zu Überschrift

Der Aufbau lief seit Wochen. Beim Zusammenschreiben dieses Beitrags bin ich über einen Widerspruch in meinen eigenen Notizen gestolpert und habe zum ersten Mal nachgesehen, wie die VLAN-Zuweisung eigentlich verkabelt wird.

Zwei Optionen sind dafür zuständig:

vlan_bridge=br-lan.
vlan_tagged_interface=br-lan

Die erste sagt hostapd, es soll pro VLAN eine Bridge namens br-lan.<id> anlegen. Die zweite sagt, es soll einen getaggten Uplink dazuhängen — der nach derselben Namenskonvention gebildet wird und deshalb genauso heisst.

Diagramm: Die Optionen vlan_bridge und vlan_tagged_interface erzeugen beide den Namen br-lan.47. Der Versuch scheitert mit File exists und Symbolic link loop. Die eigentliche Verkabelung macht das Hotplug-Skript, das das Interface direkt an br-lan hängt.

Zwei Optionen, ein Name — und ein Skript, das seit Wochen die eigentliche Arbeit macht.

Im Log sieht das so aus, bei jeder einzelnen Zuweisung:

VLAN: vlan_add: ADD_VLAN_CMD failed for br-lan: File exists
VLAN: br_addif: ioctl[...] failed for br_name=br-lan.47 if_name=br-lan.47: Symbolic link loop

Die Bridge existiert bereits, der Name ist vergeben, also scheitert das Uplink-Device. Und danach versucht hostapd, br-lan.47 in br-lan.47 zu hängen. Die beiden Optionen löschen sich über einen Namen gegenseitig aus.

Zurück blieben leere Bridges ohne einen einzigen Port. Der Verkehr lief die ganze Zeit anders herum: über das kleine Hotplug-Skript aus Teil 7, das jedes neu entstehende VLAN-Interface in die VLAN-fähige Bridge hängt und die passende Zuordnung einträgt. Sichtbar an einer einzigen Zeile:

ip link show dev phy0.0-ap0.47 | grep -o 'master [^ ]*'
# master br-lan

master br-lan, nicht master br-lan.47. Das Skript hat gewonnen, und zwar seit sechs Wochen.

Die Auflösung stand die ganze Zeit im Quelltext von netifd, ein paar Zeilen unter der Stelle, die die beiden Optionen schreibt: Es gibt einen dritten Schalter, vlan_no_bridge, der hostapd das Bridging ganz abgewöhnt. Und der wird automatisch gesetzt — aber nur, wenn vlan_bridge leer ist. Wer die beiden Optionen nie angefasst hätte, hätte den sauberen Modus geschenkt bekommen.

Die Korrektur waren drei Zeilen pro Abschnitt:

uci delete wireless.default_radio0.vlan_bridge
uci delete wireless.default_radio0.vlan_tagged_interface
uci set wireless.default_radio0.vlan_no_bridge='1'

Sechs Abschnitte, ein Reload, und danach: keine Fehlermeldung mehr, keine leeren Bridges, unverändert korrekte Zuweisung. Es funktioniert jetzt aus demselben Grund wie vorher, nur ohne den gescheiterten Versuch daneben.

Die Lehre: Eine Konfiguration, die aussieht, als würde sie arbeiten, und ein System, das trotzdem läuft — das ist die unangenehmste Kombination. Nichts zwingt einen zum Hinschauen. Ich habe das nur gefunden, weil ich beim Schreiben zwei eigene Notizen nebeneinandergelegt habe, die sich widersprachen.

Nachtrag zu Teil 7 Link zu Überschrift

Zwei Dinge stelle ich damit richtig:

Das Hotplug-Skript aus Teil 7 ist nicht überholt. Es ist der tragende Mechanismus der gesamten WLAN-Segmentierung. Es steht in keiner Konfigurationsdatei und wandert bei einem Neuaufbau des Access Points nicht von allein mit. Fehlt es, bekommt kein WLAN-Gerät mehr ein VLAN, und alle landen im nativen Netz.

Und der Fehler, der wie ein Client-Problem aussieht, ist mir an diesem Nachmittag ein zweites Mal begegnet:

IEEE 802.11: authenticated
IEEE 802.11: Could not set STA to kernel driver

Nach mehreren Neustarts des Funkdienstes verhakt sich der WLAN-Treiber. RADIUS antwortet korrekt, hostapd authentifiziert, und dann verweigert der Treiber die Station. In Teil 7 hat mich das einen ausgebauten Mediaplayer gekostet, weil ich den Fehler beim Gerät gesucht habe. Diesmal habe ich zuerst ins Log des Access Points geschaut und den Neustart gleich gemacht.

Praktische Konsequenz: Änderungen an der WLAN-Konfiguration sammeln und einmal laden. Jeder zusätzliche Reload ist ein Risiko, und das Symptom zeigt in die falsche Richtung.

Warum das für Handys nichts taugt Link zu Überschrift

Ein Punkt, an dem der ursprüngliche Plan gescheitert ist: Ich wollte alle Geräte per MAB einsortieren und mir 802.1X für den Rest sparen.

Das geht nicht, weil moderne Handys private WLAN-Adressen verwenden. Die MAC ist zufällig erzeugt, erkennbar an einem gesetzten Bit im ersten Byte, und sie rotiert. Ein Eintrag hält bis zur nächsten Rotation.

Dazu kommt etwas, das beim Debuggen für Verwirrung sorgt: Pro SSID gibt es eine eigene Adresse. Mein Handy steht zweimal in der Lease-Tabelle, mit völlig verschiedenen MACs und in zwei verschiedenen Netzen — auf der Enterprise-SSID im Handy-VLAN, auf der PSK-SSID im Gästenetz, weil die Adresse dort unbekannt ist. Beim ersten Hinsehen hält man das für zwei Geräte.

Beides ist bei mir übrigens gewollt und gleichzeitig der laufende Nachweis, dass der Fallback-Pfad tut, was er soll.

Handys gehören auf 802.1X. Dort ist die MAC egal, die Zuordnung läuft über den Benutzernamen im verschlüsselten Teil der Anmeldung.

Was aus dem Plan geworden ist Link zu Überschrift

In Teil 1 stand: eine 40er-Serie, ein VLAN pro Vertrauens-Situation, und als Beispiel dafür der Lichtcontroller und der Mediaplayer, die sich wegen der Ambientebeleuchtung ein VLAN teilen.

Gebaut habe ich das Gegenteil, und das ist die interessantere Erkenntnis dieses Abschnitts: Die beiden liegen heute in getrennten VLANs, mit einer einzelnen Regel für genau den einen Datenstrom, den sie brauchen.

Vergleich: Geplant war ein gemeinsames VLAN 41 für Mediaplayer und Lichtcontroller. Gebaut wurden zwei getrennte VLANs 42 und 43 mit einer einzelnen Regel für UDP 4048 dazwischen.

Das Gründungsbeispiel für „ein VLAN pro Vertrauens-Situation" ist als erstes daran gescheitert.

Das Muster hat sich verallgemeinert. Statt Gerätepaare zusammenzulegen, bekommt inzwischen jedes Gerät sein eigenes VLAN, und was miteinander reden muss, bekommt eine gezielte Regel. Die Nummer ist billig, das Interface auch. Was man sich spart, ist die Frage „darf dieses Gerät eigentlich mit dem anderen reden", die in einem gemeinsamen VLAN gar nicht mehr gestellt werden kann — dort geht der Verkehr über den Switch und kommt an der Firewall nie vorbei.

Der zweite Planungsfehler betraf das Auffangnetz. Vorgesehen war eine Quarantäne mit einer hohen Nummer, komplett isoliert. Geworden ist es das Gästenetz mit Internet und ohne Zugriff nach innen. Der Grund ist praktischer Natur: Ein komplett isoliertes Netz erzeugt bei jedem Gerät, das versehentlich darin landet, einen Fehler, der wie ein Netzwerkausfall aussieht. Ein Gästenetz erzeugt ein funktionierendes Gerät an der falschen Stelle. Beides will man merken — aber das zweite kann ich in der Lease-Tabelle sehen, das erste ruft mich am Telefon an.

Wo ich jetzt stehe Link zu Überschrift

Zwei SSIDs, zwei Mechanismen, eine Stelle für die Entscheidung. Der Receiver liegt in seinem VLAN, die Konsole in ihrem, das Handy je nach SSID in zweien, und alles, was ich nicht kenne, landet im Gästenetz.

Der Access Point weiss von alldem nichts. Er fragt, bekommt eine Nummer zurück und hängt das Gerät dorthin. Wenn ich ein Gerät umsortieren will, ändere ich eine Zeile auf dem RADIUS-Server.

Was noch offen ist: Die neuen VLANs sind Netze, aber noch keine Grenzen. Der Verkehr zwischen ihnen ist eingeschränkt, die Regeln dafür habe ich beim Bauen mitgeschrieben — aber sauber durchdacht und dokumentiert sind sie nicht. Das ist der nächste Teil, und diesmal wirklich.

Nächster Schritt Link zu Überschrift

Die Firewall-Regeln. Ein wiederkehrendes Muster aus vier Regeln pro Geräte-VLAN, warum die Reihenfolge alles entscheidet, warum ich bei Konsolen und Gästen bewusst keinen Portfilter setze — und warum eine Regel, die auf 80 und 443 einschränkt, bei einem Android-Gerät dazu führt, dass es freiwillig in ein anderes Netz zurückwechselt.


Teil einer Serie über den Umbau meines Heimnetzes. Stand: September 2026. Fragen, Korrekturen und eigene Erfahrungen gerne in die Kommentare.