use futures::TryFutureExt;
use std::{io, net::SocketAddr, sync::Arc, time::SystemTime};
use tokio::net::TcpListener;
use tokio_rustls::{
rustls::{
server::{ClientCertVerified, ClientCertVerifier},
ServerConfig,
},
TlsAcceptor,
};
use warp::hyper::service::{self, Service};
use tokio::net;
pub struct AllowAnyClient {}
impl ClientCertVerifier for AllowAnyClient {
fn client_auth_root_subjects(&self) -> Option<tokio_rustls::rustls::DistinguishedNames> {
Some(vec![])
}
fn verify_client_cert(
&self,
_: &tokio_rustls::rustls::Certificate,
_: &[tokio_rustls::rustls::Certificate],
_: SystemTime,
) -> Result<ClientCertVerified, tokio_rustls::rustls::Error> {
Ok(ClientCertVerified::assertion())
}
}
pub async fn serve_mtls<F>(
listener: &net::TcpListener,
tls_config: Arc<ServerConfig>,
warp_filter: F,
) -> io::Result<()>
where
F: warp::Filter + Clone + Send + Sync + 'static,
<F::Future as futures::TryFuture>::Ok: warp::Reply,
{
let tls_acceptor = TlsAcceptor::from(tls_config);
let (socket, _) = listener.accept().await?;
let stream = tls_acceptor
.accept(socket)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("Problem accepting TLS connection: {:?}", err),
)
})
.await?;
tokio::task::spawn(async move {
let (_, server_connection) = stream.get_ref();
let client_cert = server_connection.peer_certificates().and_then(|certs| {
if certs.is_empty() {
None
} else {
Some(certs[0].clone())
}
});
let mut svc = warp::service(warp_filter.clone());
let service = service::service_fn(move |mut req| {
if let Some(cert) = client_cert.to_owned() {
req.extensions_mut().insert(cert);
}
svc.call(req)
});
if let Err(e) = hyper::server::conn::Http::new()
.serve_connection(stream, service)
.await
{
eprintln!("Error handling request: {}", e);
}
});
Ok(())
}