我们需要做两件事(对于我的测试环境来说是三件事)。
- 读取客户端凭据
- 将密码套件设置为
defaultParamsClient 设置一个空密码套件(我不知道为什么。)。
- (对于我的测试环境)读取 CA 根证书,因此我们可以验证提供给我们的服务器证书。如果您不在测试环境中,则此证书应安装在系统证书存储中,并且应该是默认的。在这种情况下,您可以从程序中删除
CertificateStore 处理。
以下程序中的函数mkMyTLSSettings 替换了defaultParamsClient 结果中提到的部分。在用作onCertificateRequest 的函数中,您可以使用参数并根据参数values 分发不同的凭据。需要的值本身被读入
main 摆脱IO。
对于以下程序,我修改了我在此answer 中找到的位。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Default.Class
import Network.AMQP
import Network.Socket.Internal (PortNumber)
import Network.TLS
import Network.TLS.Extra.Cipher (ciphersuite_default)
import Data.X509.CertificateStore (CertificateStore (..), readCertificateStore)
import Data.Maybe
import qualified Data.ByteString as BS
import qualified Network.Connection as C
import qualified Data.ByteString.Lazy.Char8 as BL
mkMyTLSSettings :: CertificateStore -> Credential -> C.TLSSettings
mkMyTLSSettings castore creds =
let defaultParams = defaultParamsClient "127.0.0.1" BS.empty
newClientShared = (clientShared defaultParams) { sharedCAStore = castore }
newClientSupported = (clientSupported defaultParams) { supportedCiphers = ciphersuite_default }
newClientHooks = (clientHooks defaultParams) { onCertificateRequest = \_ -> return (Just creds) }
in C.TLSSettings $ defaultParams { clientShared = newClientShared
, clientSupported = newClientSupported
, clientHooks = newClientHooks
}
myTLSSettings :: CertificateStore -> Credential -> TLSSettings
myTLSSettings castore creds = TLSCustom $ mkMyTLSSettings castore creds
myTLSConnectionOpts :: TLSSettings -> ConnectionOpts
myTLSConnectionOpts opts = ConnectionOpts
[("127.0.0.1", 5671 :: PortNumber)]
"/"
[plain "guest" "guest"]
(Just 131072)
Nothing
(Just 1)
(Just opts)
testConnectionOpts :: ConnectionOpts -> IO ()
testConnectionOpts opts = do
conn <- openConnection'' opts
chan <- openChannel conn
declareQueue chan newQueue {queueName = "hello"}
putStrLn "Trying to register callback"
consumeMsgs chan "hello" Ack myCallback
publishMsg chan "" "hello" newMsg {msgBody = (BL.pack "hello world"), msgDeliveryMode = Just Persistent}
getLine
closeConnection conn
putStrLn "connection closed"
main :: IO ()
main = do
testConnectionOpts defaultConnectionOpts
putStrLn "trying with tls"
castore <- maybe (error "couldn't read CA root Certificate") id <$> (readCertificateStore "/pathto/rootCA.pem")
creds <- either error id <$> credentialLoadX509 "/pathto/client.pem" "/pathto/client.key"
let opts = myTLSSettings castore creds
testConnectionOpts (myTLSConnectionOpts opts)
myCallback :: (Message, Envelope) -> IO ()
myCallback (msg, env) = do
putStrLn $ "received message: " ++ (BL.unpack $ msgBody msg)
ackEnv env
作为gist。
我在这个程序中的第一次通信是为了确保rabbitmq设置正确,我真的只遇到了TLS错误。如果您删除第 20 和 23 行,您可以测试您是否正确配置了 rabbitmq。在这种情况下,连接尝试应该会失败,因为我们没有提供客户端证书。
我创建了一个玩具 CA 用于测试,并颁发了用于 rabbitmq 服务器和客户端的证书。所以我有一个文件rootCA.pem,它存储了CA根证书和rabbitmq.key和rabbitmq.pem之类的文件,它们用于使用rabbitmq设置TLS。还为客户提供client.pem 和client.key。我将 rabbitmq 配置为只为提供可信证书的客户端提供服务,通过设置
fail_if_no_peer_cert 到 true 并设置 {verify, verify_peer} 选项。
在我第一次尝试时,我遇到了LeafNotV3 的各种错误,这意味着我在第一次尝试时创建了我的rabbitmq.pem 错误。这是一个 X509.v1 证书,Network.TLS 默认不接受。我需要确保创建一个 X509.v3 证书,这是通过在颁发证书rabbitmq.pem 时启用某些扩展来完成的,请参阅here。我需要将选项 -req 添加到那里引用的命令行以使其工作。