Baol's little place on the Internet
Complex software needs no complicated solutions.

Private Mosquitto using docker

New to docker?

Me too!!!

So, brave as we are we fire up a linux server (a free AWS instance for now, may as well be a digital ocean basic server or something even cheaper) and just install and start it

ssh ...
sudo yum/apt install docker
sudo service docker start

It happens that downloading and starting services with docker is straightforward. First you need to know the name for your service and than just search for it on https://hub.docker.com

One of the most popular dockerized mosquitto at the time that we write this is https://hub.docker.com/r/toke/mosquitto/

So we simply read the page we just mentioned, and install it with

sudo docker pull toke/mosquitto

Et voilà!

We could now run it as it is, but we may want to configure it first to better suite our needs! After all the title says “Private”!

But we cannot wait, so we do it anyway :)

sudo docker run --name mqtt toke/mosquitto

We started the docker container without mapping any port or volumes, so that the service is not yet accessible from the outside, but we can still do something interesting with it, like having a look at what’s inside firing up a shell into the container:

sudo docker exec -i -t mqtt /bin/bash

Interestingly enough there is no init process, just our mosquitto server, and our home points to the place where the source for mosquitto was unpacked. Wandering around we find out that this image keeps the configuration in /etc/mosquitto.

When we are done, we need to stop and remove the instance from docker so that we can run it again later with different, more useful, options:

sudo docker stop mqtt
sudo docker rm mqtt

Let’s create some empty folders that we will use later as docker volumes:

sudo mkdir -p /srv/toke-mosquitto/{config,data,log}

Obtaining a certificate

In order for our service to be (hopefully) private we need to set up a secure channel. We need a certificate.

My favourite at the moment is https://letsencrypt.org/

With a few commands we are set-up:

sudo yum install epel-release nginx

We configure our virtualhost in /etc/nginx/conf.d/virtual.conf as we usually do and we open the port 443 in the firewall (or in the Security groups if we are using AWS).

sudo service nginx restart
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
./certbot-auto --nginx -d <my-domain> certonly [--debug]

Our new certificate should now be in /etc/letsencrypt folder.

This is not a full guide on how to obtain a certificate, but following the errors that you will likely get on the screen, should allow you to end up with working certificates.

We will need to copy the certificates in a place accessible by docker:

sudo cp -a /etc/letsencrypt /srv/toke-mosquitto/config/

(we should not forget to do this again when we renew the certs)

Following the instructions here https://mosquitto.org/2015/12/using-lets-encrypt-certificates-with-mosquitto/ we also create the chain-ca.pem file needed by mosquitto:

cd /srv/toke-mosquitto/config
wget https://letsencrypt.org/certs/isrgrootx1.pem
sudo bash -c 'cat /letsencrypt/live/<mydomain>/chain.pem isrgrootx1.pem > chain-ca.pem'

Note: We learn as we do, but looking at the OwnTracks website, for example, and at the fact that MQTT applications use the certificates in a different way (the server is not public, so there is no need for a public CA), it may be that we do not need to go through letsencrypt, we could just have used some other tool to generate owr own CA, certificates and even pre shared keys for our devices… And with less problems of expired keys… Next time we’ll do better!

Configuring Mosquitto

Merging infos from https://github.com/toke/docker-mosquitto and few other places we can to override the mosquitto configuration and also persist logs using docker volumes

In which we can put our own mosquitto.conf and access control files.

listener 8883
allow_anonymous false
password_file /mqtt/config/pwfile
cafile /mqtt/config/chain-ca.pem
certfile /mqtt/config/letsencrypt/live/<my-domain>/cert.pem
keyfile /mqtt/config/letsencrypt/live/<my-domain>/privkey.pem

To create the missing pwfile we can use the mosquitto_pass utility using another machine or sudo docker exec -i -t mqtt /bin/bash on an image with the config file mounted rw.

Note: on AWS we also need to open port 8883 in the security group to make it reachable.

Ready to start

We are ready to start our mosquitto with ssl support using docker:

sudo docker run -ti -p 8883:8883 -v /srv/toke-mosquitto/config/:/mqtt/config:ro -d --name mqtt toke/mosquitto

And we can test that it works from our laptop by issuing the following command:

mosquitto_pub -h <my-domain> -t test/topic -m test -p 8883 --capath /etc/ssl/certs/

To troubleshoot issue with SSL we can also use

wget https://letsencrypt.org/certs/isrgrootx1.pem
openssl s_client -connect <my-domain>:8883 -CAfile isrgrootx1.pem

Happy hacking!

What I learned

I learned a couple of things while doing this:

  • Next time I should generate my own CA and certificates, and also authenticate users with certificates instead of passwords.

  • Instead of writing pages like this I could just have written a much shorter Dockerfile to do all the required steps!