Support totally ignoring backend HTTPS certificate stuff

This commit is contained in:
Alex 2021-12-08 23:45:24 +01:00
parent ca8c5aad23
commit 207f467b87
No known key found for this signature in database
GPG key ID: EDABF9711E244EB1
6 changed files with 117 additions and 12 deletions

View file

@ -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"] } tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] }
bytes = "1" bytes = "1"
acme-micro = "0.12" acme-micro = "0.12"
rustls = "0.20" rustls = { version = "0.20", features = [ "dangerous_configuration" ] }
rustls-pemfile = "0.2" rustls-pemfile = "0.2"
chrono = { version = "0.4", features = [ "serde" ] } chrono = { version = "0.4", features = [ "serde" ] }
hyper = { version = "0.14", features = [ "full" ] } hyper = { version = "0.14", features = [ "full" ] }

View file

@ -18,6 +18,6 @@ COPY ./src ./src
RUN cargo build --release RUN cargo build --release
FROM debian:bullseye-slim 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 COPY --from=builder /srv/target/release/tricot /usr/local/sbin/tricot
CMD ["/usr/local/sbin/tricot"] CMD ["/usr/local/sbin/tricot"]

View file

@ -56,8 +56,8 @@ impl CertStore {
} }
} }
debug!("Ensuring we have certs for domains: {:#?}", domains);
for dom in domains.iter() { for dom in domains.iter() {
info!("Ensuring we have certs for domains: {:?}", domains);
if let Err(e) = self.get_cert(dom).await { if let Err(e) = self.get_cert(dom).await {
warn!("Error get_cert {}: {}", dom, e); warn!("Error get_cert {}: {}", dom, e);
} }

View file

@ -5,6 +5,7 @@ use futures::TryFutureExt;
use std::net::SocketAddr; use std::net::SocketAddr;
use structopt::StructOpt; use structopt::StructOpt;
mod tls_util;
mod cert; mod cert;
mod cert_store; mod cert_store;
mod consul; mod consul;

View file

@ -1,16 +1,23 @@
//! Copied from https://github.com/felipenoris/hyper-reverse-proxy //! Copied from https://github.com/felipenoris/hyper-reverse-proxy
//! See there for original Copyright notice //! 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 anyhow::Result;
use log::*; use log::*;
use std::convert::TryInto;
use http::header::HeaderName; use http::header::HeaderName;
use hyper::header::{HeaderMap, HeaderValue}; use hyper::header::{HeaderMap, HeaderValue};
use hyper::{Body, Client, Request, Response, Uri}; use hyper::{Body, Client, Request, Response, Uri};
use rustls::{Certificate, ServerName};
use rustls::client::{ServerCertVerifier, ServerCertVerified};
use lazy_static::lazy_static; 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 { fn is_hop_header(name: &str) -> bool {
use unicase::Ascii; use unicase::Ascii;
@ -149,12 +156,12 @@ pub async fn call_https(
trace!("Proxied request (HTTPS): {:?}", proxied_request); trace!("Proxied request (HTTPS): {:?}", proxied_request);
let https = hyper_rustls::HttpsConnectorBuilder::new() let tls_config = rustls::client::ClientConfig::builder()
.with_native_roots() .with_safe_defaults()
.https_only() .with_custom_certificate_verifier(Arc::new(DontVerifyServerCert))
.enable_http1() .with_no_client_auth();
.build(); let connector = HttpsConnectorFixedDnsname::new(tls_config, "dummy");
let client: Client<_, hyper::Body> = Client::builder().build(https); let client: Client<_, hyper::Body> = Client::builder().build(connector);
let response = client.request(proxied_request).await?; let response = client.request(proxied_request).await?;
trace!("Inner response (HTTPS): {:?}", response); trace!("Inner response (HTTPS): {:?}", response);
@ -162,3 +169,21 @@ pub async fn call_https(
let proxied_response = create_proxied_response(response); let proxied_response = create_proxied_response(response);
Ok(proxied_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<Item = &[u8]>,
_ocsp_response: &[u8],
_now: SystemTime
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
}

79
src/tls_util.rs Normal file
View file

@ -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<T> {
http: T,
tls_config: Arc<rustls::ClientConfig>,
fixed_dnsname: &'static str,
}
type BoxError = Box<dyn std::error::Error + Send + Sync>;
impl HttpsConnectorFixedDnsname<HttpConnector> {
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<T> Service<Uri> for HttpsConnectorFixedDnsname<T>
where
T: Service<Uri>,
T::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
T::Future: Send + 'static,
T::Error: Into<BoxError>,
{
type Response = MaybeHttpsStream<T::Response>;
type Error = BoxError;
#[allow(clippy::type_complexity)]
type Future =
Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T::Response>, BoxError>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
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()
}
}