【问题标题】:ActiveMQ - STOMP+SSL with Python STOMP clientActiveMQ - 带有 Python STOMP 客户端的 STOMP+SSL
【发布时间】:2018-03-27 23:24:41
【问题描述】:

谁能解释我如何将 SSL 添加到我正在使用的 Python STOMP 客户端。 我在 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()

我创建了 http://activemq.apache.org/how-do-i-use-ssl.html 文档中给出的密钥库和信任库,并将它们添加到代理中的 SSL_OPTS 环境变量中,但我无法找到如何使用密钥库初始化 Python STOMP 客户端和信任商店。我是否应该使用stomp.Connection() 方法中给出的 SSL 参数,如果是,如何使用?

谁能解释一下是否有其他方法可以通过 STOMP 添加 SSL?

【问题讨论】:

    标签: python ssl activemq stomp stomp.py


    【解决方案1】:

    Python STOMP client(从 4.1.20 版开始)使用 SSLContext 来处理其密钥对/证书,因此没有理由为客户端生成 Java KeyStore。

    考虑到这一点,让我们完成设置 ApacheMQ 以支持 SSL 包装的 STOMP 连接的整个过程。以下过程已在 ApacheMQ 5.15.4 上进行了测试。我们通过在代理和客户端之间手动移动自签名证书来明确设置双向信任;也可以使用证书颁发机构,但如何做到这一点是另一个问题。

    创建客户端证书

    如上所述,在 Python 方面,KeyStore 几乎没有用处,由于 SSLContext 需要 PEM 编码的证书,我们不妨手动创建密钥对和证书(即使用 @987654325 @)。首先,在客户端机器上,让我们创建一个 4096 位的 RSA 密钥:

    openssl genrsa -out client.key 4096
    

    使用它,将公钥部分变成证书,并用密钥本身对其进行签名;由于我们将手动将证书移动到代理,因此自签名证书不是问题:

    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
    

    STOMP 客户端将需要签名证书client.pem 和私钥client.key,而代理只需要证书。

    创建代理证书

    在代理上,我们可以按照 Apache 指南的第一部分,使用 Java keytool 为服务器创建一个带有密钥的 KeyStore:

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

    当提示输入“名字和姓氏”时,提供服务器的主机名,在我们的示例中,我们将简单地取为localhost;如果代理和客户端在不同的服务器上运行,请确保将其设置为 Python 客户端最终用于识别代理的任何内容:

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

    所有其他输入值都可以保留为“未知”。

    在一天结束时,我们只希望允许来自具有我们知道的证书的客户端连接到代理,因此此时将上面生成的client.pem 复制到代理并通过以下方式将其添加到信任库中

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

    如果代理允许来自任何客户端的连接,则可以跳过最后一步。

    设置 ApacheMQ

    默认情况下,所有通过 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>
    

    确保删除任何现有的纯文本连接器,例如默认的 STOMP 连接器,否则客户端将能够简单地使用这些连接器并绕过 SSL 要求。另请注意,needClientAuth=true 是强制客户端证书验证的原因;没有这个,客户端可以在不提供可信证书的情况下进行连接。

    要将 ApacheMQ 配置为使用上面定义的密钥和信任存储,请通过(在 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
    

    或(在 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
    

    这里的两个密码是在上一步运行keytool后选择的。如果不需要客户端证书验证,只需省略trustStoretrustStorePassword 的配置即可。

    有了这个,ActiveMQ 可以像往常一样通过bin/activemq start 启动。为确保 SSL 配置符合预期,请注意启动服务器时输出的 JVM args 部分。

    测试 STOMP 客户端

    通过正确设置代理,我们也可以配置客户端。在这里,我们为stomp.Connection.set_ssl 提供了对第一步中创建的密钥和证书的引用。假设 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()
    

    为确保 ApacheMQ 确实在验证客户端证书,我们可以重复步骤 1 并创建一个新对,例如 client2.key/client2.pem,然后使用它。这样做应该会导致 ApacheMQ 打印以下无意义的错误消息:

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

    验证代理证书

    现在,细心的读者会注意到,我们实际上从未将代理证书移动到客户端,但无论如何,一切似乎都可以正常工作。事实证明,stomp.py 的默认行为是根本不执行证书验证,从而允许(主动)攻击者 MITM 连接。

    由于我们正在滚动自签名证书,因此我们需要做的就是向 Python 客户端提供实际的代理证书。在broker上,通过

    导出证书
    keytool -exportcert -rfc -alias broker -keystore broker.ks -file broker.pem
    

    并将 broker.pem 移动到 Python 客户端。现在,在上面的测试脚本中,通过将 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')
    

    如上所述,我们可以通过重复代理证书生成过程来创建一个broker2.pem,在测试脚本中使用它,并注意它会失败并显示一个

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

    【讨论】:

    • 是使用 python 代码创建连接所必需的证书。我正在尝试连接到已经启用 ssl 的 amazon mq
    【解决方案2】:

    试试这个。

     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()
    

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

    【讨论】:

      【解决方案3】:

      关键点是了解密钥/证书文件必须存在 - 否则即使在其存储库中的 stomp.py 测试也无法正常工作。如果未(正确)指定文件名,TLS 将无法在 stomp.py 中工作。

      在此处查看代码和密钥生成说明:https://gist.github.com/borislitvak/6ccea503abf1b2f9c89e87309d6dab88

      【讨论】:

        猜你喜欢
        • 2012-07-15
        • 2017-03-06
        • 2021-05-16
        • 2012-10-02
        • 2011-12-24
        • 1970-01-01
        • 2011-12-06
        • 2016-08-27
        • 1970-01-01
        相关资源
        最近更新 更多