Skip to main content

Mosquitto v2 MQTT Broker on Debian Bullseye

TST, Hong Kong

Mosquitto is a message broker that implements the MQTT protocol. Mosquitto is an open-source project developed by Eclipse. MQTT protocol uses a publish/subscribe model. Client can publish message to a broker and other clients can subscribe to the topic of that message. A broker is a central component that receives all messages from the clients and then publishes the messages to all subscribed clients.

Install Mosquitto on Debian Bullseye

Check Building Eclipse Mosquitto v2 from Source for building the broker from it's source code under Arch Linux.

Add the Mosquitto repository:

wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
sudo apt-key add mosquitto-repo.gpg.key
cd /etc/apt/sources.list.d/
wget http://repo.mosquitto.org/debian/mosquitto-bullseye.list

Install Mosquitto broker:

apt update
apt install mosquitto

Check Mosquitto version:

mosquitto -h | grep version
mosquitto version 2.0.14

Check if Mosquitto service is running:

service mosquitto status                                                                                              
● mosquitto.service - Mosquitto MQTT Broker
Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2022-02-05 14:06:50 HKT; 25min ago
Docs: man:mosquitto.conf(5)
man:mosquitto(8)
Process: 574 ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto (code=exited, status=0/SUCCESS)
Process: 586 ExecStartPre=/bin/chown mosquitto /var/log/mosquitto (code=exited, status=0/SUCCESS)
Process: 593 ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto (code=exited, status=0/SUCCESS)
Process: 598 ExecStartPre=/bin/chown mosquitto /run/mosquitto (code=exited, status=0/SUCCESS)
Main PID: 599 (mosquitto)
Tasks: 1 (limit: 9357)
Memory: 3.6M
CPU: 1.184s
CGroup: /system.slice/mosquitto.service
└─599 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

Feb 05 14:06:49 nomad-minion systemd[1]: Starting Mosquitto MQTT Broker...
Feb 05 14:06:50 nomad-minion mosquitto[599]: 1644041210: Loading config file /etc/mosquitto/conf.d/custom.conf
Feb 05 14:06:50 nomad-minion systemd[1]: Started Mosquitto MQTT Broker.

Here I can see that the Mosquitto binary was installed in /usr/sbin and it is being started with a default configuration file in /etc/mosquitto/mosquitto.conf.

Configure Mosquitto

Mosquitto installs 5 further tools that we can use with our broker:

ls -la /usr/bin | grep mosquitto
-rwxr-xr-x 1 root root 60448 Jun 9 2021 mosquitto_ctrl
-rwxr-xr-x 1 root root 31456 Jun 9 2021 mosquitto_passwd
-rwxr-xr-x 1 root root 60296 Jun 9 2021 mosquitto_pub
-rwxr-xr-x 1 root root 64608 Jun 9 2021 mosquitto_rr
-rwxr-xr-x 1 root root 64608 Jun 9 2021 mosquitto_sub

Password file can be created using mosquitto_passwd tool:

mosquitto_passwd -c /etc/mosquitto/password_file admin
Password: instar
Reenter password: instar

First argument is path to a file, second argument is username. The -c option means that new password file will be created. Run the following command and enter a password for the user.

Next, open Mosquitto configuration file:

cat /etc/mosquitto/mosquitto.conf
# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example

pid_file /run/mosquitto/mosquitto.pid

persistence true
persistence_location /var/lib/mosquitto/

log_dest file /var/log/mosquitto/mosquitto.log

include_dir /etc/mosquitto/conf.d

Here I can see that the "actual" configuration file, the one that I need to edit, must be located in /etc/mosquitto/conf.d. So let's take a look:

/etc/mosquitto/conf.d/custom.conf

# =================================================================
# General configuration
# =================================================================
# per_listener_settings false
# allow_zero_length_clientid true
auto_id_prefix zeroid-
# check_retain_source true
#max_inflight_bytes 0
#max_inflight_messages 20
#max_keepalive 65535
#max_packet_size 0
#max_queued_bytes 0
#max_qos 2
#max_queued_messages 1000
#memory_limit 0
#message_size_limit 0
persistent_client_expiration 7d
# pid_file /var/run/mosquitto/mosquitto.pid
queue_qos0_messages true
#retain_available true
#set_tcp_nodelay false
#sys_interval 10
#upgrade_outgoing_qos false
#user mosquitto
# =================================================================
# Listeners
# =================================================================
listener 1883
protocol mqtt
listener 1885
protocol websockets
#socket_domain
#bind_interface
#http_dir
#max_connections -1
#mount_point
#use_username_as_clientid
#websockets_headers_size
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
#certfile
#keyfile
#ciphers
#ciphers_tls1.3
#crlfile
#dhparamfile
#require_certificate false
#cafile
#capath
#use_identity_as_username false
# -----------------------------------------------------------------
# Pre-shared-key based SSL/TLS support
# -----------------------------------------------------------------
#psk_hint
#ciphers
#use_identity_as_username false
# =================================================================
# Persistence
# =================================================================
#autosave_interval 1800
#autosave_on_changes false
#persistence false
#persistence_file mosquitto.db
#persistence_location
# =================================================================
# Logging
# =================================================================
#log_dest stderr
log_type error
log_type warning
log_type notice
log_type information
connection_messages true
#log_facility
log_timestamp true
log_timestamp_format %Y-%m-%dT%H:%M:%S
#websockets_log_level 0
# =================================================================
# Security
# =================================================================
#clientid_prefixes
allow_anonymous false
# -----------------------------------------------------------------
# Default authentication and topic access control
# -----------------------------------------------------------------
password_file /etc/mosquitto/passwordfile
acl_file /etc/mosquitto/acl.file
# -----------------------------------------------------------------
# External authentication and topic access plugin options
# -----------------------------------------------------------------
# auth_plugin
# auth_opt_db_host
# auth_opt_db_port
# auth_opt_db_username
# auth_opt_db_password
# =================================================================
# Bridges
# =================================================================
#connection <name>
#address <host>[:<port>] [<host>[:<port>]]
#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]
#bridge_bind_address
#bridge_attempt_unsubscribe true
#bridge_protocol_version mqttv311
#idle_timeout 60
#keepalive_interval 60
#local_clientid
#notifications true
#notification_topic
#remote_clientid
#remote_password
#remote_username
# restart_timeout 20
# restart_timeout 10 30
#restart_timeout 5 30
#round_robin false
#start_type automatic
#threshold 10
#try_private true
#bridge_outgoing_retain true
#bridge_max_packet_size 0
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
#bridge_cafile
#bridge_capath
#bridge_alpn
#bridge_insecure false
#bridge_certfile
#bridge_keyfile
# -----------------------------------------------------------------
# PSK based SSL/TLS support
# -----------------------------------------------------------------
#bridge_identity
#bridge_psk
# =================================================================
# External config files
# =================================================================
#include_dir

Make changes as needed and restart the Mosquitto service with:

service mosquitto restart && service mosquitto status

Verify that the Broker is working

Mosquitto CLI Tools

Verify that the broker is working using the Mosquitto subscription and publication tools:

mosquitto_sub  -h localhost -p 1883 -u admin -P instar -v -t '$SYS/broker/uptime'
$SYS/broker/uptime 1089 seconds
$SYS/broker/uptime 1100 seconds
$SYS/broker/uptime 1111 seconds

Subscribing to $SYS/broker/uptime will give you the broker uptime in an 10s interval (interval can be adjusted in the mosquitto config).

Connecting Clients

And to something more exciting - I will connect an INSTAR IP camera:

Mosquitto2 MQTT Broker

And send an update to switch the privacy area 1 on and off again using the command topic:

mosquitto_pub -h 192.168.2.111 -p 1883 -u admin -P instar -t 'cameras/117/multimedia/privacy/region1/enable' -m '{"val":"1"}' -d
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'cameras/117/multimedia/privacy/region1/enable', ... (11 bytes))
Client (null) sending DISCONNECT

mosquitto_pub -h 192.168.2.111 -p 1883 -u admin -P instar -t 'cameras/117/multimedia/privacy/region1/enable' -m '{"val":"0"}' -d
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'cameras/117/multimedia/privacy/region1/enable', ... (11 bytes))
Client (null) sending DISCONNECT

Subscribing to the status topic I can see that the command was executed as expected:

mosquitto_sub -h localhost -p 1883 -u admin -P instar -v -t 'cameras/117/status/multimedia/privacy/region1/enable'    
cameras/117/status/multimedia/privacy/region1/enable {"val":"1"}
cameras/117/status/multimedia/privacy/region1/enable {"val":"0"}

Mosquitto2 MQTT Broker

Websockets

MQTT Explorer

So far I have been using the configured default listener on port 1885 using the MQTT protocol. But I added an additional listener on port 8885 that requires a websocket connection:

# =================================================================
# Listeners
# =================================================================
listener 1883
protocol mqtt
listener 1885
protocol websockets

In the MQTT Explorer you can switch the used protocol to ws to test the service:

Mosquitto2 MQTT Broker

MQTT.js Web Client

Using a MQTT.js React Dashboard to connect to the WS service:

import React, { useState, Fragment } from 'react';
import './App.css';

var mqtt = require('mqtt');
var options = {
protocol: 'ws',
username: 'admin',
password: 'instar',
// clientId uniquely identifies client
// choose any string you wish
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
};
var client = mqtt.connect('ws://192.168.2.111:1885', options);

// Get Broker Uptime MQTT topic
client.subscribe('$SYS/broker/uptime');

function App() {
var note;
client.on('message', function (topic, message) {
note = message.toString();
// Updates React state with message
setMsg(note);
console.log(note);
client.end();
});

// Sets default React state
const [msg, setMsg] = useState(<Fragment><em>connecting...</em></Fragment>);

return (
<div className="App">
<header className="App-header">
<h1>Mosquitto Broker</h1>
<p>Uptime: {msg}</p>
</header>
</div>
);
}

export default App;

Mosquitto2 MQTT Broker

Mosquitto Webserver

When a listener is using the websockets protocol, it is possible to serve http data as well. Set http_dir to a directory which contains the files you wish to serve. If this option is not specified, then no normal http connections will be possible. Let's add this third listener to the configuration file:

# =================================================================
# Listeners
# =================================================================
listener 1883
protocol mqtt
listener 1885
protocol websockets
listener 8080
protocol websockets
http_dir /opt/mqtt-tree

I am going to use this D3 Tree from @hardillb and download to /opt/mqtt-tree. All I need to add the the ./index.html file is the Mosquitto WS URL, Port and the broker login to display a tree view of all registered topics:

var options = {
// host: location.hostname,
// port: parseInt(location.port),
host: '192.168.2.111',
port: 1885,
clientID: "web" + new Date().getTime(),
connectOpts: {
userName: 'admin',
password: 'instar',
// useSSL: true,
keepAliveInterval: 30,
timeout: 10,
cleanSession: false,
onSuccess: onConnect,
onFailure: onFailure
}
}

Restart the Mosquitto service and visit the broker IP on port 8080 with your web browser:

Mosquitto2 MQTT Broker

Adding Encryption

Creating Self-Signed Certificates

Client Requirements

  • A CA (certificate authority) certificate of the CA that has signed the server certificate on the Mosquitto Broker.

Broker Requirements

  • CA certificate of the CA that has signed the server certificate on the Mosquitto Broker.
  • CA certificated server certificate.
  • Server Private key for decryption.
openssl version
OpenSSL 1.1.1l 24 Aug 2021

Mosquitto already creates a directory for self-signed certificates - if it does not exists create it with:

mkdir -p /etc/mosquitto/certs
chown mosquitto:mosquitto /etc/mosquitto/certs

CA Certificates

m

cd /etc/mosquitto/certs
openssl genrsa -out ca.key 4096

Important: The FQDN must not be the same as the server FQDN, otherwise you might end up with SSL errors. On a local network using a AVM Fritzbox router every device receives both a hostname - for example my Mosquitto server identifies as debian11 - and a domain name hostname + .fritz.box. So, for example, I am able to access the Mosquitto webserver via http://debian11.fritz.box:8080/:

openssl req -new -x509 -days 1825 -key ca.key -out ca.crt

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:INSTAR
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:debian11.fritz.box
Email Address []:
chown mosquitto:mosquitto ca.{crt,key}

Server Certificates

openssl genrsa -out server.key 4096

Important: Here the FQDN must be the hostname, otherwise you might end up with SSL errors. As mentioned above my server can be reached via the hostname debian11 - I can test this by visiting the mosquitto webserver http://debian11:8080/:

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

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:INSTAR
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:debian11
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 1825

Signature ok
subject=C = AU, ST = Some-State, O = INSTAR, CN = debian11
Getting CA Private Key
chown mosquitto:mosquitto server.{csr,key,crt}

The certs directory should now contain the following files:

-rw-r--r-- 1 mosquitto mosquitto 1956 Feb 16 20:31 ca.crt
-rw------- 1 mosquitto mosquitto 3243 Feb 16 20:30 ca.key
-rw-r--r-- 1 root root 41 Feb 16 20:37 ca.srl
-rw-r--r-- 1 mosquitto mosquitto 1834 Feb 16 20:37 server.crt
-rw-r--r-- 1 mosquitto mosquitto 1659 Feb 16 20:37 server.csr
-rw------- 1 mosquitto mosquitto 3243 Feb 16 20:34 server.key

Mosquitto Server Configuration

Add the following configuration to /etc/mosquitto/conf.d/custom.conf:

# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
listener 8883
tls_version tlsv1.2
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key

Then restart the service:

service mosquitto restart && service mosquitto status

Testing the Connection

All you need to establish a server connection is the ca.crt file on your client machine. Then you can try to subscribe to the last-will topic of your camera and should receive it's online status:

mosquitto_sub -h debian11 -t 'cameras/117/status/connection' -p 8883 --insecure --cafile ca.crt --tls-version tlsv1.2 -u admin -P instar -v

cameras/117/status/connection {"val":"online"}
cameras/117/status/connection {"val":"offline"}
cameras/117/status/connection {"val":"online"}

Or with the MQTT Explorer:

Mosquitto2 MQTT Broker

Add the ca.crt file here:

Mosquitto2 MQTT Broker

Client Certificates

For more security, you can add client certificates, which need to be signed by the server.

Creating a client certificate

Generate client certificates for the MQTT clients. Important: Don’t use the server name as FQDN:

openssl genrsa -out client.key 4096
openssl req -out client.csr -key client.key -new

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:INSTAR
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:mqtt.client
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
openssl x509 -req -in client.csr -CA ca.crt --CAkey ca.key --CAcreateserial -out client.crt -days 1825

Signature ok
subject=C = AU, ST = Some-State, O = INSTAR, CN = mqtt.client
Getting CA Private Key
chown mosquitto:mosquitto client.{csr,crt,key}

Restart the Mosquitto server:

service mosquitto restart && service mosquitto status

Update the Mosquitto Server Configuration

To enforce the usage of client certificates, you will need to add require_certificate true to your listener configuration:

# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
listener 8883
tls_version tlsv1.2
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
require_certificate true

Testing the Connection

mosquitto_sub -h debian11 -t 'cameras/117/status/connection' -p 8883 --insecure --cafile ca.crt --cert client.crt --key client.key --tls-version tlsv1.2 -u admin -P instar -v

cameras/117/status/connection {"val":"online"}

For the MQTT Explorer upload the Client Key and Cert:

Mosquitto2 MQTT Broker

Connect your INSTAR IP Camera

To connect your INSTAR camera you first have to do 2 things:

  1. Combine the client.key and client.crt into a single file called client.pem:
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAorc0ouM2Uh0pBlZ5IbCSonwOACUCPQ+FqWjhRl5FbAAke2iK

...

l6hxaLG33DoTvYoEbjBEmLtsBAz4sdnTGi2z6HOYfMsqGjMehPJmr2XH/kA=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIFIDCCAwgCFHLf6A9ycbmW6ExF+DBGE3T3qhFAMA0GCSqGSIb3DQEBCwUAMFAx

...

CVSpxYNxMG6gIpeIFrTogygOfdc=
-----END CERTIFICATE-----
  1. Take the ca.crt and save it as ca.pem.

Now open your camera's SSL Cert menu, add the client.pem as your custom certificate and enable it:

Mosquitto2 MQTT Broker

And secondly add the ca.pem to the Custom CA Store of your camera:

Mosquitto2 MQTT Broker

Configure the client to use your MQTT brokers Hostname instead of it's local IP to be able to activate the Verify Certificate option:

Mosquitto2 MQTT Broker

Start your MQTT client and check the http://debian11/tmpfs/mqtt-log - you should see it connect on port 8883 using TLS:

2022-2-18 4:11:11: [Info] Config: Version 2
2022-2-18 4:11:11: [Info] Config: Units loaded: 283
2022-2-18 4:11:11: [Info] Config: Memory required: 40749
2022-2-18 4:11:11: [Info] Authenticate with Mqtt-Broker
2022-2-18 4:11:11: [Info] Activate TLS
2022-2-18 4:11:11: [Info] Connect to Mqtt-Broker debian11 on port 8883...
2022-2-18 4:11:11: [Info] Synchronize Cgi-Server with Mqtt-Broker
2022-2-18 4:11:11: [Info] Mqtt listen thread has been started.
2022-2-18 4:11:11: [Info] Synchronize Cgi-Server with Mqtt-Broker
2022-2-18 4:11:12: [Info] Adapter connected!