Central broker must be available from the public Internet and must be maximally secured. This means that it must accept encrypted connections only and every client have to be authenticated.
Broker implementation is mosquitto. It is open source software, compliant with MQTT version 3.1 and 3.1.1.
There are two ways how to establish encrypted connection between local and central brokers:
Reason for that VPN tunnel is not preferred, it the tunnel itself. There is always possibility that attacker can compromise server with central broker. In that case, he gains access to many devices which can act as gateway to their local network. If some local broker at other side of the tunnel has some exploitable vulnerability, attacker can do pivoting and spread deeper into network.
This is unacceptable risk for many networks and VPN tunnel should not be used.
First of all, let's install mosquitto:
# apt-get install mosquitto
Central broker should be configured with following attributes:
mosquitto creates its default configuration file
/etc/mosquitto/mosquitto.conf which looks like this:
# 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 pid_file /var/run/mosquitto.pid persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log include_dir /etc/mosquitto/conf.d
It specifies persistence directory. This is useful when clients uses messages with retain feature and broker is restarted for some reason. Persistence will save retain messages over broker restarts.
To configure central broker, create configuration file
/etc/mosquitto/conf.d/central-broker.conf with following content:
# Listen on localhost interface. bind_address localhost # Internet listener witch SSL encryption. listener 1883 <public IP address> cafile <cafile> certfile <certfile> keyfile <keyfile> # listen on VPN interface. Uncomment this to make mosquitto listen on your VPN interface. #listener 1883 <VPN tunnel address> # User authentication. password_file <password file> allow_anonymous false
Following configuration options must be adjusted:
listener- Specify public IP address of your server.
cafile- Path to your CA file. For example:
certfile- Path to your cert file. For example:
keyfile- Path to your key file. For example:
password_file- Path o your password file. For example:
Next part defines on which addresses mosquitto should listen for incoming connections. Default listener listens on localhost. Another
listener directives declare other listen addresses. Listening on localhost is useful for some data processing services which can run on the same machine.
Finally, last part defines user authentication. Unfortunately, authentication is required to clients from all interfaces. It would be great to allow anonymous access from localhost.
To authenticate MQTT users, we need to store user names and passwords somewhere. Mosquitto comes with mosquitto_passwd utility for managing password files. To create new file issue following commad:
# mosquitto_passwd -c /etc/mosquitto/mosquitto.passwd testuser
Command asks you for password and store it in hashed form in appropriate file. Unfortunately, this utility cannot accept password via command line argument, so using some generated password is quite complicated.
I also noticed, that command can accept -U option, which updates password file to use hashed password instead of plain text. I don't know why, but it re-hash already hashed passwords. This makes this feature little bit useless.
By default, mosquitto doesn't use standard systemd unit. It is started using traditional System V script located at
/etc/init.d/mosquitto. You can check this by following command:
# systemctl status mosquitto ● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker Loaded: loaded (/etc/init.d/mosquitto) Active: active (running) since Tue 2015-09-29 14:41:55 CEST; 9s ago CGroup: /system.slice/mosquitto.service └─3037 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
I like modern systemd units, which runs services in foreground. So let's create is. Stop current mosquitto service at first:
# systemctl stop mosquitto # update-rc.d mosquitto remove # rm /etc/init.d/mosquitto
Now create systemd unit file
/etc/systemd/system/mosquitto.service with following content:
[Unit] Description=MQTT v3.1 message broker # If mosquitto is not cofigured to listen on VPN interface. After=network.target # If you are listening on VPN interface, instruct systemd that it should wait # until VPN service is started up. #After=network.target firstname.lastname@example.org #Wants=network.target email@example.com [Service] Type=simple ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf Restart=always [Install] WantedBy=multi-user.target
Lets talk about systemd unit configuration directives little bit. Requires directive specifies that this unit needs active
network.target unit and
firstname.lastname@example.org unit. If any unit listed there is stopped or fails, this unit will be stopped as well. After directive defines activation order. Activation of this unit is delayed until all units listed at After directive are started up.
Both After and Requires directives has same values.
network.target tells that service should be started after network related stuff is initialized (such as initialization of TCP/IP stack). This dependecy is much more important for shut-down procedure. It instructs systemd that this service should be terminated before network connectivity. This ensures that broker can nicely close all its connections. For more information about systemd network targets, you can read official Running Services After the Network is up page.
email@example.com defines dependency on service which provides VPN connectivity. If mosquitto is configured to listen at VPN tunnel IP address, this service must be started up before mosquitto gets activated.
Other configuration directives are self explanatory. If you are not sure about their meaning, check systemd documentation.
After unit file is created, reload systemd configuration, enable unit and start it up:
# systemctl daemon-reload # systemctl enable mosquitto # systemctl start mosquitto