引言主要介绍了如何通过 Docker 安装 RabbitMQ 并配置 SSL;配置成功之后给出了 SpringBoot 和 Python 集成的例子。
生成证书1 2 3 4 5 6 7 8 9 10 11 12 13 14 git clone https://github.com/Berico-Technologies/CMF-AMQP-Configuration.git cd CMF-AMQP-Configuration/ssl/ sed -i "s/valid=365/valid=3650/g" create_client_cert.sh sed -i "s/valid=365/valid=3650/g" make_server_cert.sh sed -i "s/valid=365/valid=3650/g" setup_ca.sh sed -i "s/default_days = 365/default_days = 3650/g" openssl.cnf # Greyfoss 为自定义的证书签发机构名称,该脚本会生成一个ca目录,存储证书颁发机构的信息以及签发的证书 sh setup_ca.sh Greyfoss # 生成服务端公钥和私钥 rabbit-server为生成的密钥前缀 123456为该秘钥自定义的密码 sh make_server_cert.sh rabbit-server 123456 # 生成客户端公钥和私钥 sh create_client_cert.sh rabbit-client 123456`
生成 Java 客户端需要的证书使用 java 的 keytool 工具生成客户端需要的证书。
1 keytool -import -alias rabbit-server -file server/rabbit-server.cert.pem -keystore server.keystore -storepass 123456
import 将已签名数字证书导入密钥库
alias 指定导入条目的别名
file 指定要导入的证书
keystore 指定密钥库的名称
storepass 指定生成密钥库的密码
运行该命令会生成密钥库文件 server.keystore
。
先启动 rabbitmq 容器
1 docker run --name rabbitmq --restart=unless-stopped -d -p 5672:5672 -p 15672:15672 rabbitmq:management
在宿主机上创建以下文件夹:
1 mkdir -p /deploy/rabbitmq/conf/ssl
拷贝容器中的文件到宿主机 /deploy/rabbitmq/conf
目录下:
1 2 3 docker cp rabbitmq:/etc/rabbitmq/conf.d /deploy/rabbitmq/conf docker cp rabbitmq:/etc/rabbitmq/enabled_plugins /deploy/rabbitmq/conf docker cp rabbitmq:/etc/rabbitmq/rabbitmq.conf /deploy/rabbitmq/conf #这个文件3.8版本才有,3.9版本不存在
拷贝需要的证书到 /deploy/rabbitmq/conf/ssl
下
1 2 3 cp server/rabbit-server.cert.pem /deploy/rabbitmq/conf/ssl cp server/rabbit-server.key.pem /deploy/rabbitmq/conf/ssl cp ca/cacert.pem /deploy/rabbitmq/conf/ssl
编辑 /deploy/rabbitmq/conf/conf.d/rabbitmq.conf
文件,替换为以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 `# 禁用非tls连接 listeners.tcp = none # SSL\TLS通信的端口 listeners.ssl.default = 5671 # 管理控制台端口 management.tcp.port = 15672 # 服务端私钥和证书文件配置 ssl_options.cacertfile = /etc/rabbitmq/ssl/cacert.pem ssl_options.certfile = /etc/rabbitmq/ssl/rabbit-server.cert.pem ssl_options.keyfile = /etc/rabbitmq/ssl/rabbit-server.key.pem # 有verify_none和verify_peer两个选项,verify_none表示完全忽略验证证书的结果,verify_peer表示要求验证对方证书 ssl_options.verify = verify_peer # 若为true,服务端会向客户端索要证书,若客户端无证书则中止SSL握手;若为false,则客户端没有证书时依然可完成SSL握手 ssl_options.fail_if_no_peer_cert = true # 指定开启的tls版本 ssl_options.versions.1=tlsv1.2 ssl_options.versions.2=tlsv1.1 # 指定对应的cipher suites ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384 ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384 ssl_options.ciphers.3 = ECDHE-ECDSA-AES256-SHA384 ssl_options.ciphers.4 = ECDHE-RSA-AES256-SHA384 ssl_options.ciphers.5 = ECDHE-ECDSA-DES-CBC3-SHA ssl_options.ciphers.6 = ECDH-ECDSA-AES256-GCM-SHA384 ssl_options.ciphers.7 = ECDH-RSA-AES256-GCM-SHA384 ssl_options.ciphers.8 = ECDH-ECDSA-AES256-SHA384 ssl_options.ciphers.9 = ECDH-RSA-AES256-SHA384 ssl_options.ciphers.10 = DHE-DSS-AES256-GCM-SHA384 ssl_options.ciphers.11= DHE-DSS-AES256-SHA256 ssl_options.ciphers.12 = AES256-GCM-SHA384 ssl_options.ciphers.13 = AES256-SHA256 ssl_options.ciphers.14 = ECDHE-ECDSA-AES128-GCM-SHA256 ssl_options.ciphers.15 = ECDHE-RSA-AES128-GCM-SHA256 ssl_options.ciphers.16 = ECDHE-ECDSA-AES128-SHA256 ssl_options.ciphers.17 = ECDHE-RSA-AES128-SHA256 ssl_options.ciphers.18 = ECDH-ECDSA-AES128-GCM-SHA256 ssl_options.ciphers.19= ECDH-RSA-AES128-GCM-SHA256 ssl_options.ciphers.20 = ECDH-ECDSA-AES128-SHA256 ssl_options.ciphers.21 = ECDH-RSA-AES128-SHA256 ssl_options.ciphers.22 = DHE-DSS-AES128-GCM-SHA256 ssl_options.ciphers.23 = DHE-DSS-AES128-SHA256 ssl_options.ciphers.24 = AES128-GCM-SHA256 ssl_options.ciphers.25 = AES128-SHA256 ssl_options.ciphers.26 = ECDHE-ECDSA-AES256-SHA ssl_options.ciphers.27 = ECDHE-RSA-AES256-SHA ssl_options.ciphers.28 = DHE-DSS-AES256-SHA ssl_options.ciphers.29 = ECDH-ECDSA-AES256-SHA ssl_options.ciphers.30 = ECDH-RSA-AES256-SHA ssl_options.ciphers.31= AES256-SHA ssl_options.ciphers.32 = ECDHE-ECDSA-AES128-SHA ssl_options.ciphers.33 = ECDHE-RSA-AES128-SHA ssl_options.ciphers.34 = DHE-DSS-AES128-SHA ssl_options.ciphers.35 = DHE-DSS-AES128-SHA256 ssl_options.ciphers.36 = ECDH-ECDSA-AES128-SHA ssl_options.ciphers.37 = ECDH-RSA-AES128-SHA ssl_options.ciphers.38 = AES128-SHA`
删除正在运行的 rabbitmq:
使用下面命令重新创建 rabbitmq 容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 version: "2.1" services: bitwarden: #image: rabbitmq:management image: rabbitmq:3-management-qz container_name: rabbitmq environment: - TZ=Asia/Shanghai volumes: - /deploy/rabbitmq/data:/var/lib/rabbitmq - /deploy/rabbitmq/conf:/etc/rabbitmq/ ports: - 15672:15672 - 5672:5672 - 5671:5671 restart: unless-stopped
docker logs -f rabbitmq
看到日志内以下内容说明配置成功。
1 2 3 4 5 6 7 8 9 `2021-07-09 02:47:14.453 [info] <0.882.0> started TLS (SSL) listener on [::]:5671 {removed_failing_handler,rabbit_log} 2021-07-09 02:47:17.092 [info] <0.736.0> Server startup complete; 4 plugins started. * rabbitmq_prometheus * rabbitmq_management * rabbitmq_web_dispatch * rabbitmq_management_agent completed with 4 plugins. 2021-07-09 02:47:17.093 [info] <0.736.0> Resetting node maintenance status`
默认的 guest 用户不能远程访问,因此需要创建一个可以远程访问的用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 进入容器内部 $ docker exec -it rabbitmq bash # 创建一个用户名为rabbit,密码为rabbit的用户 $ rabbitmqctl add_user rabbit rabbit # 设置用户权限为超级管理员 $ rabbitmqctl set_user_tags rabbit administrator # 授权远程访问 $ rabbitmqctl set_permissions -p / rabbit "." "." ".*" # 退出容器 $ exit # 重启rabbitmq docker restart rabbitmq
通过刚才创建的用户 rabbit
访问管理员页面 http://ip:15672
看到 amqp/ssl 5671
说明配置成功! 🥳
SpringBoot 集成 RabbitMQ SSL首先在配置文件中添加以下配置,比如在 application-dev.yaml
中添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 `spring: rabbitmq: virtual-host: / host: 127.0.0.1 port: 5671 username: ${RABBIT_USERNAME} password: ${RABBIT_PASSWORD} ssl: enabled: true key-store: classpath:keyStore/rabbit-client.keycert.p12 key-store-password: ${KEYSTORE_PASSWORD} trust-store: classpath:keyStore/server.keystore trust-store-password: ${TRUSTSTORE_PASSWORD} algorithm: TLSv1.2 verify-hostname: false`
其中 ${RABBIT_USERNAME}
等配置在系统变量中。
比较简单,只要导入依赖:
1 2 3 4 `<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>`
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
类就会自动配置。
Python 集成 RabbitMQ SSL1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import logging import pika import ssl # 请替换所有证书路径 # 以下证书分别在目录 ~/rabbitmq/etc/rabbitmq/ssl/ sa和client文件下 ca_certfile = "certs/cacert.pem" certfile = "certs/rabbit-client.cert.pem" private_key = "certs/rabbit-client.key.pem" # 服务器信息 hostname = "127.0.0.1" port = 5671 # logging.basicConfig(level=logging.INFO) credentials = pika.credentials.PlainCredentials('rabbit', 'rabbit') context = ssl.create_default_context(cafile=ca_certfile) # 注意:由于证书是不受信任的,因此必须要关闭证书验证,否则会运行报错,提示证书验证失败! context = ssl._create_unverified_context() context.load_cert_chain(certfile, private_key) ssl_options = pika.SSLOptions(context, hostname) conn_params = pika.ConnectionParameters(host=hostname,port=port, ssl_options=ssl_options,credentials=credentials) with pika.BlockingConnection(conn_params) as conn: ch = conn.channel() ch.queue_declare("ssl_test") ch.basic_publish("", "ssl_test", "Hello, world!") conn.close()`
FAQ 启动 rabbitmq 容器失败,报 ssl_options.keyfile invalid, file does not exist or cannot be read by the node启动后查看容器日志,发现以下报错:
1 2 3 4 5 02:56:48.341 [error] ssl_options.keyfile invalid, file does not exist or cannot be read by the node 02:56:48.351 [error] Error preparing configuration in phase validation: 02:56:48.351 [error] - ssl_options.keyfile invalid, file does not exist or cannot be read by the node 02:56:48.356 [error] 02:56:48.356 [error] BOOT FAILED
进入宿主机下 ~/rabbitmq/etc/rabbitmq/ssl
目录,确定 cacert.pem
、rabbit-server.cert.pem
、rabbit-server.key.pem
的读写权限。
1 2 3 4 5 6 7 8 ll total 24 drwxrwxr-x 2 yjw yjw 4096 Jul 9 09:58 ./ drwxrwxr-x 4 yjw yjw 4096 Jul 9 10:25 ../ -rw-rw-r-- 1 yjw yjw 1046 Jul 9 09:56 cacert.pem -rw------- 1 yjw yjw 2349 Jul 9 09:58 rabbit-client.keycert.p12 -rw-rw-r-- 1 yjw yjw 1070 Jul 9 09:56 rabbit-server.cert.pem -rw------- 1 yjw yjw 1675 Jul 9 09:56 rabbit-server.key.pem
这里就需要把 rabbit-server.key.pem
的读写权限设成和其他两个一样。
1 chmod 664 rabbit-server.key.pem