使用 pika 与 RabbitMQ 的 TLS 加密连接

TLS-Encrypted Connection with RabbitMQ Using pika

我发现无法在客户端使用 python 的 pika 库与 RabbitMQ 代理建立加密连接。我的起点是 pika 教程示例 here,但我无法让它工作。我进行了如下操作。

(1) RabbitMQ 配置文件 是:

listeners.tcp.default = 5672
listeners.ssl.default = 5671

ssl_options.verify               = verify_peer
ssl_options.fail_if_no_peer_cert = false
ssl_options.cacertfile           = /etc/cert/tms.crt
ssl_options.certfile             = /etc/cert/tms.crt
ssl_options.keyfile              = /etc/cert/tmsPrivKey.pem

auth_mechanisms.1 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.3 = EXTERNAL

(2) rabbitmq-auth-mechanism-ssl 插件 已使用以下命令启用:

rabbitmq-plugins enable rabbitmq_auth_mechanism_ssl

通过以下方式检查启用状态确认启用成功:rabbitmq-plugins list

(3) TLS 证书 的正确性已通过使用 openssl 工具验证 here

(4) 建立连接的客户端程序是:

#!/usr/bin/env python
import logging
import pika
import ssl
from pika.credentials import ExternalCredentials

logging.basicConfig(level=logging.INFO)
context = ssl.create_default_context(
                        cafile="/Xyz/sampleNodeCert/tms.crt")
context.load_cert_chain("/Xyz/sampleNodeCert/node.crt",
                        "/Xyz/sampleNodeCert/nodePrivKey.pem")

ssl_options = pika.SSLOptions(context, '127.0.0.1')
conn_params = pika.ConnectionParameters(host='127.0.0.1',
                                        port=5671,
                                        ssl_options=ssl_options,
                                        credentials=ExternalCredentials())

with pika.BlockingConnection(conn_params) as conn:
     ch = conn.channel()
     ch.queue_declare("foobar")
     ch.basic_publish("", "foobar", "Hello, world!")
     print(ch.basic_get("foobar"))

(5) 客户端程序失败,出现以下 错误消息

pika.exceptions.ProbableAuthenticationError: ConnectionClosedByBroker: (403) 'ACCESS_REFUSED - Login was refused using authentication mechanism EXTERNAL. For details see the broker logfile.'

(6) RabbitMQ 代理中的 日志消息 是:

2019-10-15 20:17:46.028 [info] <0.642.0> accepting AMQP connection <0.642.0> (127.0.0.1:48252 -> 127.0.0.1:5671)
2019-10-15 20:17:46.032 [error] <0.642.0> Error on AMQP connection <0.642.0> (127.0.0.1:48252 -> 127.0.0.1:5671, state: starting):
EXTERNAL login refused: user 'CN=www.node.com,O=Node GmbH,L=NodeTown,ST=NodeProvince,C=DE' - invalid credentials
2019-10-15 20:17:46.043 [info] <0.642.0> closing AMQP connection <0.642.0> (127.0.0.1:48252 -> 127.0.0.1:5671)

(7) 完成此测试的 环境 是 Ubuntu 18.04,在 Erlang 22.0.7 上使用 RabbitMQ 3.7.17。客户端使用了python3版本3.6.8

问题:有人知道我的测试失败的原因吗?我在哪里可以找到使用 pika 设置到 RabbitMQ 的加密连接的完整工作示例?

注意:我熟悉 ,但是 post 中的 none 提示对我有帮助。

在学习了link provided above by Luke Bakken之后,我现在可以回答我自己的问题了。相对于我的原始示例的主要变化是我使用无密码用户配置 RabbitMQ 代理,该用户与服务器端和客户端上的 TLS 证书的 CN 字段具有相同的名称。为了说明,下面我再详细看一遍我的例子:

(1) RabbitMQ配置文件为:

listeners.tcp.default = 5672
listeners.ssl.default = 5671

ssl_cert_login_from = common_name

ssl_options.verify               = verify_peer
ssl_options.fail_if_no_peer_cert = true
ssl_options.cacertfile           = /etc/cert/tms.crt
ssl_options.certfile             = /etc/cert/tms.crt
ssl_options.keyfile              = /etc/cert/tmsPrivKey.pem

auth_mechanisms.1 = EXTERNAL
auth_mechanisms.2 = PLAIN
auth_mechanisms.3 = AMQPLAIN

请注意,使用 ssl_cert_login_from 配置选项,我要求从 TLS 证书的 "common name" (CN) 字段中获取 RabbitMQ 帐户的用户名。

(2) 使用以下命令启用 rabbitmq-auth-mechanism-ssl 插件

rabbitmq-plugins enable rabbitmq_auth_mechanism_ssl

启用成功可以通过命令查看启用状态来确认:rabbitmq-plugins list.

(3) 签名的 TLS 证书 的颁发者和主题 CN 字段必须彼此相等,并且等于 RabbitMQ 代理节点的主机名。在我的例子中,检查 RabbitMQ 日志文件(在 /var/log/rabbitmq 中)显示代理在名为 rabbit@pnp-vm2 的节点上 运行。因此,主机名是 pnp-vm2。为了检查客户端证书的 CN 字段,我使用以下命令:

ap@pnp-vm2:openssl x509 -noout -text -in /etc/cert/node.crt | fgrep CN
        Issuer: C = CH, ST = CH, L = Location, O = Organization GmbH, CN = pnp-vm2
        Subject: C = DE, ST = NodeProvince, L = NodeTown, O = Node GmbH, CN = pnp-vm2

如您所见,Issuer CN 字段和 Subject CN 字段都等于:"pnp-vm2"(这是 RabbitMQ 代理的主机名,见上文)。我尝试仅将此名称用于两个 CN 字段之一,但无法建立与代理的连接。在我的测试环境中,创建具有相同 CN 名称的客户端证书很容易,但在操作环境中,这可能要困难得多。另外,我不太明白这个限制的原因:它是一个错误还是一个功能?它是源自我正在使用的特定 RabbitMQ 库(python's pika)还是源于 AMQP 协议?这些问题可能值得专门 post.

(4) 建立连接的客户端程序是:

#!/usr/bin/env python
import logging
import pika
import ssl
from pika.credentials import ExternalCredentials

logging.basicConfig(level=logging.INFO)
context = ssl.create_default_context(cafile="/home/ap/RocheTe/cert/sampleNodeCert/tms.crt")
context.load_cert_chain("/home/ap/RocheTe/cert/sampleNodeCert/node.crt",
                        "/home/ap/RocheTe/cert/sampleNodeCert/nodePrivKey.pem")

ssl_options = pika.SSLOptions(context, 'pnp-vm2')
conn_params = pika.ConnectionParameters(host='a.b.c.d',
                                        port=5671,
                                        ssl_options=ssl_options,
                                        credentials=ExternalCredentials(),
                                        heartbeat=0)

with pika.BlockingConnection(conn_params) as conn:
    ch = conn.channel()
    ch.queue_declare("foobar")
    ch.basic_publish("", "foobar", "Hello, world!")
    print(ch.basic_get("foobar"))
    input("Press Enter to continue...")

这里,"a.b.c.d"是RabbitMQ代理所在机器的IP地址运行。

(5) 完成此测试的 环境 是 Ubuntu 18.04,在 Erlang 22.0.7 上使用 RabbitMQ 3.7.17。客户端使用了python3版本3.6.8。

最后一个警告:使用此配置,我能够建立到 RabbitMQ Broker 的安全连接,但是由于我仍然不明白的原因,无法启动 RabbitMQ Web 管理工具。 .