Can anyone explain me how to add SSL to the Python STOMP client I'm using. I added the stomp+ssl transport connector in the ActiveMQ configuration file and my basic Python STOMP client is below:
import time
import sys
import stomp
class MyListener(stomp.ConnectionListener):
def on_error(self, headers, message):
print('received an error "%s"' % message)
def on_message(self, headers, message):
print('received a message "%s"' % message)
conn = stomp.Connection()
conn.set_listener('', MyListener())
conn.start()
conn.connect('admin', 'password', wait=True)
conn.subscribe(destination='/queue/test', id=1, ack='auto')
conn.send(body=' '.join(sys.argv[1:]), destination='/queue/test')
time.sleep(2)
conn.disconnect()
I created the key store and trust store given in the http://activemq.apache.org/how-do-i-use-ssl.html docs and added them to the SSL_OPTS
environment variable in the broker but I'm unable to find how to initialize the Python STOMP client with the key store and trust store. Should I use the SSL paraments given in the stomp.Connection()
method, and if yes how to do so?
Can anyone please explain if there is any other way to add SSL over STOMP?
The Python STOMP client (as of version 4.1.20) uses an
SSLContext
to process its key pair/certificate, so there is no reason to produce a Java KeyStore for the client.With this in mind, let us go through the entire process of setting up ApacheMQ to support SSL-wrapped STOMP connections. The process below has been tested on ApacheMQ 5.15.4. We explicitly set up two-way trust by manually moving self-signed certificates between the broker and client; using a certificate authority is also possible but how to do so is a different question.
Create a client certificate
As mentioned above, on the Python side of things, a KeyStore will have little use, and since
SSLContext
expects PEM encoded certificates, we might as well create the key pair and certificate by hand (that is, usingopenssl
). First, on the client machine, let us create a 4096-bit RSA key:Using this, turn the public key part into a certificate and sign it with the key itself; since we will be manually moving the certificate to the broker, self-signing the certificate is not an issue:
The STOMP client will need both the signed certificate,
client.pem
, and the private key,client.key
, while the broker will only need the certificate.Create a broker certificate
On the broker, we can follow the first part of the Apache guide and use the Java
keytool
to create a KeyStore with a key for the server:When prompted for "first and last name", provide the hostname of the server, which in our example we will take simply to be
localhost
; if the broker and client are running on different servers, make sure that this is set to whatever the Python client will end up using to identify the broker:All other input values can be left as "Unknown".
At the end of the day, we will only want to allow connections to the broker from clients with certificates that we know, so at this point copy the
client.pem
generated above to the broker and add it to a trust store throughIf the broker is to allow connections from any client, then this final step can be skipped.
Setting up ApacheMQ
By default, all connections through STOMP (and indeed all connections) are plaintext ones, and in order to enable STOMP connections over SSL, add the following
<transportConnector />
toconf/apachemq.xml
:Make sure to remove any existing plaintext connectors such as the default STOMP connector as otherwise clients will be able to simply use those and bypass the SSL requirement. Note also that
needClientAuth=true
is what forces client certificate validation; without this, clients are able to connect without providing a trusted certificate.To configure ApacheMQ to use the key and trust stores defined above, define the environment variable
ACTIVEMQ_SSL_OPTS
through (on Unix)or (on Windows)
Here, the two passwords are those chosen after running
keytool
in the previous step. If client certificate validation is not desired, simply leave out the configuration oftrustStore
andtrustStorePassword
.With this, ActiveMQ can be started as usual through
bin/activemq start
. To make sure that the SSL configuration matches expectation, pay attention to theJVM args
part of the output printed when starting the server.Testing the STOMP client
With the broker properly set up, we can configure the client as well. Here, we provide
stomp.Connection.set_ssl
with references to the key and certificate created in the first step. Assuming that the ActiveMQ server is running on localhost:61613, your test script simply becomesTo make sure that ApacheMQ is indeed validating the client certificate, we could repeat step 1 and create a new pair,
client2.key
/client2.pem
say, and use that instead. Doing so should result in the following non-sensical error message being printed by ApacheMQ:Validating the broker certificate
Now, the attentive reader will have noticed that we never actually moved the broker certificate to the client, and yet things seem to work regardless. As it turns out, the default behavior of
stomp.py
is to perform no certificate validation at all, allowing an (active) attacker to MITM the connection.As we are rolling self-signed certificates, all we need to do to fix this situation is to provide the actual broker certificate to the Python client. On the broker, export the certificate through
and move
broker.pem
to the Python client. Now, in the test script above, include the certificate by replacing the SSL configuration withAs above, we can test that this is indeed performing the proper validation by repeating the broker certificate generation process to create a
broker2.pem
, use that in the test script, and note that it will fail with anTry this.
or