HsOpenSSL APIの適切な使用によるTLSサーバーの実装


141

同時コンテキストでOpenSSL.Session API を適切に使用する方法を理解しようとしています

たとえば、私がを実装したいと仮定するとstunnel-style ssl-wrapper、素朴なものを実装する次の基本的なスケルトン構造が期待されますfull-duplex tcp-port-forwarder:

runProxy :: PortID -> AddrInfo -> IO ()
runProxy localPort@(PortNumber lpn) serverAddrInfo = do
  listener <- listenOn localPort

  forever $ do
    (sClient, clientAddr) <- accept listener

    let finalize sServer = do
            sClose sServer
            sClose sClient

    forkIO $ do
        tidToServer <- myThreadId
        bracket (connectToServer serverAddrInfo) finalize $ \sServer -> do
            -- execute one 'copySocket' thread for each data direction
            -- and make sure that if one direction dies, the other gets
            -- pulled down as well
            bracket (forkIO (copySocket sServer sClient
                             `finally` killThread tidToServer))
                    (killThread) $ \_ -> do
                copySocket sClient sServer -- "controlling" thread

 where
  -- |Copy data from source to dest until EOF occurs on source
  -- Copying may also be aborted due to exceptions
  copySocket :: Socket -> Socket -> IO ()
  copySocket src dst = go
   where
    go = do
        buf <- B.recv src 4096
        unless (B.null buf) $ do
            B.sendAll dst buf
            go

  -- |Create connection to given AddrInfo target and return socket
  connectToServer saddr = do
    sServer <- socket (addrFamily saddr) Stream defaultProtocol
    connect sServer (addrAddress saddr)
    return sServer

上記のスケルトンをどうやってに変換しfull-duplex ssl-wrapping tcp-forwarding proxyますか?HsOpenSSL APIによって提供される関数呼び出しの同時/並列実行(上記のユースケースのコンテキスト)に対するWRTの危険性はどこにありますか?

PS:例外やリソースリークに対してコードを堅牢なwrtにする方法を完全に理解するのにまだ苦労しています。したがって、この質問の主な焦点ではありませんが、上記のコードに問題があることに気付いた場合は、コメントを残してください。


11
これはSOにとっては広すぎる質問かもしれません。
Don Stewart、

1
これについては後で
連絡

2
:ドキュメントへのリンクが壊れている、ここで働いているものですhackage.haskell.org/packages/archive/HsOpenSSL/0.10.2/doc/html/...を
パスカルQYY

4
私は似たような(full-duplex ssl-rewrapping tcp-forwarding)を作成しましたが、代わりにNetwork.TLS(パッケージtls)を使用しました。そしてそれは醜かった。興味があれば、ここで見つけることができます。

回答:


7

これを行うにはcopySocket、2つの異なる関数に置き換える必要があります。1つはプレーンソケットからSSLへのデータを処理し、もう1つはSSLからプレーンソケットへのデータを処理します。

  copyIn :: SSL.SSL -> Socket -> IO ()
  copyIn src dst = go
   where
    go = do
        buf <- SSL.read src 4096
        unless (B.null buf) $ do
            SB.sendAll dst buf
            go

  copyOut :: Socket -> SSL.SSL -> IO ()
  copyOut src dst = go
   where
    go = do
        buf <- SB.recv src 4096
        unless (B.null buf) $ do
            SSL.write dst buf
            go

次にconnectToServer、SSL接続を確立するように変更する必要があります。

  -- |Create connection to given AddrInfo target and return socket
  connectToServer saddr = do
    sServer <- socket (addrFamily saddr) Stream defaultProtocol
    putStrLn "connecting"
    connect sServer (addrAddress saddr)
    putStrLn "establishing ssl context"
    ctx <- SSL.context
    putStrLn "setting ciphers"
    SSL.contextSetCiphers ctx "DEFAULT"
    putStrLn "setting verfication mode"
    SSL.contextSetVerificationMode ctx SSL.VerifyNone
    putStrLn "making ssl connection"
    sslServer <- SSL.connection ctx sServer
    putStrLn "doing handshake"
    SSL.connect sslServer
    putStrLn "connected"
    return sslServer

finalizeSSLセッションをシャットダウンするように変更します

let finalize sServer = do
        putStrLn "shutting down ssl"
        SSL.shutdown sServer SSL.Unidirectional
        putStrLn "closing server socket"
        maybe (return ()) sClose (SSL.sslSocket sServer)
        putStrLn "closing client socket"
        sClose sClient

最後に、以内にあなたのメインのものを実行することを忘れないでくださいwithOpenSSLのように

main = withOpenSSL $ do
    let hints = defaultHints { addrSocketType = Stream, addrFamily = AF_INET }
    addrs <- getAddrInfo (Just hints) (Just "localhost") (Just "22222")
    let addr = head addrs
    print addr
    runProxy (PortNumber 11111) addr

これはすでにかなり役に立ちます。これにより、stunnelsクライアントモードに対応するローカル非sslからリモートsslへのプロキシが提供されます。また、ローカルsslソケットをリッスンする方法の例を提供できます(たとえば、ローカルsslからリモートへの非sslを提供するため) sslプロキシ)?
hvr 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.