From 207f467b879194c993c70a092c232daca8ad1057 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 8 Dec 2021 23:45:24 +0100 Subject: [PATCH] Support totally ignoring backend HTTPS certificate stuff --- Cargo.toml | 2 +- Dockerfile | 2 +- src/cert_store.rs | 2 +- src/main.rs | 1 + src/reverse_proxy.rs | 43 +++++++++++++++++++----- src/tls_util.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 src/tls_util.rs diff --git a/Cargo.toml b/Cargo.toml index aa1b4fb..2130197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ serde_json = "1.0.53" tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] } bytes = "1" acme-micro = "0.12" -rustls = "0.20" +rustls = { version = "0.20", features = [ "dangerous_configuration" ] } rustls-pemfile = "0.2" chrono = { version = "0.4", features = [ "serde" ] } hyper = { version = "0.14", features = [ "full" ] } diff --git a/Dockerfile b/Dockerfile index ebda1fc..41df609 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,6 @@ COPY ./src ./src RUN cargo build --release FROM debian:bullseye-slim -RUN apt-get update && apt-get install -y libssl1.1 iptables +RUN apt-get update && apt-get install -y libssl1.1 iptables ca-certificates COPY --from=builder /srv/target/release/tricot /usr/local/sbin/tricot CMD ["/usr/local/sbin/tricot"] diff --git a/src/cert_store.rs b/src/cert_store.rs index ced9966..fe2f8b0 100644 --- a/src/cert_store.rs +++ b/src/cert_store.rs @@ -56,8 +56,8 @@ impl CertStore { } } + debug!("Ensuring we have certs for domains: {:#?}", domains); for dom in domains.iter() { - info!("Ensuring we have certs for domains: {:?}", domains); if let Err(e) = self.get_cert(dom).await { warn!("Error get_cert {}: {}", dom, e); } diff --git a/src/main.rs b/src/main.rs index 987c3ec..1fffcbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use futures::TryFutureExt; use std::net::SocketAddr; use structopt::StructOpt; +mod tls_util; mod cert; mod cert_store; mod consul; diff --git a/src/reverse_proxy.rs b/src/reverse_proxy.rs index 9d7780c..82e7c7a 100644 --- a/src/reverse_proxy.rs +++ b/src/reverse_proxy.rs @@ -1,16 +1,23 @@ //! Copied from https://github.com/felipenoris/hyper-reverse-proxy //! See there for original Copyright notice +use std::sync::Arc; +use std::convert::TryInto; +use std::time::SystemTime; +use std::net::IpAddr; +use std::str::FromStr; + use anyhow::Result; use log::*; -use std::convert::TryInto; use http::header::HeaderName; use hyper::header::{HeaderMap, HeaderValue}; use hyper::{Body, Client, Request, Response, Uri}; +use rustls::{Certificate, ServerName}; +use rustls::client::{ServerCertVerifier, ServerCertVerified}; use lazy_static::lazy_static; -use std::net::IpAddr; -use std::str::FromStr; + +use crate::tls_util::HttpsConnectorFixedDnsname; fn is_hop_header(name: &str) -> bool { use unicase::Ascii; @@ -149,12 +156,12 @@ pub async fn call_https( trace!("Proxied request (HTTPS): {:?}", proxied_request); - let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots() - .https_only() - .enable_http1() - .build(); - let client: Client<_, hyper::Body> = Client::builder().build(https); + let tls_config = rustls::client::ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(DontVerifyServerCert)) + .with_no_client_auth(); + let connector = HttpsConnectorFixedDnsname::new(tls_config, "dummy"); + let client: Client<_, hyper::Body> = Client::builder().build(connector); let response = client.request(proxied_request).await?; trace!("Inner response (HTTPS): {:?}", response); @@ -162,3 +169,21 @@ pub async fn call_https( let proxied_response = create_proxied_response(response); Ok(proxied_response) } + +struct DontVerifyServerCert; + +impl ServerCertVerifier for DontVerifyServerCert { + fn verify_server_cert( + &self, + _end_entity: &Certificate, + _intermediates: &[Certificate], + _server_name: &ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: SystemTime + ) -> Result { + Ok(ServerCertVerified::assertion()) + } +} + + diff --git a/src/tls_util.rs b/src/tls_util.rs new file mode 100644 index 0000000..054c35a --- /dev/null +++ b/src/tls_util.rs @@ -0,0 +1,79 @@ +use core::future::Future; +use core::task::{Context, Poll}; +use std::convert::TryFrom; +use std::pin::Pin; +use std::sync::Arc; +use std::io; + +use futures_util::future::*; +use rustls::ServerName; +use hyper::client::connect::Connection; +use hyper::client::HttpConnector; +use hyper::service::Service; +use hyper::Uri; +use hyper_rustls::MaybeHttpsStream; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_rustls::TlsConnector; + + +#[derive(Clone)] +pub struct HttpsConnectorFixedDnsname { + http: T, + tls_config: Arc, + fixed_dnsname: &'static str, +} +type BoxError = Box; +impl HttpsConnectorFixedDnsname { + pub fn new(mut tls_config: rustls::ClientConfig, fixed_dnsname: &'static str) -> Self { + let mut http = HttpConnector::new(); + http.enforce_http(false); + tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + Self { + http, + tls_config: Arc::new(tls_config), + fixed_dnsname, + } + } +} +impl Service for HttpsConnectorFixedDnsname +where + T: Service, + T::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, + T::Future: Send + 'static, + T::Error: Into, +{ + type Response = MaybeHttpsStream; + type Error = BoxError; + #[allow(clippy::type_complexity)] + type Future = + Pin, BoxError>> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + match self.http.poll_ready(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), + Poll::Pending => Poll::Pending, + } + } + + fn call(&mut self, dst: Uri) -> Self::Future { + let is_https = dst.scheme_str() == Some("https"); + assert!(is_https); + + let cfg = self.tls_config.clone(); + let connecting_future = self.http.call(dst); + let dnsname = + ServerName::try_from(self.fixed_dnsname).expect("Invalid fixed dnsname"); + let f = async move { + let tcp = connecting_future.await.map_err(Into::into)?; + let connector = TlsConnector::from(cfg); + let tls = connector + .connect(dnsname, tcp) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(MaybeHttpsStream::Https(tls)) + }; + f.boxed() + } +} +