Raspi – Setting up Mosquitto (MQTT broker) on Raspberry Pi / Docker

This article uses user pi to install Docker and Mosquitto.

All commands have to be executed on the Raspberry Pi (log in to Raspberry Pi directly or use ssh from your PC) with a user allowed to use the sudo command..

Setting up Docker

The recommended method is to install Docker Desktop, this works on Linux, Windows and Mac plattforms and includes everything you need (installation guide). Here we describe an alternative way to install Docker manually:

Install Docker on Raspberry Pi
curl -sSL https://get.docker.com | sh

Some of the affected directories:
/var/lib/docker
/etc/docker
/opt/containerd

Add user pi to group docker (this enables you to use the docker command without sudo):
sudo usermod -aG docker pi

This Guide uses also Docker Compose. If Docker Compose is not installed with Docker, you have to install it manually.

sudo reboot

Setting up Mosquitto (MQTT Broker)

Create the following directory tree (you may select any other directory as root):
/docker
/docker/mosquitto
/docker/mosquitto/config
/docker/mosquitto/data
/docker/mosquitto/log

Create the config file for Mosquitto with the following content:
nano /docker/mosquitto/config/mosquitto.conf

# Config file for mosquito
listener 1883
#protocol websockets
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
allow_anonymous false

Create the config file for docker-compose with the following content (pay attention to the indentation of the lines in the YAML file, use 4 spaces per indentation, no tabs):
cd /docker
sudo nano docker-compose.yaml

version: '3'

services:
    mosquitto:
        container_name: mosquitto
        restart: always
        image: eclipse-mosquitto
        ports:
            - "1883:1883"
            - "9001:9001"
        volumes: 
            - ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
            - ./mosquitto/data:/mosquitto/data
            - ./mosquitto/log:/mosquitto/log
        networks:
            - default

networks:
    default:

Now you can install and start Mosquitto:
docker compose up -d

Check if Mosquitto is running:
docker ps

Create a password file for Mosquitto. with the user mqtt-user
docker exec -it mosquitto sh executes a (temporary) shell in the running mosquitto service (you may use any username and password):
docker exec -it mosquitto sh
    mosquitto_passwd -c /mosquitto/data/pwfile mqtt-user

    Password: anyPassword
    exit

Update the Mosquitto config file with the path to the password file (add the following line to the file):
Mosquitto must be stopped during editing the config file.
sudo docker stop mosquitto
sudo nano /docker/mosquitto/config/mosquitto.conf


password_file /mosquitto/data/pwfile

sudo docker start mosquitto
sudo docker ps

Testing the MQTT brocker

For testing the Mosquitto service you may start two terminals on Raspberry Pi or log in twice using ssh.
The -u and -p options specify username and password. You must replace them with the values you used when creating the Mosquitto user (see above).

On terminal A execute the following commands to start a subscriber client:
docker exec -it mosquitto sh
mosquitto_sub -u mqtt-user -P pw4mqtt-user -d -t home/kitchen/temperature

On terminal B execute the following commands to start a publisher client:
docker exec -it mosquitto sh
mosquitto_pub -u mqtt-user -P pw4mqtt-user -d -t home/kitchen/temperature -m "21.3"

Terminal A must display all messages send from Terminal B if both are using the same topic (home/kitchen/temperature). E.g.

Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: home/kitchen/temperature, QoS: 0, Options: 0x00)
Client null received SUBACK
Subscribed (mid: 1): 0
Client null received PUBLISH (d0, q0, r0, m0, 'home/kitchen/temperature', ... (4 bytes))
21.3
Client null received PUBLISH (d0, q0, r0, m0, 'home/kitchen/temperature', ... (4 bytes))
21.7
Client null sending PINGREQ
Client null received PINGRESP

Tools for debugging MQTT

MQTT Explorer

MQTT Explorer can be installed on the development PC (may be it can also be install on Raspberry Pi OS using the Snap package manager – I haven’t tested it). After connecting to a MQTT broker the MQTT Explorer displays all topics and it allows also to publish topics.

Using MQTT Explorer with TLS and (self-signed) client certificate:

Wireshark

Wireshark is a network protocol analyzer for Windows, MacOS or Linux/Raspberry Pi OS. To avoid problems with the promiscuous mode in connection with network switches, Wireshark should be installed directly on the computer running Mosquitto.

sudo apt-get install wireshark
sudo wireshark

Wireshark knows the MQTT protocol and can decode it:

Configuring Mosquitto for SSL/TLS

The configuration of Mosquitto for SSL/TLS is optional. As long as you use the MQTT broker only in an internal/private network (intranet), you can do without it. However, if the broker is also accessible from the Internet, TLS (Transport Layer Security) should be used.

The easiest way is to create the certificates directly on the Raspberry Pi.

Create CA certificate

First we have to create a symmetric key pair and a certificate for the Certificate Authority (CA).

cd ~
mkdir certs
cd certs

Create CA key pair
openssl genrsa -des3 -out ca.key 2048
    pass phrase: myPassPhrase (enter a pass phrase to protect the private key)
    -> ca.key

Create CA certificate
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
    pass phrase: (enter you pass phrase used when creating the CA key pair)
    You may enter empty strings to all inputs, except …
    … common Name (e.g. server FQDN or YOUR name): MyCA (or whatever you want to call it)
    -> ca.crt

If you want to install the new CA certificate into the systemwide certificate store of Raspberry Pi OS you can do this but it is not required for Mosquitto (further instructions are available in /etc/ca-certificates.conf):
Copy CA certificate to /user/share/ca-certificates:
    sudo cp ca.crt /usr/share/ca-certificates/my-ca.crt
Update CA certificates in /etc/ssl/certs…:
    sudo dpkg-reconfigure ca-certificates
In the certificates list displayed by dpkg-reconfigure you must find and select your new CA certificate, my-ca.crt.

Create Server Certificate used by Mosquitto for SSL/TLS

The server certificate must be signed with the CA certificate just created.

Create key pair for Mosquitto
openssl genrsa -out mosquitto_server.key 2048
-> mosquito_server.key

Create Certificate Signing Request
openssl req -new -out mosquitto_server.csr -key mosquitto_server.key
    You may enter empty strings to all inputs, except …
    … common Name (e.g. server FQDN or YOUR name): MyServer (enter hostname or IP address of your server)
-> mosquito_server.csr   

Verify and Sign the Certificate Request
openssl x509 -req -in mosquitto_server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out mosquitto_server.crt -days 3650
    pass phrase: (enter you pass phrase used when creating the CA key pair)
-> mosquito_server.crt
-> ca.srl

Move all files to a secure place.

Configuring Mosquitto

From the keys and certificates just created copy the following ones to the Mosquitto volume:

cd ~/certs
sudo mkdir /docker/mosquitto/certs
sudo cp ca.crt /docker/mosquitto/
sudo cp mosquitto_server.crt /docker/mosquitto/certs/
sudo cp mosquitto_server.key /docker/mosquitto/certs/

Update the Mosquitto config file with the listener for port 8883 and the paths to the certificate and key files. If you want to use the broker in a production and/or Internet environment, you should remove listener 1883. However, for development it is very useful to support both, since debugging can be easier without encryption, e.g. with Wireshark (see below).
Mosquitto must be stopped during editing the config file.
sudo docker stop mosquitto
sudo nano /docker/mosquitto/config/mosquitto.conf

# Config file for mosquito
listener 1883
listener 8883
#protocol websockets
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
allow_anonymous false
password_file /mosquitto/data/pwfile

cafile /mosquitto/certs/ca.crt
certfile /mosquitto/certs/mosquitto_server.crt
keyfile /mosquitto/certs/mosquitto_server.key

Update docker-compose.yaml to include the the port 8883 and the certs volume (pay attention to the indentation of the lines in the YAML file, use 4 spaces per indentation, no tabs):
sudo nano /docker/docker-compose.yaml

version: '3'

services:
    mosquitto:
        container_name: mosquitto
        restart: always
        image: eclipse-mosquitto
        ports:
            - "1883:1883"
            - "8883:8883"
            - "9001:9001"
        volumes:
            - ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
            - ./mosquitto/data:/mosquitto/data
            - ./mosquitto/log:/mosquitto/log
            - ./mosquitto/certs:/mosquitto/certs
        networks:
            - default

networks:
    default:

sudo docker start mosquitto
sudo docker ps

Check in the Mosquitto log file that no errors occurred:
sudo cat /docker/mosquitto/log/mosquitto.log

If you see any errors, stop the Mosquitto container again, try to fix them in the config files and start the container again. IMPORTANT: If you have made any changes to the docker-compose.yaml you must install and start again with the following command (docker start ignores changes in the YAML file):
cd /docker
docker-compose up -d

Using client certificates

Client certificates are optional, but they can further increase security. Instead of username and password client certificates can be used to authenticate the client device against the MQTT broker.

cd ~/certs

Create key pair for client:
openssl genrsa -out device001_client.key 2048
-> device001_client.key

Create certificate request for client certificate:
openssl req -new -out device001_client.csr -key device001_client.key
You may enter empty strings to all inputs, except …
    … common Name (e.g. server FQDN or YOUR name): device001 (or whatever you want to call it)
-> device001_client.csr

Create certificate from certificate request:
openssl x509 -req -in device001_client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out device001_client.crt -days 3650
pass phrase: (enter you pass phrase used when creating the CA key pair)
-> device001_client.crt

Create PFX (personal information exchange) file, it contains the client certificate and the private key:
openssl pkcs12 -export -out device001_client.pfx -inkey device001_client.key -in device001_client.crt
Export Password: anyPassword (enter a password to protect the PFX file)
-> device001_client.pfx

Optionally, a Mosquitto user with the same username can be set up. This is not absolutely necessary, but makes the whole thing clearer if the additional authentication with username and password is required (which is recommended, see also following comments on use_identity_as_username or use_subject_as_username).

sudo docker exec -it mosquitto sh
mosquitto_passwd -b /mosquitto/data/pwfile device001 anyPassword

Verify that the user was added to the password file:
cat /mosquitto/data/pwfile

Update the Mosquitto config file with settings for client certificate:
sudo nano /docker/mosquitto/config/mosquitto.conf

# Config file for mosquito
listener 1883
listener 8883
#protocol websockets
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
allow_anonymous false
password_file /mosquitto/data/pwfile

cafile /mosquitto/certs/schaeren_ca.crt
certfile /mosquitto/certs/mosquitto_server.crt
keyfile /mosquitto/certs/mosquitto_server.key

require_certificate true
use_identity_as_username false
use_subject_as_username false
  • require_certificate true -> clients must specify a client certificate during connection establishment. I.e. only clients whose certificate was issued by the same Ceriticate Authority CA as the server certificate are accepted.
  • use_identity_as_username false -> do not use the CN value in the subject of the certificate as username.
  • use_subject_as_username false -> do not use the (entire) certificate subject as username.
  • Because both use_…_as_username are configured to false, username and password are additionally required for authentication. This has the advantage that a client/user’s access can be revoked later by simply deleting the corresponding line from the Mosquitto password file. Otherwise, you will have to deal with a certificate revocation list to prevent access again afterwards.
    If either of the two entries is true, any client with a valid certificate is accepted (it’s not necessary that a identity/subject is equal to a username in the Mosquitto password file)

Mosquitto must be restarted for the changes in the configuration file to take effect:
sudo docker restart mosquitto

The client certificate files (e.g. device001_client.pfx) must be transmitted by the MQTT clients during the connection establishment to the broker.

Testing SSL/TLS and client certificates

A test is possible e.g. with the MQTT Explorer tool:

On the connection page enable the options Validate certificate and Encryption (tls), enter the hostname or IP address and port number of the broker, enter username and password …

… click ADVANCED and then CERTIFICATES and load the CA certificate (e.g. ca.crt), the client certificate (e.g. device001_client.crt) and the client key (e.g. device001_client.key).

Click BACK twice and and CONNECT.

If you have problems with the hostname not matching the CN in the server certificate or with the CA certificate, you may disable the option Validate certificate and try again.