MQTT Bridge with Mosquitto and nginx
Author: | akeil |
---|---|
Date: | 2017-03-05 |
Version: | 1 |
Set up a Mosquitto [1] MQTT broker which is available in the internet. nginx [2] is used as a reverse proxy and to handle SSL encryption. An additional Mosquitto instance on the local network is used as a bridge to forward MQTT messages from the local network to the internet and vice versa.
This allows to easily connect devices which do not support authentication or encryption on the local network with with other devices or services connected via the internet.
Mosquitto (public)
The public Mosquitto instance needs some configuration changes. The configuration file is located at /etc/mosquitto/mosquitto.conf.
Since we will be running behind nginx, make the MQTT service listen on localhost only:
bind_address localhost
Change MQTT Service Port (optional)
Change the default port number from 1883 to something else:
listener 9883
This is strictly not necessary as the default is normally used for unencrypted connections and we will configure nginx to listen on port 8883 (the default port for MQTT over SSL).
Finally, set up mosquitto to require authentication with username and password:
allow_anonymous false password_file = /etc/mosquitto/passwords
Create the password file and the first user with:
# mosquitto_passwd -c /etc/mosquitto/passwords username
(You will be prompted for a password.)
To test authentication, attempt to subscribe to any topic. A valid username and password should work, a subscription attempt with invalid credentials should be rejected.
$ mosquitto_sub -p 9883 -t '#' -u username -P secret
nginx
Configure nginx as a reverse proxy for TCP streams [3] and to terminate the SSL encryption [4].
First, define Mosquitto as an upstream service. Use either 1883 or whatever alternative port is set for the default (unencrypted) MQTT service.
Next, set up a server to listen on port 8883 (MQTT over SSL) and tell it to pass requests to the upstream service.
To enable SSL, configure the location of the SSL certificate and private key. Set additional SSL options as needed; in this case taken from cipherli.st [5].
Note
The example assumes a Let's Encrypt [6] certificate.
The nginx configuration looks like this (with stream being a toplevel-directive):
stream { upstream mosquitto { server localhost:9883; } server { listen 8883 ssl; proxy_pass mosquitto; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # from https://cipherli.st/ ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_dhparam /etc/ssl/certs/dhparam.pem; } }
MQTT-Bridge
On the local (LAN) MQTT broker, edit the configuration to add a bridge:
connection bridge address <ip-or-hostname>:8883 remote_username foo remote_password xxx remote_clientid LAN-broker local_client public-broker # must specify cafile to enable SSL bridge_cafile /etc/ssl/certs/DST_Root_CA_X3.pem bridge_insecure false # topic mappings # topic PATTERN [out|in|both] QOS LOCAL-PREFIX REMOTE-PREFIX topic foo both 0
Remember to specify a port with the bridge connection, otherwise 1883 is used. The bridge_cafile setting is required to enable SSL. See Mosquitto conf [7] for more details.
Restart mosquitto to apply the new settings.
The Mosquitto log file for the local broker should contain a line like this:
Connecting bridge bridge (<ip-or-hostname>:8883)
On the remote side, the bridge looks like a normal client connection:
New client connected from ::1 as <remote_clientid> (c0, k60, u'<remote_username>').
To test, subscribe to foo on the local broker...
$ mosquitto_sub -h <localbroker> -t foo
... and publish to foo on the remote broker:
$ mosquitto_pub -h <remotebroker> -p 8883 -u username -P secret -t foo -m test
You should see the "test" message appear on the session subscribed to the local broker.
Topic Mappings
A topic mapping consists of a pattern to subscribe to, the direction of the messages (in, out or both and a QoS setting. Additionally, a local prefix and remote prefix can be defined.
Only the pattern is required. Direction defaults to out, QoS defaults to 0.
Examples:
# forward all 'foo' messages to the bridge topic foo topic foo out 0 # same as above # two-way forwarding for everything under "foo" topic foo/# both # (only) receive all foo messages topic foo/# in
This can be used to forward only selected topics and to insert topics from the bridged broker into the local topic tree.
If no topic directive is present, no messages are forwarded across the bridge.
[1] | https://mosquitto.org/ |
[2] | https://nginx.com/ |
[3] | https://www.nginx.com/resources/admin-guide/tcp-load-balancing/ |
[4] | https://www.nginx.com/resources/admin-guide/nginx-tcp-ssl-termination/ |
[5] | https://cipherli.st/ |
[6] | https://letsencrypt.org/ |
[7] | https://mosquitto.org/man/mosquitto-conf-5.html |