简体   繁体   English

ActiveMQ - STOMP+SSL 与 Python STOMP 客户端

[英]ActiveMQ - STOMP+SSL with Python STOMP client

Can anyone explain me how to add SSL to the Python STOMP client I'm using.谁能解释一下如何将 SSL 添加到我正在使用的 Python STOMP 客户端。 I added the stomp+ssl transport connector in the ActiveMQ configuration file and my basic Python STOMP client is below:我在 ActiveMQ 配置文件中添加了 stomp+ssl 传输连接器,我的基本 Python STOMP 客户端如下:

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.我创建了http://activemq.apache.org/how-do-i-use-ssl.html文档中给出的密钥库和信任库,并将它们添加到代理中的SSL_OPTS环境变量中,但我无法了解如何使用密钥库和信任库初始化 Python STOMP 客户端。 Should I use the SSL paraments given in the stomp.Connection() method, and if yes how to do so?我应该使用stomp.Connection()方法中给出的 SSL 参数,如果是,如何使用?

Can anyone please explain if there is any other way to add SSL over STOMP?谁能解释一下是否还有其他方法可以通过 STOMP 添加 SSL?

The Python STOMP client (as of version 4.1.20) uses anSSLContext to process its key pair/certificate, so there is no reason to produce a Java KeyStore for the client. Python STOMP 客户端(从 4.1.20 版开始)使用SSLContext来处理其密钥对/证书,因此没有理由为客户端生成 Java KeyStore。

With this in mind, let us go through the entire process of setting up ApacheMQ to support SSL-wrapped STOMP connections.考虑到这一点,让我们完成设置 ApacheMQ 以支持 SSL 包装的 STOMP 连接的整个过程。 The process below has been tested on ApacheMQ 5.15.4.以下流程已在 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, using openssl ).如上所述,在 Python 方面,KeyStore 几乎没有用,而且由于SSLContext需要 PEM 编码的证书,我们不妨手动创建密钥对和证书(即使用openssl )。 First, on the client machine, let us create a 4096-bit RSA key:首先,在客户端机器上,让我们创建一个 4096 位的 RSA 密钥:

openssl genrsa -out client.key 4096

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:由于我们将手动将证书移动到代理,因此自签名证书不是问题:

openssl req -new -out client.csr -key client.key
openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.pem
rm client.csr

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. STOMP 客户端将需要签名证书client.pem和私钥client.key ,而代理只需要证书。

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:在代理上,我们可以按照 Apache 指南的第一部分,并使用 Java keytool创建一个带有服务器密钥的 KeyStore:

keytool -genkeypair -alias broker -keyalg RSA -keysize 4096 -keystore broker.ks

When prompted for "first and last name", provide the hostname of the server, which in our example we will take simply to be localhost ;当提示输入“名字和姓氏”时,提供服务器的主机名,在我们的示例中,我们将其简单地视为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:如果代理和客户端在不同的服务器上运行,请确保将其设置为 Python 客户端最终用于识别代理的任何内容:

What is your first and last name?
  [Unknown]:  localhost

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 through归根结底,我们只希望允许从具有我们知道的证书的客户端连接到代理,因此此时将上面生成的client.pem复制到代理并通过以下方式将其添加到信任存储中

keytool -import -alias client -keystore broker.ts -file client.pem

If the broker is to allow connections from any client, then this final step can be skipped.如果代理允许来自任何客户端的连接,则可以跳过这最后一步。

Setting up ApacheMQ设置 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 /> to conf/apachemq.xml :默认情况下,通过 STOMP 的所有连接(实际上所有连接)都是纯文本连接,为了启用通过 SSL 的 STOMP 连接,请将以下<transportConnector />conf/apachemq.xml

<transportConnectors>
    <transportConnector name="stomp+ssl" uri="stomp+nio+ssl://0.0.0.0:61613?transport.enabledProtocols=TLSv1.2&amp;needClientAuth=true" />
</transportConnectors>

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.确保删除任何现有的纯文本连接器,例如默认的 STOMP 连接器,否则客户端将能够简单地使用这些连接器并绕过 SSL 要求。 Note also that needClientAuth=true is what forces client certificate validation;另请注意, needClientAuth=true是强制客户端证书验证的原因; 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)要将 ApacheMQ 配置为使用上面定义的密钥和信任存储, ACTIVEMQ_SSL_OPTS通过(在 Unix 上)定义环境变量ACTIVEMQ_SSL_OPTS

export ACTIVEMQ_SSL_OPTS = -Djavax.net.ssl.keyStore=/path/to/broker.ks -Djavax.net.ssl.trustStore=/path/to/broker.ts -Djavax.net.ssl.keyStorePassword=passwordForBrokerKs -Djavax.net.ssl.trustStorePassword=passwordForBrokerTs

or (on Windows)或(在 Windows 上)

set ACTIVEMQ_SSL_OPTS=-Djavax.net.ssl.keyStore=C:\path\to\broker.ks -Djavax.net.ssl.trustStore=C:\path\to\broker.ts -Djavax.net.ssl.keyStorePassword=passwordForBrokerKs -Djavax.net.ssl.trustStorePassword=passwordForBrokerTs

Here, the two passwords are those chosen after running keytool in the previous step.这里,两个密码是在上一步运行keytool后选择的密码。 If client certificate validation is not desired, simply leave out the configuration of trustStore and trustStorePassword .如果不需要客户端证书验证,只需省略trustStoretrustStorePassword的配置。

With this, ActiveMQ can be started as usual through bin/activemq start .有了这个,ActiveMQ 可以像往常一样通过bin/activemq start To make sure that the SSL configuration matches expectation, pay attention to the JVM args part of the output printed when starting the server.为确保 SSL 配置符合预期,请注意启动服务器时打印的输出的JVM args部分。

Testing the STOMP client测试 STOMP 客户端

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.在这里,我们为stomp.Connection.set_ssl提供了对第一步中创建的密钥和证书的引用。 Assuming that the ActiveMQ server is running on localhost:61613, your test script simply becomes假设 ActiveMQ 服务器在 localhost:61613 上运行,您的测试脚本就变成了

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)

host = 'localhost'
port = 61613
conn = stomp.Connection([(host, port)])
conn.set_ssl(for_hosts=[(host, port)], key_file='/path/to/client.key', cert_file='/path/to/client.pem')
conn.set_listener('', MyListener())
conn.start()
conn.connect('admin', 'password', wait=True)
conn.subscribe(destination='/queue/test', id=1, ack='auto')
conn.send(body='test message', destination='/queue/test')
time.sleep(2)
conn.disconnect()

To 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.为了确保 ApacheMQ 确实在验证客户端证书,我们可以重复步骤 1 并创建一个新对, client2.key / client2.pem ,然后使用它。 Doing so should result in the following non-sensical error message being printed by ApacheMQ:这样做应该会导致 ApacheMQ 打印以下无意义的错误消息:

ERROR | Could not accept connection from null : {}
java.io.IOException: javax.net.ssl.SSLHandshakeException: General SSLEngine problem

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.事实证明, stomp.py的默认行为是stomp.py不执行证书验证,从而允许(主动)攻击者对连接进行中间人攻击。

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.由于我们正在滚动自签名证书,因此我们需要做的就是向 Python 客户端提供实际的代理证书来解决这种情况。 On the broker, export the certificate through在代理上,通过导出证书

keytool -exportcert -rfc -alias broker -keystore broker.ks -file broker.pem

and move broker.pem to the Python client.并将broker.pem移动到 Python 客户端。 Now, in the test script above, include the certificate by replacing the SSL configuration with现在,在上面的测试脚本中,通过将 SSL 配置替换为包含证书

conn.set_ssl(for_hosts=[(host, port)], key_file='/path/to/client.key', cert_file='/path/to/client.pem', ca_certs='/path/to/broker.pem')

As 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 an如上所述,我们可以通过重复代理证书生成过程以创建broker2.pem来测试这确实执行了正确的验证,在测试脚本中使用它,并注意它将失败并显示

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)

Try this.试试这个。

 conn = stomp.Connection([(host, port)])
 conn.set_listener('', MyListener())

 conn.set_ssl(for_hosts=[(host, port)], ssl_version=ssl.PROTOCOL_TLS)

 conn.start()
 conn.connect(login, password, wait=True)
 conn.send(body=message, destination=queue)
 conn.disconnect()

or

conn.set_ssl(for_hosts=[(host, port)], ssl_version=_ssl.PROTOCOL_TLS)

The key point is understanding the key/cert files must be there - otherwise even stomp.py tests in their repository will not work.关键是了解密钥/证书文件必须在那里 - 否则即使他们的存储库中的 stomp.py 测试也将不起作用。 TLS will not work in stomp.py if the file names are not specified (correctly).如果未(正确)指定文件名,则 TLS 将无法在 stomp.py 中工作。

See the code & key generation instructions here: https://gist.github.com/borislitvak/6ccea503abf1b2f9c89e87309d6dab88请参阅此处的代码和密钥生成说明: https : //gist.github.com/borislitvak/6ccea503abf1b2f9c89e87309d6dab88

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM