Das MQTT-Protokoll eignet sich gut dazu, Serverdienste zu überwachen. Wenn die Übertragung aber über das Internet erfolgt, sollten die Informationen verschlüsselt werden. Im Folgenden werde ich die diversen Methoden dazu vorstellen.

Mosquitto installieren und Authentifizierung einrichten

Ich gehe von einer Debian/Ubuntu-Installation aus. Auf anderen Systemen sollte die Installation ähnlich verlaufen, auch wenn die Konfigurations-Dateien zum Teil an anderen Orten liegen können. Die Installation von Mosquitto und den Clients ist einfach:

Mosquitto beherrscht eine Reihe von Sicherungsmöglichkeiten, die auch in der Dokumentation recht ausführlich beschrieben sind. Neben der Verschlüsselung der Übertragung kann man Kanäle auch durch eine Authentifizierung absichern. Am einfachsten ist das über eine Passwort-Datei möglich:

Der obige Befehl erstellt eine Datei /etc/mosquitto/passwords mit einem Benutzer mqtt. Das Passwort wird entsprechend abgefragt. Zum Testen kann man z.B. „12345“ eingeben.

Was darf der Benutzer mqtt? Das beschreibt eine weitere Datei, in der die Zugangsberechtigungen gespeichert sind:

Die Datei /etc/mosquitto/access.acl enthält zwei Einträge. Zunächst wird der Benutzer definiert, für den die nachfolgenden Berechtigungen gelten. Die Zeile mit Pattern bestimmt das Muster, das erlaubt ist. In diesem Fall darf der Benutzer mqtt in allen Pfaden (#) sowohl lesen als auch schreiben. In echten Szenarios bietet es sich natürlich an, dass Clients entweder lesen oder schreiben dürfen, bzw. dass es überhaupt mehrere Nutzer gibt.

Danach erweitern wir die Konfiguration von Mosquitto, damit das Programm weiß, wo die Passwörter liegen:

Hier werden zum einen die beiden oben erstellten Dateien eingebunden und zum anderen der anonyme Zugriff auf den Server gesperrt. Danach laden wir den Server neu und testen die Einstellungen:

Die Authentifizierung bringt an sich wenig zusätzliche Sicherheit, da die Passwörter im Klartext übertragen werden. Sinnvoll ist es daher, den Übertragungskanal zu verschlüsseln.

TLS-PSK-Verschlüsselung

Eine einfache Verschlüsselung kann man mit dem sogenannten „Preshared Key“-Verfahren erreichen. Sowohl Client als auch Server müssen dazu im Grunde ein gemeinsames Passwort kennen. Das Verfahren ist daher nicht ganz so sicher wie das unten vorgestellte TLS/SSL-Verfahren mit öffentlichen und privaten Schlüsseln. Außerdem wird dieses Verfahren von den meisten Programmbibliotheken (Paho, mqtt für Nodejs u.ä.) nicht unterstützt.

Die Authentifizierung oben lassen wir so, wie sie ist. Wir erstellen lediglich eine sichere Verbindung:

Der psk-Hint ist frei wählbar und muss dem Client auch bekannt sein, damit das Verfahren funktioniert. Die Datei /etc/mosquitto/access.acl enthält die Schlüssel aller Clients:

Pro Zeile wird also ein Benutzername und ein hexadezimaler String angegeben. Der Benutzername muss nicht zwingend der selbe wie bei der Authentifizierung oben sein.

Die Kommunikation kann nun verschlüsselt erfolgen:

TLS/SSL-Verschlüsselung

Diese Verschlüsselung funktioniert im Grunde wie oben, nur mit öffentlichem und privatem Schlüssel. Einfach ist die Handhabung, wenn man ein selbst unterzeichnetes Zertifikat erstellt. Owntracks stellt dazu ein eigenes Skript zur Verfügung:

Die Erstellung eines eigenen Zertifikats ist auch in der Dokumentation grob beschrieben: http://mosquitto.org/man/mosquitto-tls-7.html.

Nun überschreiben wir die oben erstellte Datei mit den neuen Einstellungen:

Die Clients müssen nun lediglich die CA-Datei kennen. Dieser öffentliche Schlüssel ist unkritisch und kann offen an die Clients verteilt werden.

Zum Testen kann man folgende Skripte ausführen:

Vergleich der Übertragungsmethoden

Wie schneiden die Übertragungsmethoden ab? Um das zu vergleichen, habe ich die Größe der TCP-Streams miteinander verglichen, wie folgende Tabelle zeigt:

in Bytes Quality 0 Quality 1 Quality 2
ohne Verschlüsselung 72 78 86
TLS-PSK 1172 1224 1331
TLS/SSL 3742 3777 3843

Zu den Größen des TCP-Inhalts kommen natürlich noch der Protokoll-Overhead von TCP/IP, wobei das nur bei der Übertragung ohne Verschlüsselung eine relevante Größe ist, da die Header in dem Fall größer sind als die übertragene Datenmenge (v.a. dank TCP-Handshake). Dennoch wird deutlich, dass die verschlüsselten Übertragungen einen deutlichen Overhead haben. Dieser kommt durch das TLS-Verfahren zustande, das ebenfalls entsprechende Handshakes und Schlüsselaustausch benötigt. Da der Schlüssel in der Regel größer ist als der übertragene Inhalt (im Fall von TLS/SSL z.B. 2048 Bytes), ist die übertragene Datenmenge pro Publish recht hoch. Weniger stark steigt die Menge der übertragenen Daten durch einen erhöhten QoS, wenn man von zusätzlich verschickten IP-Paketen einmal absieht.

Das macht die Verfahren für die Überwachung von Serverdiensten etwas problematisch. Ein Rechenbeispiel: Ein Dienst meldet jede Minute den aktuellen Stand an einen zentralen Server, also 1440 Mal pro Tag oder etwa 43500 Mal im Monat. Ohne Verschlüsselung und in MQTT-Qualität 0 liegt die Menge der übertragenen Daten im Monat dann bei etwa 3 MB. Mit Netzwerk-Overhead dürfte die Menge dann real bei etwa 9 MB liegen, dennoch ist dies nicht besonders viel.

Anders schaut dies bei der verschlüsselten Übertragung aus. Bei TLS-PSK steigt die Menge der Übertragenen Daten pro Monat auf etwa 48 MB an, bei TLS/SSL liegt er sogar bei 155 MB (ohne Protokolloverhead)! Will man 10 Dienste überwachen kommen also satte 1,5 GB Übertragungsvolumen pro Monat zusammen, eine Menge, die durchaus relavant sein kann.

Damit lohnt sich die verschlüsselte Übertragung eher für Dienste, die entweder relativ selten Updates schicken oder gleich sowieso so viele, dass der Unterschied zur unverschlüsselten Übertragung irrelevant wird.

Alternative: SSH-Tunnel

Will man auf Sicherheit trotzdem nicht verzichten, bietet sich ein SSH-Tunnel an (oder alternativ eine VPN-Verbindung, auf die ich hier nicht eingehe). Dabei wird ein verschlüsselter Kanal zwischen zwei Maschinen aufgebaut. Weder Broker noch MQTT-Dienste müssen eine Verschlüsselung unterstützen, für Publisher und Subscriber scheint es so, als liefe der Broker auf dem lokalen System. Der SSH-Tunnel besitzt ebenfalls einen gewissen Overhead: Am Anfang werden Schlüssel ausgetauscht u.ä. Die Übertragung der Daten selbst ist jedoch deutlich leichtgewichtiger als bei den TLS-Übertragungen, die ja pro Übertragung die Verschlüsselungsinformationen austauschen müssen. Im Test kam der oben beispielhaft verwendete Publish auf eine Datenmenge von 636 Bytes. Das liegt vor allem daran, fass SSH ein TCP over TCP-Tunnelprotokoll ist und der im Tunnel verschlüsselte TCP-Verkehr zum Übertragungsvolumen angerechnet werden muss. Die Datenmenge ist damit immer noch hoch, aber deutlich geringer als bei den TLS-Verfahren. Rechnerisch kommt man pro Monat und Dienst dann auf eine Datenmenge von 26 MB + Protokolloverhead – akzeptabel.

Ach ja, um SSH-Tunneling zu testen, kann man auf der lokalen Maschine folgendes eingeben:

22 gibt dabei die Portnummer des SSH-Dienstes an (ändern, falls man einen anderen Port verwendet), mein-server.de muss natürlich der Name oder die IP einer entfernten Maschine sein. Nun kann man die mosquitto_sub und -pub-Befehle auf der lokalen Maschine ausführen als würde auf ihr ein lokaler Broker laufen.

Nachteil des Tunnels ist dabei, dass er eigens erstellt und überwacht werden muss. Das lässt sich glücklicherweise einigermaßen einfach automatisieren, wenn man autossh verwendet. Zur Einrichtung von autossh gibt es im Netz einige gute Anleitungen.