I have been playing for quite a while MQTT to have my Arduino devices communicate with Node-Red for all kinds of automation. Due to the constraints of the Arduino’s, so far I have not been able to effectively secure this as SSL/TLS is not an option on these devices.
Mosquitto supports multiple listeners out of the box though aside from the listener-specific settings all listeners share the same global (e.g. authentication and plugin) settings. To secure my setup and to work on a more permanent solution I needed different settings per listener for which multiple Mosquitto instances are required (i.e. multiple instances of the mosquitto
daemon with their configuration and listeners different IP addresses / ports). This was not difficult on my Debian server but as I did not find much documentation on this I will document the necessary steps in this post.
The Debian mosquitto package does not provide support for multiple instances. SystemD (the sysv-init replacement shipped in recent Debian versions) however makes it quite easy to do this anyway. The steps below describe what has to be done to setup an instance named mosquitto_dev (which I needed for development) but you can name it as you like (just replace mosquitto_dev
below for whatever name you choose). Please note that this assumes that you have already installed and enabled the mosquitto package and not customized it too much.
The first step is to create the necessary directories for the configuration/state and copy the default mqtt config files with:
sudo install -d -m 0755 -o root -g root /etc/mosquitto/conf_dev.d/
sudo install -d -m 0755 -o mosquitto -g root /var/lib/mosquitto_dev
sudo cp -a /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto_dev.conf
sudo cp -a /etc/mosquitto/conf.d/mqtt.conf /etc/mosquitto/conf_dev.d/
sudo cp -a /etc/mosquitto/conf.d/listener_mqtt.conf /etc/mosquitto/conf_dev.d/
Open /etc/mosquitto/mosquitto_dev.conf
with an editor and modify it so that it looks like this:
# Place your local configuration in /etc/mosquitto/conf_dev.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
pid_file /var/run/mosquitto_dev.pid
persistence true
persistence_location /var/lib/mosquitto_dev/
log_dest file /var/log/mosquitto/mosquitto_dev.log
include_dir /etc/mosquitto/conf_dev.d
The actual configuration for the instance will now be under /etc/mosquitto/conf_dev.d
. Review the settings in /etc/mosquitto/conf_dev.d/mqtt.conf
(there is no real need to change it if it wasn’t changed after package installation to make the setup work.
Next edit /etc/mosquitto/conf_dev.d/listener_mqtt.conf
and check that the value of the listener
config parameter are unique (i.e. port/address combination is different from the main instance). To make the new instance listen to the non-standard port 1884 set this to:
listener 1884
To check the configuration, issue the following command to start the daemon with instance configuration in verbose mode:
sudo mosquitto -v -c /etc/mosquitto/mosquitto_dev.conf
This should result in an MQTT server running on the configured port and address and not give any error messages. If so, press CRTL-C to abort the daemon. Also check the logfile /var/log/mosquitto/mosquitto2.log
to ensure there were no issues with the configuration or resolve them and retest before you continue with the next steps to automate it’s start/stop by systemd
.
As already mentioned, the Debian package does not support multiple instances even though the setup turned out to be pretty simple. All that is required is a separate start-up script in /etc/init.d
for each of the instances similar to the one that comes with the Debian package and have it picked up by systemd
, which does that automatically. First, as root, create the file /etc/init.d/mosquitto_dev
with the following content:
#! /bin/sh
# JFL 20190529: patched startup script for multiple mosquitto instances. Rename
# to an instance name, e.g. mosquitto_dev to initiate an instance expecting its
# config in /etc/mosquitto/mosquitto_dev (i.e. based on this script's name)
### BEGIN INIT INFO
# Provides: mosquitto
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Additional mosquitto MQTT v3.1 message broker
# Description:
# This is a message broker that supports version 3.1 of the MQ Telemetry
# Transport (MQTT) protocol.
#
# MQTT provides a method of carrying out messaging using a publish/subscribe
# model. It is lightweight, both in terms of bandwidth usage and ease of
# implementation. This makes it particularly useful at the edge of the network
# where a sensor or other simple device may be implemented using an arduino for
# example.
### END INIT INFO
set -e
NAME=`basename $0`
PIDFILE=/var/run/$NAME.pid
DAEMON=/usr/sbin/mosquitto
CONFFILE=/etc/mosquitto/$NAME.conf
# /etc/init.d/mosquitto: start and stop the mosquitto MQTT message broker
test -x ${DAEMON} || exit 0
umask 022
. /lib/lsb/init-functions
# Are we running from init?
run_by_init() {
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
case "$1" in
start)
if init_is_upstart; then
exit 1
fi
log_daemon_msg "Starting network daemon:" "$NAME"
if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
log_end_msg 0
else
log_end_msg 1
fi
;;
stop)
if init_is_upstart; then
exit 0
fi
log_daemon_msg "Stopping network daemon:" "$NAME"
if start-stop-daemon --stop --quiet --oknodo --pidfile ${PIDFILE}; then
log_end_msg 0
rm -f ${PIDFILE}
else
log_end_msg 1
fi
;;
reload|force-reload)
if init_is_upstart; then
exit 1
fi
log_daemon_msg "Reloading network daemon configuration:" "$NAME"
if start-stop-daemon --stop --signal HUP --quiet --oknodo --pidfile $PIDFILE; then
log_end_msg 0
else
log_end_msg 1
fi
;;
restart)
if init_is_upstart; then
exit 1
fi
log_daemon_msg "Restarting network daemon:" "$NAME"
if start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile ${PIDFILE}; then
rm -f ${PIDFILE}
fi
if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
log_end_msg 0
else
log_end_msg 1
fi
;;
try-restart)
if init_is_upstart; then
exit 1
fi
log_daemon_msg "Restarting Mosquitto message broker" "$NAME"
set +e
start-stop-daemon --stop --quiet --retry 30 --pidfile ${PIDFILE}
RET="$?"
set -e
case $RET in
0)
# old daemon stopped
rm -f ${PIDFILE}
if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
log_end_msg 0
else
log_end_msg 1
fi
;;
1)
# daemon not running
log_progress_msg "(not running)"
log_end_msg 0
;;
*)
# failed to stop
log_progress_msg "(failed to stop)"
log_end_msg 1
;;
esac
;;
status)
if init_is_upstart; then
exit 1
fi
status_of_proc -p ${PIDFILE} ${DAEMON} mosquitto && exit 0 || exit $?
;;
*)
log_action_msg "Usage: $0 {start|stop|reload|force-reload|restart|try-restart|status}"
exit 1
esac
exit 0
This is a copy of the debian package’s /etc/init.d/mosquitto
with minor modifications to use its own name to generate the config and pid file so that it can be used for multiple instances. Make this file owned by root and executable with:
sudo chown root.root /etc/init.d/mosquitto_dev
sudo chmod 755 /etc/init.d/mosquitto_dev
One of the neat things of SystemD (the System and Service Manager used by modern Debian installations) is that it will automatically pick-up service management scripts located in /etc/init.d
upon startup or reload. To have it pick up our new mosquitto_dev instance, simply reload the systemd daemon with:
sudo systemctl daemon-reload
This will generate the necessary systemd configuration based on the added /etc/init.d/mosquitto_dev
and use it start and stop the service. To check that the service is not running execute:
sudo service mosquitto_dev status
Which should show that the service exists but is not yet running, to start it run:
sudo service mosquitto_dev start
After which the service should be running (check by running the status check once more). Systemd will now start this service after a reboot and also ensure it will be restarted when needed. This approach can be repeated to define more instances when needed as long as they listen to different ports/addresses as other services.