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의 위험은 어디에 있습니까?

추신 : 나는 여전히 코드를 예외 및 리소스 누출로 강력하게 만드는 방법을 완전히 이해하기 위해 고심하고 있습니다. 따라서이 질문의 주요 초점은 아니지만 위의 코드에서 잘못된 점이 발견되면 의견을 남겨주십시오.


11
나는 이것이 너무 광범위한 질문이라고 생각합니다.
돈 스튜어트

1
나는 이것에 대해 당신에게 돌아올 것이다 :-)
Abhineet

2
문서에 대한 링크가 끊어졌습니다. 다음은 작업중 인 사람입니다. hackage.haskell.org/packages/archive/HsOpenSSL/0.10.2/doc/html/…
Pascal Qyy

4
비슷한 ( full-duplex ssl-rewrapping tcp-forwarding)을 만들었지 만 대신 Network.TLS(package tls)를 사용했습니다. 그리고 그것은 추악했습니다. 관심이 있다면 여기 에서 찾을 수 있습니다 .

답변:


7

이를 위해서는 copySocket두 가지 기능 으로 교체해야 합니다. 하나는 일반 소켓에서 SSL로, 다른 하나는 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

그런 다음 connectToServerSSL 연결을 설정 하도록 수정해야 합니다

  -- |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클라이언트 모드에 해당하는 local-non-ssl-to-remote-ssl 프록시를 제공합니다. 또한 로컬 ssl 소켓을 수신하는 방법 (예 : local-ssl-to-remote-non- SSL 프록시)?
hvr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.