自RabbitMQ 3.4.0起, 為防止 POODLE attack 攻擊,已經自動禁用了SSLv3.
使用TLS時,推薦安裝的Erlang/OTP版本為17.5或以上版本. R16版本在某些證書中可以工作,但存在major limitations.
必須安裝Erlang加密程序,并且保證它能工作.對于那些從源碼進行Erlang編譯的Windows用戶來說,可能會出現一些問題.
對于將RabbitMQ作為服務來運行的Windows XP用戶而言: 要在Windows XP上結合OpenSSL 0.9.8及其之后版本,并使用SSL來將RabbitMQ作為服務運行是不可行的.此bug已在Windows XP SP3和OpenSSL v0.9.8r以及v1.0.0d版本上經過了確認. 如果你想以服務來運行RabbitMQ,建議將其升級為Windows 7或者將OpenSSL降級為更早的版本 (v0.9.7e可以正常工作).
對于那些從源碼編譯Erlang而言:必須確保配置能找到OpenSSL,并且能構建加密程序.
密鑰,證書和CA證書
OpenSSL 是一個龐大而復雜的主題. 如何更全面地了解OpenSSL,以及如何好好使用,我們建議參考其它資源,如Network Security with OpenSSL.
OpenSSL可用來簡單地建立一個加密通道, 還可以用來在通道的各個端點(end points)之間交換簽名證書,并可驗證這些證書. 證書驗證需要從知名,可信的root證書處建立一條信任鏈. root certificate 是自簽名證書,可提供一個證書頒發機構.也存在一些收費的商業公司,它們可以簽名你已經生成的SSL證書.
對于本指南,我們會創建我們自己的證書頒發機構.一旦完成這個步驟,我們就可以為server和clients生成多種格式的簽名證書,它們將被用于Java,.Net以及Erlang AMQP 客戶端.
注意,Mono對于OpenSSL證書(存在一些bug)有更嚴格的要求,所以我們將使用更嚴格的關鍵約束是必要的.
# mkdir testca # cd testca # mkdir certs private # chmod 700 private # echo 01 > serial # touch index.txt
現在將下面的內容放置到openssl.cnf(其位置在我們剛創建的testca目錄之下)中:
[ ca ] default_ca = testca [ testca ] dir = . certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial default_crl_days = 7 default_days = 365 default_md = sha256 policy = testca_policy x509_extensions = certificate_extensions [ testca_policy ] commonName = supplied stateOrProvinceName = optional countryName = optional emailAddress = optional organizationName = optional organizationalUnitName = optional [ certificate_extensions ] basicConstraints = CA:false [ req ] default_bits = 2048 default_keyfile = ./private/cakey.pem default_md = sha256 prompt = yes distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions [ root_ca_distinguished_name ] commonName = hostname [ root_ca_extensions ] basicConstraints = CA:true keyUsage = keyCertSign, cRLSign [ client_ca_extensions ] basicConstraints = CA:false keyUsage = digitalSignature extendedKeyUsage = 1.3.6.1.5.5.7.3.2 [ server_ca_extensions ] basicConstraints = CA:false keyUsage = keyEncipherment extendedKeyUsage = 1.3.6.1.5.5.7.3.1
現在我們將使用我們自己的test證書頒發機構來生成密鑰和證書,仍然在testca目錄中執行:
# openssl req -x509 -config openssl.cnf -newkey rsa:2048 -days 365
\ -out cacert.pem -outform PEM -subj /CN=MyTestCA/ -nodes # openssl x509 -in cacert.pem -out cacert.cer -outform DER
這就是生成我們自己test證書頒發機構所需的操作.root證書位于testca/cacert.pem,也存在于testca/cacert.cer. 這兩個文件包含相同的信息,但格式是不同的. 雖然大多數都使用PEM 格式, Microsoft和Mono則喜歡使用不同的格式DER格式.
在設置了證書頒發機構后,現在我們需要為clients和server生成密鑰和證書.Erlang client和RabbitMQ broker都可以直接使用PEM 文件. 它們可通過三個文件來通知: 隱式可信任的root證書, 用于證明公共證書的所有權的私有密鑰,以及標識對等的公共密鑰自身.
為了方便起見,我們為Java和.Net clients提供了PKCS #12 存儲(包含client證書和密鑰). PKCS存儲通常用密碼進行自我保護,因此必須提供密碼.
創建server和client證書的過程是相似的.唯一的區別是簽名證書時添加的keyUsage字段.首先從server開始:
# cd .. # ls testca # mkdir server # cd server # openssl genrsa -out key.pem 2048 # openssl req -new -key key.pem -out req.pem -outform PEM \ -subj /CN=$(hostname)/O=server/ -nodes # cd ../testca # openssl ca -config openssl.cnf -in ../server/req.pem -out \ ../server/cert.pem -notext -batch -extensions server_ca_extensions # cd ../server # openssl pkcs12 -export -out keycert.p12 -in cert.pem -inkey key.pem -passout pass:MySecretPassword
現在是client:
# cd .. # ls server testca # mkdir client # cd client # openssl genrsa -out key.pem 2048 # openssl req -new -key key.pem -out req.pem -outform PEM \ -subj /CN=$(hostname)/O=client/ -nodes # cd ../testca # openssl ca -config openssl.cnf -in ../client/req.pem -out \ ../client/cert.pem -notext -batch -extensions client_ca_extensions # cd ../client # openssl pkcs12 -export -out keycert.p12 -in cert.pem -inkey key.pem -passout pass:MySecretPassword
在RabbitMQ中啟用SSL支持
要在RabbitMQ中啟用SSL/TLS支持,我們必須為RabbitMQ提供root證書, server證書文件,以及server密鑰的位置. 我們還需要告訴它監聽SSL連接的套接字,我們需要告訴它是否應該要求客戶提供證書,如果客戶端沒有提供一個證書,如果我們不能建立信任鏈,我們是否應該接受證書. RabbitMQ通過兩個參數來配置:
-rabbit ssl_listeners
用于監聽SSL連接的端口列表.要在單個網絡接口上監聽,需要在列表中添加{"127.0.0.1", 5671}這樣的配置.
-rabbit ssl_options
這是new_ssl 選項的元組列表. 完整可用的ssl_options信息可通過erl -man new_ssl手冊頁面查看,但最重要的東西是cacertfile, certfile and keyfile.
設置這些選項最簡單的方式是編輯configuration file.配置文件的示例如下,它會在此主機機的所有網絡接口上的5671端口上監聽SSL連接:
[ {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"}, {certfile,"/path/to/server/cert.pem"}, {keyfile,"/path/to/server/key.pem"}, {verify,verify_peer}, {fail_if_no_peer_cert,false}]} ]} ].
Windows用戶注意: 配置文件中的反斜杠("\")會被解釋為轉義序列- 因此如果將CA證書的路徑指定為c:\cacert.pem,那么你需要輸入{cacertfile, "c:\\cacert.pem"}或{cacertfile, "c:/cacert.pem"}.
當web瀏覽器連接到一個HTTPSweb服務器時, 服務器會提供它的公共證書,web瀏覽器會試圖在根證書和服務器證書之間建立一個信任鏈,如果一切進行得很好的話,加密通信通道就建立了.盡管在web瀏覽器和web服務器不常用, SSL 允許服務器要求客戶端提供證書. 通過這種方式,服務器可以驗證客戶端的身份.
服務器是否要求客戶端提供證書以及它們是否相信證書的策略,是由verify和fail_if_no_peer_certarguments 控制的. 通過{fail_if_no_peer_cert,false}選項,我們聲明了我們準備接受客戶端,它們可以不向我們發送證書,但通過{verify,verify_peer}選項,我們聲明了如果客戶端沒有向我們發送證書, 我們必須能建立信任鏈. 注意,這些值會隨著Erlang/OTP中的ssl版本變化, 因此檢查erl -man new_ssl 來確保你有正確值.
注意,如果使用了 {verify, verify_none}, 在客戶端和服務器之間將不會發生證書交換,rabbitmqctl list_connections 將輸出對等證書信息項的空字符串.
在啟動broker后,在rabbit.log中,你會看到下面的輸出:
=INFO REPORT==== 9-Aug-2010::15:10:55 === started TCP Listener on 0.0.0.0:5672 =INFO REPORT==== 9-Aug-2010::15:10:55 === started SSL Listener on 0.0.0.0:5671
同時,注意最后一行,它將展示RabbitMQ 服務器正在運行,且監聽了ssl連接.
信任Client的Root CA
目前,我們告知RabbitMQ查看testca/cacert.pem 文件. 這包含了我們test認證中心的公共證書. 我們可能存在由多個不同認證中心簽發的證書,我們希望RabbitMQ全部信任它們.因此,我們可以簡單地將這些證書追加到另一個新文件中,以作為RabbitMQ的cacerts參數:
# cat testca/cacert.pem >> all_cacerts.pem # cat otherca/cacert.pem >> all_cacerts.pem
提供證書密碼
可使用password選項來為私有密鑰提供密碼:
[ {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"}, {certfile, "/path/to/server_certificate.pem"}, {keyfile, "/path/to/server_key.pem"}, {password, "t0p$3kRe7"} ]} ]} ].
了解 TLS 漏洞: POODLE, BEAST, etc
POODLE
POODLE 是一個已知的能破壞SSLv3的SSL/TLS攻擊.從3.4.0版本開始, RabbitMQ服務器拒絕接受SSLv3連接. 在2014年12月,一個經過改良的能影響TLSv1.0的POODLE攻擊被 宣布. 因此,建議使用Erlang 18.0+的版本( 消除了TLS 1.0 POODLE攻擊漏洞)或者禁用TLSv1.0支持 (參考下面的章節).
BEAST
BEAST attack 是一個能攻擊TLSv1.0的漏洞. 為了減輕它的攻擊,禁用TLSv1.0支持(參考下面的章節).
通過配置來禁用 SSL/TLS 版本
為了限制SSL/TLS協議版本,使用版本選項:
%% Disable SSLv3.0 support, leaves TLSv1.0 enabled. [ {ssl, [{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}]}, {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"}, {certfile, "/path/to/server_certificate.pem"}, {keyfile, "/path/to/server_key.pem"}, {versions, ['tlsv1.2', 'tlsv1.1', tlsv1]} ]} ]} ].
%% Disable SSLv3.0 and TLSv1.0 support. [ {ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]}, {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"}, {certfile, "/path/to/server_certificate.pem"}, {keyfile, "/path/to/server_key.pem"}, {versions, ['tlsv1.2', 'tlsv1.1']} ]} ]} ].
要驗證,可使用
openssl s_client:
# connect using SSLv3 openssl s_client -connect 127.0.0.1:5671 -ssl3
# connect using TLSv1.0 through v1.2 openssl s_client -connect 127.0.0.1:5671 -tls1
可以看到下面的輸出:
SSL-Session: Protocol : TLSv1
JDK 和 .NET支持的TLS版本
禁用tlsv1.0限制客戶端平臺的支持。下面是一個表,說明JDK和.NET支持的TLS版本 。
配置密碼套件
可配置RabbitMQ使用的密碼套件.注意現在所有的套件都可以在所有系統上使用. 例如,使用橢圓曲線密碼,請運行最新的Erlang發布。下面的例子演示了如何使用TLS的密碼選項。
%% List allowed ciphers [ {ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]}, {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"}, {certfile, "/path/to/server_certificate.pem"}, {keyfile, "/path/to/server_key.pem"}, {versions, ['tlsv1.2', 'tlsv1.1']}, {ciphers, [{ecdhe_ecdsa,aes_128_cbc,sha256}, {ecdhe_ecdsa,aes_256_cbc,sha}]} ]} ]} ].
要列出Erlang 運行時安裝的所有密碼套件,可使用:
rabbitmqctl eval 'ssl:cipher_suites(openssl).'
信任級別
設置SSL連接時,協議中有兩個重要的階段.第一個階段發生對等端點交換證書(可選)的時候.當證書交換后,對等端點可選擇在根證書和其它存在的證書之間建立信任鏈. 這用來驗證對等端點的身份(提供的私有密鑰不會被偷!).
第二階段是協商對等節點的加密密鑰,該密鑰將用于通信的其余部分。如果交換證書,公鑰/私鑰將在密鑰協商中使用。
因此,您可以創建一個加密的SSL連接,而無需驗證證書。java客戶端支持兩種操作模式。
密鑰管理器,信任管理器,以及密鑰庫
在Java安全框架中,有三個需要注意的組件: 密鑰管理, 信任管理以及密鑰庫.
密鑰管理器用于對待節點管理其證書.也就是說,在會話設置時,密鑰管理會控制發送哪些證書給遠端對待節點.
信任管理器用于對等節點管理遠程證書.也就是說,在會話設置時,信任管理遠端節點的那些證書是可信任的.
密鑰庫是證書的Java封裝. Java會將證書轉換為Java特定的二進制格式或PKCS#12格式. 這些格式是使用Key Store類來管理的.對于服務端證書,我們會使用Java二進制格式,但對于客戶端 密鑰/證書對,我們將使用 PKCS#12格式.
沒有驗證證書的連接
我們的第一個示例將展示一個簡單的client,通過不驗證證書以及不存在任何客戶端證書的SSL來連接RabbitMQ服務器.
import java.io.*; import java.security.*; import com.rabbitmq.client.*; publicclass Example1 { public static void main(String[] args) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setPort(5671); factory.useSslProtocol(); // Tells the library to setup the default Key and Trust managers for you
// which do not do any form of remote server trust verification Connection conn = factory.newConnection(); Channel channel = conn.createChannel(); //non-durable, exclusive, auto-delete queue channel.queueDeclare("rabbitmq-java-test", false, true, true, null); channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes()); GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false); if(chResponse == null) { System.out.println("No message retrieved"); } else { byte[] body = chResponse.getBody(); System.out.println("Recieved: " + new String(body)); } channel.close(); conn.close(); } }
這個簡單的示例只是一個echo test.它會創建一個rabbitmq-java-test隊列,并向默認direct交換器中發送消息,然后讀回發布的消息,并進行回顯. 注意,我們使用的是專用的,非持久化,自動刪除的隊列,因此我們不需要擔心后期的手動清除.
存在和驗證證書
首先,我們需要設置密鑰庫.我們假設有要連接的服務器的證書,所以我們現在需要將它添加到我們的密鑰庫中(信任管理器會使用).
# keytool -import -alias server1 -file /path/to/server/cert.pem -keystore /path/to/rabbitstore
上面的命令會將cert.pem導入到rabbitstore中,并在內部稱其為server1. alias參數用于有多個證書或密鑰的時候用于指定別名,因為在內部必須有不同的名稱.
在關于是否相信證書的問題上,必須回答yes, 并要選擇一個密碼. 在這個例子中,我的密碼為rabbitstore。
然后我們要用PKCS#12文件中的客戶端證書和密鑰,已經在上面展示過了.
下面的例子會修改上面的代碼, 以使用我們的密鑰庫,密鑰管理器,以及信任管理器:
import java.io.*; import java.security.*; import javax.net.ssl.*; import com.rabbitmq.client.*; publicclass Example2 { publicstaticvoid main(String[] args) throws Exception { char[] keyPassphrase = "MySecretPassword".toCharArray(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream("/path/to/client/keycert.p12"), keyPassphrase); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passphrase); char[] trustPassphrase = "rabbitstore".toCharArray(); KeyStore tks = KeyStore.getInstance("JKS"); tks.load(new FileInputStream("/path/to/trustStore"), trustPassphrase); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(tks); SSLContext c = SSLContext.getInstance("TLSv1.1"); c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setPort(5671); factory.useSslProtocol(c); Connection conn = factory.newConnection(); Channel channel = conn.createChannel(); channel.queueDeclare("rabbitmq-java-test", false, true, true, null); channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes()); GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false); if(chResponse == null) { System.out.println("No message retrieved"); } else { byte[] body = chResponse.getBody(); System.out.println("Recieved: " + new String(body)); } channel.close(); conn.close(); } }
為了確保上述代碼能在其它情況下工作,可使用未導入到密鑰庫中的安全證書來嘗試,這時你可能會看到屏幕上的難異常信息.
配置 .Net 客戶端
為了能讓服務器安全證書能在.Net平臺上使用, 它們可以是多種格式,包括DER 和PKCS #12, 但不能是PEM. 對于DER格式, .Net希望它們能存儲在.cer擴展名的文件中.在上面的步驟中,當創建test認證機構時,我們會把PEM 轉換成DER格式,命令如下:
# openssl x509 -in /path/to/testca/cacert.pem -out /path/to/testca/cacert.cer -outform DER
PEM是base64編碼的DER格式, 使用分隔符進行封閉。此編碼通常是使它更容易在7位有限協議傳輸數據,如電子郵件(SMTP)。
RFC 5280, Certificate Key Usage and Mono
正如上面所提到的,Mono是相當嚴格的,強制證書只能應用于它聲明的特定目的.
SSL 證書和密鑰可應用于各種各樣的用途, 例如, 電子郵件簽名,代碼簽名, 通信加密等等. (我們這里的目的是TCP 通信加密). RFC 5280 指定了許多不同的目的, 并允許一個證書為特定的一組目的進行簽名.
SSL v3 證書可包含許多不同的擴展.處理證書如何使用的擴展被稱為Key Usage Extension. 不同使用一般來說,都支持得很好,即使是定義良好,它們的使用也是廣泛地解釋. 一些關鍵的用法已經廢棄,大部分已經完全忽視。
這里有一個更進一步的擴展, 它也指定了使用用途, 但它選擇O.I.Ns來進行, 如"1.3.6.1.5.5.7.3.1".顯然,英語缺乏一些明顯的用于添加的隨機數字。這是一個擴展密鑰使用擴展,序列的對象標識符,進一步定義了那些證書的使用是允許的。
而Mono,看上去認為這些擴展都是重要的, 需要有待進一步的觀察. 如果證書遺漏了Key Usage Extension,Mono會讓證書無效.默認情況下, OpenSSL 對于自簽名證書省略了Key Usage Extension ,因為它希望如果沒有找到Key Usage Extension, 該證書是有效的,可用于任何目的。
這就是為什么在樣例openssl.cnf文件的列舉中,root_ca_extensions, client_ca_extensions 和 server_ca_extensions 都指定了keyUsage,并且最后兩個還有extendedKeyUsage定義.因此上面生成的證書對于Mono的使用是有效的; keyEncipherment 指定了證書可通過SSL服務器來使用, digitalSignature指定了證書可由SSL客戶端使用. extendedKeyUsage 字段中的值都說了同樣的事情.
你可以使用this small tool 來檢查Mono是否接受RabbitMQ提供的證書. 注意,你需要使用合適的OpenSSL命令來轉換server/cert.pem 到tserver/cert.cer:
# openssl x509 -in /path/to/server/cert.pem -out /path/to/server/cert.cer -outform DER # mono certcheck.exe /path/to/server/cert. Checking if certificate is SSLv3... Ok Checking for KeyUsageExtension (2.5.29.15)... Ok Checking if KeyEncipherment flag is set... Ok This certificate CAN be used by Mono for Server validation
信任 .Net
在 .NET 平臺上, 遠程證書是通過任意數量的存儲庫來管理的.這些存儲庫的管理是通過 'certmgr'(Microsoft .Net 實現和Mono都可用)工具來完成的.
NB: 在某些Windows平臺上,有兩種版本的命令-一個是隨著操作系統自帶的,只提供了圖形界面,另一個Windows SDK自帶的,提供了圖形界面和命令行界面. 兩者都可以很好的完成工作,但示例將以后者為基礎.
對于我們的情況,因為我們提供的客戶端certificate/key對都在單獨的PKCS #12 文件中,我們要做的只是導入根證書機構的證書到Root (Windows) / Trust(Mono) 存儲庫中.在存儲庫中所有簽名的證書都是自動可信的.
對比java客戶端,會樂于使用SSL連接而不驗證服務器的證書,該.Net客戶端需要驗證成功。為阻止驗證,應用程序可在SslOptions.AcceptablePolicyErrors中設置System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable 以及System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors 標志.
Certmgr證書管理
certmgr 允許Add, Delete, List 以及在特定的Store上執行其它操作.這些stores 可以是用戶stores, 或機器范圍的.只有管理員用戶有機器范圍stores的寫訪問權限.
為了將證書加入users Root (Windows) / Trust (Mono) store,我們可以運行:
(Windows) > certmgr -add -all \path\to\cacert.cer -s Root (Mono) $ certmgr -add -c Trust /path/to/cacert.cer
將證書加入機器證書存儲庫,我們要運行
(Windows) > certmgr -add -all \path\to\cacert.cer -s -r localMachine Root (Mono) $ certmgr -add -c -m Trust /path/to/cacert.cer
在將證書添加到store后,我們可以用-list命令來展示證書內容:
(Windows) > certmgr -all -s Root (Mono) $ certmgr -list -c Trust Mono Certificate Manager - version 2.4.0.0 Manage X.509 certificates and CRL from stores. Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed. Self-signed X.509 v3 Certificate Serial Number: AC3F2B74ECDD9EEA00 Issuer Name: CN=MyTestCA Subject Name: CN=MyTestCA Valid From: 25/08/2009 14:03:01 Valid Until: 24/09/2009 14:03:01 Unique Hash: 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E
正如我們看到的,在信任庫里有一個自簽名的X.509 v3 證書. Unique Hash在store里是唯一的標識符.
要刪除這個證書,可使用unique hash:
(Windows) > certmgr -del -c -sha1 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E -s Root (Mono) $ certmgr -del -c Trust 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E Mono Certificate Manager - version 2.4.0.0 Manage X.509 certificates and CRL from stores. Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed. Certificate removed from store.
使用相同的步驟,我們可以在客戶端上也可以執行add/delete/list 我們根證書的操作.
創建連接
要創建RabbitMQ的SSL連接,我們需要在ConnectionFactory參數字段中設置一些新字段.為了讓事情更簡單,這里有一新字段Parameters.Ssl,它可作為需要設置所有其它字段的命名空間 . 這些字段是:
- Ssl.CertPath: 如果你的服務器希望驗證客戶端的話,這是PKCS#12格式的客戶端證書的路徑.這是可選的.
- Ssl.CertPassphrase:如果你正在使用PKCS#12 格式的客戶端證書,那么可能會需要一個密碼,你可以在這個字段中進行指定.
- Ssl.Enabled: 這是一個boolean字段,用以打開或關閉SSL支持.默認是off.
- Ssl.ServerName: 記住 .Net希望這匹配發送證書的CN.
例子
這與Java部分的例子是一樣的.它創建了一個channel, rabbitmq-dotnet-test隊列,并使用默認direct交換器來發布,然后再讀回發送的消息并進行回顯.注意,我們使用的是exclusive, non-durable, auto-delete隊列,因此我們不必擔憂事后的手動清除.
using System; using System.IO; using System.Text; using RabbitMQ.Client; using RabbitMQ.Util; namespace RabbitMQ.Client.Examples { public class TestSSL { public static int Main(string[] args) { ConnectionFactory cf = new ConnectionFactory(); cf.Ssl.ServerName = System.Net.Dns.GetHostName(); cf.Ssl.CertPath = "/path/to/client/keycert.p12"; cf.Ssl.CertPassphrase = "MySecretPassword"; cf.Ssl.Enabled = true; using (IConnection conn = cf.CreateConnection()) { using (IModel ch = conn.CreateModel()) { ch.QueueDeclare("rabbitmq-dotnet-test", false, false, false, null); ch.BasicPublish("", "rabbitmq-dotnet-test", null, Encoding.UTF8.GetBytes("Hello, World")); BasicGetResult result = ch.BasicGet("rabbitmq-dotnet-test", true); if (result == null) { Console.WriteLine("No message received."); } else { Console.WriteLine("Received:"); DebugUtil.DumpProperties(result, Console.Out, 0); } ch.QueueDelete("rabbitmq-dotnet-test"); } } return 0; } } }
注意,在Windows XP上,運行此例,可能會出現失敗
CryptographicException: Key not valid for use in specified state.
在這種情況下,你需要從證書存儲庫加載證書并直接設置
ConnectionFactory的Ssl.Certs參數才能成功運行.
R16B01之前的Erlang版本
有可能在老版本的Erlang中使用SSL.這些老版本可以與某些證書工作,其它則很困難.同時,它們也包含OTP-10905 bug,這使得它不能禁用掉任何協議版本.實際上,由于它不能禁用SSLv3, 使用SSL的老版本Erlang 對于POODLE attack (PDF link)是不安全的.
如果探測到老版本的Erlang,RabbitMQ 3.4.0會自動禁用SSL監聽器.如果這不是你所希望的,你可以設置ssl_allow_poodle_attack rabbit 配項為true.
ssl_allow_poodle_attack 是一個全局設置;在rabbit程序中的設置會控制所有SSL監聽器的行為(AMQP, management, STOMP, etc).
下面的例子進行了說明:
[ {rabbit, [ {ssl_listeners, [5671]}, {ssl_allow_poodle_attack, true}, {ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"}, {certfile,"/path/to/server/cert.pem"}, {keyfile,"/path/to/server/key.pem"}, {verify,verify_peer}, {fail_if_no_peer_cert,false}]} ]} ].
證書鏈和驗證深度
使用由中間人CA簽名的客戶端證書時,可能需要配置RabbitMQ服務器使用更高的驗證深度。該深度是非自頒發的中間證書的最大數量,可以按照有效的證書路徑中的對等證書。因,如果depth為0,對等節點(如.client)證書 必須由CA直接簽發,如果為1,路徑可以是"對等節點, CA, 信任的CA",如果設置為2,則路徑可以是 "peer, CA, CA, trusted CA"等等.下面的例子演示了如何來配置RabbitMQ server的證書驗證深度:
[ {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"}, {certfile,"/path/to/server/cert.pem"}, {keyfile,"/path/to/server/key.pem"}, {depth, 2}, {verify,verify_peer}, {fail_if_no_peer_cert,false}]} ]} ].
當在RabbitMQ 插件中使用SSL時,如federation或 shovel,有可能需要為Erlang客戶端配置證書的驗證深度,正如下面所描述的.
配置Erlang client
在RabbitMQ Erlang client 中啟用SSL是相當直接的. 在#amqp_params_network記錄,我們只需要在ssl_options字段中提供值. 你會認識到我們指定RabbitMQ的選項。
Erlang SSL 選項
必須提供三個重要選項:
- cacertfile 選項用于指定明確信任的根證書機構的證書.
- certfile是client擁有的PEM格式的證書
- keyfile是客戶端PEM格式的私有密鑰文件
作為RabbitMQ自身, verify 和fail_if_no_peer_cert 選項可用來指定當服務器未提供證書或我們不能根據服務器證書建立信任鏈時下的動作. depth配置了證書驗證的深度(參考上面部分).
代碼
Params = #amqp_params_network { port = 5671, ssl_options = [{cacertfile, "/path/to/testca/cacert.pem"}, {certfile, "/path/to/client/cert.pem"}, {keyfile, "/path/to/client/key.pem"}, %% only necessary with intermediate CAs %% {depth, 2}, {verify, verify_peer}, {fail_if_no_peer_cert, true}] }, {ok, Conn} = amqp_connection:start(Params),
現在你就可以像普通連接一樣來使用Conn了.
TLS/SSL故障排除
介紹
本頁收集一些技巧來幫助診斷SSL錯誤。策略是使用其它的SSL實現來測試必要的組件,在故障排除的過程中識別故障.
請記住,如果兩個特定的組件之間有相互作用的話,這個過程不保證識別問題.
我們也解釋了在日志中可能出現的一些常見錯誤消息.
Erlang中檢測SSL支持
建立與broker的SSL連接的第一個要求是在broker中要有SSL支持. 可通過運行erl(或Windows上的werl.exe)來確認Erlang VM是否支持SSL,命令為:
ssl:versions().
其輸出看起來像這樣(版本號可能有所不同):
[{ssl_app,"5.3.6"}, {supported,['tlsv1.2','tlsv1.1',tlsv1]}, {available,['tlsv1.2','tlsv1.1',tlsv1]}]
相反,如果你收到了錯誤信息,那么就要確認Erlang是否使用了OpenSSL來構建.在基于Debian的系統上,你需要安裝erlang-ssl包.
使用OpenSSL來檢查密鑰和證書
我們現在用其它的SSL實現來驗證配置文件中指定的證書和密鑰.這個例子使用 OpenSSL s_client 和 s_server. 我們將確認用于連接兩端的證書和密鑰.
針對下面的例子,我們假設你有下面的信息:
Item | Location |
CA certificate | testca/cacert.pem |
Server certificate | server/cert.pem |
Server key | server/key.pem |
Client certificate | client/cert.pem |
Client key | client/key.pem |
在一端執行下面的命令:
openssl s_server -accept 8443 -cert server/cert.pem -key server/key.pem \ -CAfile testca/cacert.pem
在另一端執行
openssl s_client -connect localhost:8443 -cert client/cert.pem -key client/key.pem \ -CAfile testca/cacert.pem
如果證書和密鑰創建正確,SSL連接建立序列將出現,并且終端會連接.一端的輸入會出現在另一端.如果建立了信任鏈,第二個終端將顯示下面的信息:
Verify return code: 0 (ok)
如果收到錯誤,需要確認證書和密鑰是否正確創建.
檢查broker正在監聽
此步驟可檢查broker是否正在期望的AMQPS端口上進行監聽.當你使用有效的SSL配置文件進行啟動時, broker會在日志文件中報告SSL監聽的地址.你可以類似于下面的東西:
=INFO REPORT==== 8-Aug-2011::11:51:47 === started SSL Listener on 0.0.0.0:5671
如果你設置了"ssl_listeners"配置指令但沒有看到這樣的信息,那么可能是你的配置文件沒有被broker讀取. 確認配置文件引用的broker日志中包含SSL配置選項.
嘗試與broker的SSL連接
一旦RabbitMQ broker監聽于SSL端口,你可使用OpenSSL s_client來驗證SSL 連接, 這次針對的是broker. 這可以檢測broker是否配置正確,不必配置AMQPS client.此例假設broker使用"ssl_listeners"指令配置監聽SSL連接的端口5671:
openssl s_client -connect localhost:5671 -cert client/cert.pem -key client/key.pem \ -CAfile testca/cacert.pem
輸出類似于端口8443的情況. 當連接建立的時候,broker日志文件應該包含一個新的條目:
=INFO REPORT==== 8-Aug-2011::11:55:13 === accepting AMQP connection <0.223.0> (127.0.0.1:58954 -> 127.0.0.1:5671)
如果你現在為broker提供8個隨機字節,broker將會使用字符串"AMQP"緊跟編碼的版本號數字進行回復. 如果你認出了"AMQP"字符串,那么你能確定你已經連接上了AMQP broker.
使用stunnel來驗證客戶端連接
最后的檢查是驗證AMQPS clients. 我們將使用stunnel 來提供SSL能力.要這個配置中,AMQPS clients會使用stunnel來作安全連接,它將傳遞解密后的數據給broker的AMQP端口. 這提供了一些信心,客戶端的SSL配置是獨立于broker的SSL配置的正確配置.
stunnel會以守護進程的方式運行在與broker的同一臺機器上.在下面的討論中,假定只會暫時使用stunnel.
在下面的討論中,假定只會暫時使用stunnel。當然可能使用Stunnel提供SSL能力更永久,但是隨著broker的集成缺乏,意味著管理報告功能和認證的插件(使用SSL信息)將無法這樣做。
在這個例子中,stunne會連接到未加密的AMQP端口(5672) ,并接受具有SSL能力的客戶端5679端口上的連接:
cat client/key.pem client/cert.pem > client/key-cert.pem stunnel -r localhost:5672 -d 5679 -f -p client/key-cert.pem -D 7
stunnel 需要證書和相應的密鑰.
生成的客戶端證書和相應的密鑰應該使用和如上面所示的cat命令連接。Stunnel要求的密鑰不需要密碼保護。SSL能力客戶端現在可以連接端口5679,任何SSL錯誤會在stunnel啟動時出現在控制臺.
連接client和broker
如果上面的步驟沒有出錯,那么你可以測試AMQPS客戶端已經連接到broker的 AMQPS端口, 首先須確保停止任何運行的OpenSSL或stunnel會話.
證書鏈和驗證深度
當使用第三方CA簽發的客戶端證書時,有必要配置RabbitMQ server使用更高的驗證深度. 該depth是非自頒發的中間證書的最大數量,可以按照有效的證書路徑中的對等證書。
參考TLS/SSL guide來了解如何配置驗證深度.
理解 SSL logs
上述步驟會產生新broker日志文件條目.這些條目與診斷輸出一起可以幫助確認SSL錯誤的原因.
下面的是一些常見的錯誤條目:
- Entries containing {ssl_upgrade_error, ekeyfile} or {ssl_upgrade_error, ecertfile}
這意味著broker的 keyfile 或certificate文件是無效的.須確認keyfile匹配證書,且兩者都是PEM 格式的. PEM格式是一種可打印的編碼與識別的分隔符. 證書以 -----BEGIN CERTIFICATE----- 并以-----END CERTIFICATE----- 結束. keyfile 會以-----BEGIN RSA PRIVATE KEY----- 開始,并以-----END RSA PRIVATE KEY----- 結束.
- Entries containing {ssl_upgrade_failure, ... certify ...}
- 此錯誤與客戶端驗證有關.客戶端提供了一個無效的證書或無證書. 如果ssl_options設置了verify 選項為verify_peer,那么將使用臨時使用verify_none.必須確保客戶端證書已經正確生成了,并且客戶端提交正確的證書。
- Entries containing {ssl_upgrade_error, ...}
這是一個通用的錯誤,可能有許多原因。確保你使用的是Erlang的推薦版本。
- Entries containing {tls_alert,"bad record mac"}
服務器已嘗試驗證它所接收的數據的完整性和檢查失敗。這可能是由于有問題的網絡設備, 無意的客戶端socket共享(如.因為使用了fork(2))或者客戶端實現的TLS的bug.
posted on 2016-08-02 22:25
胡小軍 閱讀(11904)
評論(0) 編輯 收藏 所屬分類:
RabbitMQ