How to Install and Secure the Mosquitto MQTT Messaging Broker on Ubuntu 20.04

Mosquitto is an open-source message broker that uses the Message Queuing Telemetry Transport (MQTT) Protocol. The protocol is designed to provide lightweight communication to the Internet of Things (IoT) devices. It is commonly used for GPS tracking of vehicles, home automation, environmental sensors, and large-scale data collection.

The MQTT protocol runs on top of the TCP/IP model. Being lightweight, its small code footprint allows you to create applications for devices with minimal resources. It relies on the publish/subscribe model. In this model, the client connects to the Mosquitto server, which acts as a broker to send information to other clients subscribed to a channel.

In this tutorial, you will install Mosquitto and set up the broker to use SSL to protect communications.

Prerequisites

  • An Ubuntu 20.04 server with a non-root user with sudo privileges.
  • A domain name (myqtt.example.com) pointed at your server.

Step 1 - Install Mosquitto Server and Client

Ubuntu ships with the older 1.6 version of Mosquitto. To install the latest version, add the official Mosquitto repository.

$ sudo add-apt-repository ppa:mosquitto-dev/mosquitto-ppa

Install the Mosquitto server and the client.

$ sudo apt install mosquitto mosquitto-clients

Check the status of the server.

$ sudo systemctl status mosquitto
? mosquitto.service - Mosquitto MQTT Broker
     Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-01-25 09:18:40 UTC; 25s ago
       Docs: man:mosquitto.conf(5)
             man:mosquitto(8)
   Main PID: 119694 (mosquitto)
      Tasks: 1 (limit: 2274)
     Memory: 1.0M
     CGroup: /system.slice/mosquitto.service
             ??119694 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

Jan 25 09:18:39 <userid> systemd[1]: Starting Mosquitto MQTT Broker...
Jan 25 09:18:40 <userid> systemd[1]: Started Mosquitto MQTT Broker.

Step 2 - Configure MQTT Password Authentication

Mosquitto comes with a utility to generate a password file called mosquitto_passwd. Mosquitto stores all the configurations in the /etc/mosquitto directory.

Run the following command to generate an encrypted password file at /etc/mosquitto/passwd for the username username. Enter a password of your choice.

$ sudo mosquitto_passwd -c /etc/mosquitto/passwd username
Password:
Reenter password:

Next, create a default.conf file under the /etc/mosquitto/conf.d directory and open it for editing.

$ sudo nano /etc/mosquitto/conf.d/default.conf

Paste the following lines to specify the location of the password file. If you omit the listener field, it will always connect anonymously, irrespective of the configuration.

listener 1883
password_file /etc/mosquitto/passwd

Save the file by pressing Ctrl + X and entering Y when prompted.

Restart the Mosquitto server to implement the change.

$ sudo systemctl restart mosquitto

Step 3 - Test Mosquitto Client

Depending on the use case, you can use the Mosquitto client to send and receive messages on different topics. A client is either a subscriber or publisher.

The next step is to subscribe to a topic. In the MQTT protocol, a topic refers to a string used by the server/broker to filter messages for the connected clients. Here are some sample topics you can use in a home automation application.

  • home/lights/sitting_room
  • home/lights/kitchen
  • home/lights/master_bedroom
  • home/lights/kids_bedroom

To subscribe to a topic, run the mosquitto_sub -t command followed by the topic. For example, to subscribe to home/lights/kitchen topic, run the following command.

$ mosquitto_sub -u username -P YOUR_PASSWORD -t "home/lights/kitchen"

Don't close the existing window. Open a new terminal window to publish a message to the home/lights/kitchen topic using the following command.

$ mosquitto_pub -u username -P YOUR_PASSWORD -m "ON" -t "home/lights/kitchen"

Go back to the first terminal window, and you will receive ON payload.

ON

Next, send the OFF message on the same topic from the second terminal.

$ mosquitto_pub -u username -P YOUR_PASSWORD -m "OFF" -t "home/lights/kitchen"

The first terminal will show the newly published message.

ON
OFF

If you try to send an unauthenticated comment, it will fail. For example, try the following command.

$ mosquitto_sub -t "home/lights/sitting_room"
Connection error: Connection Refused: not authorised.

It is not recommended, but you need to add the following line to the /etc/mosquitto/conf.d/default.conf file if you want to run the commands without authentication.

allow_anonymous true

Step 4 - Install SSL

To install an SSL certificate using Let's Encrypt, we need to download the Certbot tool. We will use the Snapd package installer for that.

Install Snap installer.

$ sudo apt install snapd

Ensure that your version of Snapd is up to date.

$ sudo snap install core 
$ sudo snap refresh core

Install Certbot.

$ sudo snap install --classic certbot

Use the following command to ensure that the Certbot command runs by creating a symbolic link to the /usr/bin directory.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Generate an SSL certificate.

$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d mqtt.example.com

The above command will download a certificate to the /etc/letsencrypt/live/mqtt.example.com directory on your server.

Generate a Diffie-Hellman group certificate.

$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Create a challenge web root directory for Let's Encrypt auto-renewal.

$ sudo mkdir -p /var/lib/letsencrypt

Create a Cron Job to renew the SSL. It will run every day to check the certificate and renew if needed. For that, first, create the file /etc/cron.daily/certbot-renew and open it for editing.

$ sudo nano /etc/cron.daily/certbot-renew

Paste the following code.

#!/bin/sh
certbot renew --cert-name mqtt.example.com --webroot -w /var/lib/letsencrypt/

Save the file by pressing Ctrl + X and entering Y when prompted.

Change the permissions on the task file to make it executable.

$ sudo chmod +x /etc/cron.daily/certbot-renew

Step 5 - Configure MQTT SSL

Now that we have the SSL certificates ready, we need to provide Mosquitto access to them. For this, we need to copy the certificates to a location from where Mosquitto can access them.

$ sudo cp /etc/letsencrypt/live/mqtt.example.com/fullchain.pem /etc/mosquitto/certs/server.pem
$ sudo cp /etc/letsencrypt/live/mqtt.example.com/privkey.pem /etc/mosquitto/certs/server.key

Change the ownership of the /etc/mosquitto/certs directory to the mosquitto user created during the installation.

$ sudo chown mosquitto: /etc/mosquitto/certs

The next step to enable the SSL encryption for Mosquitto is to specify the location of the SSL certificates. Open the configuration file for editing.

$ sudo nano /etc/mosquitto/conf.d/default.conf

Paste the following code at the end of the file.

. . .
listener 8883
certfile /etc/mosquitto/certs/server.pem
cafile  /etc/ssl/certs/ISRG_Root_X1.pem
keyfile /etc/mosquitto/certs/server.key
dhparamfile /etc/ssl/certs/dhparam.pem

Save the file by pressing Ctrl + X and entering Y when prompted. Be sure to leave a trailing newline at the end of the file.

The listener 8883 portion sets up the encrypted listener. It is the standard port for MQTT + SSL, referred to as MQTTS. The next four lines specify the location of the SSL files.

Restart Mosquitto to update the settings.

$ sudo systemctl restart mosquitto

You will need to update the firewall to allow connections to port 8883.

$ sudo ufw allow 8883

Next, we need to test the functionality using the mosquitto_pub command.

$ mosquitto_pub -h mqtt.example.com -t "home/lights/kitchen" -m "hello" -p 8883 --capath /etc/ssl/certs/ -u username -P YOUR_PASSWORD

As you can see, we have included certain additional parameters, including the port number and the path to the SSL certificates. Whenever you need to use SSL, you will always have to specify the full hostname, i.e., mqtt.example.com instead of localhost otherwise, it would give an error.

You will also need to add the --capath directive every time. It tells the Mosquitto client to look for root certificates installed by the operating system.

Step 6 - Configure SSL Renewal

Certbot will automatically renew your certificate before it expires. But it needs to be told to copy the renewed certificates to the /etc/mosquitto/certs directory and restart Mosquitto service.

We are going to do that by creating a shell script. Create a file mosquitto-copy.sh in the /etc/letsencrypt/renewal-hooks/deploy directory.

$ sudo nano /etc/letsencrypt/renewal-hooks/deploy/mosquitto-copy.sh

Paste the following code in it. Replace the value of the MY_DOMAIN variable with your domain. The ${RENEWED_LINEAGE} variable points to the /etc/letsencrypt/live/mqtt.example.com directory during renewal.

# Set which domain this script will be run for
MY_DOMAIN=mqtt.example.com
# Set the directory that the certificates will be copied to.
CERTIFICATE_DIR=/etc/mosquitto/certs

if [ "${RENEWED_DOMAINS}" = "${MY_DOMAIN}" ]; then
	# Copy new certificate to Mosquitto directory
	cp ${RENEWED_LINEAGE}/fullchain.pem ${CERTIFICATE_DIR}/server.pem
	cp ${RENEWED_LINEAGE}/privkey.pem ${CERTIFICATE_DIR}/server.key

	# Set ownership to Mosquitto
	chown mosquitto: ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key

	# Ensure permissions are restrictive
	chmod 0600 ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key

	# Tell Mosquitto to reload certificates and configuration
	pkill -HUP -x mosquitto
fi

Save the file by pressing Ctrl + X and entering Y when prompted.

Make the file executable.

$ sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-copy.sh

This script will be automatically run on every successful renewal of the certificate.

If you are running Mosquitto and a web server like Nginx, you need to instruct Certbot to stop the server before renewal and start it again once finished. To do that, open the file etc/letsencrypt/renewal/mqtt.example.com.conf.

$ sudo nano /etc/letsencrypt/renewal/mqtt.example.com.conf

Add the following lines at the end of the file. Change the commands accordingly to the webserver you are using.

pre_hook = systemctl stop nginx
post_hook = systemctl start nginx

Save the file by pressing Ctrl + X and entering Y when prompted.

Run a Certbot dry run to verify.

$ sudo certbot renew --dry-run

If you see no errors, it means everything is set.

Step 7 - Configure Websockets

You can configure Mosquitto to use the MQTT protocol from within browsers using Javascript using the Websockets functionality. To enable it, open the configuration file.

$ sudo nano /etc/mosquitto/conf.d/default.conf

Paste the following lines at the end of the file.

. . .
listener 8083
protocol websockets
certfile /etc/mosquitto/certs/server.pem
cafile  /etc/ssl/certs/ISRG_Root_X1.pem
keyfile /etc/mosquitto/certs/server.key
dhparamfile /etc/ssl/certs/dhparam.pem

Save the file by pressing Ctrl + X and entering Y when prompted.

If you notice, it is the same block as the one we used for enabling SSL except for the port number and protocol fields. 8083 is the most common port used by MQTT to talk using WebSockets.

Restart the Mosquitto service.

$ sudo systemctl restart mosquitto

Open port 8083.

$ sudo ufw allow 8083

We need to use a browser-based MQTT client to test the WebSockets functionality. There are a lot of clients available, but we will use the HiveMQ Websocket client for our purpose. Launch the client in your browser, and you will see the following.

HiveMQ Websockets Client

As shown in the screenshot above, fill the fields as shown.

  • The host should be the domain of your Mosquitto server, mqtt.example.com.
  • Port should be 8083.
  • ClientID field can be left as it is.
  • The username should be your Mosquitto username.
  • Password should be the password you created above.
  • Check the SSL box.

Press the Connect button, and the HiveMQ client will be connected to your Mosquitto server.

Once connected, enter home/lights/kitchen as the topic, enter any message and press Publish.

Mosquitto Websocket Publish

The message will show up in your mosquitto_sub terminal window confirming the successful connection.

Mosquitto Websockets Terminal Response

This shows that the Websockets implementation is successful.

Conclusion

This concludes our setup of a secure, password-protected, and SSL-encrypted MQTT server on a Ubuntu 20.04 based machine. If you have any questions, post them in the comments below.

Share this page:

4 Comment(s)