Ziel dieses Tutorials
In diesem Tutorial installieren wir den Mosquitto MQTT-Server inklusive der Implementierung des Websockets in einem Debian 12.6 LXC-Container auf einem Proxmox-Host. Zudem werden wir den Mosquitto-Server härten mittels SSL/TLS-Zertifikaten von Let’s Encrypt via acme.sh.
Systemanforderungen und Vorbereitung
Der Server benötigt nur sehr geringe Hardware-Ressourcen. Meine Empfohlene Konfiguration:
- 16GB Speicher
- 512MB RAM
- 1 vCPU
Ich empfehle zudem noch die Installation des MQTT-Explorer: MQTT-Explorer. Mit diesem können wir später den MQTT-Server sehr gut testen. Der MQTT-Eplorer ermöglicht es komfortabel alle Aktivitäten zu sehen aber auch Befehle zu schicken. Bei dem MQTT-Explorer handelt es sich um eine Desktop-App mit übersichtlicher GUI und einer gut strukturierten Übersicht der Aktivitäten auf dem MQTT-Server.
Installation
Wenn der Container erfolgreich erstellt ist, sollten wir zuerst
|
|
ausführen. Anschließend können wir die beiden Pakete mosquitto
und mosquitto-clients
installieren. Da wir den Server später auch testen möchten, macht es Sinn mosquitto-clients direkt mitzu installieren. Die Version aus dem Debian repository wird immer ein wenig „hinter her hinken“ möchte man das vermeiden kann man es auch selbst compilieren: Mosquitto.org. Die nötigen Infos dazu findet man dort.
Wir installieren beide Pakete nun mit den Befehlen:
|
|
Der Output des Status-Befehls sollte, wenn alles geklappt hat so aussehen:
Defaultkonfiguration testen
Wir können nun indem wir eine zweite Terminialsitzung öffnen einen ersten Test durchführen. Dazu führen wir im ersten Terminal den Befehl
|
|
aus. Im zweiten Terminal führen wir anschließend den Befehl
|
|
aus. Funktioniert alles seht ihr nun im ersten Terminal die Nachricht ‘Hi there’. Da der Mosquitto-Server nun grundlegend funktioniert kümmern wir uns nun um die einrichtung und das härten des Servers.
Benutzer-Authentifizierung einrichten
Da wir nicht möchten, dass sich Geräte ohne authentifizierung anmelden können definieren wir ein Benutzernamen und Passwort mit dem sich Geräte zukünftig anmelden müssen. Die Konfigurationsmöglchkeiten hierzu werden alle sehr ausführlich und gut in der Dokumentation unter Mosquitto Man-Pages erklärt. Um eine Benutzer/Passwort kombination zu erstellen muss der Befehl
|
|
ausgeführt werden. Wobei du $USER
durch einen Benuternamen deiner Wahl ersetzen solltest. Wenn du den Befehl ausführst wirst du anschließend um ein Passwort gebeten. Speichere das Passwort gut ab und beende den Befehl mit Enter. Unter /etc/mosquitto
sollte nun eine neue, gehashte Datei Namens .passwd
liegen. Damit der Mosquitto-Dienst die Datei auch nutzen kann, müssen wir unter /etc/mosquitto/conf.d/
eine neue Datei erstellen.
Alle Dateien in diesem Ordner die mit .conf
enden werden automatisch eingelesen. Das wird in der Datei /etc/mosquitto/mosquitto.conf
so definiert.
Nun erstellen wir die entsprechende Config-Datei:
|
|
Inhalt der Konfigurationsdatei:
|
|
In der ersten Zeile definieren wir den Port. Hier Port 1883 welches der Standardport für ungesichterte Verbindungen zu dem MQTT Server ist. Wir können Port 1883 mit User-Authentification also immer als Fallback nutzen, Falls ein Gerät einen höheren Standard nicht beherrscht. In der zweiten Zeile untersagen wir Verbindungen ohne die Korrekte User/Passwort kombination und in der letzen Zeile sagen wir Mosquitto noch wo die Passwort-Datei, die wir zuvor erstellt haben für den Abgleich liegt. Jetzt können wir den Server neu starten mit dem Befehl
|
|
damit die Änderungen gültig werden. Um die neuen Einstellungen zu testen haben wir zwei Möglichkeiten: wir können wieder mit zwei Termialsessions arbeiten oder den MQTT-Explorer nehmen. Ich zeige euch nun letzteres.
Wir müssen dort das Protocoll, in unserem Fall mqtt://
, die IP des Servers und den Port angeben. Für den Port benutzen wir 1883. Wenn wir uns nun anmelden wird es nicht funktionieren. Ihr könnt dazu paralel im Terminalfenster den Befehl
|
|
ausführen. Dieser ermöglicht euch eine Liveansicht des Logs. Dort seht ihr auch den fehlgeschlagen Loginversuch. Gebt ihr aber die richtige User/Passwort Kombination ein, könnt ihr euch erfolgreich anmelden. Auch das zeigt euch der Log.
SSL und TLS-Zertifikate einrichten
Um den Server mit SSL/TLS-Zertifikaten abzusichern müssen wir eine eigene TLD haben. Das erstellen auf .local oder .home ist nicht möglich. Ich rate auch davon ab diese Endungen zu nehmen. Die Endung .local ist laut RFC6762 für mDNS Wikipedia: mDNS vorgesehen und führt früher oder später zu Problemen. Ich empfehle, wo immer möglich, immer auf eine gesicherte Verbindung mittels SSL/TLS zu setzen. Nur so kann eine verschlüsselte Datenübertragung zwischen Client und MQTT-Broker erzielt werden. Wir brauchen also eine eigene Domain auf die wir Zugriff haben. Die Zertifikate werden wir mittels acme.sh und Let’s Encrypt generieren. Ich stelle euch dazu mein Script für die DNS-01 Challange vor. Provider mit Support für acme.sh findet ihr in einer sehr übersichtlichen Liste hier: Let’s Encrypt Forum Ich benutze deSEC und die zur Verfügung gestellte API. Der Service ist kostenlos, es besteht aber die Möglichkeit zu Spenden sofern ihr mit dem Dienstleister zufrieden seid und ihn nutzen wollt. Ich gehe hier nicht auf die generelle API-Nutzung und das einrichten ein. Dafür gibt es eine gute Dokumentation. Ihr könnt aber in meinem Script die Schritte sehen die Nötig sind um ein Let’s Encrypt Zertifikat zu generieren.
Achtung! Generiert die Zertifikate immer erst dann wenn ihr einen Dry-run gemacht habt und dieser erfolgreich war. Anonsten gerät man sehr schnell in das Rate-Limit von Let’s Encrypt. Das würde bedeuten, dass ihr lange Wartezeiten habt bis ihr einen neuen Versuch starten könnt.
Die folgenden Schritte sind auch notwendig wenn ihr einen anderen Weg nutzen wollt und weichen erst gegen Ende ab.
Wir werden im Verlauf den Befehl curl
benutzen. Also schauen wir als erstes ob dieser Vorhanden ist. Wir machen das mit dem Befehl
|
|
Ist curl verfügbar sollte das so aussehen:
Hier seht ihr das curl bereits erfolgreich installiert ist. Ist das nicht der Fall holen wir das mit sudo apt install curl -y
nach.
Nun erstellen wir einen neunen Benutzer der für die Generierung der Zertifikate zuständig ist Wir machen das mit dem Befehl
|
|
Das Flag --gecos ""
verhindert, dass wir nach Credentials gefragt werden und da wir für den technisches User keine Benötigen spart uns das ein paar Schritte.
Anschließed fügen wir den neuen Benutzer zu der Gruppe mosquitto hinzu
|
|
Damit der User acmeuser
der jetzt Teil der Gruppe mosquitto
ist auch schreiben darf, müssen wir noch die Ordnerrecht anpassen und der Gruppe mosquitto
Schreibrechte geben. Das machen wir mit
|
|
Nun erlauben wir dem Benutzer ebenfalls den Mosquitto Server neuzustarten und die Rechte der Zertifikate anzupassen. Das machen wir mit
|
|
Die soeben erstellte Datei öffnen wir nun mit
|
|
Das -f
Flag prüft die Datei direkt beim Schließen auf eine gültige Syntax. Das ist wichtig, damit der User acmeuser später alle nötigen Berechtigungen, ohne das Passwort einzugeben, besitzt.
In die Datei tragen wir die folgenden Zeilen ein:
|
|
Die Datei müssen wir noch mit den richtigen Rechten versehen, so dass nur root sie lesen kann und andere nutzer sie nicht ändern können. Das machen wir mit
|
|
Der Output von ls -l /etc/sudoers.d/acmeuser
sollte in etwa so aussehen: -r--r-----
.
Nachdem der User der später unsere Zertifikate verwaltet nun erstellt ist, installieren wir die benötige Software für die Zertifikatserstellung mit dem Befehl
|
|
Als letzten Schritt vor der generierung der Zertifikate setzen wir noch Let’s Encrypt als unseren Standard-Issuer mit dem Befehl:
|
|
Vor dem generieren der Zertifikate müssen die Pfade vorhanden sein und Berechtigungen gesetzt werden. Jetzt, nachdem alle Vorbindungen erfüllt sind kommen wir zur Zertifikatserstellung. Ich benutze dafür wie schon geschrieben die API von deSEC und die DNS-01 Challange. Dazu erstellen wir ein neues Script mit dem Befehl
|
|
und fügen den follgeden Inhalt ein:
|
|
Wir müssen das Script noch ausführbar machen und dem User acmeuser zuweisen. Das machen wir mit den beiden Befehlen
|
|
Das Script bezieht ein Zertifikat für eine definierte Domain und setzt anschließend ebenfalls die korrekten Rechte, den richtigen Eigentümer und startet anschließend den Dienst mosquitto.service neu.
Automatische Erneuerung der Zertifikate mittels SystemD-Timer
Die DNS-01-Challange erfordert eine manuelle Erneuerung der Zertifikate in regelmäßigen Abstnden. Damit wir uns darum nicht selsbt kümmern müssen, können wir das mittels eines SystemD-Timers und dazugehörigen SystemD-Dientes machen. Wir erstellen als erstes den Service:
|
|
In diesen geben wir folgenden Inhalt:
|
|
Anschließend erstellen wir den Timer:
|
|
In diesen geben wir den folgenden Inhalt:
|
|
Da wir die beiden Abhängigkeiten nun implementiert haben können wir die Dienste noch starten. Das machen wir mit
|
|
Ob alles problemlos funktioniert können wir uns mit
|
|
Anzeigen lassen.
Alternative Zertifikatserstellung ohne DNS-01-Challange
Wir können die Zertifikate auch ohne die DNS-01-Challange beziehen. Das wird deutlich breiter Unterstüzt und bietet einen deutlich geringeren Aufwand. Da es sich hier nich um meinen favorisierten Weg handelt möchte ich die Alternative nur grob aufzeigen und nicht tief in die Details einsteigen. Wir wechseln dazu zuerst in den entsprechenden User
|
|
Anschließend können wir mittels diesen Befehls die Zertifikate beantragen:
|
|
Es ist unbeding darauf zu achten, dass der Pfad vorher korrekt erstellt wurde und der User acmeuser die entsprechenden Rechte besitzt. Anschließend müssen auch hier die Zertifikate in die entsprechenden Pfade verteilt werden und mit den richtigen Rechten versehen werden. Die automatische erneuerung der Zertifikate kann man hier bequem mittels crontab einrichten mit dem Befehl
|
|
Diffie-Hellman-Schlüsseltausch
Bevor wir das Script das erste mal ausführen sind noch weitere Schritte nötig. Für zusätzliche Sicherheit ist es empfohlen noch den Diffie-Hellman-Schlüsselaustausch zu implementieren, dass erhöht die Sicherheit der Kommunikation zwischen Client und Server. Wir machen das mit dem Befehel:
|
|
Je nach System kann das etwas dauern. Ist die Generierung abgeschlossen setzten wir noch den richtigen Eigentümer mit:
|
|
Da wir nun alle nötigen Zertifikate und Schlüssel beisammen haben, müssen wir dem Mosquitto-Server noch den Umgang hiermit zeigen. Dazu erstellen wir eine weitere Configdatei mit
|
|
Die Configuration füllen wir mit folgendem Inhalt:
|
|
Um den Mosquitto-Server mit aktiviertem TLS zu betreiben, speichern wir die Datei und starten den Dienst neu:
|
|
Mit dem MQTT-Explorer oder den beiden Tools mosquitto_sub
und mosquitto_pub
können wir das soeben eingerichtet TLS testen. Hierzu stellen wir den Port auf 8883. Zudem müssen wir uns nun statt mit einer IP mit der Domain anmelden für die wir die Zertifikate ausgestellt haben. Einwählen mit der IP und aktiviertem TLS ist nicht möglich. Der MQTT Hostname muss unbedingt mit dem CN im Zertifikat übereinstimmen.
Unterstützung für Websockets aktivieren
Als letztes wollen wir noch die Verbindungsmöglichkeit über Websocket ermöglichen. Die Verbindung über Websocket kann in bestimmten Fällen von Vorteil sein, da sie eine bidirektionale Verbindung über TCP ermöglicht, Daten direkt senden kann und schneller ist als eine Verbindung über http da ein erneuer Handshake nach dem Openening entfällt. Wir haben bereits alle Vorbindungen geschaffen und können direkt die entsprechende Config anlegen:
|
|
Inhalt der Websocket-Konfigurationsdatei:
|
|
Nach dem Speichern müssen wir den Mosquitto-Dienst noch neustarten:
|
|
Um Websocket zu nutzen müssen wir den Port auf 8083 einstellen und das Protokoll auf ws://
wechseln. Im Log von Mosquitto sollten wir nun eine neue, erfolgreiche Verbindung sehen.
Fazit
Die Einrichtung eines Mosquitto MQTT-Servers in einem LXC-Container auf Proxmox bietet umfangreiche Konfigurationsmöglichekiten für MQTT-Verbindungen im Netzwerk. Mit Benutzer-Authentifizierung, SSL/TLS-Verschlüsselung und Websocket-Unterstützung ist der Server gut gerüstet für den Einsatz in verschiedenen Szenarien.
Fandest du diesen Artikel hilfreich? Dann freue ich mich über einen Kaffee! ❤️