Uso corretto dell'API HsOpenSSL per implementare un server TLS


141

Sto cercando di capire come utilizzare correttamente l' API OpenSSL.Session in un contesto concorrente

Ad esempio, suppongo che io voglia implementare un stunnel-style ssl-wrapper, mi aspetto di avere la seguente struttura di scheletro di base, che implementa un ingenuofull-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

Come faccio a trasformare lo scheletro sopra in un full-duplex ssl-wrapping tcp-forwarding proxy? Dove sono i pericoli WRT per l'esecuzione simultanea / parallela (nel contesto del caso d'uso sopra riportato) delle chiamate di funzione fornite dall'API HsOpenSSL?

PS: sto ancora lottando per comprendere appieno come rendere il codice robusto rispetto a eccezioni e perdite di risorse. Quindi, sebbene non sia l'obiettivo principale di questa domanda, se noti qualcosa di brutto nel codice sopra, lascia un commento.


11
Penso che questa potrebbe essere una domanda troppo ampia per SO.
Don Stewart,

1
Tornerò su questo :-)
Abhineet,

2
il link al documento è interrotto, ecco chi lavora: hackage.haskell.org/packages/archive/HsOpenSSL/0.10.2/doc/html/…
Pascal Qyy

4
Ho fatto qualcosa di simile ( full-duplex ssl-rewrapping tcp-forwarding), ma invece ha usato Network.TLS(pacchetto tls). Ed è stato brutto. Lo puoi trovare qui , se del caso interessato.

Risposte:


7

Per fare ciò è necessario sostituire copySocketcon due diverse funzioni, una per gestire i dati dal socket semplice a SSL e l'altra da SSL al socket semplice:

  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

Quindi è necessario modificare in connectToServermodo che stabilisca una connessione 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

e modifica finalizeper chiudere la sessione 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

Infine, non dimenticare di eseguire le tue cose principali all'interno withOpenSSLcome in

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

Questo aiuta già molto; questo fornisce un proxy ssl locale-non-da-remoto-corrispondente alla stunnelsmodalità client, potresti anche fornire un esempio su come ascoltare un socket ssl locale (ad es. per fornire un local-ssl-to-remote-non- proxy SSL)?
hvr,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.