Title: M1.2.3: MQTT Dienste absichern
Course: LF07 - Cyber-physische Systeme ergänzen - FI/ITSE - Raspberry Pi > Section: LS7.1: Messdatenerfassungssysteme funktional zusammenführen

MQTT unter Sicherheitsaspekten betrachten

MQTT Dienste absichern

Mit der bisherigen Konfiguration können alle, die Zugriff über TCP/IP auf den MQTT Broker haben, Topics anlegen, Inhalte vorhandener Topics überschreiben und die Inhalte aller Topics auslesen. Das ist sicherlich nicht in jedem Fall gewollt und eher als "Honeypot" zu sehen. Hier wird gezeigt, wie man den Zugriff auf eine Adressatengruppe beschränken kann durch

- Anlegen von Benutzerkonten
- Absicherung über Zertifikate

Anlegen von Benutzerkonten auf einem MQTT Broker

Die offizielle Dokumentation: https://mosquitto.org/documentation/

Eine sehr anschauliche Erklärung zur Einrichtung von Nutzerkonten bei MQTT ist hier zu finden: https://plantprogrammer.de/mqtt-auf-dem-raspberry-pi-mosquitto/

Hier eine kurze Zusammenfassung des Artikels:

Anlegen einer Konfigurationsdatei für mosquitto

```cd /etc/mosquitto/conf.d

sudo touch mosquitto.conf```

Mit z. B. nano folgende Zeilen in diese Datei einfügen:

```per_listener_settings true
listener 1896
password_file /etc/mosquitto/conf.d/010-access-list
allow_anonymous false```

Zeile1: Sorgt dafür, dass die unterhalb des Listenerkommandos aufgeführten Konfigurationen sich ausschließlich auf den Brokerzugang über das angegebene Port beziehen. So schafft man es, dass ein Login auf einem Port mit User/ Passwort möglich ist und auf einem anderen Listener mit anderem Port über TLS und ohne User/ Passwort. Das Zugangsverfahren hat keinen Einfluss auf die Topics. Diese sind global.

Zeile 2: mosquitto lauscht auf Port 1896, default wäre 1883. Manchmal ist es sinnvoll, aus Verschleierungsgründen vom default Port abzuweichen oder einfach nur, um seinen Heimatverein zu würdigen, Braunschweiger dürfen gerne 1895 verwenden :-)

Zeile 3: Datei, in der die Useraccounts stehen

Zeile 4: Anonymer Login nicht möglich

Nutzerkonten anlegen in der Datei /etc/mosquitto/conf.d/010-access-list:

Datei erzeugen

```sudo touch /etc/mosquitto/conf.d/010-access-list```

Nutzer eintragen

```sudo mosquitto_passwd /etc/mosquitto/conf.d/010-access-list hermine```

Hätte man für hermine nun das Passwort "Gryffindor" gegeben, so sähe der Eintrag in 010-access-list so aus:

Das Passwort wird also nicht im Klartext sondern als Hashwert abgelegt. Eine Hashfunktion erzeugt mit wenig Rechenaufwand aus einem Klartext einen Geheimtext. Aus dem Geheimtext den Klartext wieder zurückzurechnen ist nahezu unmöglich bei Verwendung eines gebräuchlichen Hashalgorithmus, z. B. SHA-256. Auch das Finden anderer Klartexte mit dem selben Hashwert ist nur sehr schwer möglich, man spricht dabei von Kollisionen. Allerdings kursieren sogenannte Rainbow Tabellen mit häufig verwendeten Passwörtern und deren Hashäquivalent. Darüber kann dann sehr leicht das ursprüngliche Passwort ermittelt werden.

Gryffindor als Passwort zu verwenden war dann sicherlich nicht sinnvoll, allenfalls für eine Testumgebung oder um zu zeigen, wie man es nicht machen sollte :-)

Server neu starten

```sudo systemctl restart mosquitto.service```

Publisher ausführen
```mosquitto_pub -h localhost -p 1896 -t "sensor1/motd" -m "Hallo Hermine" -u hermine -P Gryffindor```
Auf -u folgt der Nutzername, auf -P das Passwort

In Node-RED sieht das dann im Debugfenster so aus:

Oder den mosquitto eigenen Subscriber in einem weiteren Terminalfenster ausführen
```mosquitto_sub -h localhost -p 1896 -t "sensor1/motd" -u hermine -P Gryffindor```

MQTT über Zertifikate absichern

Das Einrichten von Benutzeraccounts erschwert zwar den Zugriff von unberechtigten Personen auf den Server, der Datenverkehr zwischen Clients und Server geschieht aber dennoch unverschlüsselt. Vermittlungsstellen auf dem Weg zwischen den Kommunikationspartnern können Einsicht in die Daten erlangen. Darüber hinaus ist für den Client nicht ersichtlich, ob der Server tatsächlich der ist, für den er sich ausgibt.

Das heute übliche Protokoll (Stand 2021) für eine verschlüsselte und authentifizierte Übertragung ist TLS (transport layer security) in der Version 1.3. TLS ist das Nachfolgeprotokoll zu SSL, das als unsicher gilt. Der Begriff SSL kursiert aber weiterhin (fälschlicherweise), wenn von verschlüsselter Übertragung mit Hilfe von Zertifikaten gesprochen wird. Siehe dazu auch https://www.ionos.de/digitalguide/server/sicherheit/tls-vs-ssl/

Der Grundbaustein von Zertifikaten ist ein Schlüsselpärchen, das aus einem privaten (geheim und nur dem Ersteller bekannt) und einem öffentlichen (der nicht geheim gehalten werden muss) Schlüssel besteht. Diese Schlüssel bestehen aus einer speziellen Zeichenkombination, die folgende Eigenschaften besitzen:

- Eine mit dem privaten Schlüssel (private key) verschlüsselte Botschaft kann nur mit dem öffentlichen Schlüssel (public key) dekodiert werden
- und umgekehrt kann eine mit dem public key verschlüsselte Botschaft nur mit dem private key entschlüsselt werden.

Solch ein Zertifikat kann mit den entsprechenden Softwaretools selbst erstellt und auch selbst signiert werden, also mit einer digitalen Unterschrift versehen werden.

Nun kann man sich nicht selber einen Reisepass ausstellen, dafür kommt eine Behörde ins Spiel, die wiederum vertrauenswürdig ist. Aber bei anderen Dokumenten, wie zum Beispiel einem Werksausweis zum Betreten eines Firmengeländes oder einem Schülerausweis kann auch das eigene Unternehmen die Beglaubigung durch eine Unterschrift (Signierung) vornehmen.

Bei öffentlichen Servern, auf die beliebige Nutzer zugreifen ist erst klar, ob der Server wirklich vertrauenswürdig ist und sein Name (FQDN) tatsächlich auch dem entspricht, den der Client erreichen möchte, wenn eine vertrauenswürdige Organisation dessen Existenz bestätigt hat.  Hier kommt die CA (certification authority) ins Spiel, die "beglaubigt", dass es sich bei dem vom Server genutzten Zertifikat auch wirklich um ein glaubwürdiges handelt. Bei einem nicht öffentlich arbeitenden Server, wie zum Beispiel unser MQTT-Server für schulische oder unternehmensinterne Zwecke kann man auch unternehmensintern ein Zertifikat signieren.

CAs sind auf die Signierung von Zertifikaten spezialisierte Unternehmen oder Organisationen. Die Signierung eines Zertifikates ist meist (nicht immer) mit Kosten verbunden. Für interne Zwecke kann man auch selber eine CA einrichten, die dann die eigenen Zertifikate signiert. Diese Lösung ist für dieses Unterrichtsprojekt angedacht. Sie zeigt das Zusammenspiel zwischen Zertifikat und Signierung durch die CA, wie sie auch im Echtbetrieb ablaufen würde.

Wissenswert: Die auf dem eigenen (Windows-)Rechner installierten Stammzertifikate kann man unter der Powershell mit ```gci -r cert:\localmachine\root | Out-GridView``` anzeigen lassen. Unter Ubuntu kann man sich den Inhalt von /etc/ssl/certs anzeigen lassen, um die auf dem System installierten Zertifikate zu listen.

Das am häufigsten benutzte Tool zur Erzeugung, Anzeige und Bearbeitung dieser Kryptographiedokumente ist openssl, das bei Linuxsystemen als Standard installiert ist.

Eine sehr detaillierte und professionelle Anleitung zu openssl findet man unter https://www.feistyduck.com/library/openssl-cookbook/online/ch-openssl.html

Folgende Schritte sind durchzuführen:

1. Erstellung eines privaten Schlüssels (verschlüsselt) für die CA
2. Erstellung Zertifikat mit öffentlichem Schlüssel für die CA
3. Erstellung privater Schlüssel (unverschlüsselt) für den Mosquitto Broker
4. Erstellung CSR (certification service request) Datei für die Anfrage zur Signierung bei der CA (unsere eigene)
5. Signierung der CSR
6. Erstellung und Signierung eines Clientzertifikates (für den Subscriber- oder Publisherclient)
7. Installation der Zertifikate beim mosquitto Broker
8. Brokerkonfiguration so, dass Zertifikate genutzt werden müssen
9. Installation der Clientzertifikate
10. Test

1. Erstellung eines privaten Schlüssels (verschlüsselt) für die CA und 2. Erstellung Zertifikat mit öffentlichem Schlüssel für die CA

# Folgende Arbeiten als root ausführen
```sudo -i```
# Variablen für Pfade zu den Zertifikatsordnern, erspart Schreibarbeit
```CA_PATH=/etc/mosquitto/ca_certificates```
# Einen 2048 Bit RSA Schlüssel erzeugen und ihn mit einem Passwort nach dem Verschlüsselungsalgorithmus AES schützen
# Der immer noch häufig genutzte openssl genrsa Befehl ist veraltet. Er wird aber auch in dem Blog, s.o. verwendet
```openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes-128-cbc -out ${CA_PATH}/ca.key```
# Erstellung des X.509 Zertifikates der CA mit öffentlichem Schlüssel basierend auf dem davor erstellten privaten Schlüssel
# man kann hier auch gleich die Unternehmensdaten in die Befehlszeile einbauen
```openssl req -new -x509 -days 1826 -key ${CA_PATH}/ca.key -subj /C=DE/ST=Niedersachsen/L=Hannover/O=MMBbS/OU=RootCA/CN=RootCA -out ${CA_PATH}/ca.crt```
# oder so, dann muss man die Daten über ein Kommandozeilenmenü angeben
```openssl req -new -x509 -days 1826 -key ${CA_PATH}/ca.key -out ${CA_PATH}/ca.crt```
# root verlassen
```exit```

Wer möchte, kann das Zertifikat auch noch in die Gruppe der vertrauenswürdigen Stammzertifikate übernehmen. Man vermeidet dann Hinweise darauf, dass es sich um ein selbst signiertes Zertifikat handelt.

Dazu die Datei ca.crt kopieren in /usr/local/share/ca-certificates/
Dann eingeben: ```sudo update-ca-certificates```
Fertig

Will man es dort wieder herausholen, löscht man die Datei aus /usr/local/share/ca-certificates/ und ruft auf
```sudo update-ca-certificates --fresh```

3. Erstellung privater Schlüssel (unverschlüsselt) für den Mosquitto Broker und 4. Erstellung CSR (certification service request) Datei für die Anfrage zur Signierung bei der CA (unsere eigene) und 5. Signierung der CSR und Erstellung des Zertifikates durch die eigene CA

# Folgende Arbeiten als root ausführen, damit nicht vor jedem Befehl ein sudo eingefügt werden muss. Wir arbeiten hier im /etc Verzeichnis. Da darf der normale User nicht einfach so hineinschreiben
```sudo -i```
# Variablen für Pfade zu den Zertifikatsordnern, erspart Schreibarbeit
# hier liegt das Zertifikat der eigenen Zertifizierungsstelle
```CA_PATH=/etc/mosquitto/ca_certificates```
# hier liegen dann die Brokerzertifikate
```CERT_PATH=/etc/mosquitto/certs```
# Schlüssel erzeugen
```openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out ${CERT_PATH}/mosquitto.key```
# csr-Datei erzeugen: Hier natürlich noch die richtige IP Adresse des Raspi eintragen

```openssl req -new -key ${CERT_PATH}/mosquitto.key -out ${CERT_PATH}/mosquitto.csr -subj /C=DE/ST=Niedersachsen/L=Hannover/O=MMBbS/OU=MQTT-Broker/CN=10.2.5.140```

# Zertifikat durch die CA signieren lassen, Dokument hat Endung .crt . Diesen Schritt würde sonst ein öffentliche CA übernehmen
# Seit ca. 2000 sollen Zertifikate über eine Erweiterung "subjetAltName" verfügen. Damit kann man auch Zertifikate für mehrere URLs oder IP Adressen erstellen
# Datei mit den Extensions erstellt
```touch ${CERT_PATH}/mqtt_san.ext
nano ${CERT_PATH}/mqtt_san.ext```
# Dort eintragen: subjectAltName = IP:<Adresse des MQTT-Brokers>
```openssl x509 -req -days 365 -in ${CERT_PATH}/mosquitto.csr -signkey ${CERT_PATH}/mosquitto.key -out ${CERT_PATH}/mosquitto.crt -extfile ${CERT_PATH}/mqtt_san.ext```
# root verlassen
```exit```

6. Erstellung und Signierung eines Clientzertifikates (für den Subscriber- oder Publisherclient)

Hier ergibt sich der gleiche Ablauf wie bei der Erstellung des Brokerzertifikates, lediglich der Name der Schlüssel- und Zertifikatsdatei ändert sich, in diesem Beispiel zu client_1.*

# Folgende Arbeiten als root ausführen
```sudo -i```
# Variablen für Pfade zu den Zertifikatsordnern, erspart Schreibarbeit
# hier liegt das Zertifikat der eigenen Zertifizierungsstelle
```CA_PATH=/etc/mosquitto/ca_certificates```
# hier liegen dann die Brokerzertifikate
```CERT_PATH=/etc/mosquitto/certs```
# Schlüssel erzeugen
```openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out ${CERT_PATH}/client_1.key```
# csr-Datei erzeugen
```openssl req -new -key ${CERT_PATH}/client_1.key -out ${CERT_PATH}/client_1.csr -subj /C=DE/ST=Niedersachsen/L=Hannover/O=MMBbS/OU=MQTT-Broker/CN=10.2.5.140```
# Datei mit den Extensions erstellt
```touch ${CERT_PATH}/mqtt_san_client_1.ext
nano ${CERT_PATH}/mqtt_san_client_1.ext```
# Dort eintragen: subjectAltName = IP:<Adresse des Clients>
```openssl x509 -req -days 365 -in ${CERT_PATH}/client_1.csr -signkey ${CERT_PATH}/client.key -out ${CERT_PATH}/client.crt -extfile ${CERT_PATH}/mqtt_san_client_1.ext```
# root verlassen
```exit```

7. Installation der Zertifikate beim mosquitto Broker und 8. Brokerkonfiguration so, dass Zertifikate genutzt werden müssen

Das Zertifikat für den Broker liegt ja bereits seit dessen Erzeugung in CERT_PATH=/etc/mosquitto/certs. Diesen Ort und dass überhaupt mit Zertifikaten gearbeitet werden soll, müssen wir nun mosquitto mitteilen. Dazu erweitern wir die Konfigurationsdatei in /etc/mosquitto/conf.d/

```sudo nano /etc/mosquitto/conf.d/mosquitto.conf```

Folgende Angaben werden dort hineingeschrieben, bzw. standen schon darin:

```per_listener_settings true
listener 1896
password_file /etc/mosquitto/conf.d/010-access-list
allow_anonymous false
listener 9696
certfile /etc/mosquitto/certs/mosquitto.crt
keyfile /etc/mosquitto/certs/mosquitto.key
cafile /etc/mosquitto/ca_certificates/ca.crt
require_certificate true```

Zeile 5: Das Port, auf dem mosquitto lauschen soll, kann ein beliebiges größer 1024 sein, sollte sich aber von dem der Übung davor unterscheiden. Das Port, auf dem die Anfrage hereinkommt, bestimmt also, wie der Zugang zum System zu behandeln ist. Im vorigen Beispiel mit Port 1896 funktionierte der Zugriff über eine User-Passwort-Kombination, hier nun über ein Zertifikat.

Zeile 6: Pfad zum Zertifikat des Brokers

Zeile 7: Pfad zum privaten Schlüssel des Brokers

Zeile 8: Pfad zum Stammzertifikat der CA zur Überprüfung, ob das Brokerzertifikat gültig ist

Zeile 9: Hiermit wird TLS verbindlich festgelegt, der Client muss ein Zertifikat liefern, anders erhält er keinen Zugriff auf den Broker über Port 9696

Jetzt noch den mosquitto neu starten

```sudo systemctl restart mosquitto```

9. Installation der Clientzertifikate

Das Clientzertifikat und eine Kopie des Stammzertifikates müssen nun auf das Clientsystem verschoben werden. Der Schlüssel des Clients darf nun auf dem Brokersystem nicht mehr gespeichert sein. Zum Testen können Broker- und Clientsystem identisch sein. In der Praxis sind es natürlich verschiedene Systeme. Zur Simulation verschieben wir das Clientzertifikat und eine Kopie des Stammzertifikates in den Userbereich. Bei unterschiedlichen Plattformen muss man die beiden Dateien auf sicherem Weg auf das Clientsystem transportieren. Dem User, hier "pi" muss der Zugriff auf die Dateien gewährt werden (chown)

```USER=pi  #Achtung, keine Leerzeichen
sudo mv /etc/mosquitto/certs/client_1.* ${HOME}
sudo chown ${USER}.${USER} ${HOME}/client_1.*
cp /etc/mosquitto/ca_certificates/ca.crt ${HOME}/ca.crt```

10. Test

Subscriber starten im 2. Terminal

```IP=10.2.5.140
mosquitto_sub  -h ${IP} -p 9696 -t "sensor1/motd" --cafile ~/ca.crt --cert ~/client_1.crt --key ~/client_1.key```

Publisher starten im 1. Terminal

```mosquitto_pub  -h ${IP} -p 9696 -t "sensor1/motd" --cafile ~/ca.crt --cert ~/client_1.crt --key ~/client_1.key```

Und, funktioniert es?

Hier noch ein gut gemeinter Rat: Systemintegratoren stehen in einer großen Verantwortung für die Absicherung von Datenbeständen vor unberechtigtem Zugriff. Das Arbeiten mit Zertifikaten ist eine sehr gute Waffe gegen Eindringlinge. Andererseits ist die Erstellung des richtigen Zertifikats eine hochanspruchsvolle Aufgabe für Einsteiger. Verzweifeln Sie nicht, wenn es nicht auf Anhieb funktioniert. Es kann viele Stunden dauern, bis man eine funktionsfähige und gleichermaßen sichere Lösung gefunden hat.