You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is there a way to limit how much time the acceptor is going to wait for the TLS handshake to take place from the client side? If a malicious client never sends its TLS handshake request part, will this block forever?
If yes then probably the example should be updated to use tokio's timeout.
@vasilakisfil This is how I've done it. The key is to not await on the accept() call but instead wrap that future in the tokio timeout.
let tls_config = rustls::ServerConfig::builder().with_safe_defaults().with_no_client_auth().with_single_cert(certs, private_key).unwrap();let tls_acceptor = tokio_rustls::TlsAcceptor::from(tls_config);// ...// Inside the acceptor loop after you have a tcp connection already accepted// ...let tls_stream = tls_acceptor.accept(tcp_stream).map_err(|e| {
std::io::Error::new(std::io::ErrorKind::Other,format!("Client TLS connect fail: {:?}", e))});// Wrap the accept future with a timeout to put an upper limit on how long a client can take to complete// the TLS handshake and get to a TLS "connected" statelet tls_handshake_timeout_seconds:u8 = 5;let tls_stream = tokio::time::timeout(tokio::time::Duration::from_secs(tls_handshake_timeout_seconds.into()), tls_stream).map_err(move |_e| {
std::io::Error::new(std::io::ErrorKind::TimedOut,format!("Client TLS handshake timeout after {} seconds", tls_handshake_timeout_seconds))});let tls_stream = match tls_stream.await{// Happy pathOk(tls_stream) => tls_stream,// Some kind of errorErr(e) => ...// TimeoutOk(Err(e)) => ...};
Thanks, yes I have done the same thing. On event-driven architectures (aka channels), not having a timeout limit by default can lead to serious unexpected stalls in an application. Probably it's a good idea to add something on the documentation about it.
This seems like a serious issue to me, the lack of a TLS timeout means that applications built on tokio-rustls are vulnerable out of the box to a DoS attack to exhaust FDs, like a very basic Slowloris attack but with TLS.
I think by default there should be an aggressive configurable timeout for completion of the TLS handshake.
You can do a DoS on the default rustls server example by just spawning tons of nc <host> <port> instances that will never, ever timeout.
I think this is indeed quite needed! I find my server with a lot of ESTABLISHED connection with 0/0 bytes communication that hangs forever, even though I have a timeout layer in my Axum service. It turns out that these connections await at tls_acceptor.accept(stream).await.
Activity
rfarr commentedon Jul 20, 2023
@vasilakisfil This is how I've done it. The key is to not await on the accept() call but instead wrap that future in the tokio timeout.
vasilakisfil commentedon Jul 21, 2023
Thanks, yes I have done the same thing. On event-driven architectures (aka channels), not having a timeout limit by default can lead to serious unexpected stalls in an application. Probably it's a good idea to add something on the documentation about it.
nirbheek commentedon Dec 11, 2023
This seems like a serious issue to me, the lack of a TLS timeout means that applications built on tokio-rustls are vulnerable out of the box to a DoS attack to exhaust FDs, like a very basic Slowloris attack but with TLS.
I think by default there should be an aggressive configurable timeout for completion of the TLS handshake.
You can do a DoS on the default rustls server example by just spawning tons of
nc <host> <port>instances that will never, ever timeout.Congyuwang commentedon Jul 11, 2024
I think this is indeed quite needed! I find my server with a lot of ESTABLISHED connection with 0/0 bytes communication that hangs forever, even though I have a timeout layer in my Axum service. It turns out that these connections await at
tls_acceptor.accept(stream).await.djc commentedon Dec 11, 2024
(Make sure to also take care of the
LazyConfigAcceptorwhen we tackle this.)stormshield-gt commentedon Jan 13, 2025
Just to mention it, another solution is to put the application behind a reverse proxy, as it was the general wisdom in the early days of Go.
Congyuwang commentedon Jan 13, 2025
That’s a good idea.
cpu commentedon Jul 20, 2026
See #187