Właściwe użycie interfejsu API HsOpenSSL do implementacji serwera TLS

141

Próbuję dowiedzieć się, jak prawidłowo używać API OpenSSL.Session w kontekście współbieżnym

Załóżmy na przykład, że chcę zaimplementować stunnel-style ssl-wrapper, spodziewałbym się , że będę miał następującą podstawową strukturę szkieletu, która implementuje naiwność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

Jak przekształcić powyższy szkielet w full-duplex ssl-wrapping tcp-forwarding proxy? Gdzie są niebezpieczeństwa WRT do równoczesnego / równoległego wykonywania (w kontekście powyższego przypadku użycia) wywołań funkcji udostępnianych przez HsOpenSSL API?

PS: Wciąż staram się w pełni zrozumieć, jak sprawić, by kod był odporny na wyjątki i wycieki zasobów. Tak więc, chociaż nie jest to główny temat tego pytania, jeśli zauważysz coś złego w powyższym kodzie, zostaw komentarz.

hvr
źródło
11
Myślę, że to może być zbyt szerokie pytanie dla SO.
Don Stewart
1
Skontaktuję
2
link do dokumentu jest uszkodzony, oto ten, który pracuje: hackage.haskell.org/packages/archive/HsOpenSSL/0.10.2/doc/html/…
Pascal Qyy
4
Zrobiłem coś podobnego ( full-duplex ssl-rewrapping tcp-forwarding), ale zamiast tego użyłem Network.TLS(pakiet tls). I to było brzydkie. Możesz go znaleźć tutaj , jeśli w ogóle jesteś zainteresowany.

Odpowiedzi:

7

Aby to zrobić, musisz zastąpić copySocketdwie różne funkcje, jedną do obsługi danych ze zwykłego gniazda na SSL, a drugą z SSL do zwykłego gniazda:

  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

Następnie musisz zmodyfikować, connectToServeraby ustanowił połączenie 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

i zmień, finalizeaby zamknąć sesję SSL

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

Wreszcie, nie zapomnij, aby uruchomić główne rzeczy wewnątrz withOpenSSL, jak w

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
Geoff Reedy
źródło
To już bardzo pomaga; zapewnia to lokalny serwer proxy bez ssl-zdalny-ssl odpowiadający stunnelstrybowi klienta, czy możesz również podać przykład, jak nasłuchiwać lokalnego gniazda ssl (np. aby zapewnić lokalny-zdalny-ssl-nie- ssl proxy)?
hvr