MQTT Binding and SSL

@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.

Step 1 success! I’m able to connect an MQTT client to my 8883 listener with the Certbot certificates!

This listener config line was wrong:
listener 1883 localhost

adding the host at the end binds all traffic on the port to have to come from that host only… which obviously is not what I want. The line needs to be:
listener 1883

Now on to OwnTracks, then openHAB, then ACLs.

Mike

P.S. Did you hear me scream in elation? :wink:
P.P.S. Symlinks worked.

@vzorglub

Many thanks for the excellent guide.
I have the impression that the following command is wrong.

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

In any case it didnt work for me. What worked is

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

And I confirm that it works ! I just used it to get some sensor values from “The things network” based on LoRaWAN sensors located 500 meters away !

Christos (or anyone), how did you get your addtrustexternalroot.cert file? When I go to the link in the instructions (CONTACT US - Comodo: Cloud Native Cyber Security Platform) it downloads a addtrustexternalroot.crt file. the extension is different, cert vs crt.

Do be honest it was a while ago and I may have made a mistake and it was crt and not cert

You need to generate a jks file from it in the next step, have you tried this with your crt file?

I think that it does not really matter what the extension is. It is the content that matters. You can put whatever extension you like so crt should be ok

1 Like

when I run this and enter my password:

keytool -import -alias myopenhab -file /home/pi/Desktop/temp/ca.crt -storetype JKS -keystore keystore.jks

I get this error:

keytool error: java.lang.Exception: Public keys in reply and keystore don't match

Try this:

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

Replace your files…

I can connect to my broker via:

mosquitto_sub --cert ca.crt --key ca.key --tls-version tlsv1 -t '#' -p 8883

However, my mqtt binding in openhab cannot. I get this error in openhab console:

15:23:48.293 [ERROR] [openhab.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) [202:org.openhab.io.transport.mqtt:1.12.0]
        at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:650) [202:org.openhab.io.transport.mqtt:1.12.0]
        at java.lang.Thread.run(Thread.java:745) [?:?]
Caused by: java.net.ConnectException: 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 org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:70) ~[?:?]
        ... 2 more

my configurations:

/etc/openhab2/services/mqtt.cfg:

mosquitto.url=ssl://localhost:8883
mosquitto.user=mqtt_user
mosquitto.pwd=password
mosquitto.qos=1
mosquitto.retain=true
mosquitto.async=false
mosquitto.clientId=openhab2

/etc/mosquitto/mosquitto.conf

pid_file /var/run/mosquitto.pid

persistence true
persistence_location /var/lib/mosquitto/

log_dest file /var/log/mosquitto/mosquitto.log

Allow_anonymous true
Password_file /etc/mosquitto/pwfile
Listener 8883

cafile /etc/mosquitto/ca_certificates/ca.crt
keyfile /etc/mosquitto/certs/server.key
certfile /etc/mosquitto/certs/server.crt
tls_version tlsv1

/usr/share/openhab2/runtime/bin/setenv:

export JAVA_OPTS="${JAVA_OPTS}
  -Dopenhab.home=${OPENHAB_HOME}
  -Dopenhab.conf=${OPENHAB_CONF}
  -Dopenhab.runtime=${OPENHAB_RUNTIME}
  -Dopenhab.userdata=${OPENHAB_USERDATA}
  -Dopenhab.logdir=${OPENHAB_LOGDIR}
  -Dfelix.cm.dir=${OPENHAB_USERDATA}/config
  -Djetty.host=${HTTP_ADDRESS}
  -Djetty.http.compliance=RFC2616
  -Dorg.ops4j.pax.web.listening.addresses=${HTTP_ADDRESS}
  -Dorg.osgi.service.http.port=${HTTP_PORT}
  -Dorg.osgi.service.http.port.secure=${HTTPS_PORT}"
  -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=adminadmin
  -Dcom.ibm.ssl.keyStoreType=JKS
  -Dcom.ibm.ssl.keyStoreProvider=SUN
  -Dcom.ibm.ssl.trustStore=/etc/truststore.jks
  -Dcom.ibm.ssl.trustStorePassword=adminadmin
  -Dcom.ibm.ssl.trustStoreType=JKS
  -Dcom.ibm.ssl.trustStoreProvider=SUN

It is a very long time I have done this
I have since done a full re-install of OH on a new ubuntu OS and the SSL worked out of the box
Which version of openHAB are you running?

Read also the other posts under the original post. Some users had some work arounds and other ideas

I’m using version 2.3.0

why would you bother encrypting the data between 2 local processes ?

1 Like

This is just a test. latter I’ll go global. Please tell me why I’m getting this error

did you fix your JKS?