From 4ae09ac6aea2e0a4f86c241440fedde00ec9eb00 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Fri, 20 Mar 2026 08:06:32 +0000 Subject: [PATCH] fix(rustproxy): use SNI-based certificate resolution for QUIC TLS connections --- changelog.md | 7 ++++++ rust/crates/rustproxy/src/lib.rs | 41 +++++++++----------------------- ts/00_commitinfo_data.ts | 2 +- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/changelog.md b/changelog.md index be71c88..c2615af 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-03-20 - 25.17.8 - fix(rustproxy) +use SNI-based certificate resolution for QUIC TLS connections + +- Replaces static first-certificate selection with the shared CertResolver used by the TCP/TLS path. +- Ensures QUIC connections can present the correct certificate per requested domain. +- Keeps HTTP/3 ALPN configuration while improving multi-domain TLS handling. + ## 2026-03-20 - 25.17.7 - fix(readme) document QUIC and HTTP/3 compatibility caveats diff --git a/rust/crates/rustproxy/src/lib.rs b/rust/crates/rustproxy/src/lib.rs index e2fa057..fb56f26 100644 --- a/rust/crates/rustproxy/src/lib.rs +++ b/rust/crates/rustproxy/src/lib.rs @@ -1003,44 +1003,25 @@ impl RustProxy { fn build_quic_tls_config( tls_configs: &HashMap, ) -> Option> { - // Find the first available cert (prefer wildcard, then any) - let cert_config = tls_configs.get("*") - .or_else(|| tls_configs.values().next()); - - let cert_config = match cert_config { - Some(c) => c, - None => return None, - }; - - // Parse cert chain from PEM - let mut cert_reader = std::io::BufReader::new(cert_config.cert_pem.as_bytes()); - let certs: Vec> = - rustls_pemfile::certs(&mut cert_reader) - .filter_map(|r| r.ok()) - .collect(); - - if certs.is_empty() { + if tls_configs.is_empty() { return None; } - // Parse private key from PEM - let mut key_reader = std::io::BufReader::new(cert_config.key_pem.as_bytes()); - let key = match rustls_pemfile::private_key(&mut key_reader) { - Ok(Some(key)) => key, - _ => return None, - }; - - let mut tls_config = match rustls::ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, key) - { - Ok(c) => c, + // Reuse CertResolver for SNI-based cert selection (same as TCP/TLS path). + // This ensures QUIC connections get the correct certificate for each domain + // instead of a single static cert. + let resolver = match rustproxy_passthrough::tls_handler::CertResolver::new(tls_configs) { + Ok(r) => r, Err(e) => { - warn!("Failed to build QUIC TLS config: {}", e); + warn!("Failed to build QUIC cert resolver: {}", e); return None; } }; + let mut tls_config = rustls::ServerConfig::builder() + .with_no_client_auth() + .with_cert_resolver(Arc::new(resolver)); + // QUIC requires h3 ALPN tls_config.alpn_protocols = vec![b"h3".to_vec()]; diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 4391545..c2fe19a 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '25.17.7', + version: '25.17.8', description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' }