Introduction: Secure Mosquitto MQTT Server for IoT Devices (ESP32, JavaScript, Python) With TLS

So if you want to build some IoT stuff and use an MQTT Broker such as Mosquitto to control everything you'll want it to be secure. You can use TLS to secure the connection between the broker and the clients.

I'll be setting up a web server using Apache first and generating certificates for it using LetsEncrypt. We'll tell Mosquitto to use the same certificates to make it secure.

If everything goes well, your ESP32 devices, JavaScript clients and Python scripts will all be able to connect to the server. You won't need to have a certificate.crt file or anything like that since we're using a CA signed certificate.

I'm not an expert at everything here so there's probably some mistakes but hopefully it helps.

Step 1: Requirements...

  • You need a server running Ubuntu 18.04. Or close to it.
  • The server needs a static IP. You can't do it at home with a Dynamic IP. I don't know if a DynDNS + Dynamic IP Service will work with LetsEncrypt.
  • You need a domain name registered and have the A-Record pointing to your server.

Step 2: Setting Up Apache...

So first off, you need to get your web server running. You can find any LAMP Stack tutorial online and follow it.

You don't need PHP or MySQL for this but if you're going to use it you may as well get it all up and running.

Once that's all done, we'll get the certificates installed.

Installing CertBot:

sudo apt-get update

sudo apt-get install software-properties-common

sudo add-apt-repository universe

sudo add-apt-repository ppa:certbot/certbot

Press Enter if prompted

sudo apt-get update

sudo apt-get install certbot python-certbot-apache

sudo certbot --apache

You'll be asked to enter an email address for security alerts.

You'll be asked to accept the Terms of Service.

You'll be asked if you want to share your email address with the EFF.

You'll be asked to select your domain name from a list or enter a new one.

You'll be asked if you want to redirect HTTP traffic to HTTPS.

After going through all that you should be done. You should be able to browse your web server via HTTPS.
Your certificates should automatically renew 30 days before they're set to expire. You can test this to make sure it works by running this command:

sudo certbot renew --dry-run

Step 3: Setting Up Mosquitto...

Setting up Mosquitto:

sudo apt-get install mosquitto

sudo apt-get install mosquitto-clients

Once installed you'll want to set a password for authentication. The bellow command will create a new password file called passwd and add the user USERNAME to it and then ask you to set the password.

sudo mosquitto_passwd -c /etc/mosquitto/passwd USERNAME

Later on you might want to add users. To do so run the same command above but omit the '-c' otherwise it'll overwrite the passwd file and you will be back to one user.

Now it's time to edit the Mosquitto configuration file. We'll use the nano editor.

sudo nano /etc/mosquitto/mosquitto.conf

You might want to read up on how to edit your mosquitto.conf file elsewhere as I'm sure I don't know
everything about it but to get you started you'll want to add the below text to it. The contents of mosquitto.conf are in bold.

Add a listener on port 1883. This is the port mostly used for unsecured connections. Because we don't want unsecured connections at all we'll only allow localhost connections. This means only software within the server itself can talk to the MQTT server. Nothing from the outside world.

listener 1883 localhost

Next up is a listener running on port 9001. This is mostly used for websockets. So for example the JavaScript client will use websockets. The ESP32 client won't.

listener 9001

protocol websockets

This is the important bit. We're going to tell Mosquitto to use the certificates we generated back when we installed Apache. We need to do this for all secured listeners. We will only have two. Make sure you replace DOMAIN with your domain you configured earlier.

certfile /etc/letsencrypt/live/DOMAIN/cert.pem

cafile /etc/letsencrypt/live/DOMAIN/chain.pem

keyfile /etc/letsencrypt/live/DOMAIN/privkey.pem

Next up is a listener running on port 8883. This is the default port for secure connections using TLS. We need to specify the same pem files from before.

listener 8883

certfile /etc/letsencrypt/live/DOMAIN/cert.pem

cafile /etc/letsencrypt/live/DOMAIN/chain.pem

keyfile /etc/letsencrypt/live/DOMAIN/privkey.pem

And lastly some extra settings for Mosquitto. We'll set allow_anonymous to false to require authentication. The password_file is the file we were writing passwords to back when we were adding users. acl_file is optional. Remove the comment to enable it and create the acl file using nano. This will let you only allow certain users to read and or write to certain topics. This is why you might have multiple users.

allow_anonymous false

password_file /etc/mosquitto/passwd

# acl_file /etc/mosquitto/acl

So that's all done! Double check it for typos. When you're satisfied everything is good do CTRL+X to exit. It will ask you to save, overwriting the old file (mosquitto.conf) so do that.

Now you can restart the Mosquitto service.

sudo service mosquitto restart

Step 4: Finishing...

Everything should be set up except one last thing. The firewall. If you have one, which you should you need to allow traffic via the ports you're using above. Look that one up on Google but it's quick and easy to do, just allow 9001 (if you're going to use websockets) and allow 8883.

Now you can fire up an MQTT client. For Windows I'm using MQTT.fx. For MQTT.fx I'll enter (my domain) for the address, set the port to 8883, enter my authentication details, enable SSL/TLS (TLSv1.2) and set the certificate setting to CA signed server certificate.

Save and connect. You should get a green bubble and all should be good. If not... you have a lot of troubleshooting to do.