自RabbitMQ 3.4.0起, 為防止 POODLE attack 攻擊,已經(jīng)自動禁用了SSLv3.
使用TLS時,推薦安裝的Erlang/OTP版本為17.5或以上版本. R16版本在某些證書中可以工作,但存在major limitations.
必須安裝Erlang加密程序,并且保證它能工作.對于那些從源碼進行Erlang編譯的Windows用戶來說,可能會出現(xiàn)一些問題.
對于將RabbitMQ作為服務(wù)來運行的Windows XP用戶而言: 要在Windows XP上結(jié)合OpenSSL 0.9.8及其之后版本,并使用SSL來將RabbitMQ作為服務(wù)運行是不可行的.此bug已在Windows XP SP3和OpenSSL v0.9.8r以及v1.0.0d版本上經(jīng)過了確認. 如果你想以服務(wù)來運行RabbitMQ,建議將其升級為Windows 7或者將OpenSSL降級為更早的版本 (v0.9.7e可以正常工作).
對于那些從源碼編譯Erlang而言:必須確保配置能找到OpenSSL,并且能構(gòu)建加密程序.
密鑰,證書和CA證書
OpenSSL 是一個龐大而復(fù)雜的主題. 如何更全面地了解OpenSSL,以及如何好好使用,我們建議參考其它資源,如Network Security with OpenSSL.
OpenSSL可用來簡單地建立一個加密通道, 還可以用來在通道的各個端點(end points)之間交換簽名證書,并可驗證這些證書. 證書驗證需要從知名,可信的root證書處建立一條信任鏈. root certificate 是自簽名證書,可提供一個證書頒發(fā)機構(gòu).也存在一些收費的商業(yè)公司,它們可以簽名你已經(jīng)生成的SSL證書.
對于本指南,我們會創(chuàng)建我們自己的證書頒發(fā)機構(gòu).一旦完成這個步驟,我們就可以為server和clients生成多種格式的簽名證書,它們將被用于Java,.Net以及Erlang AMQP 客戶端.
注意,Mono對于OpenSSL證書(存在一些bug)有更嚴格的要求,所以我們將使用更嚴格的關(guān)鍵約束是必要的.
# mkdir testca # cd testca # mkdir certs private # chmod 700 private # echo 01 > serial # touch index.txt
現(xiàn)在將下面的內(nèi)容放置到openssl.cnf(其位置在我們剛創(chuàng)建的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
現(xiàn)在我們將使用我們自己的test證書頒發(fā)機構(gòu)來生成密鑰和證書,仍然在testca目錄中執(zhí)行:
# 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證書頒發(fā)機構(gòu)所需的操作.root證書位于testca/cacert.pem,也存在于testca/cacert.cer. 這兩個文件包含相同的信息,但格式是不同的. 雖然大多數(shù)都使用PEM 格式, Microsoft和Mono則喜歡使用不同的格式DER格式.
在設(shè)置了證書頒發(fā)機構(gòu)后,現(xiàn)在我們需要為clients和server生成密鑰和證書.Erlang client和RabbitMQ broker都可以直接使用PEM 文件. 它們可通過三個文件來通知: 隱式可信任的root證書, 用于證明公共證書的所有權(quán)的私有密鑰,以及標識對等的公共密鑰自身.
為了方便起見,我們?yōu)镴ava和.Net clients提供了PKCS #12 存儲(包含client證書和密鑰). PKCS存儲通常用密碼進行自我保護,因此必須提供密碼.
創(chuàng)建server和client證書的過程是相似的.唯一的區(qū)別是簽名證書時添加的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
現(xiàn)在是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密鑰的位置. 我們還需要告訴它監(jiān)聽SSL連接的套接字,我們需要告訴它是否應(yīng)該要求客戶提供證書,如果客戶端沒有提供一個證書,如果我們不能建立信任鏈,我們是否應(yīng)該接受證書. RabbitMQ通過兩個參數(shù)來配置:
-rabbit ssl_listeners
用于監(jiān)聽SSL連接的端口列表.要在單個網(wǎng)絡(luò)接口上監(jiān)聽,需要在列表中添加{"127.0.0.1", 5671}這樣的配置.
-rabbit ssl_options
這是new_ssl 選項的元組列表. 完整可用的ssl_options信息可通過erl -man new_ssl手冊頁面查看,但最重要的東西是cacertfile, certfile and keyfile.
設(shè)置這些選項最簡單的方式是編輯configuration file.配置文件的示例如下,它會在此主機機的所有網(wǎng)絡(luò)接口上的5671端口上監(jiān)聽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用戶注意: 配置文件中的反斜杠("\")會被解釋為轉(zhuǎn)義序列- 因此如果將CA證書的路徑指定為c:\cacert.pem,那么你需要輸入{cacertfile, "c:\\cacert.pem"}或{cacertfile, "c:/cacert.pem"}.
當web瀏覽器連接到一個HTTPSweb服務(wù)器時, 服務(wù)器會提供它的公共證書,web瀏覽器會試圖在根證書和服務(wù)器證書之間建立一個信任鏈,如果一切進行得很好的話,加密通信通道就建立了.盡管在web瀏覽器和web服務(wù)器不常用, SSL 允許服務(wù)器要求客戶端提供證書. 通過這種方式,服務(wù)器可以驗證客戶端的身份.
服務(wù)器是否要求客戶端提供證書以及它們是否相信證書的策略,是由verify和fail_if_no_peer_certarguments 控制的. 通過{fail_if_no_peer_cert,false}選項,我們聲明了我們準備接受客戶端,它們可以不向我們發(fā)送證書,但通過{verify,verify_peer}選項,我們聲明了如果客戶端沒有向我們發(fā)送證書, 我們必須能建立信任鏈. 注意,這些值會隨著Erlang/OTP中的ssl版本變化, 因此檢查erl -man new_ssl 來確保你有正確值.
注意,如果使用了 {verify, verify_none}, 在客戶端和服務(wù)器之間將不會發(fā)生證書交換,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 服務(wù)器正在運行,且監(jiān)聽了ssl連接.
信任Client的Root CA
目前,我們告知RabbitMQ查看testca/cacert.pem 文件. 這包含了我們test認證中心的公共證書. 我們可能存在由多個不同認證中心簽發(fā)的證書,我們希望RabbitMQ全部信任它們.因此,我們可以簡單地將這些證書追加到另一個新文件中,以作為RabbitMQ的cacerts參數(shù):
# 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服務(wù)器拒絕接受SSLv3連接. 在2014年12月,一個經(jīng)過改良的能影響TLSv1.0的POODLE攻擊被 宣布. 因此,建議使用Erlang 18.0+的版本( 消除了TLS 1.0 POODLE攻擊漏洞)或者禁用TLSv1.0支持 (參考下面的章節(jié)).
BEAST
BEAST attack 是一個能攻擊TLSv1.0的漏洞. 為了減輕它的攻擊,禁用TLSv1.0支持(參考下面的章節(jié)).
通過配置來禁用 SSL/TLS 版本
為了限制SSL/TLS協(xié)議版本,使用版本選項:
%% 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使用的密碼套件.注意現(xiàn)在所有的套件都可以在所有系統(tǒng)上使用. 例如,使用橢圓曲線密碼,請運行最新的Erlang發(fā)布。下面的例子演示了如何使用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).'
信任級別
設(shè)置SSL連接時,協(xié)議中有兩個重要的階段.第一個階段發(fā)生對等端點交換證書(可選)的時候.當證書交換后,對等端點可選擇在根證書和其它存在的證書之間建立信任鏈. 這用來驗證對等端點的身份(提供的私有密鑰不會被偷!).
第二階段是協(xié)商對等節(jié)點的加密密鑰,該密鑰將用于通信的其余部分。如果交換證書,公鑰/私鑰將在密鑰協(xié)商中使用。
因此,您可以創(chuàng)建一個加密的SSL連接,而無需驗證證書。java客戶端支持兩種操作模式。
密鑰管理器,信任管理器,以及密鑰庫
在Java安全框架中,有三個需要注意的組件: 密鑰管理, 信任管理以及密鑰庫.
密鑰管理器用于對待節(jié)點管理其證書.也就是說,在會話設(shè)置時,密鑰管理會控制發(fā)送哪些證書給遠端對待節(jié)點.
信任管理器用于對等節(jié)點管理遠程證書.也就是說,在會話設(shè)置時,信任管理遠端節(jié)點的那些證書是可信任的.
密鑰庫是證書的Java封裝. Java會將證書轉(zhuǎn)換為Java特定的二進制格式或PKCS#12格式. 這些格式是使用Key Store類來管理的.對于服務(wù)端證書,我們會使用Java二進制格式,但對于客戶端 密鑰/證書對,我們將使用 PKCS#12格式.
沒有驗證證書的連接
我們的第一個示例將展示一個簡單的client,通過不驗證證書以及不存在任何客戶端證書的SSL來連接RabbitMQ服務(wù)器.
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.它會創(chuàng)建一個rabbitmq-java-test隊列,并向默認direct交換器中發(fā)送消息,然后讀回發(fā)布的消息,并進行回顯. 注意,我們使用的是專用的,非持久化,自動刪除的隊列,因此我們不需要擔心后期的手動清除.
存在和驗證證書
首先,我們需要設(shè)置密鑰庫.我們假設(shè)有要連接的服務(wù)器的證書,所以我們現(xiàn)在需要將它添加到我們的密鑰庫中(信任管理器會使用).
# keytool -import -alias server1 -file /path/to/server/cert.pem -keystore /path/to/rabbitstore
上面的命令會將cert.pem導(dǎo)入到rabbitstore中,并在內(nèi)部稱其為server1. alias參數(shù)用于有多個證書或密鑰的時候用于指定別名,因為在內(nèi)部必須有不同的名稱.
在關(guān)于是否相信證書的問題上,必須回答yes, 并要選擇一個密碼. 在這個例子中,我的密碼為rabbitstore。
然后我們要用PKCS#12文件中的客戶端證書和密鑰,已經(jīng)在上面展示過了.
下面的例子會修改上面的代碼, 以使用我們的密鑰庫,密鑰管理器,以及信任管理器:
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(); } }
為了確保上述代碼能在其它情況下工作,可使用未導(dǎo)入到密鑰庫中的安全證書來嘗試,這時你可能會看到屏幕上的難異常信息.
配置 .Net 客戶端
為了能讓服務(wù)器安全證書能在.Net平臺上使用, 它們可以是多種格式,包括DER 和PKCS #12, 但不能是PEM. 對于DER格式, .Net希望它們能存儲在.cer擴展名的文件中.在上面的步驟中,當創(chuàng)建test認證機構(gòu)時,我們會把PEM 轉(zhuǎn)換成DER格式,命令如下:
# openssl x509 -in /path/to/testca/cacert.pem -out /path/to/testca/cacert.cer -outform DER
PEM是base64編碼的DER格式, 使用分隔符進行封閉。此編碼通常是使它更容易在7位有限協(xié)議傳輸數(shù)據(jù),如電子郵件(SMTP)。
RFC 5280, Certificate Key Usage and Mono
正如上面所提到的,Mono是相當嚴格的,強制證書只能應(yīng)用于它聲明的特定目的.
SSL 證書和密鑰可應(yīng)用于各種各樣的用途, 例如, 電子郵件簽名,代碼簽名, 通信加密等等. (我們這里的目的是TCP 通信加密). RFC 5280 指定了許多不同的目的, 并允許一個證書為特定的一組目的進行簽名.
SSL v3 證書可包含許多不同的擴展.處理證書如何使用的擴展被稱為Key Usage Extension. 不同使用一般來說,都支持得很好,即使是定義良好,它們的使用也是廣泛地解釋. 一些關(guān)鍵的用法已經(jīng)廢棄,大部分已經(jīng)完全忽視。
這里有一個更進一步的擴展, 它也指定了使用用途, 但它選擇O.I.Ns來進行, 如"1.3.6.1.5.5.7.3.1".顯然,英語缺乏一些明顯的用于添加的隨機數(shù)字。這是一個擴展密鑰使用擴展,序列的對象標識符,進一步定義了那些證書的使用是允許的。
而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服務(wù)器來使用, digitalSignature指定了證書可由SSL客戶端使用. extendedKeyUsage 字段中的值都說了同樣的事情.
你可以使用this small tool 來檢查Mono是否接受RabbitMQ提供的證書. 注意,你需要使用合適的OpenSSL命令來轉(zhuǎn)換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 平臺上, 遠程證書是通過任意數(shù)量的存儲庫來管理的.這些存儲庫的管理是通過 'certmgr'(Microsoft .Net 實現(xiàn)和Mono都可用)工具來完成的.
NB: 在某些Windows平臺上,有兩種版本的命令-一個是隨著操作系統(tǒng)自帶的,只提供了圖形界面,另一個Windows SDK自帶的,提供了圖形界面和命令行界面. 兩者都可以很好的完成工作,但示例將以后者為基礎(chǔ).
對于我們的情況,因為我們提供的客戶端certificate/key對都在單獨的PKCS #12 文件中,我們要做的只是導(dǎo)入根證書機構(gòu)的證書到Root (Windows) / Trust(Mono) 存儲庫中.在存儲庫中所有簽名的證書都是自動可信的.
對比java客戶端,會樂于使用SSL連接而不驗證服務(wù)器的證書,該.Net客戶端需要驗證成功。為阻止驗證,應(yīng)用程序可在SslOptions.AcceptablePolicyErrors中設(shè)置System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable 以及System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors 標志.
Certmgr證書管理
certmgr 允許Add, Delete, List 以及在特定的Store上執(zhí)行其它操作.這些stores 可以是用戶stores, 或機器范圍的.只有管理員用戶有機器范圍stores的寫訪問權(quán)限.
為了將證書加入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命令來展示證書內(nèi)容:
(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.
使用相同的步驟,我們可以在客戶端上也可以執(zhí)行add/delete/list 我們根證書的操作.
創(chuàng)建連接
要創(chuàng)建RabbitMQ的SSL連接,我們需要在ConnectionFactory參數(shù)字段中設(shè)置一些新字段.為了讓事情更簡單,這里有一新字段Parameters.Ssl,它可作為需要設(shè)置所有其它字段的命名空間 . 這些字段是:
- Ssl.CertPath: 如果你的服務(wù)器希望驗證客戶端的話,這是PKCS#12格式的客戶端證書的路徑.這是可選的.
- Ssl.CertPassphrase:如果你正在使用PKCS#12 格式的客戶端證書,那么可能會需要一個密碼,你可以在這個字段中進行指定.
- Ssl.Enabled: 這是一個boolean字段,用以打開或關(guān)閉SSL支持.默認是off.
- Ssl.ServerName: 記住 .Net希望這匹配發(fā)送證書的CN.
例子
這與Java部分的例子是一樣的.它創(chuàng)建了一個channel, rabbitmq-dotnet-test隊列,并使用默認direct交換器來發(fā)布,然后再讀回發(fā)送的消息并進行回顯.注意,我們使用的是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上,運行此例,可能會出現(xiàn)失敗
CryptographicException: Key not valid for use in specified state.
在這種情況下,你需要從證書存儲庫加載證書并直接設(shè)置
ConnectionFactory的Ssl.Certs參數(shù)才能成功運行.
R16B01之前的Erlang版本
有可能在老版本的Erlang中使用SSL.這些老版本可以與某些證書工作,其它則很困難.同時,它們也包含OTP-10905 bug,這使得它不能禁用掉任何協(xié)議版本.實際上,由于它不能禁用SSLv3, 使用SSL的老版本Erlang 對于POODLE attack (PDF link)是不安全的.
如果探測到老版本的Erlang,RabbitMQ 3.4.0會自動禁用SSL監(jiān)聽器.如果這不是你所希望的,你可以設(shè)置ssl_allow_poodle_attack rabbit 配項為true.
ssl_allow_poodle_attack 是一個全局設(shè)置;在rabbit程序中的設(shè)置會控制所有SSL監(jiān)聽器的行為(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服務(wù)器使用更高的驗證深度。該深度是非自頒發(fā)的中間證書的最大數(shù)量,可以按照有效的證書路徑中的對等證書。因,如果depth為0,對等節(jié)點(如.client)證書 必須由CA直接簽發(fā),如果為1,路徑可以是"對等節(jié)點, CA, 信任的CA",如果設(shè)置為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 選項用于指定明確信任的根證書機構(gòu)的證書.
- certfile是client擁有的PEM格式的證書
- keyfile是客戶端PEM格式的私有密鑰文件
作為RabbitMQ自身, verify 和fail_if_no_peer_cert 選項可用來指定當服務(wù)器未提供證書或我們不能根據(jù)服務(wù)器證書建立信任鏈時下的動作. 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),
現(xiàn)在你就可以像普通連接一樣來使用Conn了.
TLS/SSL故障排除
介紹
本頁收集一些技巧來幫助診斷SSL錯誤。策略是使用其它的SSL實現(xiàn)來測試必要的組件,在故障排除的過程中識別故障.
請記住,如果兩個特定的組件之間有相互作用的話,這個過程不保證識別問題.
我們也解釋了在日志中可能出現(xiàn)的一些常見錯誤消息.
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來構(gòu)建.在基于Debian的系統(tǒng)上,你需要安裝erlang-ssl包.
使用OpenSSL來檢查密鑰和證書
我們現(xiàn)在用其它的SSL實現(xiàn)來驗證配置文件中指定的證書和密鑰.這個例子使用 OpenSSL s_client 和 s_server. 我們將確認用于連接兩端的證書和密鑰.
針對下面的例子,我們假設(shè)你有下面的信息:
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 |
在一端執(zhí)行下面的命令:
openssl s_server -accept 8443 -cert server/cert.pem -key server/key.pem \ -CAfile testca/cacert.pem
在另一端執(zhí)行
openssl s_client -connect localhost:8443 -cert client/cert.pem -key client/key.pem \ -CAfile testca/cacert.pem
如果證書和密鑰創(chuàng)建正確,SSL連接建立序列將出現(xiàn),并且終端會連接.一端的輸入會出現(xiàn)在另一端.如果建立了信任鏈,第二個終端將顯示下面的信息:
Verify return code: 0 (ok)
如果收到錯誤,需要確認證書和密鑰是否正確創(chuàng)建.
檢查broker正在監(jiān)聽
此步驟可檢查broker是否正在期望的AMQPS端口上進行監(jiān)聽.當你使用有效的SSL配置文件進行啟動時, broker會在日志文件中報告SSL監(jiān)聽的地址.你可以類似于下面的東西:
=INFO REPORT==== 8-Aug-2011::11:51:47 === started SSL Listener on 0.0.0.0:5671
如果你設(shè)置了"ssl_listeners"配置指令但沒有看到這樣的信息,那么可能是你的配置文件沒有被broker讀取. 確認配置文件引用的broker日志中包含SSL配置選項.
嘗試與broker的SSL連接
一旦RabbitMQ broker監(jiān)聽于SSL端口,你可使用OpenSSL s_client來驗證SSL 連接, 這次針對的是broker. 這可以檢測broker是否配置正確,不必配置AMQPS client.此例假設(shè)broker使用"ssl_listeners"指令配置監(jiān)聽SSL連接的端口5671:
openssl s_client -connect localhost:5671 -cert client/cert.pem -key client/key.pem \ -CAfile testca/cacert.pem
輸出類似于端口8443的情況. 當連接建立的時候,broker日志文件應(yīng)該包含一個新的條目:
=INFO REPORT==== 8-Aug-2011::11:55:13 === accepting AMQP connection <0.223.0> (127.0.0.1:58954 -> 127.0.0.1:5671)
如果你現(xiàn)在為broker提供8個隨機字節(jié),broker將會使用字符串"AMQP"緊跟編碼的版本號數(shù)字進行回復(fù). 如果你認出了"AMQP"字符串,那么你能確定你已經(jīng)連接上了AMQP broker.
使用stunnel來驗證客戶端連接
最后的檢查是驗證AMQPS clients. 我們將使用stunnel 來提供SSL能力.要這個配置中,AMQPS clients會使用stunnel來作安全連接,它將傳遞解密后的數(shù)據(jù)給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 需要證書和相應(yīng)的密鑰.
生成的客戶端證書和相應(yīng)的密鑰應(yīng)該使用和如上面所示的cat命令連接。Stunnel要求的密鑰不需要密碼保護。SSL能力客戶端現(xiàn)在可以連接端口5679,任何SSL錯誤會在stunnel啟動時出現(xiàn)在控制臺.
連接client和broker
如果上面的步驟沒有出錯,那么你可以測試AMQPS客戶端已經(jīng)連接到broker的 AMQPS端口, 首先須確保停止任何運行的OpenSSL或stunnel會話.
證書鏈和驗證深度
當使用第三方CA簽發(fā)的客戶端證書時,有必要配置RabbitMQ server使用更高的驗證深度. 該depth是非自頒發(fā)的中間證書的最大數(shù)量,可以按照有效的證書路徑中的對等證書。
參考TLS/SSL guide來了解如何配置驗證深度.
理解 SSL logs
上述步驟會產(chǎn)生新broker日志文件條目.這些條目與診斷輸出一起可以幫助確認SSL錯誤的原因.
下面的是一些常見的錯誤條目:
- Entries containing {ssl_upgrade_error, ekeyfile} or {ssl_upgrade_error, ecertfile}
這意味著broker的 keyfile 或certificate文件是無效的.須確認keyfile匹配證書,且兩者都是PEM 格式的. PEM格式是一種可打印的編碼與識別的分隔符. 證書以 -----BEGIN CERTIFICATE----- 并以-----END CERTIFICATE----- 結(jié)束. keyfile 會以-----BEGIN RSA PRIVATE KEY----- 開始,并以-----END RSA PRIVATE KEY----- 結(jié)束.
- Entries containing {ssl_upgrade_failure, ... certify ...}
- 此錯誤與客戶端驗證有關(guān).客戶端提供了一個無效的證書或無證書. 如果ssl_options設(shè)置了verify 選項為verify_peer,那么將使用臨時使用verify_none.必須確保客戶端證書已經(jīng)正確生成了,并且客戶端提交正確的證書。
- Entries containing {ssl_upgrade_error, ...}
這是一個通用的錯誤,可能有許多原因。確保你使用的是Erlang的推薦版本。
- Entries containing {tls_alert,"bad record mac"}
服務(wù)器已嘗試驗證它所接收的數(shù)據(jù)的完整性和檢查失敗。這可能是由于有問題的網(wǎng)絡(luò)設(shè)備, 無意的客戶端socket共享(如.因為使用了fork(2))或者客戶端實現(xiàn)的TLS的bug.
posted on 2016-08-02 22:25
胡小軍 閱讀(11932)
評論(0) 編輯 收藏 所屬分類:
RabbitMQ