user-icon Christoph Walcher
20. März 2020
timer-icon 5 Min.

MQTT meets Web - Anbindung von Webapps über MQTT

Da ein MQTT-Broker in vielen IoT-Projekten als zentrale Anlaufstelle für Zustandsinformationen sämtlicher Geräte gilt, möchten wir diesen verwenden um sämtliche Daten direkt in einer Webapp darzustellen, jedoch ohne, dass Sicherheitsaspekte unberücksichtigt bleiben.

Motivation

Das IoT-Messaging Protokoll MQTT erfreut sich in letzter Zeit immer mehr Beliebtheit, vor allem wenn es um die Anbindung und Vernetzung von IoT-Geräten geht. MQTT arbeitet dabei nach dem Publish/Subscribe-Prinzip. Dies bedeutet, dass Clients, welche sich für Nachrichten zu einem bestimmtem Thema interessieren, sich beim MQTT-Broker auf das jeweilige Topic registrieren (subscribe). Sollte daraufhin ein anderer Client eine Nachricht zu diesem Topic an den Broker senden (publish), leitet der Broker diese an die Clients weiter, welche sich zuvor auf das Topic registriert haben.

MQTT message flow

Icons: ©Fontawesome

Da ein MQTT-Broker in vielen IoT-Projekten als zentrale Anlaufstelle für Zustandsinformationen sämtlicher Geräte gilt, wäre es praktisch diese auch direkt aus dem Broker heraus in einer Webapp darstellen zu können. Jedoch darf dies auf keinen Fall ohne die notwendigen Schutzmechanismen geschehen.

Übersicht

Architektur

In unserem Fall benutzen wir HiveMQ als zentralen MQTT-Broker. An diesem ist zum einen ein Client verbunden, welcher ein Gerät simulieren soll und zum anderen die mittels Angular umgesetzte Webapp. Als Identityprovider/Authentifizierungsserver kommt zusätzlich noch Keycloak zum Einsatz. Keycloak stellt der Webapp, ein Token (JWT) zur Verfügung, falls die Authentifizierung des Benutzers erfolgreich war.  Daraufhin kann die Webapp, dieses Token dazu verwenden, sich gegenüber dem Broker zu authentifizieren. Dieser Vorgang läuft dabei nach dem OpenID-Connect Standard ab. In einem größeren System, kann dieser Identityprovider natürlich auch für weitere Dienste verwendet werden.

Das ganze Projekt haben wir auf GitHub veröffentlicht und schauen uns deshalb nur die wichtigsten Stellen an.

Keycloak

Beginnen wir mit Keycloak, unserem Identityprovider. Für dessen Einrichtung sind folgende Schritte nötig, um die Clients und Benutzer anzulegen: 

  1. Aufruf des Adminpanels, bei unserem Container: http://localhost:8083/auth/admin/ Benutzername: admin, Passwort: Pa55w0rd
  2. Anlegen des Clients für den Broker: Links im Menu: Clients -> Create -> Client ID: hivemq -> Save -> Access Type: confidential, Standard Flow Enabled: off -> Save -> Credentials -> Secret wird später benötigt.
  3. Anlegen des Clients für die Webapp: Links im Menu: Clients -> Create -> Client ID: frontend -> Save -> Implicit Flow Enabled: on, Standard Flow Enabled: off, -> Save
  4. Anlegen extra Benutzer: Links im Menü: Users -> Add User

HiveMQ

Wir haben uns für HiveMQ als MQTT-Broker entschieden, weil er in großen Anwendungen nahezu linear im Cluster skaliert. Außerdem bietet er die Enterprise-Security-Extension an, die uns dabei hilft, Clients über ihre mit übermittelten Token bzw. OpenID-Connect zu authentifizieren.

Den Broker müssen wir so einrichten, dass er auch auf MQTT-Nachrichten über WebSockets lauscht. Dafür haben wir in die config.xml folgenden Block eingefügt:


Die Authentifikation über OpenID-Connect/JWTs übernimmt bei HiveMQ die Enterprise-Security-Extension. Sie musste noch dementsprechend konfiguriert werden, um die Tokens über unseren Keycloak zu authentifizieren. Über den JWKS-Endpoint erhält der Broker das Zertifikat um das Token lokal zu überprüfen. Der Introspection-Endpoint dient als zusätzliche Sicherheitsstufe. An diesen Endpoint wird beim Verbindungsaufbau eines MQTT-Client das jeweilige Token gesendet, um es zusätzlich zu überprüfen. Dieser Schritt ist jedoch nur optional.


HiveMQ bietet schon direkt einfache Dockerimages an, da wir dem Image aber noch eine eigene Konfiguration und die Enterprise-Security-Extension anfügen möchten, mussten wir das Basisimage erweitern:

Webapp

In der Angular-Webapp läuft letztendlich alles zusammen:

1. Die Authentifizierung des Benutzers: Dafür nutzen wir das freie angular-oauth2-oidc  Modul welches einen Großteil des Login-Prozesses übernimmt und nach dem sogenannten Implicit Flow abläuft. Dieser funktioniert grob beschrieben wie folgt: Wenn der Benutzer auf die Webapp zugreift und noch nicht eingeloggt ist, wird er automatisch auf die Loginseite des Keycloaks umgeleitet. Nachdem er sich dort erfolgreich authentifiziert hat, wird er zurück auf die Webapp geleitet. Die Token werden dabei in der URL transportiert, so dass sie die Webapp auswerten und benutzen kann. Dieser Schritt lässt sich noch zusätzlich mittels PKCE absichern (Blogpost).

2. Die Verbindung zu unserem MQTT-Broker: Hierfür kommt ngx-mqtt zum Zuge, dieses Module „umhüllt“ MQTT.js, so dass die für Angular typischen Observables genutzt werden können. Die Anmeldedaten der MQTT-Verbindung werden dabei von den zuvor empfangenen Token übernommen.


Wenn nun alle Dienste gestartet werden, sieht alles zusammen nachdem Login bei uns wie folgt aus:

Demo

Webapp in zwei verschiedenen, über MQTT synchronisierten Browsern und zusätzlich ein Mockdevice welches die Änderungen auf der Konsole anzeigt

Fazit

Mit den von uns verwendeten Diensten, lies sich innerhalb von kurzer Zeit und mit wenig Aufwand, eine Webapp an einen MQTT-Broker anbinden. Dies ermöglicht uns eine schnelle Umsetzung vieler IoT Use Cases, aber auch die einfache Synchronisierung mehrere Browser von unterschiedlichen Benutzern, ohne auf IT-Sicherheit verzichten zu müssen.

Zusätzlich sollte noch, um die Integrität und Vertraulichkeit der Nachrichten sicherzustellen, auf jeden Fall TLS als Transportverschlüsselung aktiviert werden, dies ist direkt in HiveMQ, aber auch mittels eines Ingress-Gateways/Proxy möglich.

Je nach Fall muss auch die Zugriffsberechtigungen für einzelne Topics auf dem Broker überprüft werden. Die Enterprise-Security-Extension bietet die Möglichkeit, dies datenbankgestützt zu prüfen.

CTA_IOT

Bildnachweise: © Fontawesome

Artikel kommentieren