Eclipse Mosquitto Secure MQTT Broker Docker Installation

Aus Laub-Home Wiki

In diesem Tutorial erkläre ich wie man mittels Docker Compose den MQTT Broker Mosquitto von Eclipse deployt und diesen mittels openssl Self Signed Zertifikaten absichert. Mehr Informationen rund um das Thema MQTT findet ihr hier:

Wie man Docker und Docker Compose Installiert hier:

SSL Zertifikate erstellen

Zuerst erstellen wir unseren Compose Projekt Ordner und den SSL Zertifikats Ordner:

mkdir -p /opt/mosquitto/certs
mkdir -p /opt/mosquitto/data/mosquitto/conf/certs

nun erstellen wir im certs Ordner ein kleines Skript welches uns die Zertifikate erstellt:

Download hier möglich: https://raw.githubusercontent.com/alaub81/scripts/master/generate-certs.sh

/opt/mosquitto/certs/generate-certs.sh

#!/usr/bin/env bash
#########################################################################
#Name: generate-certs.sh
#Subscription: This Script generates ssl certs
##by A. Laub
#andreas[-at-]laub-home.de
#
#License:
#This program is free software: you can redistribute it and/or modify it
#under the terms of the GNU General Public License as published by the
#Free Software Foundation, either version 3 of the License, or (at your option)
#any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
#or FITNESS FOR A PARTICULAR PURPOSE.
#########################################################################
#Set the language
export LANG="en_US.UTF-8"
#Load the Pathes
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# set the variables

# Just change to your belongings
COMPOSE_PROJECT_DIR="/opt/mosquitto"
IP="FQDN / IP ADRESS"
SUBJECT_CA="/C=SE/ST=Berlin/L=Berlin/O=himinds/OU=CA/CN=$IP"
SUBJECT_SERVER="/C=SE/ST=Berlin/L=Berlin/O=himinds/OU=Server/CN=$IP"
SUBJECT_CLIENT="/C=SE/ST=Berlin/L=Berlin/O=himinds/OU=Client/CN=$IP"

### Do the stuff
function generate_CA () {
   echo "$SUBJECT_CA"
   openssl req -x509 -nodes -sha256 -newkey rsa:2048 -subj "$SUBJECT_CA"  -days 3650 -keyout $COMPOSE_PROJECT_DIR/certs/ca.key -out $COMPOSE_PROJECT_DIR/certs/ca.crt
}

function generate_server () {
   echo "$SUBJECT_SERVER"
   openssl req -nodes -sha256 -new -subj "$SUBJECT_SERVER" -keyout $COMPOSE_PROJECT_DIR/certs/server.key -out $COMPOSE_PROJECT_DIR/certs/server.csr
   openssl x509 -req -sha256 -in $COMPOSE_PROJECT_DIR/certs/server.csr -CA $COMPOSE_PROJECT_DIR/certs/ca.crt -CAkey $COMPOSE_PROJECT_DIR/certs/ca.key -CAcreateserial -out $COMPOSE_PROJECT_DIR/certs/server.crt -days 3650
}

function generate_client () {
   echo "$SUBJECT_CLIENT"
   openssl req -new -nodes -sha256 -subj "$SUBJECT_CLIENT" -out $COMPOSE_PROJECT_DIR/certs/client.csr -keyout $COMPOSE_PROJECT_DIR/certs/client.key 
   openssl x509 -req -sha256 -in $COMPOSE_PROJECT_DIR/certs/client.csr -CA $COMPOSE_PROJECT_DIR/certs/ca.crt -CAkey $COMPOSE_PROJECT_DIR/certs/ca.key -CAcreateserial -out $COMPOSE_PROJECT_DIR/certs/client.crt -days 3650
}

function copy_keys_to_broker () {
   cp $COMPOSE_PROJECT_DIR/certs/ca.crt $COMPOSE_PROJECT_DIR/data/mosquitto/conf/certs/
   cp $COMPOSE_PROJECT_DIR/certs/server.crt $COMPOSE_PROJECT_DIR/data/mosquitto/conf/certs/
   cp $COMPOSE_PROJECT_DIR/certs/server.key $COMPOSE_PROJECT_DIR/data/mosquitto/conf/certs/
}

generate_CA
generate_server
generate_client
copy_keys_to_broker

sollte openssl nicht installiert sein:

apt install openssl -y

nun vergeben wir das execute Recht und starten unser Script, damit es uns die Zertifikate erstellt:

chmod +x /opt/mosquitto/certs/generate-certs.sh
/opt/mosquitto/certs/generate-certs.sh

Die Zertifikate die der MQTT Brocker benötigt liegen dann unter /opt/mosquitto/data/mosquitto/conf/certs

Mosquitto via Docker Compose

Um Mosquitto via docker-compose zu deployen, brauchen wir folgendes docker-compose.yml und .env File:

/opt/mosquitto/.env

# Config File for Mosquitto MQTT Application
# Port Configuration Mosquitto MQTT
MQTT_PORT=1883
MQTT_TLS_PORT=8883

# Timezone
TZ="Europe/Berlin"

/opt/mosquitto/docker-compose.yml

version: '3.7'

services:
  mosquitto:
    image: eclipse-mosquitto:latest
    restart: always
    volumes:
      - "./data/mosquitto/conf:/mosquitto/config"
      - mosquitto_data:/mosquitto/data
      - mosquitto_log:/mosquitto/log
    ports:
      - "${MQTT_PORT}:1883"
      - "${MQTT_TLS_PORT}:8883"    
    environment:
      TZ: ${TZ}
    labels:
      com.centurylinklabs.watchtower.enable: "true"
    #network_mode: host
    networks:
      frontend-nw:
        aliases:
          - mqtt

networks:
  frontend-nw:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br-mosquitto

volumes:
  mosquitto_data:
  mosquitto_log:

Möchte man das Ganze zum Beispiel wie ich direkt mit openHAB deployen, dann einfach die Zeilen in das dortige docker-compose.yml und .env file kopieren. Im Kapitel openHAB Integration

Nun brauchen wir noch die mosquitto.conf, die den Broker nach unseren Bedürfnissen konfiguriert:

/opt/mosquitto/data/mosquitto/conf/mosquitto.conf

listener 1883
listener 8883

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false
password_file /mosquitto/config/mosquitto.passwd

cafile /mosquitto/config/certs/ca.crt
certfile /mosquitto/config/certs/server.crt
keyfile /mosquitto/config/certs/server.key

# By default a TLS enabled listener will operate in a similar fashion to a
# https enabled web server, in that the server has a certificate signed by a CA
# and the client will verify that it is a trusted certificate. The overall aim
# is encryption of the network traffic. By setting require_certificate to true,
# the client must provide a valid certificate in order for the network
# connection to proceed. This allows access to the broker to be controlled
# outside of the mechanisms provided by MQTT.
#require_certificate true

Jetzt legen wir noch eine Dummy Passwort Datei an, da sonst der Docker Container nicht startet:

touch /opt/mosquitto/data/mosquitto/conf/mosquitto.passwd

nun starten wir das Ganze:

cd /opt/mosquitto
docker-compose up -d

jetzt sollte der Aufruf docker ps einen laufenden Mosquitto Docker Container zeigen:

docker ps

####
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS                  PORTS                                                                         NAMES
f6e4a6840aa6        eclipse-mosquitto:latest        "/docker-entrypoint.…"   7 seconds ago       Up 5 seconds            0.0.0.0:1883->1883/tcp, 0.0.0.0:8883->8883/tcp

Nun legen wir noch einen oder mehrere Benutzer an, mit denen unsere Clients sich in Zukunft am MQTT Brocker anmelden können. Dafür nehmen wir den laufenden Docker Container:

docker-compose exec mosquitto mosquitto_passwd -c /mosquitto/config/mosquitto.passwd mosquitto

Testing mittels python Script

Da unser Broker nun läuft, mittels angelegtem Benutzer und Passwort geschützt ist und zusätzlich noch eine SSL Verschlüsselte Übertragung bietet, können wir diesen nun testen. Dafür reicht folgendes einfaches python Script, welches den paho MQTT client verwendet. Dafür müssen wir diesen, wie auch python3 installieren:

apt update && apt install python3 python3-pip python3-paho-mqtt -y

# oder via pip3
apt update && apt install python3 python3-pip
pip3 install paho-mqtt

nun erstellen wir das folgende Test Script.

Download hier möglich: https://raw.githubusercontent.com/alaub81/scripts/master/mqtt-test.py

/root/mqtt-test.py

#!/usr/bin/env python3
import ssl, time, random
import paho.mqtt.client as paho

# set the variables
broker='FQDN / IP of Broker'
port=8883
clientid=f'python-mqtt-{random.randint(0, 1000)}'
username='mosquitto'
password='password'
insecure=True
qos=1
retain_message=True

# do the stuff
client=paho.Client(clientid)
client.username_pw_set(username, password)
client.tls_set(cert_reqs=ssl.CERT_NONE) #no client certificate needed
client.tls_insecure_set(insecure)

##### define callback
def on_message(client, userdata, message):
    time.sleep(1)
    print("received message =",str(message.payload.decode("utf-8")))

##### Bind function to callback
client.on_message=on_message
#####
print(clientid,"is connecting to broker",broker, port)
client.connect(broker, port) #connect
client.loop_start() #start loop to process received messages
print("subscribing ")
client.subscribe("house/+") #subscribe to all house topics
time.sleep(4)
print("publishing ")
client.publish("house/bulb1","ON",qos,retain_message) #publish
client.publish("house/bulb2","OFF",qos,retain_message) #publish
client.publish("house/bulb3","NONE",qos,retain_message) #publish
time.sleep(4)
client.disconnect() #disconnect
client.loop_stop() #stop loop

nun geben wir dem Script noch das execute Recht und starten es einfach:

chmod +x /root/mqtt-test.py
/root/mqtt-test.py

Testing mittels MQTT Explorer

Der MQTT Explorer ist ein super Tool um grafisch zu sehen, was auf einem Broker los ist. Verfügbar ist er für alle Plattformen. Ich habe ihn einfach im Apple App Store auf meinen Mac heruntergeladen. Man kann mit diesem nicht nur eine subscription machen sonder auch publishen. Aber zunächst zeigt er uns ersteinmal, ob unser Broker online ist und man sich zu ihm verbinden kann. Dafür einfach die folgenden Verbindungen anlegen und schauen ob man sich verbinden kann:

Ohne TLS Verschlüsselung
Mit TLS Verschlüsselung

Nach dem die Verbindung hergestellt wurde, sieht man $SYS bereits. Bei Publish kann man nun eine neue Message publizieren.

Nachricht publizieren

openHAB Integration

Möchte man den Mosquitto MQTT Broker in das openHAB docker compose Projekt einfügen, muss man ein paar Dinge beachten:

  • openHAB nutzt kein docker Netzwerk sondern im besten Fall die host network mode.
  • Das ändern der Ports muss somit im mosquitto.conf stattfinden da beim host network mode keine Ports publiziert werden

das ganze sieht im docker-compose.yml dann wie folgt aus:

...
  mosquitto:
    image: eclipse-mosquitto:latest
    restart: always
    volumes:
      - "./data/mosquitto/conf:/mosquitto/config"
      - mosquitto_data:/mosquitto/data
      - mosquitto_log:/mosquitto/log
    environment:
      TZ: ${TZ}
    labels:
      com.centurylinklabs.watchtower.enable: "true"
    network_mode: host
...

...
volumes:
  mosquitto_data:
  mosquitto_log:

das .env File ist nur noch für die TimeZone verantwortlich, da diese bereits im openHAB .env ist, brauchen wir hier nichts mehr zu machen.

in der mosquito.conf müssen nun ggf. die Ports angepasst werden. Dies geht bei den listener Einstellungen

Ein docker-compose up -d deployed dann unseren Mosquitto.

Wie man Items via MQTT in openHAB bekommt könnt ihr hier nachlesen:

Quellen