Azure IoT Operations MQTT Broker von außen erreichbar machen - LoadBalancer, TLS und NSG Schritt für Schritt
Einleitung
Du hast Azure IoT Operations auf einem Azure Arc-enabled Kubernetes Cluster in Betrieb und möchtest den eingebauten MQTT Broker auch von außen - also über das Internet - erreichen? Dann bist du hier richtig.
Standardmäßig ist der MQTT Broker in Azure IoT Operations nur innerhalb des Kubernetes-Clusters zugänglich. Das ist bewusst so gewählt: Sicherheit by Default. Wenn du aber MQTT-Clients aus dem Internet anbinden möchtest - z. B. ein MQTTX-Client auf deinem Laptop, externe IoT-Geräte oder ein Backend-System in einer anderen Cloud - musst du den Broker aktiv nach außen öffnen.
In diesem Post zeige ich dir, wie du das Schritt für Schritt umsetzt:
- Was du über den Default-BrokerListener wissen musst
- Neuen LoadBalancer-Listener in Azure IoT Operations anlegen
- Kubernetes: dem Service eine öffentliche IP zuweisen (ServiceLB / MetalLB)
- Azure-seitig: öffentliche IP und Load Balancer konfigurieren
- Netzwerksicherheitsgruppe (NSG) anpassen
- TLS konfigurieren (automatisch mit cert-manager)
- Verbindung mit MQTTX testen
- Debugging-Checkliste wenn es nicht klappt
Was du wissen musst: Der Default BrokerListener
Wenn du Azure IoT Operations installierst, wird automatisch ein Default BrokerListener miterstellt. Dieser Listener ist wichtig - er wird von den internen AIO-Komponenten verwendet und darf deshalb nicht verändert werden.
Der Default-Listener hat folgende Eigenschaften:
- Service-Typ:
ClusterIP(nicht von außen erreichbar) - Port:
18883 - TLS: aktiviert (automatisch verwaltetes Zertifikat)
- Authentifizierung: Kubernetes Service Account Tokens (K8S-SAT)
⚠️ Wichtig: Ändere niemals den Default-Listener! Dadurch wird die interne Kommunikation von IoT Operations unterbrochen. Erstelle stattdessen immer einen neuen Listener für externe Verbindungen.
Der MQTT Broker unterstützt maximal einen Listener pro Service-Typ. Da der Default-Listener ClusterIP verwendet, kannst du problemlos einen neuen LoadBalancer-Listener erstellen.
Schritt 1: Neuen LoadBalancer-Listener erstellen
Der erste Schritt ist die Konfiguration auf der Kubernetes- bzw. AIO-Seite. Wir erstellen einen neuen BrokerListener mit dem Service-Typ LoadBalancer.
Option A: Über das Azure Portal
- Öffne das Azure Portal und navigiere zu deiner IoT Operations Instanz
- Wähle unter Components den Eintrag MQTT Broker
- Wähle MQTT broker listener for LoadBalancer → Create
Gib folgende Einstellungen ein:
| Feld | Wert |
|---|---|
| Name | mqtt-external |
| Service name | leer lassen (wird automatisch aus dem Namen abgeleitet) |
| Service type | LoadBalancer (bereits vorausgewählt) |
Füge anschließend einen Port hinzu:
| Feld | Wert |
|---|---|
| Port | 8883 |
| Authentication | default (oder eigene Policy) |
| Authorization | None |
| Protocol | MQTT |
| TLS | Hinzufügen (automatisch, Issuer: azure-iot-operations-aio-certificate-issuer) |
Klicke auf Create listener.
Option B: Als Kubernetes-Manifest (YAML)
apiVersion: mqttbroker.iotoperations.azure.com/v1
kind: BrokerListener
metadata:
name: mqtt-external
namespace: azure-iot-operations
spec:
brokerRef: default
serviceType: LoadBalancer
ports:
- port: 8883
authenticationRef: default
tls:
mode: Automatic
certManagerCertificateSpec:
issuerRef:
name: azure-iot-operations-aio-certificate-issuer
kind: ClusterIssuer
group: cert-manager.io
Anwenden mit:
kubectl apply -f mqtt-external-listener.yaml
External IP abfragen
Nach der Erstellung weist Kubernetes dem Service automatisch eine externe IP zu (sofern der Cluster LoadBalancer-IPs bereitstellen kann). Prüfe den Status:
kubectl get service mqtt-external -n azure-iot-operations
Die Ausgabe sollte in etwa so aussehen:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mqtt-external LoadBalancer 10.x.x.x 20.x.x.x 8883:31234/TCP 2m
Die Spalte EXTERNAL-IP zeigt die öffentliche IP-Adresse, über die der MQTT Broker später erreichbar sein wird.
💡 Hinweis: Wenn die
EXTERNAL-IPim Status<pending>steckt, liegt das meistens daran, dass der Kubernetes-Cluster selbst noch nicht weiß, welche IP er dem Service zuweisen soll. Das ist der häufigste Fallstrick bei Arc-enabled Clustern auf Ubuntu-VMs - und genau darum geht es in Schritt 2.
Schritt 2: Kubernetes-Cluster für LoadBalancer-Services konfigurieren
Das ist der Schritt, der im Draft ursprünglich fehlte - und der häufigste Grund, warum die EXTERNAL-IP auf <pending> stecken bleibt.
Wenn du einen Arc-enabled K3s-Cluster auf Ubuntu-VMs betreibst (kein verwalteter AKS), muss Kubernetes wissen, welche externe IP es einem LoadBalancer-Service zuweisen soll. Es gibt zwei Ansätze:
Option A: K3s ServiceLB mit node-external-ip (empfohlen für einfache Setups)
K3s bringt einen eigenen, eingebauten LoadBalancer-Controller mit namens ServiceLB (früher: Klipper LB). Dieser weist einem LoadBalancer-Service automatisch die externe IP des Nodes zu - sofern der Node eine externe IP kennt.
Schritt 1: Externe IP am Node setzen
Beim Start von K3s die öffentliche IP der VM als externe Node-IP mitgeben. Dafür die K3s-Konfigurationsdatei anpassen:
sudo nano /etc/rancher/k3s/config.yaml
Folgenden Eintrag hinzufügen (mit der tatsächlichen öffentlichen IP deiner VM):
node-external-ip: "20.x.x.x" # Öffentliche IP deiner Azure VM
K3s neu starten:
sudo systemctl restart k3s
Schritt 2: Verifizieren
kubectl get nodes -o wide
In der Spalte EXTERNAL-IP sollte jetzt die öffentliche IP erscheinen. Sobald der BrokerListener-Service als LoadBalancer erstellt wird, übernimmt ServiceLB diese IP automatisch als EXTERNAL-IP des Services.
kubectl get service mqtt-external -n azure-iot-operations
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mqtt-external LoadBalancer 10.x.x.x 20.x.x.x 8883:31234/TCP 1m
⚠️ Wichtig: ServiceLB öffnet den Port direkt per
hostPortauf allen Nodes mit der externen IP. Das bedeutet: Der Traffic landet am Node-Port, nicht an einem separaten Azure Load Balancer. Du brauchst in diesem Fall keinen manuellen Azure Load Balancer (Schritt 3 überspringen), aber die NSG-Regel für Port 8883 ist trotzdem notwendig (Schritt 4).
Option B: MetalLB für IP-Pools (empfohlen für Multi-Node und Produktionsumgebungen)
Für Setups mit mehreren Nodes oder wenn du mehrere LoadBalancer-Services auf verschiedenen IPs betreiben möchtest, ist MetalLB die sauberere Lösung. MetalLB ist ein populärer Open-Source LoadBalancer-Controller für Bare-Metal- und On-Premises-Kubernetes-Cluster.
Schritt 1: ServiceLB in K3s deaktivieren (MetalLB und ServiceLB vertragen sich nicht gleichzeitig)
# K3s-Config anpassen
sudo nano /etc/rancher/k3s/config.yaml
disable:
- servicelb
sudo systemctl restart k3s
Schritt 2: MetalLB installieren
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
Warten, bis alle MetalLB-Pods laufen:
kubectl wait --namespace metallb-system \
--for=condition=ready pod \
--selector=app=metallb \
--timeout=120s
Schritt 3: IP-Pool konfigurieren
Erstelle eine metallb-config.yaml mit der öffentlichen IP (oder einem IP-Bereich) deiner VM:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: mqtt-pool
namespace: metallb-system
spec:
addresses:
- 20.x.x.x/32 # Öffentliche IP deiner Azure VM (als /32 für eine einzelne IP)
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: mqtt-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- mqtt-pool
kubectl apply -f metallb-config.yaml
MetalLB weist dem LoadBalancer-Service nun automatisch die IP 20.x.x.x zu. Verifizieren:
kubectl get service mqtt-external -n azure-iot-operations
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mqtt-external LoadBalancer 10.x.x.x 20.x.x.x 8883:31234/TCP 1m
💡 Welche Option für welches Szenario?
Szenario Empfehlung Single-Node K3s auf einer Azure VM ServiceLB mit node-external-ip- einfach und ohne zusätzliche KomponentenMulti-Node K3s, mehrere externe Services MetalLB - flexibler, skalierbar AKS (verwalteter Cluster) Kein extra Schritt nötig - Azure CCM übernimmt das automatisch
Schritt 3: Azure-seitig - öffentliche IP und Load Balancer konfigurieren
💡 Wann brauchst du diesen Schritt? Bei AKS und wenn du einen dedizierten Azure Load Balancer vor den Cluster schalten möchtest (z. B. für Health Probes, mehrere Backend-Nodes oder erweiterte Routing-Regeln). Wenn du ServiceLB oder MetalLB mit direkter Node-IP verwendest (Schritt 2, Option A oder B), kannst du diesen Schritt überspringen - die NSG-Regel aus Schritt 4 benötigst du aber in jedem Fall.
Wenn du Azure Kubernetes Service (AKS) oder einen Arc-enabled Cluster auf Azure-VMs betreibst, kann Azure automatisch einen Load Balancer bereitstellen - das passiert oft schon durch die Erstellung des LoadBalancer-Service in Kubernetes. Für Arc-enabled Cluster auf eigener Infrastruktur (z. B. Ubuntu-VMs) musst du den Load Balancer manuell konfigurieren.
Öffentliche IP erstellen
az network public-ip create \
--resource-group <deine-resource-group> \
--name mqtt-broker-public-ip \
--sku Standard \
--allocation-method Static \
--location <deine-region>
Load Balancer erstellen
az network lb create \
--resource-group <deine-resource-group> \
--name mqtt-broker-lb \
--sku Standard \
--frontend-ip-name mqtt-frontend \
--public-ip-address mqtt-broker-public-ip
Backend Pool anlegen
Der Backend Pool bildet die Kubernetes Nodes ab, auf denen der MQTT Broker läuft:
az network lb address-pool create \
--resource-group <deine-resource-group> \
--lb-name mqtt-broker-lb \
--name mqtt-backend-pool
Health Probe erstellen
Der Load Balancer benötigt eine Health Probe, um den Zustand der Nodes zu überwachen:
az network lb probe create \
--resource-group <deine-resource-group> \
--lb-name mqtt-broker-lb \
--name mqtt-probe \
--protocol Tcp \
--port 8883
Load Balancing Rule erstellen
Die Regel leitet eingehenden Traffic auf Port 8883 an den Backend Pool weiter:
az network lb rule create \
--resource-group <deine-resource-group> \
--lb-name mqtt-broker-lb \
--name mqtt-rule \
--protocol Tcp \
--frontend-port 8883 \
--backend-port 8883 \
--frontend-ip-name mqtt-frontend \
--backend-pool-name mqtt-backend-pool \
--probe-name mqtt-probe
💡 Bei AKS: Wenn du AKS verwendest, übernimmt Kubernetes automatisch die Erstellung des Azure Load Balancers. Du musst lediglich den
LoadBalancer-Service in Kubernetes anlegen - AKS erledigt den Rest im Hintergrund. Die manuelle Konfiguration ist primär für Arc-enabled Cluster auf eigener Infrastruktur notwendig.
Schritt 3: Netzwerksicherheitsgruppe (NSG) anpassen
Ohne eine entsprechende NSG-Regel blockiert Azure den eingehenden Traffic auf Port 8883. Füge eine Inbound-Regel hinzu:
Über das Azure Portal
- Navigiere zur Netzwerksicherheitsgruppe deiner Cluster-VMs (oder des AKS-Clusters)
- Wähle Inbound security rules → Add
- Füge folgende Regel hinzu:
| Feld | Wert |
|---|---|
| Source | Any |
| Source port ranges | * |
| Destination | Any |
| Destination port ranges | 8883 |
| Protocol | TCP |
| Action | Allow |
| Priority | 310 |
| Name | Allow-MQTT-TLS-Inbound |
Über die Azure CLI
az network nsg rule create \
--resource-group <deine-resource-group> \
--nsg-name <nsg-name> \
--name Allow-MQTT-TLS-Inbound \
--protocol Tcp \
--direction Inbound \
--priority 310 \
--source-address-prefixes '*' \
--source-port-ranges '*' \
--destination-port-ranges 8883 \
--access Allow
🔐 Sicherheitshinweis: Für Produktionsumgebungen empfehle ich,
source-address-prefixesauf die IP-Adressen oder IP-Ranges einzugrenzen, von denen tatsächlich Verbindungen erwartet werden. Eine komplett offene Regel (*) ist für Tests in Ordnung, aber in der Produktion ein Sicherheitsrisiko.
Schritt 4: TLS-Zertifikat für externe Verbindungen einrichten
Dieser Schritt ist besonders wichtig und wird oft unterschätzt: Wenn MQTTX oder ein anderer Client sich mit TLS verbindet, muss der Client das Server-Zertifikat des Brokers vertrauen.
Default Issuer für Entwicklung und Testing
Azure IoT Operations kommt mit einem eingebauten Default-Issuer für TLS-Zertifikate (azure-iot-operations-aio-certificate-issuer). Dieser ist für Entwicklungs- und Testzwecke geeignet, verwendet aber ein selbst-signiertes CA-Zertifikat, dem externe Clients nicht automatisch vertrauen.
Du musst das Root-CA-Zertifikat aus dem Cluster exportieren und in MQTTX importieren:
kubectl get configmap azure-iot-operations-aio-ca-trust-bundle \
-n azure-iot-operations \
-o jsonpath='{.data.ca\.crt}' > ca.crt
Dieses ca.crt musst du später in MQTTX als CA Certificate importieren.
Für TLS mit externer IP: SAN-Zertifikat konfigurieren
Wenn du dich über eine öffentliche IP verbindest, muss diese IP als Subject Alternative Name (SAN) im Zertifikat enthalten sein. Sonst schlägt die TLS-Verifikation fehl.
Approach 1: Listener zuerst erstellen, dann SAN-Zertifikat
- Erstelle den BrokerListener-Service zunächst ohne Zertifikat
- Warte, bis die externe IP vergeben wurde:
kubectl get svc mqtt-external -n azure-iot-operations - Notiere die
EXTERNAL-IP(z. B.20.x.x.x) - Erstelle ein Zertifikat mit der externen IP als SAN (z. B. mit Step CLI oder cert-manager)
- Importiere das Zertifikat als Kubernetes Secret und verknüpfe es mit dem Listener
Approach 2: DNS-Name statt IP verwenden
Erstelle einen DNS-Eintrag für die öffentliche IP (z. B. in Azure DNS oder über einen anderen DNS-Provider) und verwende den Hostnamen im Zertifikat. Das ist die sauberere Lösung für Produktionsumgebungen.
Automatisches TLS-Zertifikat mit cert-manager und externer IP
Falls du das selbst-signierte CA verwenden möchtest, aber die externe IP als SAN im Zertifikat brauchst:
# issuer-ca.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: mqtt-external-issuer
namespace: azure-iot-operations
spec:
ca:
secretName: azure-iot-operations-aio-ca-secret
# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: mqtt-external-cert
namespace: azure-iot-operations
spec:
secretName: mqtt-external-tls
issuerRef:
name: azure-iot-operations-aio-certificate-issuer
kind: ClusterIssuer
ipAddresses:
- 20.x.x.x # Deine externe IP hier eintragen
dnsNames:
- mqtt.meinedomain.de # Optional: DNS-Name
duration: 2160h # 90 Tage
renewBefore: 360h # 15 Tage vor Ablauf erneuern
kubectl apply -f certificate.yaml
Verknüpfe dann das Secret mqtt-external-tls mit dem BrokerListener:
# Im BrokerListener:
spec:
ports:
- port: 8883
tls:
mode: Manual
manual:
secretRef: mqtt-external-tls
Schritt 5: Verbindung mit MQTTX testen
Jetzt kommt der Moment der Wahrheit. Öffne MQTTX und lege eine neue Verbindung an:
MQTTX Verbindungseinstellungen
| Feld | Wert |
|---|---|
| Name | AIO External |
| Client ID | mqttx-test-client |
| Host | mqtts:// + deine externe IP (z. B. 20.x.x.x) |
| Port | 8883 |
| SSL/TLS | ✅ aktiviert |
| SSL Secure | ✅ aktiviert |
| CA File | ca.crt (aus Schritt 4 exportiert) |
| Username/Password | je nach konfigurierter Authentifizierung |
⚠️ MQTT-Version: Stelle sicher, dass du MQTT 5.0 verwendest, wenn du mit Kubernetes Service Account Tokens (K8S-SAT) authentifizierst. Die Enhanced Authentication von MQTT 5.0 wird für K8S-SAT benötigt.
Klicke auf Connect. Wenn die Verbindung erfolgreich ist, siehst du in der Log-Ansicht von MQTTX Connected.
Debugging-Checkliste
Falls die Verbindung nicht klappt, gehe diese Checkliste durch:
✅ 1. External IP prüfen
kubectl get service mqtt-external -n azure-iot-operations
Ist eine EXTERNAL-IP vergeben (kein <pending>)?
✅ 2. Pod Status prüfen
kubectl get pods -n azure-iot-operations -l app=aio-broker
Sind alle Pods im Status Running?
✅ 3. NSG-Regeln verifizieren
Prüfe im Azure Portal, ob die Inbound-Regel für Port 8883 aktiv ist und keine höher priorisierte Regel den Traffic blockiert.
✅ 4. Connectivity-Test mit telnet oder nc
telnet <externe-ip> 8883
# oder
nc -zv <externe-ip> 8883
Kommt eine TCP-Verbindung zustande? Wenn nicht, liegt das Problem im Netzwerk (Load Balancer, NSG oder Firewall).
✅ 5. TLS-Zertifikat prüfen
openssl s_client -connect <externe-ip>:8883 -CAfile ca.crt
Was zeigt die Ausgabe? Siehst du Verify return code: 0 (ok) oder einen Zertifikatsfehler?
Häufige Fehler:
certificate verify failed: Die CA in MQTTX stimmt nicht mit dem Issuer des Server-Zertifikats überein →ca.crtneu exportierenIP address mismatch: Die externe IP fehlt als SAN im Zertifikat → Zertifikat neu erstellen (siehe Schritt 4)connection refused: NSG-Regel fehlt oder Load Balancing Rule ist falsch konfiguriert
✅ 6. Broker Logs prüfen
kubectl logs -l app=aio-broker -n azure-iot-operations --tail=50
Gibt es Fehlermeldungen im Broker selbst? Typische Meldungen:
Server certificate not found→ Das TLS-Secret fehlt oder ist falsch benanntAuthentication failed→ Falsche Credentials in MQTTX
✅ 7. BrokerListener Status prüfen
kubectl describe brokerlistener mqtt-external -n azure-iot-operations
Schau in die Status-Sektion. Dort siehst du, ob der Listener korrekt provisioniert wurde.
✅ 8. Health Manager Logs
kubectl logs -l app=health-manager -n azure-iot-operations --tail=50
Der Health Manager ist für das Certificate Management zuständig und gibt hier Hinweise auf Probleme mit TLS-Zertifikaten.
Sicherheitsempfehlungen für den Produktiveinsatz
Wenn du den MQTT Broker dauerhaft über das Internet erreichbar machen willst, beachte folgende Punkte:
Niemals ohne TLS!
Port 1883 (MQTT ohne TLS) sollte im Internet niemals geöffnet werden. Verwende immer Port 8883 mit TLS. Ohne Verschlüsselung:
- sind MQTT-Credentials im Klartext übertragbar
- können Angreifer Topics mitlesen und veröffentlichen
- besteht Risiko für DDoS-Angriffe auf den Broker
Starke Authentifizierung verwenden
Statt keine Authentifizierung zu konfigurieren, empfehle ich für externe Verbindungen:
- X.509 Client-Zertifikate: Jedes Gerät bekommt ein eigenes Zertifikat. Widerruf ist möglich.
- Username/Password via Custom Authentication: Wenn Zertifikate nicht praktikabel sind
NSG-Regeln einschränken
Schränke die source-address-prefixes auf bekannte IP-Adressen oder Ranges ein. Wenn möglich, nutze Azure Firewall oder ein Application Gateway als vorgelagerten Layer.
CA-Zertifikat für Produktion
Der Default-Issuer von Azure IoT Operations ist für Produktionsumgebungen nicht geeignet. Verwende ein Zertifikat einer vertrauenswürdigen CA (z. B. Let's Encrypt, DigiCert, oder eine eigene PKI).
Zusammenfassung
Folgende Schritte sind notwendig, um den Azure IoT Operations MQTT Broker über das Internet erreichbar zu machen:
| Schritt | Was | Wo |
|---|---|---|
| 1 | Neuen BrokerListener (Typ: LoadBalancer) erstellen | Azure Portal / kubectl |
| 2a | K3s ServiceLB mit node-external-ip oder MetalLB konfigurieren |
kubectl / K3s config |
| 2b | Azure Load Balancer einrichten (nur wenn kein ServiceLB/MetalLB) | Azure CLI / Portal |
| 3 | NSG-Inbound-Regel für Port 8883 hinzufügen | Azure CLI / Portal |
| 4 | TLS-Zertifikat mit externer IP als SAN erstellen | cert-manager / kubectl |
| 5 | CA-Zertifikat in MQTTX importieren und verbinden | MQTTX |
Das Wichtigste: Den Default-BrokerListener niemals anfassen. Immer einen separaten Listener für externe Verbindungen anlegen. Und TLS ist kein Optional-Feature - es ist die Grundvoraussetzung für sichere externe Verbindungen.
Weiterführende Links
- BrokerListener konfigurieren - Microsoft Docs
- MQTT Broker Verbindungen testen - Microsoft Docs
- MQTT Broker Authentifizierung konfigurieren - Microsoft Docs
- TLS mit automatischem Certificate Management - Microsoft Docs
- K3s Networking Services (ServiceLB) - K3s Docs
- MetalLB Installation - metallb.universe.tf
- Arc-enabled Kubernetes Cluster vorbereiten - Microsoft Docs
- MQTTX Client Download