最近在开发过程中, 有收到要求,rabbitmq 不能使用明文传输,常规想法就是TLS加密传输。看到rabbitmq 官网有提供TLS支持的说明。
Rabbitmq 加密传输由2种方式:
- 使用TLS加密直连
- 通过类似HA代理的方式从https转换成http 比如nginx, TLS_termination_proxy等都可以实现
官网也阐述了其他一些ssl支持的说明,这边就不介绍了。详细内容查阅官网
本文使用的rabbitmq 环境是,依赖docker 构建的rabbitmq 3.7.28版本的试验环境。
这边具体说一下,怎么构建TLS链接。
步骤1:使用TLS工具生成证书(开发测试环境),生产环境建议使用商业的CA机构制作的证书文件
git clone https://github.com/michaelklishin/tls-gen tls-gen
cd tls-gen/basic
# private key password
make PASSWORD=密码
make verify
make info
ls -l ./result
其中make PASSWORD=密码 这一行命名是生成证书的过程,其他几行都是校验检测,可以不执行。
make PASSWORD=rabbit
python3 profile.py regenerate --password "rabbit" \
--common-name izbp1exs0zcohfndb4w0o5z \
--client-alt-name izbp1exs0zcohfndb4w0o5z \
--server-alt-name izbp1exs0zcohfndb4w0o5z \
--days-of-validity 3650 \
--key-bits 2048
Removing /mnt/tls-gen/basic/testca
Removing /mnt/tls-gen/basic/result
Removing /mnt/tls-gen/basic/server
Removing /mnt/tls-gen/basic/client
Will generate a root CA and two certificate/key pairs (server and client)
=> [openssl_req]
Generating a 2048 bit RSA private key
...........................................................+++
....................+++
writing new private key to '/mnt/tls-gen/basic/testca/private/cakey.pem'
-----
=> [openssl_x509]
Will generate leaf certificate and key pair for server
Using izbp1exs0zcohfndb4w0o5z for Common Name (CN)
Using parent certificate path at /mnt/tls-gen/basic/testca/cacert.pem
Using parent key path at /mnt/tls-gen/basic/testca/private/cakey.pem
Will use RSA...
=> [openssl_genpkey]
..........................................................+++
...........................................................+++
=> [openssl_req]
=> [openssl_ca]
Using configuration from /tmp/tmpaxii46i1
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'izbp1exs0zcohfndb4w0o5z'
organizationName :ASN.1 12:'server'
localityName :ASN.1 12:'$$$$'
Certificate is to be certified until Nov 13 15:01:00 2030 GMT (3650 days)
Write out database with 1 new entries
Data Base Updated
=> [openssl_pkcs12]
Will generate leaf certificate and key pair for client
Using izbp1exs0zcohfndb4w0o5z for Common Name (CN)
Using parent certificate path at /mnt/tls-gen/basic/testca/cacert.pem
Using parent key path at /mnt/tls-gen/basic/testca/private/cakey.pem
Will use RSA...
=> [openssl_genpkey]
....................+++
.......+++
=> [openssl_req]
=> [openssl_ca]
Using configuration from /tmp/tmpaxii46i1
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'izbp1exs0zcohfndb4w0o5z'
organizationName :ASN.1 12:'client'
localityName :ASN.1 12:'$$$$'
Certificate is to be certified until Nov 13 15:01:00 2030 GMT (3650 days)
Write out database with 1 new entries
Data Base Updated
=> [openssl_pkcs12]
Done! Find generated certificates and private keys under ./result!
python3 profile.py verify
Will verify generated certificates against the CA...
Will verify client certificate against root CA
/mnt/tls-gen/basic/result/client_certificate.pem: OK
Will verify server certificate against root CA
/mnt/tls-gen/basic/result/server_certificate.pem: OK
上述命令会生成一些目录和证书文件
其中 result 目录,将CA buddle, server ,client 证书信息都汇集了一些,没有说明不同。
步骤2: 使用jdk的keytool 生成Java相关的配置信息, 中间过程,需要输入密码信息
keytool -import -alias server1 -file /mnt/rabbitmq/cert/server_certificate.pem -keystore /mnt/rabbitmq/cert/rabbitstore
这个过程其实就是想rabbitmq 服务端的证书适配成符合Java规则的rabbitstore文件,注意这个是server端的certificate.
输出结果:
Enter keystore password:
Re-enter new password:
Owner: O=server, CN=izbp1exs0zcohfndb4w0o5z
Issuer: L=$$$$, CN=TLSGenSelfSignedtRootCA
Serial number: 1
Valid from: Sun Nov 15 21:54:44 CST 2020 until: Wed Nov 13 21:54:44 CST 2030
Certificate fingerprints:
MD5: E0:84:44:70:74:27:5D:CE:09:CF:02:63:8E:65:01:1B
SHA1: F4:42:AF:0A:6E:B7:C9:D6:44:04:DB:E0:61:88:AD:BD:46:B4:3B:EE
SHA256: C0:C9:79:27:05:B9:09:7C:F0:8B:3A:FD:14:C0:21:16:B6:58:F9:12:89:11:5C:08:F1:EC:53:55:86:DA:6F:7D
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 3B B1 04 9E D9 66 1A 5C 7A B6 F8 38 11 79 17 A2 ;....f.\z..8.y..
0010: 7C 44 6F 9C .Do.
]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
#3: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
]
#4: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
]
#5: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: izbp1exs0zcohfndb4w0o5z
DNSName: izbp1exs0zcohfndb4w0o5z
DNSName: localhost
]
#6: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 65 45 D5 3F C8 13 F1 C4 F8 DD A8 28 14 61 0E 7E eE.?.......(.a..
0010: 19 54 AB AA .T..
]
]
Trust this certificate? [no]:
Certificate was not added to keystore
[root@izbp1exs0zcohfndb4w0o5z cert]# keytool -import -alias server1 -file /mnt/rabbitmq/cert/server_certificate.pem -keystore /mnt/rabbitmq/cert/rabbitstore
Enter keystore password:
Re-enter new password:
Owner: O=server, CN=izbp1exs0zcohfndb4w0o5z
Issuer: L=$$$$, CN=TLSGenSelfSignedtRootCA
Serial number: 1
Valid from: Sun Nov 15 21:54:44 CST 2020 until: Wed Nov 13 21:54:44 CST 2030
Certificate fingerprints:
MD5: E0:84:44:70:74:27:5D:CE:09:CF:02:63:8E:65:01:1B
SHA1: F4:42:AF:0A:6E:B7:C9:D6:44:04:DB:E0:61:88:AD:BD:46:B4:3B:EE
SHA256: C0:C9:79:27:05:B9:09:7C:F0:8B:3A:FD:14:C0:21:16:B6:58:F9:12:89:11:5C:08:F1:EC:53:55:86:DA:6F:7D
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 3B B1 04 9E D9 66 1A 5C 7A B6 F8 38 11 79 17 A2 ;....f.\z..8.y..
0010: 7C 44 6F 9C .Do.
]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
#3: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
]
#4: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
]
#5: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: izbp1exs0zcohfndb4w0o5z
DNSName: izbp1exs0zcohfndb4w0o5z
DNSName: localhost
]
#6: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 65 45 D5 3F C8 13 F1 C4 F8 DD A8 28 14 61 0E 7E eE.?.......(.a..
0010: 19 54 AB AA .T..
]
]
#####这边要求输入yes
Trust this certificate? [no]: yes
Certificate was added to keystore
步骤3 配置rabbit MQ 配置文件
###授权方式, 其实就是再默认的基础上加一个EXTERNAL, 如果单独配置EXTERNAL, java 客户端会出现无法找不到auth_mechanisms的问题, 这个具体还不知道原因
auth_mechanisms.1 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.3 = EXTERNAL
management_agent.disable_metrics_collector = true
ssl_options.client_renegotiation = false
ssl_options.secure_renegotiate = true
###禁止 明文tcp 连接
listeners.tcp = none
##管理端只允许本地连接
management.tcp.ip = 127.0.0.1
management.tcp.port = 15672
ssl_options.password=rabbit
###tls 端口
listeners.ssl.default = 5671
###tls工具,将需要的文件存放再result 目录中
ssl_options.cacertfile = /mnt/tls-gen/basic/result/ca_certificate.pem
ssl_options.certfile = /mnt/tls-gen/basic/result/server_certificate.pem
ssl_options.keyfile = /mnt/tls-gen/basic/result/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
ssl_cert_login_from = common_name
###支持TLS协议,
ssl_options.versions.1 = tlsv1
ssl_options.versions.2 = tlsv1.1
ssl_options.versions.3 = tlsv1.2
###TLS加密cipher 顺序
ssl_options.honor_cipher_order = true
ssl_options.honor_ecc_order = true
###TLS加密cipher
ssl_options.ciphers.1=ECDHE-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.2=ECDHE-RSA-AES256-GCM-SHA384
ssl_options.ciphers.3=ECDH-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.4=ECDH-RSA-AES256-GCM-SHA384
ssl_options.ciphers.5=DHE-RSA-AES256-GCM-SHA384
ssl_options.ciphers.6=DHE-DSS-AES256-GCM-SHA384
ssl_options.ciphers.7=ECDHE-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.8=ECDHE-RSA-AES128-GCM-SHA256
ssl_options.ciphers.9=ECDH-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.10=ECDH-RSA-AES128-GCM-SHA256
ssl_options.ciphers.11=DHE-RSA-AES128-GCM-SHA256
ssl_options.ciphers.12=DHE-DSS-AES128-GCM-SHA256
ssl_options.ciphers.13=ECDHE-ECDSA-AES256-SHA384
ssl_options.ciphers.14=ECDHE-RSA-AES256-SHA384
ssl_options.ciphers.15=ECDHE-ECDSA-DES-CBC3-SHA
ssl_options.ciphers.16=ECDH-ECDSA-AES256-SHA384
ssl_options.ciphers.17=ECDH-RSA-AES256-SHA384
ssl_options.ciphers.18=DHE-DSS-AES256-SHA256
ssl_options.ciphers.19=AES256-GCM-SHA384
ssl_options.ciphers.20=AES256-SHA256
ssl_options.ciphers.21=ECDHE-ECDSA-AES128-SHA256
ssl_options.ciphers.22=ECDHE-RSA-AES128-SHA256
ssl_options.ciphers.23=ECDH-ECDSA-AES128-SHA256
ssl_options.ciphers.24=ECDH-RSA-AES128-SHA256
ssl_options.ciphers.25=DHE-DSS-AES128-SHA256
ssl_options.ciphers.26=AES128-GCM-SHA256
ssl_options.ciphers.27=AES128-SHA256
ssl_options.ciphers.28=ECDHE-ECDSA-AES256-SHA
ssl_options.ciphers.29=ECDHE-RSA-AES256-SHA
ssl_options.ciphers.30=DHE-DSS-AES256-SHA
ssl_options.ciphers.31=ECDH-ECDSA-AES256-SHA
ssl_options.ciphers.32=ECDH-RSA-AES256-SHA
ssl_options.ciphers.33=AES256-SHA
ssl_options.ciphers.34=ECDHE-ECDSA-AES128-SHA
ssl_options.ciphers.35=ECDHE-RSA-AES128-SHA
ssl_options.ciphers.36=DHE-DSS-AES128-SHA
ssl_options.ciphers.37=ECDH-ECDSA-AES128-SHA
ssl_options.ciphers.38=ECDH-RSA-AES128-SHA
ssl_options.ciphers.39=AES128-SHA
步骤4 Java 客户端连接测试
这边说明一下,官网的demo,大部分都是对的,但是会有些小问题
比如: 需要将factory.enableHostnameVerification(); 取消。
public static void main(String[] args) throws Exception {
// tls 执行命令中设置的密码 make PASSWORD=rabbit
char[] passphrase = "rabbit".toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS12");
//tls生成的result目录下的client_key.p12
ks.load(new FileInputStream("D:\\work\\client_key.p12"), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
//keytools 生成过程中,设置的密码
char[] trustPassphrase = "rabbit".toCharArray();
KeyStore tks = KeyStore.getInstance("JKS");
//keytools 生成的rabbitstore
tks.load(new FileInputStream("D:\\work\\rabbitstore"), trustPassphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(tks);
SSLContext c = SSLContext.getInstance("TLSv1.2");
c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("IP地址");
factory.setPort(5671);
factory.useSslProtocol(c);
factory.setUsername("admin");
factory.setPassword("admin");
// factory.enableHostnameVerification();
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {
});
}
上面就是基本的rabbitmq tls 的基础支持。