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.