From 5d0ecadf7cb56039ad541f515135e94d634f1752 Mon Sep 17 00:00:00 2001 From: Stas Kelvich Date: Wed, 12 Apr 2023 16:16:39 +0300 Subject: [PATCH] Add support for non-SNI case in multi-cert proxy When no SNI is provided use the default certificate, otherwise we can't get to the options parameter which can be used to set endpoint name too. That means that non-SNI flow will not work for CNAME domains in verify-full mode. --- proxy/src/config.rs | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/proxy/src/config.rs b/proxy/src/config.rs index ad51502b49..0ceb556ca1 100644 --- a/proxy/src/config.rs +++ b/proxy/src/config.rs @@ -40,7 +40,7 @@ pub fn configure_tls( let mut cert_resolver = CertResolver::new(); // add default certificate - cert_resolver.add_cert(key_path, cert_path)?; + cert_resolver.add_cert(key_path, cert_path, true)?; // add extra certificates if let Some(certs_dir) = certs_dir { @@ -52,8 +52,11 @@ pub fn configure_tls( let key_path = path.join("tls.key"); let cert_path = path.join("tls.crt"); if key_path.exists() && cert_path.exists() { - cert_resolver - .add_cert(&key_path.to_string_lossy(), &cert_path.to_string_lossy())?; + cert_resolver.add_cert( + &key_path.to_string_lossy(), + &cert_path.to_string_lossy(), + false, + )?; } } } @@ -78,16 +81,23 @@ pub fn configure_tls( struct CertResolver { certs: HashMap>, + default: Option>, } impl CertResolver { fn new() -> Self { Self { certs: HashMap::new(), + default: None, } } - fn add_cert(&mut self, key_path: &str, cert_path: &str) -> anyhow::Result<()> { + fn add_cert( + &mut self, + key_path: &str, + cert_path: &str, + is_default: bool, + ) -> anyhow::Result<()> { let priv_key = { let key_bytes = std::fs::read(key_path).context("TLS key file")?; let mut keys = rustls_pemfile::pkcs8_private_keys(&mut &key_bytes[..]) @@ -136,10 +146,13 @@ impl CertResolver { "Failed to parse common name from certificate at '{cert_path}'." ))?; - self.certs.insert( - common_name, - Arc::new(rustls::sign::CertifiedKey::new(cert_chain, key)), - ); + let cert = Arc::new(rustls::sign::CertifiedKey::new(cert_chain, key)); + + if is_default { + self.default = Some(cert.clone()); + } + + self.certs.insert(common_name, cert); Ok(()) } @@ -172,7 +185,17 @@ impl rustls::server::ResolvesServerCert for CertResolver { } } } else { - None + // No SNI, use the default certificate, otherwise we can't get to + // options parameter which can be used to set endpoint name too. + // That means that non-SNI flow will not work for CNAME domains in + // verify-full mode. + // + // If that will be a problem we can: + // + // a) Instead of multi-cert approach use single cert with extra + // domains listed in Subject Alternative Name (SAN). + // b) Deploy separate proxy instances for extra domains. + self.default.as_ref().cloned() } } }