From 7599dfc0ef5b06d92eca8f9a954663e008a0b1fb Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 8 Dec 2021 12:02:39 +0100 Subject: Use self-signed certificates if necessary --- src/cert_store.rs | 47 +++++++++++++++++++++++++++++++++++++++++------ src/proxy_config.rs | 8 ++++++-- 2 files changed, 47 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cert_store.rs b/src/cert_store.rs index 8d45df4..4cc2fc0 100644 --- a/src/cert_store.rs +++ b/src/cert_store.rs @@ -19,6 +19,7 @@ use crate::proxy_config::*; pub struct CertStore { consul: Consul, certs: RwLock>>, + self_signed_certs: RwLock>>, rx_proxy_config: watch::Receiver>, } @@ -27,6 +28,7 @@ impl CertStore { Arc::new(Self { consul, certs: RwLock::new(HashMap::new()), + self_signed_certs: RwLock::new(HashMap::new()), rx_proxy_config, }) } @@ -66,16 +68,23 @@ impl CertStore { } // Check in local memory if it exists - let certs = self.certs.read().unwrap(); - if let Some(cert) = certs.get(domain) { + if let Some(cert) = self.certs.read().unwrap().get(domain) { if !cert.is_old() { return Ok(cert.clone()); } } - // Not found in local memory + // Not found in local memory, try to get it in background tokio::spawn(self.clone().get_cert_task(domain.to_string())); - bail!("Certificate not found (will try to get it in background)"); + + // In the meantime, use a self-signed certificate + if let Some(cert) = self.self_signed_certs.read().unwrap().get(domain) { + if !cert.is_old() { + return Ok(cert.clone()); + } + } + + self.gen_self_signed_certificate(domain) } pub async fn get_cert_task(self: Arc, domain: String) -> Result> { @@ -221,6 +230,27 @@ impl CertStore { info!("Cert successfully renewed: {}", domain); Ok(cert) } + + fn gen_self_signed_certificate(&self, domain: &str) -> Result> { + let subject_alt_names = vec![domain.to_string(), "localhost".to_string()]; + let cert = rcgen::generate_simple_self_signed(subject_alt_names)?; + + let certser = CertSer { + hostname: domain.to_string(), + date: Utc::today().naive_utc(), + valid_days: 1024, + key_pem: cert.serialize_private_key_pem(), + cert_pem: cert.serialize_pem()?, + }; + let cert = Arc::new(Cert::new(certser)?); + self.self_signed_certs + .write() + .unwrap() + .insert(domain.to_string(), cert.clone()); + info!("Added self-signed certificate for {}", domain); + + Ok(cert) + } } pub struct StoreResolver(pub Arc); @@ -228,7 +258,12 @@ pub struct StoreResolver(pub Arc); impl rustls::server::ResolvesServerCert for StoreResolver { fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option> { let domain = client_hello.server_name()?; - let cert = self.0.get_cert_for_https(domain).ok()?; - Some(cert.certkey.clone()) + match self.0.get_cert_for_https(domain) { + Ok(cert) => Some(cert.certkey.clone()), + Err(e) => { + warn!("Could not get certificate for {}: {}", domain, e); + None + } + } } } diff --git a/src/proxy_config.rs b/src/proxy_config.rs index 2807a3b..1b46305 100644 --- a/src/proxy_config.rs +++ b/src/proxy_config.rs @@ -60,13 +60,17 @@ impl std::fmt::Display for ProxyEntry { HostDescription::Hostname(h) => write!(f, "{}", h)?, HostDescription::Pattern(p) => write!(f, "Pattern('{}')", p.as_str())?, } - write!(f, "{} {}", self.path_prefix.as_ref().unwrap_or(&String::new()), self.priority)?; + write!( + f, + "{} {}", + self.path_prefix.as_ref().unwrap_or(&String::new()), + self.priority + )?; if !self.add_headers.is_empty() { write!(f, "+Headers: {:?}", self.add_headers)?; } Ok(()) } - } #[derive(Debug)] -- cgit v1.2.3