banner left Boxtec Banner
Platzhalter BoxtecProdukteForumShopKontaktPlaygroundn/aJobs
 
 

Mosquitto Logo

Mosquitto MQTT message broker

Das MQTT Nachrichtenprotokoll ermöglicht den Austausch von Nachrichten resp. Telemetriedaten zwischen Geräten. Im Open Source Bereich ist MQTT mittlerweile synonym mit der Mosquitto MQTT Software Suite.

Über einen oder mehrere zentrale MQTT Broker (Server) können Clients Nachrichten senden (publish) und empfangen (subscribe).

Zu sendende Nachrichten werden mit einem Thema (Topic) versehen, andere Clients erhalten diese Nachricht nur, wenn sie das entsprechende Topic abonniert (subscribed) haben und über die nötigen Berechtigungen verfügen.

Broker (Server)

Als Message Broker (Server) wird bisher fast ausschliesslich Mosquitto eingesetzt. Zum momentanten Zeitpunkt in der Version 1.4.10 erhältlich. Da die Software zurzeit noch erhebliche Entwicklungsschritte erfährt, ist es auch für Linux User sinnvoll sich nicht auf die Pakete der Distribution zu verlassen, sondern die aktuelle Version herunterzuladen und zu kompilieren.

Mosquitto aus Quellen installieren

Installieren von Abhängigkeiten (ev. noch mehr):

sudo apt-get install build-essential libc-ares-dev uuid-dev

Herunterladen der aktuellen Quellen von https://mosquitto.org/download/:

cd /tmp; wget http://mosquitto.org/files/source/mosquitto-1.4.10.tar.gz

Auspacken des Archivs:

tar xzvf mosquitto-1.4.10.tar.gz

Kompilieren:

cd mosquitto-1.4.10/
make

Installieren direkt:

make install

oder als deinstallierbares Paket mit checkinstall:

sudo checkinstall -D --install=no make install

Mosquitto konfigurieren (Broker/Server)

Damit wir den Mosquitto Server als broker starten können, müsssen wir noch ein paar Kleinigkeiten konfigurieren:

Einen User für Mosquitto anlegen (soll ja nicht als root laufen..):

adduser --disabled-password mosquitto

Erstellen einer leeren, aber dokumentierten Konfigurationsdatei die später mit Optionen und Direktiven erweitert werden kann:

sudo cp /etc/mosquitto/mosquitto.conf.example /etc/mosquitto/mosquitto.conf

Nun können wir den Broker für einen ersten Testlauf starten:

sudo mosquitto -c /etc/mosquitto/mosquitto.conf

Die darauffolgende Ausgabe sollte in etwa so aussehen:

1485093598: mosquitto version 1.4.10 (build date 2017-01-22 13:37:36+0100) starting
1485093598: Using default config.
1485093598: Opening ipv4 listen socket on port 1883.
1485093598: Opening ipv6 listen socket on port 1883.

Clients (Subscriber/Publisher)

Mit der Installation von mosquitto wie unter Mosquitto aus Quellen installieren beschrieben erhalten wir auch die beiden Client Programme mosquitto_sub und mosquitto_pub.

Einfacher Sub/Pub Test

Nach dem der Server auf dem gleichen Rechner wie unter Mosquitto konfigurieren beschrieben läuft, können wir mit einem Client lauschen und mit einem anderen eine Nachricht senden. Am besten geht das mit zwei Terminalfenstern nebeneinander.

Im ersten Fenster subscriben wir auf alles (#):

mosquitto_sub -v -t "#"

Das -v sorgt dafür, dass uns auch das Topic ausgegeben wird.

Im zweiten Fenster senden wir eine Nachricht:

mosquitto_pub -t "public/test1" -m "Hello World"

Im ersten Fenster sollte nun sowas ähnliches auftauchen wie:

public/test1 Hello World

Bravo, der Server läuft und die Clients können verbinden, senden und empfangen.

Weitere Schritte (things to try)

Verbindung über Netzwerk

Unser Demo Mosquitto Server kann natürlich auch über das Netzwerk erreicht werden, dazu ist einfach beim Aufruf von mosquitto_sub resp. mosquitto_pub der Aufruf um -h hostname/ip zu erweitern:

mosquitto_sub -h 192.0.2.25 -v -t "#"

resp.

mosquitto_pub -h 192.0.2.25 -t "public/test1" -m "Hello World"

Wenn die IP Adresse des Mosquitto server 192.0.2.251) ist.

Verwenden mit anderen Programmen (piping)

Eine der Stärken von Unix ist unter anderem das Piping. Das bedeutet, dass wir die Ausgabe eines Programmes als Eingabe für ein anderes Programm verwenden könen und umgekehrt.

Im Zusammenhang mit Mosquitto ergeben sich daraus viele interessante Möglichkeiten, hier nur mal ein paar exemplarische Anregungen:

Logfile überwachen

So können wir z.B. ein Logfile auf einem anderen Rechner überwachen:

sudo tail -f /var/log/auth.log | mosquitto_pub -h mosquitto-host.sample.tld -t "public/test1" -l

Statusmeldungen als Bildschirm Overlay ausgeben

Mit aosd_cat2) kann man einfach Texte als Bildschirm Overlay ausgeben:

mosquitto_sub -t "#" | \
/usr/bin/aosd_cat -p 0 -x 360 -y 150 -B blue -b 100 -f 6000 -o 6000 -n "Cantarell 20" -w 1200

Das sieht dann in etwa so aus (vorausgesetzt, irgendwo publiziert ein anderer Client die entsprechende Nachricht):

aosd_cat Ausgabe

Weitere Clients / Libraries

Eine Liste zurzeit verfügbarer APIs findet sich unter https://github.com/mqtt/mqtt.github.io/wiki/apis_and_examples.

Python

Installation mit

pip install paho-mqtt

Arduino (C++)

Node.js

Erweiterte Konfiguration

User Authentisierung

Mosquitto verfügt über integriertes einfaches Usermanagement zur Zugriffskontrolle. Um dieses zu nutzen muss die Serverkonfiguration /etc/mosquitto/mosquitto.conf3) wie folgt erweitert werden:

..
password_file /etc/mosquitto/mqtt_passwd
..

Nun kann dass password_file mit folgendem Befehl angelegt und ein erster User testuser erzeugt werden:

sudo mosquitto_passwd -c /etc/mosquitto/mqtt_passwd testuser

Diese User können nun zur Zugriffsteuerung mit ACLs verwendet werden.

ACL

Mosquitto bietet eingeschränkte Möglichkeiten zur Zugriffsbeschränkung mit der Konfiguration einer externen ACL4) Datei in der Mosquitto Konfiguration, z.B.:

acl_file /etc/mosquitto/mqtt_acl

In dieser Datei können dann rudimentäre Zugriffsberechtigungen vergeben werden, nachfolgend die ACL Datei die für unseren Testserver mqtt.boxtec.ch verwendet wird:

# Zugriffsberechtigungen für anonyme User, falls mit allow_anonymous true eingeschaltet
topic read #

# Bridge Status Meldungen
pattern write $SYS/broker/connection/%c/state

# Authentisierte User können auf user/meinusername/ und alles darunter lesen und schreiben
pattern readwrite user/%u/#

# Public Bereich pub/playground und alles darunter kann von jedem authentisierten User geschrieben und gelesen werden
pattern readwrite pub/playground/#

Zuoberst in der Datei werden die Berechtigungen für anonyme Zugriffe gesetzt, ist allow_anonymous false haben diese keine Wirkung.

Jede Zeile beginnt entweder mit topic, user oder pattern, folgt eine Zeile mit user, sind nachfolgende Zeilen für diesen User gültig. Eine ausführliche Beschreibung findet sich in der man-page zu mosquitto-conf.

Bridging

~TBD~

SSL/TLS

SSL/TLS kann für die Verschlüsselung der Kommunikation, aber auch für die Authentisierung der Clients auf PKI Basis dienen. In einem ersten Schritt möchten wir dafür sorgen, dass die Kommunikation mit dem Server über SSL gesichert wird.

Zertifikate und Schlüssel erstellen

Zuerst müssen wir für den Server ein Zertifikat erstellen und dies (in unserem Fall von einer eigenen) Certificat Authority signieren lassen. In einem ersten Schritt benötigen wir das openssl Paket falls es noch nicht installiert ist:

sudo apt-get install openssl
sudo mkdir /etc/mosquitto/certs
cd /etc/mosquitto/certs

Erzeugen eines eigenen CA Schlüssels und Zertifikat (PEM Passphrase gut merken!):

sudo openssl req -new -x509 -days 1999 -extensions v3_ca -keyout ca.key -out ca.crt

Erzeugen eines Server Schlüssels:

sudo openssl genrsa -out server.key 2048

Certificate Signing Request erzeugen:

sudo openssl req -out server.csr -key server.key -new

Selber signieren:

sudo openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
Serverkonfiguration

Damit haben wir alle nötigen Dateien beinander und können nun die Serverkonfiguration /etc/mosquitto/mosquitto.conf5) um folgende Zeilen erweitern:

listener 1883
listener 1884
cafile /etc/mosquitto/certs/ca.crt
keyfile /etc/mosquitto/certs/server.key
certfile /etc/mosquitto/certs/server.crt

Damit haben wir nun zwei listener, einer auf Port 1883 der auf unverschlüsselte Anfragen reagiert und einer auf 1884 der nur mit SSL Anfragen arbeitet.

Client Verbindungen mit SSL

Damit Clients nun verschlüsselt mit dem Server kommunizieren können, muss die weiter oben erstellte Datei ca.crt alle Clients zugänglich gemacht werden. Da wir den SSL Port auf den nicht-standard Port 1884 gelegt haben, muss auch dieser mitgegeben werden:

mosquitto_sub -v -t "#" --cafile /etc/mosquitto/certs/ca.crt -p 1884

zum Subscriben, resp. zum Publizieren:

mosquitto_pub -t "public/test1" -m "Hello World" --cafile /etc/mosquitto/certs/ca.crt -p 1884
Geht ned, und nu? (common pitfalls)

Der Name, den Du beim Erstellen des Certificate Signing Request als Common Name / FQDN angegeben hast, muss mit dem übereinstmmen den Du beim Client als Argument für -h verwendest. Wenn Du also z.B. als FQDN meincooler-mosquitto.local verwendet hast und danach mit -h 127.0.0.1 die Verbindung testest wird das scheitern. Du musst dann ebenfalls die Option –insecure mitgeben, also z.B.:

mosquitto_sub -v -t "#" --cafile /etc/mosquitto/certs/ca.crt -p 1884 --insecure

Nachrichten signieren mit ECDSA

Nachrichten die über Mosquitto nach dem MQTT Standard versendet werden, können unterwegs durch einen Angreifer recht problemlos verändert werden. Man kann sicher damit leben, wenn der experimentelle Temperatursensor auf dem Tisch 0.5° höhere Werte meldet als er eigentlich hat. Wenn man aber z.B. über eine solche Nachricht z.B. Lasten schaltet, sagen wir doch mal eine Sprinkler-Anlage, einen externen Alarm etc., dann gewinnt die Frage ob das Signal auch wirklich von dort kommt wo es vorgibt herzukommen und man dem Inhalt der Nachricht trauen kann an erheblichem Gewicht.

Für unser Beispiel haben wir uns für eine digitale Signatur mit einem digitalen Signatur Algortithmus6) auf Basis der Elliptische-Kurven-Kryptographie entschieden. Elliptische Kurven bieten im Gegensatz zu anderer Public Key Kryptographie bei viel geringerer Schlüsselgrösse viel höhere Sicherheit.

Als Parametrisierungs-Standard haben wir uns für secp256k17) entschieden. Der Standard secp256k1 bietet eine schnelle Berechnung bei angemessener Sicherheit und er wird unter anderem auch für Bitcoin und die meisten alternativen Kryptowährungen eingesetzt (damit sind um 15 Mia USD gesichert, was man als recht wirkungsvolle Bug-Bounty ansehen kann).

Unser Ziel in diesem kleinen Beispiel soll sein, dass wir uns beim Empfang einer signierten Meldung darauf verlassen können, dass diese vom Absender den wir erwarten kommt und auch nicht in Transit verändert wurde. Wir haben also Daten-Integrität, Authentisierung des Absenders und Nachweisbarkeit.

Beispiel: ECDSA Nachrichten mit Python versenden und empfangen

Voraussetzungen

Eine Python Installation (2.7x oder 3.x) mit dem Modul python-ecdsa installiert:

sudo apt-get install python-ecdsa

oder auf nicht Debian-basierenden Systemen:

pip install python-ecdsa
Schlüssel für ECDSA erzeugen

Für unser Beispiel benötigen wir nur einen privaten Schlüssel und den dazugehörigen öffentlichen Schlüssel (Public Key). Wir nutzen hier dafür OpenSSL, alternativ können die Schlüssel aber auch mit python-ecdsa erstellt werden, wenn auf Deiner Plattform openssl nicht vorhanden ist.

Zuerst wird der private Schlüssel erzeugt, dieser sollte wie der Name schon suggeriert auch wirklich privat bleiben, die Zuverlässigkeit des ganzen Verfahrens steht und fällt mit der Geheimhaltung dieses Schlüssels:

openssl ecparam -genkey -name secp256k1 -noout -out private.pem

Aus diesem privaten und geheimen Schlüssel wird wiederum der öffentliche Schlüssel, der nicht geheim ist, erzeugt:

openssl ec -in private1.pem -pubout -out public.pem

Der oder die Empfänger von signierten Nachrichten benötigen nun den Public Key um Signaturen von Nachrichten verifizieren zu können. Der Sender benötigt den Private Key um eine Signatur zu einer Nachricht erstellen zu können.

Python Code Sender
import paho.mqtt.client as mqtt
import time, random, base64
from ecdsa import SigningKey
from os import urandom
 
def publish_signed(client, topic, message):
  sk = SigningKey.from_pem(open("private.pem").read())  
  message = str(int(time.time())) + message
  signed_message = sk.sign(message)
  out_message = message + ":" + base64.b64encode(signed_message)
  client.publish(topic, out_message)    
 
if __name__ == '__main__':
  client = mqtt.Client()
  client.username_pw_set("meinuser", "meinpw")
  client.connect("mqtt.boxtec.ch")
  client.subscribe("user/boxtec/#", qos=1)
  random.seed(urandom(32))
  my_message = "Testmessage" + "_" + str(random.randrange(0,100))
  publish_signed(client, "user/meinuser/dsa-test", my_message)
  print "message sent, exiting."

Die Nachricht selber wird mit einem : vom Timestamp getrennt, um die Sache etwas spannender zu machen hängen wir dem String jeweils noch eine Zufallszahl von 0-99 an.

Python Code Empfänger
import paho.mqtt.client as mqtt
import time, base64
from ecdsa import VerifyingKey, BadSignatureError
 
def on_subscribe(client, userdata, mid, granted_qos):
  print "Subscribed: " + str(mid) + " " + str(granted_qos)
 
def on_message(client, userdata, msg):
  global last_timestamp
  try:
    if int(msg.payload[0:10]) <= last_timestamp:
      print "Replay attack detected, aborting."
      return
  except:
    return
  cleartext, b64sig = msg.payload.split(":")
  signature = base64.b64decode(b64sig)
  print "Timestamp:", cleartext[0:10]
  print "Cleartext:", cleartext[10:]
  print "Signature:", signature
  vk = VerifyingKey.from_pem(open("public.pem").read())
  try:
    vk.verify(signature, cleartext)
    print "Signature verified!"
    last_timestamp = int(cleartext[0:10])
  except BadSignatureError:
    print "Signature invalid!"
 
if __name__ == '__main__':
  last_timestamp = int(time.time())
  time.sleep(1)
  client = mqtt.Client()
  client.on_subscribe = on_subscribe
  client.on_message = on_message
  client.username_pw_set("meinuser", "meinpw")
  client.connect("mqtt.boxtec.ch")
  client.subscribe("user/meinuser/#", qos=1)
  while True:
    client.loop(timeout=1.0, max_packets=1)

Der Sender sendet mit jeder Sendung eine Timestamp. Um Replay-Angriffe zu erschwerden, wird vom Empfänger jeweils nur ein nächstes Paket mit einer grösseren Timestamp akzeptiert. Pakete mit gültiger Signatur und gleichhohem oder kleineren Timestamp werden verworfen.

Der Demo Code obige ist jenseits einer brauchbaren Implementation, zeigt aber auf wie einfach übertragene Nachrichten sicher signiert werden können.

Fragen, Anregungen, Kommentare

Für Anmerkungen, Vorschläge und Korrekturen sind wir sehr dankbar, Fragen beantworten wir im Rahmen unserer Möglichkeiten gerne, bitte postet dazu in das entsprechende Forum Thema unter:

http://forum.boxtec.ch/index.php/topic,3216.0.html

2) Paket aosd-cat in Debian-basierenden Distributionen
4) Access Control Lists
6) DSA
 
mqtt/start.txt · Last modified: 2017/01/27 21:39 by boxtec
 
 

zum Seitenanfang

Letzte Aktualisierung: © boxtec internet appliances · the better security products