haskell amqp x509 客户端认证
haskell amqp x509 client auth
我一直在努力弄清楚如何通过 Network.AMQP
使用 x509 客户端身份验证。似乎我需要使用(除其他外) coTLSSettings
参数创建一个 AMQP.ConnectionOpts
,如下所示:
import qualified Network.AMQP as AMQP
import Network.Connection
let opts = AMQP.ConnectionOpts {
..
, coTLSSettings = Just $ AMQP.TLSCustom $ ...
}
在这一点上(省略号),阅读了一些 Network.Connection
文档(并且超出了我的理解范围),它开始看起来非常复杂。我想知道我是否走上了正确的道路。
所以,我的问题是:如何轻松实现 x509 客户端身份验证?如果答案是 "you can't," 有谁知道我在哪里可以找到使用 Network.Connection
模块的 x509 客户端身份验证示例?
我们需要做两件事(我的测试环境是三件)。
- 读取客户端凭据
- 设置密码套件为
defaultParamsClient
设置空密码套件(我不知道为什么。)。
- (对于我的测试环境)读取 CA 根证书,因此我们可以验证提供给我们的服务器证书。如果您不在测试环境中,则此证书应安装在系统证书存储中,并且应该是默认的。在这种情况下,您可以从程序中删除
CertificateStore
处理。
下面程序中的函数mkMyTLSSettings
替换了defaultParamsClient
结果中提到的部分。在用作 onCertificateRequest
的函数中,您可以使用参数并根据参数 values 分发不同的凭据。读入所需的值本身
main
摆脱 IO
.
对于以下程序,我修改了在此 中找到的位。
{-# 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
添加到此处引用的命令行以使其工作。
我一直在努力弄清楚如何通过 Network.AMQP
使用 x509 客户端身份验证。似乎我需要使用(除其他外) coTLSSettings
参数创建一个 AMQP.ConnectionOpts
,如下所示:
import qualified Network.AMQP as AMQP
import Network.Connection
let opts = AMQP.ConnectionOpts {
..
, coTLSSettings = Just $ AMQP.TLSCustom $ ...
}
在这一点上(省略号),阅读了一些 Network.Connection
文档(并且超出了我的理解范围),它开始看起来非常复杂。我想知道我是否走上了正确的道路。
所以,我的问题是:如何轻松实现 x509 客户端身份验证?如果答案是 "you can't," 有谁知道我在哪里可以找到使用 Network.Connection
模块的 x509 客户端身份验证示例?
我们需要做两件事(我的测试环境是三件)。
- 读取客户端凭据
- 设置密码套件为
defaultParamsClient
设置空密码套件(我不知道为什么。)。 - (对于我的测试环境)读取 CA 根证书,因此我们可以验证提供给我们的服务器证书。如果您不在测试环境中,则此证书应安装在系统证书存储中,并且应该是默认的。在这种情况下,您可以从程序中删除
CertificateStore
处理。
下面程序中的函数mkMyTLSSettings
替换了defaultParamsClient
结果中提到的部分。在用作 onCertificateRequest
的函数中,您可以使用参数并根据参数 values 分发不同的凭据。读入所需的值本身
main
摆脱 IO
.
对于以下程序,我修改了在此
{-# 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
添加到此处引用的命令行以使其工作。