MQTT Binding and SSL

MQTT Binding and SSL

Purpose

Until the MQTT binding 2.0 comes along, we have to do with the 1.9 version. Unfortunately this binding, despite having the ssl option is not able to implement it. I will show how to create your own ssl certificate and key, how to use keytool to create a keystore and a truststore for private and public keys for Java and finally how to modify the OpenHAB configuration to load these keys to enable ssl.

Prerequisites

  • The MQTT Binding is installed and running
  • openHAB is running on a Linux based operating system (This is because I have no idea if openssl and keytool are available under Windows)

Configuration

Create the Java jsk keystore

This information was from:

In your /etc folder, in the console enter the following command:
If you choose to use another folder make sure to use the same one for the rest
of the tutorial.

sudo keytool -genkeypair -alias myopenhab -keyalg RSA -keystore keystore.jks

You can replace the alias and the filename with other values
but keep a note as we will need them later.

You will be asked for a password at the start. Make note of it.

you will prompted for information (for example):

Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:Brooklyn
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Brooklyn Company
Organizational Unit Name (eg, section) []:Technology Division
Common Name (e.g. server FQDN or YOUR name) []:examplebrooklyn.com
Email Address []:

You can enter dummy values, they don’t really matter.

Press enter when asked for the second password.

you should now have a file keystore.jks in your /etc folder.

Create the Java jsk truststore

This information was obtained from:

In order to create the Java truststore, you will need the public certificate
of your MQTT broker.
In my case, as I am using cloudMQTT, the public certificate is available for download
at: https://support.comodo.com/index.php?/Knowledgebase/Article/View/854/75/root-addtrustexternalcaroot

Internally, behind my firewall and reverse proxy, I am using a mosquitto broker
without SSL.

I needed to use a public broker for use with Owntracks as my mobile
(cellular) network provider doesn’t allow connection to my home server
on ports like 1883 or 8883. Go figure :frowning:

Once you have obtained the public certificate of your MQTT broker as
a *.cert file (CloudMQTT file is addtrustexternalroot.cert), you need
to wrap it in a Java truststore.

Make sure that your certificate file is in your /etc folder.

Again, in the console in your /etc folder enter the following:

sudo keytool -import -alias myopenhab -file addtrustexternalroot.cert -storetype JKS -keystore.jsk

You will be asked for a password at the start. Make note of it.

Again, you can replace the alias and truststore.jsk file names with your
own values but make note of them. Replace addtrustexternalroot.cert with
the file name of the public certificate you obtained from your MQTT broker.

Check and verify

In your **/etc** folder you should now have:
  • keystore.jks (keystore_password)
  • publicCA.cert
  • truststore.jks (truststore_password)

Modify OpenHAB environment

This is taken from:

In you console enter the following:

cd /usr/share/openhab2/runtime/bin
sudo nano setenv

scroll down and locate the section starting with:
export JAVA_OPTS="${JAVA_OPTS}"

Add the following lines to the list:

  -Dcom.ibm.ssl.trustManager=SunX509
  -Dcom.ibm.ssl.keyManager=SunX509
  -Dcom.ibm.ssl.contextProvider=SunJSSE
  -Dcom.ibm.ssl.keyStore=/etc/keystore.jks
  -Dcom.ibm.ssl.keyStorePassword=keystore_password
  -Dcom.ibm.ssl.keyStoreType=JKS
  -Dcom.ibm.ssl.keyStoreProvider=SUN
  -Dcom.ibm.ssl.trustStore=/etc/truststore.jks
  -Dcom.ibm.ssl.trustStorePassword=truststore_password
  -Dcom.ibm.ssl.trustStoreType=JKS
  -Dcom.ibm.ssl.trustStoreProvider=SUN

exit nano and save

restart openhab

sudo systemctl restart openhab2.service

Configure your MQTT binding

In your service folder edit the mqtt.cfg file
In my case my MQTT broker is:

cloudmqtt.url=ssl://m23.cloudmqtt.com:24879
cloudmqtt.clientId:openhab2
cloudmqtt.user=username
cloudmqtt.pwd=password

If everything went allright, you should now be connected to a
MQTT broker via SSL!!

4 Likes

Great tutorial.

Since I run the broker and OH on the same machine, I just kept an unencrypted listener that only binds to localhost. Anything outside localhost has to use certs.

But it is great to have other options. This would be great for those who are using off-site brokers like CloudMQTT.

Thanks for posting!

Thank you Rich, coming from you it means a lot.
I only put things together from different posts and tried to present it nicely.

I didn’t want to use a cloud MQTT provider but I had to because of my cellular provider…

Regards

@vzorglub Vincent,

Let me concur that you have put together a great tutorial. I’ve been reading what seems like hundreds of pages to try to distill down what I want to do… and so far I’ve done nothing but go down a rabbit hole!

Disclaimers:

  • I saw your note re: your cellular provider not allowing 8883… not sure whether I’ll have that issue. I have both Verizon and AT&T.

  • I’m trying to set up my internal server as my MQTTS broker rather than go with a cloud solution. Honestly, what I’m trying to achieve is to have OwnTracks send our location to openHAB.

  • The reason I went down the private MQTT broker route is that I read on the OwnTracks booklet that some cloud MQTT brokers may cap the amount of traffic you can send unless you subscribe to a paid service which increases the allowance. I may be willing to do that, but before I fork out any money, I want to create a testing environment to ensure I can actually get all the components configured and working successfully and reliably.

Where I am in my own journey:

  • I have set up my DDNS.
  • I have generated the certificates for my domain using Certbot
  • I have not yet, but I will set up a reverse proxy on my home network
  • I will set up the port forwarding for 8883 on my router

My question is, how does your procedure vary if I want to use “my” certificates?

Alternatively, when using your cloud MQTT broker, how are you using that information in your openHAB environment… and how are you receiving it? This goes to my point that all I’m trying to do is have the OwnTracks information end up in the OwnTracks binding so I can set up some geofencing rules (THE entire reason I’ve gone down this home automation hub setup in the first place!).

TIA!

Regards.

Mike

I use cloudMQTT and they will not cap the usage (A least not for our level)
I did not use my own server as I don’t wan’t to port forward anything
I will eventually use my own server when I can set-up another line and network with a physically separated server

This is my set-up:

mqtt.cfg:

# URL to the MQTT broker, e.g. tcp://localhost:1883 or ssl://localhost:8883
cloudmqtt.url=ssl://m23.cloudmqtt.com:24879
# Optional. Client id (max 23 chars) to use when connecting to the broker.
# If not provided a random default is generated.
cloudmqtt.clientId=openhab2

# Optional. True or false. If set to true, allows the use of clientId values
# up to 65535 characters long. Defaults to false.
# NOTE: clientId values longer than 23 characters may not be supported by all
# MQTT servers. Check the server documentation.
#<broker>.allowLongerClientIds=false

# Optional. User id to authenticate with the broker.
cloudmqtt.user=XXXXXXX

# Optional. Password to authenticate with the broker.
cloudmqtt.pwd=XXXXXXXXX

# Optional. Set the quality of service level for sending messages to this broker.
# Possible values are 0 (Deliver at most once),1 (Deliver at least once) or 2
# (Deliver exactly once). Defaults to 0.
#<broker>.qos=<qos>

# Optional. True or false. Defines if the broker should retain the messages sent to
# it. Defaults to false.
#<broker>.retain=<retain>

# Optional. True or false. Defines if messages are published asynchronously or
# synchronously. Defaults to true.
#<broker>.async=<async>

# Optional. Defines the last will and testament that is sent when this client goes offline
# Format: topic:message:qos:retained <br/>
#<broker>.lwt=<last will definition>

My items:

Switch PresenceVincent_Owntracks2  { mqttitude="cloudmqtt:owntracks/prafduiu/vincent/event:home" }

Works a treat!

I had your exact setup for a time. I don’t any longer so I may be miss remembering things below. If something doesn’t make sense I’ll try to resurrect that old machine and pull out the Mosquitto config.

  • I created server and client certs signed by the same CA
  • Configured the default listener (1883) without SSL/TLS
  • Configured the host based firewall to only allow connections from localhost
  • Configured openHAB to connect to 1883
  • Configured an additional listener (8883) with the certs and require_certificate set to true. I can’t remember if I set use_identity_as_username to true
  • Set up port forwarding on port 8883 so the OwnTracks clients can connect to the encrypted port
  • Copied the client certs to the phones and set up OwnTracks to use them to authenticate with the broker
  • I created ACLs to forbid anonymous clients from reading or writing any topics and set up ACLs on the user/client ids so the OwnTracks clients could only read/write to those topics it needs and openHAB has access to all of the topics. This part was probably overkill but I did it to figure out how.

Because openHAB connects unencrypted to 1883 there is no need to go through the process of adding the CA to the JVM like in the OP. Forcing the clients to have a cert to connect is just a little extra bit of protection to prevent password based attacks. ACLs mitigate the damage that can be done should someone compromise the broker connection.

@rlkoshak Rich,

The only additional component I’m trying to add is a reverse proxy as the 8883 listener. It then would forward the traffic to my broker on 1883. The reason I’m trying to do this is to protect my broker from being attacked directly. Even though ACLs protect me from anybody maliciously subscribing to my topics, or even someone successfully publishing (in essence phishing me), without the proxy or firewall in the middle, someone can maliciously generate “valid” MQTT traffic on my_Domain_or_IP:8883 and it could keep the broker so busy “ignoring” that traffic that it’s locked up and I can’t use it for my home automation communication. In essence, a DDoS situation. If they do attack me on 8883, at least my “local” function is not affected.

But I’m having a bear of a time setting up this proxy successfully!

I’m probably being paranoid. Who would want to be mean to me? Me!? :wink: But what I can see happening is that as IoT gets more and more prevalent and more people set up some sort of automation hub, it will attract the bad element. They’ll do it because they can and because they will try to extract some kind of ransom to liberate our automation hubs.

{soapbox} This is like my colleagues who owned Macs long ago and tooted their horn that Macs don’t get hacked. And I would say, it’s because Windows is the bigger target and where hackers can get the biggest bang for their buck. As soon as Macs got a large enough share of the market, guess what? All of the sudden they weren’t hack-proof any more. As soon as home automation hubs become more prevalent, they’ll attract an audience, a bad audience! {/soapbox}

Cheers!

Mike

Only if they have your client cert, in which case you have bigger problems already. They could do a DOS of sorts with repeated attempts to connect but if they don’t have the client cert the broker will not allow the connection so they can’t generate any additional mqtt traffic. And it doesn’t take much to drop a connection that fails the cert test.

And there is a solution to this as well, though it will take just a little bit of work. Set up fail2ban to monitor misquitto’s log for failed connection attempts and it will set up a firewall role to drop any future attempts from that IP for a time or forever. It takes even less CPU too drop a connection from a know bad IP than it does to drop a connection that doesn’t have a cert or the wrong cert. And with fail2ban that processing is out on iptables. What I don’t know is whether someone has written a monitor for Misquitto yet.

Finally, I’m not certain but I would be surprised if Mosquitto’s didn’t run each listener in a separate thread so an attack on one would at most tie up one CPU. Even an RPi had more than one so you overall performance may be degraded but your lock listener should still work. I don’t know this for sure so don’t go writing me on it.

There are lots of ways to skin this cat (sorry for the idiom non-us users).

Another way would be to use the Cloud MQTT service for your external MQTT stuff and bridge that to your local MQTT. I don’t think that even requires port forwards.

Personally, I think you are underestimating the amount if traffic it would take to break your MQTT server with a DOS. If you have someone after you with that sort of fire power then they can cause you no end of problems lots of other ways that would hurt more than DOSing your MQTT server.

Risk = impact a vulnerability is exploited * likelihood of the attack

In this case the impact is your MQTT server is down for awhile for external traffic (i.e. own tracks) and your MQTT performance is internally is degraded. I’d say in the 3-5 year time frame the likelihood of a successful attack is very low.

So you need to decide whether your mitigation (reverse proxying mqtt) is less expensive than the risk. If it isn’t then you are not being prudent with your time and resources.

I’ve seen some blog postings online talking about some people successfully proxying mqtt through nginx, but it was using features only available in the commercial version of nginx. So I’m not hopeful that it is even possible to do so using the community version of nginx.

@rlkoshak Rich - You’ve swayed me! Simpler is always better.

@vzorglub Vincent - I don’t think I need “your” setup for what I’m trying to do since I’m trying to bridge to an external server and set up trust with an external entity.

I set up a DDNS assigning a domain name to my WAN IP. The setup also runs a job that updates the DDNS if my IP changes.

I have my port forwarding on my router to forward traffic coming in on 8883 to be forwarded to my local server port 8883.

I used Certbot (Let’s Encrypt) to generate the certificates for my domain (i.e., self-signed since I’m the CA for my server). Certbot generates four certificate (PEM) files:

  1. privkey.pem
  2. fullchain.pem contains all certificates, including server certificate (aka leaf certificate or end-entity certificate). The CA certificate is in the content of fullchain.pem, specifically, the first certificate in the file. (PEM files are editable and contain public keys as certificates between BEGIN/END lines(these lines belong to the PEM file).
  3. cert.pem contains the server certificate by itself
  4. chain.pem contains the additional intermediate certificate or certificates that web browsers will need in order to validate the server certificate.

Certbot cannot create client certificates (yet)… but OwnTracks doesn’t actually require this.

Question: When I generate the certificates, does the domain get “incorporated” into the certificates? If I generate certificates for my DDNS domain, but I really am having my local server authenticate, do these certificates “work” on my localhost broker?

I followed Vincent’s procedure and created the keystore and truststore. Question: My Certbot certificates (i.e., the one I used to generate the truststore) expires in 90 days. I set up the Certbot job to automatically renew the certificates. Will I need to recreate the truststore when my certificates refresh? I can easily do that by putting the truststore command in my Certbot post hook script. I just need to know if that is required.

Disclaimer - I am completely baffled by this entire certificate mechanism. I’ve read and read… but my eyes glaze over. I just need a good tutorial with good visuals and diagrams. Haven’t found that succinct tutorial!

I added these lines to my mqtt.cfg file:
openhab_sslbroker.clientId=“openhab2”
openhab_sslbroker.user=“openhabian”
openhab_sslbroker.pwd=“PASSWORD”
openhab_sslbroker.url=“ssl://localhost:8883”

In the openHAB log viewer I get ‘Connection refused’:

2018-04-29 02:27:35.929 [INFO ] [t.mqtt.internal.MqttBrokerConnection] - Starting MQTT broker connection 'openhab_sslbroker'
2018-04-29 02:27:37.101 [ERROR] [penhab.io.transport.mqtt.MqttService] - Error starting broker connection
org.eclipse.paho.client.mqttv3.MqttException: Unable to connect to server
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:79) [224:org.openhab.io.transport.mqtt:1.11.0]
	at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:86) [224:org.openhab.io.transport.mqtt:1.11.0]
	at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:650) [224:org.openhab.io.transport.mqtt:1.11.0]
	at java.lang.Thread.run(Thread.java:748) [?:?]
Caused by: java.net.ConnectException: Connection refused (Connection refused)
	at java.net.PlainSocketImpl.socketConnect(Native Method) ~[?:?]
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[?:?]
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[?:?]
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[?:?]
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[?:?]
	at java.net.Socket.connect(Socket.java:589) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:673) ~[?:?]
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:70) ~[?:?]
	... 3 more

I tried a mqtt client to confirm. It asked so I chose TLSv1. Is that right? Same result - ‘Connection refused’. It’s asking me to the user credentials - which I’ve done. I highly suspect I’ve screwed up the certificate stuff.

2018-04-29 02:34:38,133  INFO --- MqttFX ClientModel             : MqttClient with ID MQTT_FX_Client assigned.
2018-04-29 02:34:40,240 ERROR --- MqttFX ClientModel             : Error when connecting
org.eclipse.paho.client.mqttv3.MqttException: Unable to connect to server
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:94) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:103) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:701) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [?:1.8.0_162]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [?:1.8.0_162]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_162]
Caused by: java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[?:1.8.0_162]
	at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.PlainSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.SocksSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.Socket.connect(Unknown Source) ~[?:1.8.0_162]
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:80) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	... 9 more
2018-04-29 02:34:40,242 ERROR --- MqttFX ClientModel             : Please verify your Settings (e.g. Broker Address, Broker Port & Client ID) and the user credentials!
org.eclipse.paho.client.mqttv3.MqttException: Unable to connect to server
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:94) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:103) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:701) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[?:1.8.0_162]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [?:1.8.0_162]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [?:1.8.0_162]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_162]
Caused by: java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[?:1.8.0_162]
	at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source) ~[?:1.8.0_162]
	at java.net.AbstractPlainSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.PlainSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.SocksSocketImpl.connect(Unknown Source) ~[?:1.8.0_162]
	at java.net.Socket.connect(Unknown Source) ~[?:1.8.0_162]
	at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:80) ~[org.eclipse.paho.client.mqttv3-1.2.0.jar:?]
	... 9 more
2018-04-29 02:34:40,246  INFO --- ScriptsController              : Clear console.
2018-04-29 02:34:40,247 ERROR --- BrokerConnectService           : Unable to connect to server

Help!

Mike

The certificate validates that the host you are talking to is the host that should be associated with a given domain name. So even while on your LAN, you must access the server through the DynDNS name or else the cert will be treated as invalid.

Put another way, the cert is for your DynDNS name, not localhost.

Yes, I’m pretty sure you do.

See above. The purpose of the cert is to verify that the machine at a given domain name is who it says it is. Therefore the cert is all about the domain name. You got a cert for some DynDNS domain name but you are accessing it using localhost.

At a super high level the way a cert works is:

  • a client prooves to a certificate authority (LetsEncrypt in this case) that they own a given domain name
  • the CA signs a certificate and gives it to you. The certificate says “I, LetsEncrypt, have proven to my satisfaction that meingraham owns mydomain.dyndns.net.”
  • now you associate that certificate with your server running at my domain.dyndns.net
  • when a client connects over SSL/TLS, the server provides the certificate to the client.
  • the client checks whether the cert was signed by a trusted CA (anyone can create a CA)
  • of it was then the client checks that the certs is for the domain name it is connecting to. This is whether it to failing for you because the cert is for mydomain.dyndns.net, not localhost.

Basically, a cert is a way to establish that you are not talk to an imposter that relies on the domain name and a trusted third party (the CA).

TLS2 would be better if it is supported.

@rlkoshak Rich,

Thanks for hanging with me! And thanks for the certificates for dummies summary. Lord knows I fit the bill!

OK, I’d tried it with openhab_sslbroker.url=“ssl://my_domain:8883” with no difference in outcome. That’s why I tried localhost. So, I did create the certificates using my domain. So it looks like we’re all good there once I update my mqtt.cfg to use my domain name instead of localhost.

I can easily refreshe the truststore when the time comes.

The only thing I can think of is that perhaps I haven’t got the right certificates in the right places. I suppose there are the server certificates (which I’m thinking have to go somewhere in the mosquitto directory tree (/etc/mosquitto/) under either certs or ca_certificates. Which certificate and which folder? Then there’s the certificate for the openHAB client. which and where?

Thanks.

Mike

P.S. I’m trying to get this to establish a successful connection. Then I’ll tackle firewall rules and ACLs. So, I’m not done with this thread even when I get this SSL stuff working :wink:

I have relied on this guy before:

http://www.steves-internet-guide.com/mosquitto-tls/

It doesn’t matter where you put them but you have to configure the listen to use them. You provide the path to the CA file and cert file in the listener config.

That is what Vincent’s original posting shows. You need to added it to the trust store. But I’m not 100% positive that will work. There may not be a way to user a client cert with openHAB.

@rlkoshak @vzorglub

I’m just chasing my tail… I’ve read what feels like EVERY post on the topic of mosquitto and SSL and openhab. I had already stumbled on Steve’s Internet Guide. But reading it again, some things now make a bit more sense than earlier times. Unfortunately, I’m still no further along getting this to function.

I’ve started over with a clean slate. Here’s what I’m doing:

I subscribed to a DDNS and defined a domain name to point to the IP address assigned to me currently by my ISP. To ensure that the domain remains sync, the setup includes a 5 minute cron job that updates the DDNS should my IP address change. For the sake of this e-mail, let’s say that my domain name is ‘MY_DOMAIN.ORG’.

$ sudo bash

# certbot certonly --standalone --standalone-supported-challenges http-01 -d' MY_DOMAIN.ORG

Certbot deposits the certificates in /etc/letsencrypt/live/MY_DOMAIN.ORG

Since these certificates expire after 90 days, I have a daily cron job to check if the certificates need to be renewed. If so, the certificates are regenerated. If new certificates are generated, it regenerates the truststore and then restarts mosquitto.

Certificate files are root:root 644

Vincent - I tweaked your procedure that generates the keystore and truststore to name the files per my naming conventions and also to use PKCS12 format stores (as recommended by the keytool utility) instead of JKS.

# keytool -genkeypair -alias my_openhab -keyalg RSA -storetype PKCS12 -keystore /etc/OPENHAB.keystore

# keytool -import -alias my_openhab -file /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem -storetype PKCS12 -keystore /etc/OPENHAB.truststore

Appended to JAVA_OPTS section

-Dcom.ibm.ssl.trustManager=SunX509
-Dcom.ibm.ssl.keyManager=SunX509
-Dcom.ibm.ssl.contextProvider=SunJSSE
-Dcom.ibm.ssl.keyStore=/etc/OPENHAB.keystore
-Dcom.ibm.ssl.keyStorePassword=KEYSTORE_PASSWORD
-Dcom.ibm.ssl.keyStoreType=PKCS12
-Dcom.ibm.ssl.keyStoreProvider=SUN
-Dcom.ibm.ssl.trustStore=/etc/OPENHAB.truststore
-Dcom.ibm.ssl.trustStorePassword=TRUSTSTORE_PASSWORD
-Dcom.ibm.ssl.trustStoreType=PKCS12
-Dcom.ibm.ssl.trustStoreProvider=SUN

Contents of openHAB mqtt.cfg

openhab_tcpbroker.url=tcp://localhost:1883
openhab_sslbroker.url=ssl://MY_DOMAIN.ORG:8883
openhab_sslbroker.clientId=openhab2
openhab_sslbroker.user=SSLBROKER_USER
openhab_sslbroker.pwd=SSLBROKER_PASSWORD

Contents of /etc/mosquitto/conf.d/listeners.conf

listener 1883 localhost
port 1883

listener 8883
allow_anonymous false
require_certificate true
certfile /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem
cafile /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem
keyfile /etc/letsencrypt/live/MY_DOMAIN.ORG/privkey.pem

I’ve tried copying the certificates to the /etc/mosquitto certificates folders. No difference. Ultimately I want to end up with symbolic links so that when Certbot regenerates the certificates, the symlinks point to the new certificates without having to copy anything around.

openHAB fails to connect to openhab_sslbroker! Same log output as previous post except that now both my 1883 and 8883 brokers are failing (because I have now added the listener configuration which must not be right either).

Can you guys put a different set of eyes on this to see where I’m going wrong?

Thanks so much for your patience!

Mike

@rlkoshak @vzorglub

Nothing obvious then?

Hmmmm. I wrote this reply days ago. Guess the problems with the forum never let ut get posted.

Does the user mosquitto is running under have permission to read the pem files? Usually, mosquitto ends up running under a mosquitto user and with root:root 644 it should be able to read them, but perhaps the parent folder doesn’t allow the mosquitto user to change to that directory or read from that directory.

You can run a quick test from the command line:

sudo -u mosquitto cat /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem

If it prints out the contents of the file you are good. If not then that is a problem.

I was never able to get this to work, but I run mosquitto in a Docker container and I can’t mount symbolic links into the container and each new cert ends up as a new file so I never went down that track and used self signed certs.

Break the problem in half. First focus on the broker half of the problem. Configure it like you think it needs to be done then test it with an MQTT client like mosquito_pub or MQTT.fx. In both cases you can pass in cert files to authenticate with the broker if it is needed.

However, like I said before, I don’t think openHAB supports client certificates so I’m pretty sure that openHAB can’t connect to a listener that has require_certificate true. It can only check whether the server’s cert is trusted based on the CA being in the trust store.

But why do you want your OH to talk to the broker on localhost encrypted anyway? It would be much easier to set up a UFW or iptables rule that prevents any communication to port 1883 except from localhost. Then just let OH talk to mosquitto unencrypted. The network traffic never leaves the host machine and nothing else can connect to the broker on that port to mess with you. Your clients are allowed to be on different listeners. Honestly, if your LAN is reasonably protected, you can skip the firewall rule entirely. Again, the network traffic never leaves the host so it can’t be sniffed (unless they are on your host in which case they don’t need to) and if there is a rogue on your network the last thing they will be trying to do is connect to your MQTT broker.

Here is how I had it set up in the past. This was way before LetsEncrypt and I’ve long since lost my mosquitto configs (I could probably dig up that old machine and recover them if I had to).

  • openHAB connects unencrypted on listener 1883
  • I used self-signed certs and created a server pem and client pems
  • I created listener 8883 configed just like you have it only using my self-signed certs and CA
  • The client certs were loaded to the phone clients and I think I checked the box to ignore cert errors. At that time (still?) there was a problem where if you import a CA file at least on Android there is a constant warning that all your traffic can be monitored because of the addition of the self-created CA. Remember it is the CA that is the third party vouching for the server and client certs so when you add your own it could indicate someone is trying to pull a fast one. So the warning is not a bad thing, but it wasn’t what I wanted so I disabled the server checking check box so OwnTracks would ignore that I was using a self-signed CA and cert. With LetsEncrypt you don’t have to do that.
  • Port forwarded 8883 through the router.
  • Did the other stuff I mentioned above.

The key takeaway is that OH doesn’t have to mess with the certs and TLS stuff. It’s traffic is already pretty well protected since it never leaves localhost. Only the OwnTracks clients need the encryption and client certs. The fact that the two connect to different listeners doesn’t matter, they can all see the topics they are allowed to see per the ACLs.

tl;dr Forget the encryption for OH. Get the encryption working with OwnTracks and let OH remain unencrypted. This will make your problem way easier. I believe OwnTracks has a tutorial to tell you how to do this. At least they did way back when.

Thanks Rich. I’d had issues when trying to post my note. I didn’t realize it was a forum-wide issue.

My intent is to NOT require encryption on localhost:1883. require_certificate true is obviously my misunderstanding thinking this applied to 8883. I gather from your response that’s an option that applies to the “default” listener. Therefore, I do not want that option in my listener config because I do not intend to encrypt the default (1883) listener; right?

I think I’m starting to get the picture more clearly.

  • I will have OwnTracks send its data to my SSL configured listener on 8883 by configuring OwnTracks to point to MY_DOMAIN.ORG:8883 and having that port forwarded on my router.
  • The encrypted OwnTracks data will be delivered to my 8883 mosquitto listener.
  • Internally on my server, I configure mosquitto to bridge 8883 over to 1883.
  • openHAB only needs to know about 1883 because the traffic from 8883 will be bridged over to that listener by the mosquitto configuration.
  • Therefore, in this scenario, I don’t need any of the keystore/truststore/openHAB setenv JAVA_OPTS stuff because I’m NOT having openHAB do the bridging from a cloud MQTT broker.

Did I understand right?

Mike

Then I’m confused by your mqtt.cfg. If you are not encrypting communication on localhost then you don’t need ANY certs or SSL config in OH. Everything is unencrypted on port 1883 between OH and the broker.

There is nothing to do. Without explicit ACLs all clients connected to all listeners can read/write to all topics regardless of which listener the client used to connect to the broker. The listener only controls the connection. Once connected everyone sees the same broker.

You only need to bridge when you have more than one broker running.

Mostly. Just the little bit of confusion about needing to do anything special to bridge between the listeners. You don’t. What you may want to do is set up some ACLs to limit what topics the OwnTracks clients can publish or subscribe to. But that too is independent of the listeners. And please don’t mess with ACLs until you get it working without them.

Yeah, sorry. I would undo everything prior. I would have no encryption between openHAB and mosquitto.

Again, more clarity… Thanks.

  • ONE broker, listening on two ports (1883 & 8883) - no bridging necessary.
  • OwnTracks sends data encrypted via the configured certificates to MY_DOMAIN.ORG:8883.
  • My router forwards port 8883 to MY_SERVER:8883.
  • My 8883 listener uses the configured certificates to decrypt that data.
  • My broker sees the data delivered by both the 1883 listener and the 8883 listener.
  • openHAB sees all the data coming into the single broker.

mqtt.cfg becomes:

openhab_tcpbroker.url=tcp://localhost:1883

listeners.conf becomes:

listener 1883 localhost
port 1883

listener 8883
certfile /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem
cafile /etc/letsencrypt/live/MY_DOMAIN.ORG/fullchain.pem
keyfile /etc/letsencrypt/live/MY_DOMAIN.ORG/privkey.pem

Mike

P.S. Right. I’m not tackling ACLs until I get 8883 receiving data successfully.

Looks reasonable to me.