Das Problem

Wenn man MQTT - "ein offenes Nachrichtenprotokoll für Machine-to-Machine-Kommunikation" (Zitat Wikipedia) - auf einem eigenen Server im Internet nutzen möchte, dann möchte man die Kommunikation natürlich absichern. Dazu gehört einerseits, Benutzernamen mit Kennwörtern zu verwenden, und andererseits SSL für den Schutz der Verbindung zu nutzen.

Die Idee

Mit Let's Encrypt gibt es mittlerweile eine kostenlose Möglichkeit, SSL-Zertifikate auf dem Server zu erstellen. Es würde sich daher anbieten, diese Zertifikate nicht nur für den Webserver, sondern auch für den MQTT Broker (also den MQTT-Server) zu verwenden.

Vorab zu tun

Nicht eingehen möchte ich an dieser Stelle auf die grundlegende Installation von Let's Encrypt und mosquitto, einen freien MQTT-Broker. Beides sollte - gerade auf Debian-basierten Linux-Versionen - über den Paketmanager der Distribution recht einfach zu installieren sein, und dazu gibt es auch bereits eine Menge Tutorials.

Ich gehe also mal davon aus, dass Ihr mosquitto grundsätzlich installiert und Let's Encrypt für die Domain(s) auf dem Server eingerichtet habt.

Konfiguration anpassen

Schritt 1: Kennwort-Datei für mosquitto anlegen

Mit dem Befehl mosquitto_passwd /etc/mosquitto/mqtt-users.txt my_username legt man zunächst einen Benutzer an, wobei man noch mal nach den Kennwort gefragt wird. Die Kennwörter werden verschlüsselt in der Datei hinterlegt, sodass man sich das gewählte Kennwort gut merken sollte.

Schritt 2: mosquitto für Benutzernamen konfigurieren

mosquitto empfiehlt, die eigenen Konfigurationen im Unterverzeichnis /etc/mosquitto/conf.d abzulegen und nicht die Haupt-Konfigurationsdatei zu verändern. Wir legen deshalb eine Datei user-restriction.conf im Unterverzeichnis and und fügen dort die folgenden Zeilen ein:

#
# access
#
allow_anonymous false
password_file /etc/mosquitto/mqtt-users.txt

Damit erzwingt man, dass sich nur MQTT Clients mit gültigen Benutzername-Kennwort-Kombinationen aus der o.g. Datei anmelden können.

Schritt 3: mosquitto SSL konfigurieren

Hierfür wird im Verzeichnis /etc/mosquitto/conf.d die Datei ssl.conf angelegt und folgende Zeilen eingetragen:

#
# security
#
cafile /etc/ssl/certs/DST_Root_CA_X3.pem
certfile /etc/letsencrypt/live/your_domain/fullchain.pem
keyfile /etc/letsencrypt/live/your_domain/privkey.pem

Statt your_domain muss dort natürlich die Domain eingetragen werden, die für Let's Encrypt angegeben wurde.

Nun sollte noch der Port geändert werden, auf dem mosquitto läuft. Dazu legt man die Datei base.conf im Konfigurationsunterverzeichnis an und trägt dort folgendes ein:

#
# base settings
#
port 8883

Danach startet man den mosquitto Dienst einmal komplett neu und wenn alles funktioniert hat, läuft er dann mit Benutzer-Authentifizierung und SSL.

Test der Verbindung

Mit MQTT.fx existiert eine schöne Java-basierte Desktop-Applikation zur Nutzung von MQTT. Dort legt man eine SSL-basierte Verbindung zum eigenen Server an, gibt Benutzername und Kennwort an und kann sich dann verbinden. Im Übrigen kann sich MQTT.fx auch auf den System-Topics von mosquitto subscriben, sodass man damit recht einfach Status-Informationen auslesen kann.

Wartung

Was tun, wenn das Let's Encrypt Zertifikat abläuft?

Leider kann mosquitto nicht erkennen, wenn sich das SSL-Zertifikat geändert hat, weil es etwa erneuert wurde (die Zertifikate von Let's Encrypt laufen ja nicht so lange). Deshalb muss der moquitto Dienst einmal neu gestartet werden, wenn das Zertifikat erneuert wurde, ansonsten wird weiterhin das alte Zertifikat verwendet.

Neue Benutzer

Mit dem oben angegebenen Befehl können natürlich weitere Benutzer in der Benutzerkonfiguration hinzugefügt werden. Allerdings konnte ich bei meinem Test nicht direkt mit einem neu angelegten Benutzer auf den MQTT Broker zugreifen, sondern musste den moquitto Dienst auch hierzu einmal neu starten.

Bitte beachtet bei der Verwendung von Clients, ob sich diese automatisch neu verbinden, wenn die Verbindung kurz unterbrochen wird. Die meisten Clients sollten dies hinbekommen, da MQTT für die Nutzung von "schlechten" Verbindungen entwickelt wurde. Da es bei den oben beschriebenen Neustarts für SSL und Benutzer zu einer solchen Unterbrechung der Verbindung kommt, sollte dies bereits beim Konfigurieren oder Implementieren der Clients beachtet werden.

Und sonst noch?

Damit wäre das Thema auch eigentlich durch, wenn ich nicht beim Rumprobieren auf ein kleines Problem gestossen wäre, nämlich wenn man versucht, sich mit paho-mqtt, einer Python-MQTT-Client-Bibliothek, mit einem wie oben beschriebenen, konfigurierten Server zu verbinden, denn so ohne weiteres klappt das leider nicht. Deshalb...

Bonus: Verbindung herstellen mit paho-mqtt

paho-mqtt kann man mit pip install paho-mqtt (oder der entsprechenden Python 3 Variante) leicht installieren. Allerdings kann es zu Fehlern bei der Verbindung bzw. Zertifikatsvalidierung kommen.

Zertifikate zusammenfassen

Um gleich die SSL-Zertifikate für den Verbindungsaufbau in paho-mqtt verwenden zu können, fasst man alle Zertifikate des Systems in einer Datei zusammen, etwa mit dem Befehl cat /etc/ssl/certs/* > all_certs.pem (je nach Distribution können die Zertifikate sich auch in einem anderen Verzeichnis befinden).

Beispielcode zum Verbinden

#!/usr/bin/env python
import ssl

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

client = mqtt.Client()
client.on_connect = on_connect

client.tls_set("/path/to/all_certs.pem", tls_version=ssl.PROTOCOL_TLSv1)
#client.tls_insecure_set(True)
client.username_pw_set("username", "password")
client.connect("your_domain", 8883, 60)

client.loop_start()

# publish, subscribe, more code...

Vor dem eigentlichen Connect gibt man einerseits die akzeptierten Zertifikate und die SSL/TLS-Version an, und andererseits die Kombination von Benutzername und Kennwort. Dieses Bespiel sollte sowohl unter Python 2.7.x als auch mit Python 3 (etwa 3.2.x) funktionieren.

Artikel

Allgemeiner Disclaimer

Alle Angaben in diesem Text sind sorgfältig zusammengestellt und für einige Szenarien getestet worden. Trotzdem sind Fehler nicht auszuschliessen. Die Anwendung des Beschriebenen erfolgt deshalb immer auf eigene Gefahr.

Zusatzinfos
Navigation