隨筆 - 41  文章 - 7  trackbacks - 0
          <2016年8月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          RabbitMQ內(nèi)置支持TLS。

          自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手冊頁面查看,但最重要的東西是cacertfilecertfile 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ù)器是否要求客戶端提供證書以及它們是否相信證書的策略,是由verifyfail_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版本 
          TLS versionMinimum JDK versionMinimum .NET version
          TLS 1.0JDK 5 (RabbitMQ Java client requires 6).NET 2.0 (RabbitMQ .NET client requires 4.5)
          TLS 1.1JDK 7 (see ProtocolsJDK 8 recommended).NET 4.5
          TLS 1.2JDK 7 (see ProtocolsJDK 8 recommended).NET 4.5

          配置密碼套件

          可配置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_extensionsclient_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 

          TLS對等驗證:你說誰呢你?

          在.NET平臺上,默認情況下,你連接的服務(wù)器的hostname需要匹配服務(wù)器安全證書上的CN (Common Name) 字段, 否則證書將會被拒絕.這就是為什么在指南開頭的命令中指定了 ...-subj /CN=$(hostname)/... 它會動態(tài)地查找你的主機名.如果你在一臺機器上生成,要在其它機器上使用那么必須交換$(hostname)部分,并使用你服務(wù)器正確的hostname來代替.

          為了抑制匹配檢查,一個應(yīng)用程序可以在SslOptions.AcceptablePolicyErrors中設(shè)置 System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch 標志.

          SslOptions.CertificateValidationCallback 可用來提供一個RemoteCertificateValidationCallback 代表. 代表會用適合應(yīng)用程序的任何邏輯來驗證對等節(jié)點(RabbitMQ節(jié)點)的身份.如果沒有指定, 默認的回調(diào)將會聯(lián)合AcceptablePolicyErrors屬性來確定遠程服務(wù)器證書是否有效.

          SslOption.CertificateSelectionCallback 可用來提供一個LocalCertificateSelectionCallback ,它將在對等節(jié)點驗證中選擇要用的本地安全證書.

          信任 .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è)你有下面的信息

          ItemLocation
          CA certificatetestca/cacert.pem
          Server certificateserver/cert.pem
          Server keyserver/key.pem
          Client certificateclient/cert.pem
          Client keyclient/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配置選項. 
          查看configuration page來了解細節(jié).

          嘗試與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
          主站蜘蛛池模板: 普洱| 偏关县| 汨罗市| 右玉县| 乌鲁木齐市| 如东县| 萨迦县| 沛县| 章丘市| 滦平县| 喀喇沁旗| 磴口县| 鹤壁市| 吉木乃县| 鸡西市| 寻乌县| 旌德县| 成武县| 永春县| 贵溪市| 清原| 太保市| 万源市| 武胜县| 大安市| 依兰县| 轮台县| 习水县| 延吉市| 区。| 玉山县| 体育| 桂林市| 股票| 乌拉特后旗| 清远市| 辽阳县| 义乌市| 临武县| 万宁市| 乐昌市|