Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New entry: Connect to an MQTT broker with TLS encryption #67

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Binary file added images/mqtt/tls-connect-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions mqtt/tls-connect-to-broker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
layout: default
title: Connect to an MQTT broker with TLS encryption

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this title in the Node-RED cookbook, I would expect more information about how to configure the mqtt-broker node for secure connections (e.g. clarification of the meanings of the fields in the TLS configuration dialog, usage of self signed certificates including validation) and not necessarily information about configuring the MQTT broker.

Maybe instead: "Set up the Mosquitto MQTT broker to accept encrypted connections and connect to it from Node-RED"? Or add more information about the configuration options in Node-RED?

slug:
- label: mqtt
url: /#mqtt
- secure connect
---

### Problem

You want to have an encrypted connection to your Mosquitto MQTT broker.

### Solution

Create a valid set of certificates and keys for the broker to use.

Change the configuration of the broker to start a TLS encrypted port (`mqtts`) using the above.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "... to accept encrypted connections..." instead of "...to start a TLS encrypted port..."?


Alter the <code class="node">MQTT Config</code> node, changing the "Server" name to start with `mqtts://`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding mqtts:// to the server name is redundant when ticking the "Enable secure connection" checkbox... Maybe just "Configure the MQTT broker node to establish a secure connection."?


#### Example

**Valid Certificate Creation**

THe easiest approach for this is to use [Let's Encrypt](https://letsencrypt.org/getting-started/). This is beyond the scope of this article but there are plenty of examples and tutorials available on the Internet. For this to work successfully, you also need to be able to use a registered domain name on your internal network because you cannot use Let's Encrypt with IP addresses or non-public domain names.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that it depends very much on the server's configuration whether Let's Encrypt is easy to use. It might be considerably easier to generate self-signed certificates than using Let's Encrypt.

Leave out "...on your internal network..."?

THe

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The internal network part is important. LE doesn't validate if you don't use a domain name for obvious reasons.


Alternatively, you can create a self-signed set of certificates. Again, this is beyond the scope of the article. However, you may need to create a trusted root certificate and provide its public cert instead of the one that Debian provides that is listed in the configuration below.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only "configuration below" that includes a CA cert is the Mosquitto configuration, which does not need a CA cert at all. Instead, when using self-signed certificates, the CA cert needs to be configured in Node-RED, but this aspect is missing in the article.


**Mosquitto configuration**

This assumes that you are using Let's Encrypt or other certificates signed by a root CA already trusted by the Debian operating system.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether the CA is trusted by Debian is not relevant for the Mosquitto configuration.


Note the entries in `<...>` which need to be replaced with your own folders and files.

For Linux installations, this goes into a file of any name of the form `*.conf` in the folder `/etc/mosquitto/conf.d/`. So you have to edit it with root privalages (e.g. using `sudo`). On other platforms, please refer to the [Mosquitto configuration documentation](https://mosquitto.org/man/mosquitto-8.html).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still platform/setup specific. Other Linux distributions, other ways of installing Mosquitto, future versions of Debian all might use different paths. The official Docker container uses Linux and uses other paths.

I'd suggest to narrow it down to a specific installation on a specific OS, or leave it out completely and point to the Mosquitto docs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "Debian Linux versions..." ?


{{ page.lcb }}% raw %}
~~~text
# Default Listener: 1883
port 1883
# Bind the default listener to localhost only if you want to force external connections to be TLS only
#bind_address localhost

# Secure listener
listener 8883
# TLS
## This is standard and should always be this when using Let's Encrypt
## If using a self-signed certificate, this needs to be your custom Root CA public certificate
cafile /etc/ssl/certs/DST_Root_CA_X3.pem
Comment on lines +48 to +50

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which cafile is configured here is only relevant for certificate based user authentication, it is not relevant for encrypted connections. The cafile entry can be left out completely. The only requirement is to have either a cafile-entry or a capath-entry in the listener configuration to enable SSL/TLS mode. This can be something like capath /var/empty.

## These are from your installation of LE

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...or the self-signed certs

certfile /<path-to-LE-cert-files>/<fullchain>.cer
keyfile /<path-to-LE-cert-files>/<private-key-name>.key
## Forces use of modern version of TLS to avoid security issues
tls_version tlsv1.2

## Forces ALL CLIENTs using this port to provide a valid certificate - change the node config to allow this from NR
#require_certificate true
TotallyInformation marked this conversation as resolved.
Show resolved Hide resolved
~~~
{: .shell}
{{ page.lcb }}% endraw %}

After making these changes, you have to restart the mosquitto broker. On Linux, you can usr the command:

~~~text
[~]$ sudo systemctl restart mosquitto
TotallyInformation marked this conversation as resolved.
Show resolved Hide resolved
~~~
{: .shell}

Other platforms, including Docker-based installations may be different.

**<code class="node">MQTT Config</code> node configuration**

![](/images/mqtt/tls-connect-1.png)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This image shows a configuration where the certificate is not validated.


Notes

* You need to use the IP name rather than IP address in the server name if using Let's Encrypt (otherwise the certificate isn't valid).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IP name -> domain name?
More specific: If using a Let's Encrypt certificate, one needs to use the domain name the certificate was issued for.

* You need to change the server name to a url, prefixed with `mqtts://`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, this is not necessary when enabling the "Enable secure connection" flag, which needs to be enabled to have the server certificate verified.


This disables the port field, I change that first to `8883` to remind me what the correct port will be.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another advantage of ticking the "Enable secure connection" checkbox: Because there is no need to add the protocol to the Server field, the port field stays enabled.


If you need to change the port to something other than the default, include it on the URL:

```
mqtts://broker.domain.tld:9999
```

* You **do not** need to set the "Enable secure connection" flag unless you want to authenticate the Node-RED client to the broker (if you set the require_certificate to true for example).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line is still misleading, or at least prominently advertising a configuration where the server certificate is not validated. In the context of this article about encrypted connections, the note that the "Enable secure connection" flag is only relevant for authenticating clients looks even wrong to me, in two regards:
(1) The flag and the TLS configuration it reveals are relevant for both, the TLS connection and the certificate based user authentication. The article explicitly mentions self-signed certificates, and to validate these, they must be configured here.
(2) The flag is not necessary for user authentication that is not certificate based.

* If you do not set the "Enable secure connection" flag however, the node will not validate the certificate chain.

### Discussion

Mosquitto allows you to create multiple ports for connectivity. This lets you use websockets and TLS encrypted connections in addition to the default connection.

The folder `/etc/mosquitto/conf.d/` can contain any number of config files which will all be applied so that you can split your custom changes into separate files if you like.

Just remember that once you use a custom file to set ports, the default port (1883) is no longer active so you have to specify that as well if you still want it to be active. The standard port for MQTT over TLS (MQTTS) is 8883. You can, however, use other ports if they are not in use. Make sure you use a port number greater than 1024 otherwise the broker must be run with root privalages which is not recommended for security reasons.
TotallyInformation marked this conversation as resolved.
Show resolved Hide resolved

You can check which ports the broker has opened with the command:

~~~text
[~]$ sudo netstat -lptu | grep mosquitto
tcp 0 0 0.0.0.0:8883 0.0.0.0:* LISTEN 17697/mosquitto
tcp 0 0 0.0.0.0:1883 0.0.0.0:* LISTEN 17697/mosquitto
tcp6 0 0 [::]:8883 [::]:* LISTEN 17697/mosquitto
tcp6 0 0 [::]:1883 [::]:* LISTEN 17697/mosquitto
~~~
{: .shell}

You can test whether the server device is allowing connections on a port by using telnet from another device.

~~~text
[~]$ telnet <ip-name> 8883
~~~
{: .shell}

If the connection opens, then the target device is accepting connections on that port.

Note that the operating system automatically opens the required ports through the devices firewall.

If you want to monitor what the broker is doing, including seeing which clients connect to which ports, use the following command:

~~~text
[~]$ sudo tail /var/log/mosquitto/mosquitto.log -f
TotallyInformation marked this conversation as resolved.
Show resolved Hide resolved
~~~
{: .shell}