carllerche / tower-web

A fast, boilerplate free, web framework for Rust
MIT License
980 stars 51 forks source link

add support for TLS #153

Closed chastabor closed 5 years ago

chastabor commented 5 years ago

Is there a way to add TLS within the tower-web library even if just a workaround approach? I'm trying to add rustls to the application and running into issues.

carllerche commented 5 years ago

This should already be possible today!

You would use the ServiceBuilder::serve function (docs). This lets you pass in your own acceptor. You can pass in a TLS acceptor if you want.

So, you would do something like:

let incoming = TcpListener::bind(...).unwrap()
    .incoming()
    .map(|tcp_stream| wrap_with_tls(tcp_stream));

tokio::run({
    ServerBuilder::new()
        // configure service
        .serve(incoming)
});

Hope this helps.

chastabor commented 5 years ago

So when I use the serve endpoint I get the following error:

error[E0277]: the trait bound tokio_rustls::TlsStream<tokio::net::TcpStream, rustls::server::ServerSession>: tower_web::net::Connection is not satisfied --> src/main.rs:516:10 516 .serve(incoming) ^^^^^ the trait tower_web::net::Connection is not implemented for tokio_rustls::TlsStream<tokio::net::TcpStream, rustls::server::ServerSession>

= note: required because of the requirements on the impl of tower_web::net::ConnectionStream for futures::stream::and_then::AndThen<futures::stream::map::Map<tokio::net::tcp::Incoming, [closure@src/main.rs:465:14: 465:59 wrap_withtls:]>, [closure@src/main.rs:466:19: 466:42], tokio_rustls::Accept>

The only place I can get it working is to implement the Connection trait for the tokio_rustls::TlsStream is in the tower-web/src/net.rs file as such:

diff --git a/Cargo.toml b/Cargo.toml
index bcc20df..c5eb476 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,6 +47,7 @@ percent-encoding = "1.0.1"
 tokio = "0.1.6"
 tokio-fs = "0.1.2"
 tokio-io = "0.1.7"
+tokio-rustls = "0.8.0"
 tower-service = "0.1.0"
 void = "1.0.2"

diff --git a/src/lib.rs b/src/lib.rs
index 506c133..596c3a7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -480,6 +480,7 @@ extern crate tokio;
 extern crate tokio_fs;
 extern crate tokio_io;
 extern crate tower_service;
+extern crate tokio_rustls;
 extern crate void;

 #[cfg(feature = "handlebars")]
diff --git a/src/net.rs b/src/net.rs
index 960f3d6..2e09579 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -7,6 +7,8 @@ use tokio_io::{AsyncRead, AsyncWrite};
 use std::io;
 use std::net::SocketAddr;

+use tokio_rustls::{TlsStream, rustls::ServerSession};
+
 /// A stream between a local and remote target.
 pub trait Connection: AsyncRead + AsyncWrite {
     /// Returns the socket address of the remote peer of this connection.
@@ -29,6 +31,12 @@ impl Connection for TcpStream {
     }
 }

+impl Connection for TlsStream<TcpStream, ServerSession> {
+    fn peer_addr(&self) -> Option<SocketAddr> {
+        TcpStream::peer_addr(self.get_ref().0).ok()
+    }
+}
+
 impl<T> ConnectionStream for T
 where
     T: Stream<Error = io::Error>,

I'm I doing something wrong? I'm thinking maybe there is another way to implement the wrap_with_tls function. I'm doing the following:

    let wrap_with_tls = {
        let mut config = ServerConfig::new(NoClientAuth::new());
        config.set_single_cert(load_certs(&*CERTS.public), load_private_keys(&*CERTS.private).remove(0))
            .expect("invalid key or certificate");
        TlsAcceptor::from(Arc::new(config))
    };

    let incoming = TcpListener::bind(&addr).unwrap()
        .incoming()
        .map(move |tcp_stream| wrap_with_tls.accept(tcp_stream))
        .and_then(|tls_stream| tls_stream);
carllerche commented 5 years ago

Ah yes, you are correct.

The hack work around would be to define your own wrapper around TlsStream that implements Connection.

To do this better, we would want to add a feature flag to tower-web to add support for rustls.

Could you open a PR w/ this diff but make the rustls dependency optional?

chastabor commented 5 years ago

will do