Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Current »

This guide will show you the required steps and some common pitfalls when setting up a HiveMQ cluster and enforcing TLS for each step of communication.

Certificate and keystore generation as well as their use will be demonstrated.

Requirements

  • HiveMQ

  • JDK 11 or higher

  • openssl

  • MQTT CLI

Instructions

  1. We will create a keystore used by our HiveMQ nodes

keytool -genkey -keyalg RSA -alias hivemq -keystore hivemq.jks -storepass changeme -validity 360 -keysize 2048

keytool will ask for the necessary information to create our root certificate and private key.

These will be stored in hivemq.jks which we need to make available to all our cluster nodes.

The common name should match the hostname of the HiveMQ node. For testing purposes ‘localhost’ will suffice.

  1. From this keystore we need to export the server’s certificate (in this case called server.pem) and make it accessible to all clients that wish to connect

keytool -exportcert -alias hivemq -keystore hivemq.jks -rfc -file server.pem

 

2. Next we need to create a client certificate (mqtt-client-cert.pem), generate an x509, DER encoded certificate from it (mqtt-client.crt) and make them available to HiveMQ in form of a keystore (hivemq-trust-store.jks)

openssl req -x509 -newkey rsa:2048 -keyout mqtt-client-key.pem -out mqtt-client-cert.pem -days 360 
openssl x509 -outform der -in mqtt-client-cert.pem -out mqtt-client-cert.crt
keytool -import -file mqtt-client-cert.crt -alias client -keystore hivemq-trust-store.jks -storepass changeme

3. Now it is time to configure HiveMQ to require TLS

In our config.xml we adjust:

<path> in <keystore> must point to our earlier generated hivemq.jks

<path> in <truststore> will be our hivemq-trust-store.jks

<private-key-password> must be identical to the one we set during creation and

<client-authentication-mode> must be set to REQUIRED

When your are running multiple HiveMQ nodes on a single server remember that each instance requires a unique listening port

<listeners>
    <tls-tcp-listener>
        <port>1883</port>
        <bind-address>0.0.0.0</bind-address>
        <proxy-protocol>true</proxy-protocol>
        <tls>
            <keystore>
                <path>/opt/hivemq/conf/hivemq.jks</path>
                <password>changeme</password>
                <private-key-password>changeme</private-key-password>
            </keystore>
            <client-authentication-mode>REQUIRED</client-authentication-mode>
            <truststore>
                <path>/opt/hivemq/conf/hivemq-trust-store.jks</path>
                <password>changeme</password>
            </truststore>
        </tls>
    </tls-tcp-listener>
</listeners>

In order to ensure the use of TLS between cluster nodes we need to modify the <cluster> section of our configuration.

We must again adjust the paths of our server’s keystore and truststore (path to our generated server.jks)

and since we are running our cluster nodes on a single machine, each must bind to a different <bind-port>

<cluster>
    <enabled>true</enabled>
    <transport>
        <tcp>
            <bind-address>YOUR_IP_HERE</bind-address>
            <bind-port>7800</bind-port>
            <tls>
                <enabled>true</enabled>
                <server-keystore>
                    <path>/opt/hivemq/conf/hivemq.jks</path>
                    <password>changeme</password>
                    <private-key-password>changeme</private-key-password>
                </server-keystore>
                <server-certificate-truststore>
                    <path>/opt/hivemq/conf/hivemq.jks</path>
                    <password>changeme</password>
                </server-certificate-truststore>
            </tls>
        </tcp>
    </transport>

    <discovery>
        <extension/>
    </discovery>
</cluster>

In a production environment we would create a separate trust store for each node and modify the location accordingly.

4. We are now ready to launch our brokers (run.sh in the bin/ subdirectory)

To avoid further binding conflicts, modify jmxremote.port to a unique value in each node’s JAVA_OPTS variable within the corresponding run.sh

Validating our setup with MQTT CLI

  1. Subscription attempt without providing any certificates

mqtt sub -t topic -q 1 -h localhost -i testclient -d

CLIENT testclient: sending CONNECT
SUBSCRIBE ERROR:: Server closed connection without DISCONNECT.

As expected HiveMQ disconnects the client since no certificate was presented to the server.

2. .. while including our server.pem

mqtt sub -t topic -q 1 -h localhost -i testclient --cafile /some/dir/server.pem -d

CLIENT testclient: sending CONNECT
PUBLISH: io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
PUBLISH: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate

We can see the TLS handshake was initiated but is aborted on the server side due to a missing client certificate

3. .. while supplying all required certificates

mqtt sub -t topic -q 1 -h localhost -i testclient --cafile /some/dir/server.pem --key /some/dir/mqtt-client-key.pem --cert /some/dir/mqtt-client-cert.pem -d

CLIENT testclient: sending CONNECT
CLIENT testclient: received CONNACK SUCCESS
CLIENT testclient: sending SUBSCRIBE: (Topic: topic, QoS: AT_LEAST_ONCE)
CLIENT testclient: received SUBACK: [GRANTED_QOS_1]

as we can see, the server accepts our subscription and we have demonstrated a successful connection attempt!

  • No labels