Intrexx Industrial - Configuration of an MQTT Message Broker

1. General

The ActiveMQ server, developed by Apache, is used for the connection to Intrexx. This is available for free as part of the Apache 2.0 license. In the following, the abbreviation "<ACTIVE_MQ_INST>" refers to the installation path of the ActiveMQ server. In the test VM provided by United Planet, the installation path is /opt/activemq.

2. SSL encryption

The following will provide a description of a simple method for creating a self-signed certificate with the Java Keytool and then integrating this into ActiveMQ. To achieve this, Java needs to be installed and configured on the system.

2.1. Create KeyStore with a self-signed private key

To begin with, the KeyStore needs to be created that contains the private key:
keytool -genkey -alias amq_server -keyalg RSA -keystore amq_server.ks
As the CN (in the dialog: "First and last name"), the FQHN of the ActiveMQ server needs to be transferred. Later, you need to ensure that the ActiveMQ is reachable via this name so that the SSL Handshake works. Furthermore, a password for accessing the KeyStore needs to be specified to begin with. The password, which should be entered later for the alias, must simply be confirmed with ENTER, meaning it is identical with the KeyStore password. Additionally, the file should be protected from access accordingly as it contains the private key for the ActiveMQ.

2.2 Export certificate for clients

The public key and the certificate will now be generated from the KeyStore file created in the last step:
keytool -exportcert -alias amq_server -keystore amq_server.ks -file amq_server.der
The certificate is exported in the DER format (therefore in binary) in this case. By using the additional parameter "-rfc", the certificate can also be saved as PEM (encoded as plain text or BASE64). Importing the certificate into a TrustStore, which is often used in Java applications, is performed with the following command:
keytool -importcert -alias amq_client -file amq_client.der -keystore amq_client.ts

2.3. Import certificate into Intrexx

So that Intrexx can establish an encrypted TLS connection to the ActiveMQ server later, the file "amq_server.der", which we just created, needs to be imported into Intrexx in the portal properties. In the certificate source dialog, select the option "Import of file" and then select the file "amq_server.der". So that the change takes effect, the portal service needs to be restarted

2.4 Activate SSL in ActiveMQ and integrate KeyStore with the private key

To do this, the path to the created KeyStore as well as its password need to be added to <ACTIVEMQ_INST>/conf/activemq.xml in the "core" namespace. Because the password is entered in plain text, the file "activemq.xml" should be protected from unauthorized access as appropriate.

According to the ActiveMQ documentation, relative paths should be used when entering the paths. By default, the working directory is <ACTIVEMQ_INST>/conf/ meaning the the entry keyStore="amq_server.ks" because "amq_server.ks" is directly in this directory.

Excerpt from activemq.xml:
	<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
...
..
.
<!-- In sslContext, the path to the KeyStore file as well as its
password nee to be entered -->
    <sslContext>
        <sslContext
            keyStore="/PATH/TO/amq_server.ks" keyStorePassword="SECRET" />
    </sslContext>

<!-- SSL then needs to be activated in the corresponding connectors.
It is best to deactivate connectors that are not required. 
Please mote: openwire is required to connect to Intrexx via JMS-->

    <transportconnectors>
        <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
        <!-- <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> -->
        <transportConnector name="openwire" uri="ssl://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
        <transportConnector name="mqtt+ssl" uri="mqtt+ssl://0.0.0.0:8883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
        <!-- transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
        <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
        <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
        <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/ -->
    </transportconnectors>
Please note that the parameters are case-sensitive. Click here for more information.

2.5. Integrate KeyStore in Jetty

ActiveMQ provides a web interface that provides an overview of certain status information such as the currently active topics or the currently registered publishers/subscribers. For this, ActiveMQ comes with the webserver Jetty from Apache. So that this can be accessed via an HTTPS connection, the KeyStore created in the previous step needs to be integrated here as well. The file <ACTIVEMQ_INST>/conf/jetty.xml already contains corresponding, but also commented-out entries, for this purpose. As in the previous steps in ActiveMQ, these also need to have the path and password of the KeyStore added to them:
    <!--
        Enable this connector if you wish to use https with web console
    -->
    <bean id="SecureConnector" class="org.eclipse.jetty.server.ServerConnector">
        <constructor-arg ref="Server" />
        <constructor-arg>
            <bean id="handlers" class="org.eclipse.jetty.util.ssl.SslContextFactory">
                <property name="keyStorePath" value="/PATH/TO/amq_server.ks" />
                <property name="keyStorePassword" value="SECRET" />
            </bean>
        </constructor-arg>
        <property name="port" value="8162" />
    </bean>
So that the redirecting from HTTP to HTTPS works, a corresponding entry needs to be made in the web.xml. This is described in the section Redirecting http requests to https in the Jetty documentation.

3. SSL - Client-side authentication

After the steps taken in chapter 2, a server-side authentication is now guaranteed. Additionally, the encryption for the connection was activated. An additional way to improve secure is by making the client authenticate themselves as well. The setup is identical to the steps taken in chapter 2 just that in this case a private key is generated for the client but the corresponding certificate with the contained public key is now integrated on the server side.

3.1. Create KeyStore with a self-signed private key for client

To begin with, the KeyStore needs to be created that contains the private key:
keytool -genkey -alias amq_client -keyalg RSA -keystore amq_client.ks

3.2. Export certificate with contained public key for ActiveMQ server

The public key and the certificate will now be generated from the KeyStore file created in the last step:
keytool -exportcert -alias amq_client -keystore amq_client.ks -file amq_client.der
The exported certificate, which is currently in the DER (binary) format, now needs to be imported into a KeyStore or TrustStore for the integration into ActiveMQ later:
keytool -importcert -alias amq_client -file amq_client.der -keystore amq_client.ts

3.3 Activate client authentication in ActiveMQ, Integrate TrustStore with certificate and public key

In the corresponding sslContext section of <ACTIVEMQ_INST>/conf/activemq.xml, which we already generated in chapter 2.4., the TrustStore, which contains the corresponding client certificates, is now added as an additional parameter:
    <sslContext>
        <sslContext
            keyStore="/PATH/TO/amq_server.ks" keyStorePassword="SECRET"
            trustStore="/PATH/TO/amq_client.ts" trustStorePassword="SECRET" />
    </sslContext>
The client authentication will now be activated in the corresponding connector using the additional URI parameter "needClientAuth=false". For the MQTT connector for example this is done as follows:
    <transportConnector name="ssl" uri="mqtt+ssl://0.0.0.0:8883?needClientAuth=false&maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>

3.4. Advice for using an officially signed certificate

The points named above describe the simplest approach using a self-signed certificate. The advantage here is that you can encrypt the connection easily. However, as the certificate is self-signed, and therefore not signed by an official certificate authority (CA), the Chain of Trust is not guaranteed. This causes for example a warning notification to appear when you access the ActiveMQ Webmin interface in the browser, as the creator is unknown. If officially signed certificates should be created, the approach is as follows: The received certificates received from and signed by the certificate authority usually arrive in different formats. The certificates are easiest to integrate if they are in the PEM format (in plain text). This is done by opening the respective files in a text editor, copying the private key, certificates and intermediate certificates, and then generating a new file that contains the entire chain. Proceed as follows to achieve this:

Create a new text file called "certificatechain.pem" where the private key and all certificated including BEGIN/END prologue are entered via copy & paste. The private key (e.g. *,key):
-----BEGIN RSA PRIVATE KEY-----
...
The certificate (e.g. *.crt):
-----BEGIN CERTIFICATE-----
...
Potential intermediate certificates (e.g. *.ca):
-----BEGIN CERTIFICATE-----
...
Afterwards, this pem file will now be converted to pkcs12 via the following command (OpenSSL needs to be installed):
openssl pkcs12 -export -name MY_ALIAS -in certificatechain.pem -out certificate_keystore.p12
The integration into ActiveMQ or Jetty functions in the same way as descibed in chapter 2.4. or 2.5. respectively. Because the newly generated KeyStore is the PKCS12 type, only one additional parameter, which describes the type of KeyStore, needs to be specified.

In jetty.xml:
        <property name="keyStorePath" value="/PATH/TO/certificate_keystore.p12" />
        <property name="keyStorePassword" value="SECRET" />
        <property name="keyStoreType" value="pkcs12" />
In activemq.xml in sslContext:
        <sslContext>
            <sslContext
                keyStore="/PATH/TO/certificate_keystore.p12" keyStorePassword="SECRET" keyStoreType="pkcs12" />
        </sslContext>

4. User configuration

4.1. Jetty Webmin user

The users who are permitted to log in to the Webmin interface are defined in the file <ACTIVEMQ_INST>/conf/jetty-realm.properties.

4.2. User permissions for the connectors

The direct definition of permissions in activemq.xml with the "SimpleAuthenticationPlugin" is the easiest way to achieve this. You can add a new user, password and group membership via a corresponding authentication entry. The permissions for the topics/queues can then be assigned to the users via the authorizationPlugin. Read and/or write permissions ought to be self explanatory. The "admin" role describes the permissions for creating a topic in this context.
    <plugins>
        <simpleAuthenticationPlugin anonymousAccessAllowed="false">
            <users>
                <authenticationUser username="admin" password="admin"
                groups="admins,publishers,consumers"/>
                <authenticationUser username="user_publ" password="admin"
                groups="publishers"/>
                <authenticationUser username="user_cons" password="admin"
                groups="consumers"/>
            </users>
        </simpleAuthenticationPlugin>

        <authorizationPlugin>
            <map>
                <authorizationMap>
                    <authorizationEntries>
                        <authorizationEntry topic=">"
                            read="admins" write="admins" admin="admins" />
                        <authorizationEntry topic="ActiveMQ.Advisory.>"
                            read="publishers,consumers" write="publishers,consumers" admin="admins,consumers,publishers" />
                        <authorizationEntry topic="test-mqtt.>"
                            read="consumers" write="publishers"
                            admin="admins" />
                    </authorizationEntries>
                </authorizationMap>
            </map>
        </authorizationPlugin>
    </plugins>
More information is available here:
http://activemq.apache.org/security.html
http://activemq.apache.org/wildcards.html

ActiveMQ provides the ability to share information about the topics via the ActiveMQ.Advisory branch, e.g. for generating a message when a client establishes a connection in the ActiveMQ.Advisory.Connection branch. If provided information is required there, the client needs to have the necessary read/write permission. If the respective branches are not available, the client requires the admin permissions to be able to generate the topic, or you need to ensure that the required Advisory topics are available. The Advisory messages can also be deactivated via the following entry in the Broker namespace of activemq.xml:
<broker advisorySupport="false">
Click here for more information.

4.3. Save permissions and restart services

The ActiveMQ server runs under the restrictive user account "activemq". Therefore, you therefore need to ensure that the owner and group membership of the modified files are still correct after the changes made in chapter 2 and 3. Both the owner and group membership of the file need to be "activemq". This can be checked using the following command:
ls -la /opt/activemq/conf/activemq.xml /opt/activemq/conf/jetty.xml

-rw-r--r-- 1 activemq activemq 7785 2017-03-20 08:14 /opt/activemq/conf/activemq.xml
-rw-r--r-- 1 activemq activemq 7841 2017-03-15 16:04 /opt/activemq/conf/jetty.xml
The same applies to the KeyStore file generated in chapter 2. The storage location of the file and the the user/group need to be readable by the activemq user. This can be corrected using the following command:
chown activemq.activemq <FILENAME>
Afterwards, the ActiveMQ service needs to be restarted:
service activemq restart

5. More information

  1. Intrexx Industrial
  2. Test MQTT Broker
  3. MQTT in Intrexx
  4. Service bus
  5. Polling
  6. Increased perfomance by only refreshing specific elements on a page
  7. Complete Java Sample Code for a seamless integration in the Processes module (event source, event handler and action)
  8. http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html