Setting up a Mosquitto MQTT server
I recently found myself setting up a mosquitto instance (yep, for this) due to a migration we're in the middle of doing and it got quite interesting, so I thought I'd post about it here. This post is also partly documentation of what I did and why, just in case future people come across it and wonder how it's setup, though I have tried to make it fairly self-documenting.
At first, I started by doing sudo apt install mosquitto and seeing if it would work. I can't remember if it did or not, but it certainly didn't after I played around with the configuration files. To this end, I decided that enough was enough and I turned the entire configuration upside-down. First up, I needed to disable the existing sysV init-based service that ships with the mosquitto package:
sudo systemctl stop mosquitto # Just in case
sudo systemctl start mosquitto
Next, I wrote a new systemd service file:
[Unit]
Description=Mosquitto MQTT Broker
After=syslog.target rsyslog.target network.target
[Service]
Type=simple
PIDFile=/var/run/mosquitto/mosquitto.pid
User=mosquitto
PermissionsStartOnly=true
ExecStartPre=-/bin/mkdir /run/mosquitto
ExecStartPre=/bin/chown -R mosquitto:mosquitto /run/mosquitto
ExecStart=/usr/sbin/mosquitto --config-file /etc/mosquitto/mosquitto.conf
ExecReload=/bin/kill -s HUP $MAINPID
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=mosquitto
[Install]
WantedBy=multi-user.target
This is broadly similar to the service file I developed in my earlier tutorial post, but it's slightly more complicated.
For one, I use PermissionsStartOnly=true and a series of ExecStartPre directives to allow mosquitto to create a PID file in a directory in /run. /run is a special directory on Linux for PID files and other such things, but normally only root can modify it. mosquitto will be running under the mosquitto user (surprise surprise), so we need to create a subdirectory for it and chown it so that it has write permissions.
A PID file is just a regular file on disk that contains the PID (Process IDentifier) number of the primary process of a system service. System service managers such as systemd and OpenRC use this number to manage the health of the service while it's running and send it various signals (such as to ask it to reload its configuration file).
With this in place, I then added an rsyslog definition at /etc/rsyslog.d/mosquitto.conf to tell it where to put the log files:
if $programname == 'mosquitto' then /var/log/mosquitto/mosquitto.log
if $programname == 'mosquitto' then stop
Thinking about it, I should probably check that a log rotation definition file is also in place.
Just in case, I then chowned the pre-existing log files to ensure that rsyslog could read & write to it:
sudo chown -R syslog: /var/log/mosquitto
Then, I filled out /etc/mosquitto/mosquitto.conf with a few extra directives and restarted the service. Here's the full configuration file:
# 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
# NOTE: We can't use tab characters here, as mosquitto doesn't like it.
pid_file /run/mosquitto/mosquitto.pid
# Persistence configuration
persistence true
persistence_location /var/lib/mosquitto/
# Not a file today, thanks
# Log files will actually end up at /var/llog/mosquitto/mosquitto.log, but will go via syslog
# See /etc/rsyslog.d/mosquitto.conf
#log_dest file /var/log/mosquitto/mosquitto.log
log_dest syslog
include_dir /etc/mosquitto/conf.d
# Documentation: https://mosquitto.org/man/mosquitto-conf-5.html
# Require a username / password to connect
allow_anonymous false
# ....which are stored in the following file
password_file /etc/mosquitto/mosquitto_users
# Make a log entry when a client connects & disconnects, to aid debugging
connection_messages true
# TLS configuration
# Disabled at the moment, since we don't yet have a letsencrypt cert
# NOTE: I don't think that the sensors currently connect over TLS. We should probably fix this.
# TODO: Point these at letsencrypt
#cafile /etc/mosquitto/certs/ca.crt
#certfile /etc/mosquitto/certs/hostname.localdomain.crt
#keyfile /etc/mosquitto/certs/hostname.localdomain.key
As you can tell, I've still got some work to do here - namely the TLS setup. It's a bit of a chicken-and-egg problem, because I need the domain name to be pointing at the MQTT server in order to get a Let's Encrypt TLS certificate, but that'll break all the sensors using the current one..... I'm sure I'll figure it out.
But wait! We forgot the user accounts. Before I started the new service, I added some user accounts for client applications to connect with:
sudo mosquitto_passwd /etc/mosquitto/mosquitto_users username1
sudo mosquitto_passwd /etc/mosquitto/mosquitto_users username1
The mosquitto_passwd program prompts for a password - that way you don't end up with the passwords in your ~/.bash_history file.
With all that taken care of, I started the systemd service:
sudo systemctl daemon-reload
sudo systemctl start mosquitto-broker.service
Of course, I ended up doing a considerable amount of debugging in between all this - I've edited it down to make it more readable and fit better in a blog post :P
Lastly, because I'm paranoid, I double-checked that it was running with htop and netstat:
sudo netstat -peanut | grep -i mosquitto
tcp 0 0 0.0.0.0:1883 0.0.0.0:* LISTEN 112 2676558 5246/mosquitto
tcp 0 0 x.y.z.w:1883 x.y.z.w:54657 ESTABLISHED 112 2870033 1234/mosquitto
tcp 0 0 x.y.z.w:1883 x.y.z.w:39365 ESTABLISHED 112 2987984 1234/mosquitto
tcp 0 0 x.y.z.w:1883 x.y.z.w:58428 ESTABLISHED 112 2999427 1234/mosquitto
tcp6 0 0 :::1883 :::* LISTEN 112 2676559 1234/mosquitto
...no idea why it want to connect to itself, but hey! Whatever floats its boat.