From 234abae5dda98a3bf0f7be562c42674f3ac6274d Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Tue, 28 May 2024 21:39:38 +0900 Subject: [PATCH] wip: support rustls-0.23 for http3-quinn --- rpxy-bin/Cargo.toml | 4 +- rpxy-bin/src/config/parse.rs | 1 - rpxy-bin/src/config/toml.rs | 5 +- rpxy-lib/Cargo.toml | 10 +-- rpxy-lib/src/backend/backend_main.rs | 8 ++- rpxy-lib/src/error.rs | 12 ++-- rpxy-lib/src/globals.rs | 1 + .../handler_manipulate_messages.rs | 10 +-- rpxy-lib/src/proxy/proxy_h3.rs | 4 +- rpxy-lib/src/proxy/proxy_quic_quinn.rs | 61 +++++++++---------- 10 files changed, 63 insertions(+), 53 deletions(-) diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index a8c89e6..395f33c 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -13,8 +13,8 @@ publish.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -# default = ["http3-quinn", "cache", "rustls-backend"] -default = ["cache", "rustls-backend"] +default = ["http3-quinn", "cache", "rustls-backend"] +# default = ["cache", "rustls-backend"] http3-quinn = ["rpxy-lib/http3-quinn"] http3-s2n = ["rpxy-lib/http3-s2n"] native-tls-backend = ["rpxy-lib/native-tls-backend"] diff --git a/rpxy-bin/src/config/parse.rs b/rpxy-bin/src/config/parse.rs index 95c87d2..f45ca17 100644 --- a/rpxy-bin/src/config/parse.rs +++ b/rpxy-bin/src/config/parse.rs @@ -73,7 +73,6 @@ pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig, // build applications let mut app_config_list_inner = Vec::::new(); - // let mut backends = Backends::new(); for (app_name, app) in apps.0.iter() { let _server_name_string = app.server_name.as_ref().ok_or(anyhow!("No server name"))?; let registered_app_name = app_name.to_ascii_lowercase(); diff --git a/rpxy-bin/src/config/toml.rs b/rpxy-bin/src/config/toml.rs index 9b6502b..957296c 100644 --- a/rpxy-bin/src/config/toml.rs +++ b/rpxy-bin/src/config/toml.rs @@ -230,7 +230,10 @@ impl Application { tls.https_redirection.unwrap() }; - Some(TlsConfig { https_redirection }) + Some(TlsConfig { + mutual_tls: tls.client_ca_cert_path.is_some(), + https_redirection, + }) } else { None }; diff --git a/rpxy-lib/Cargo.toml b/rpxy-lib/Cargo.toml index c597d65..eab620b 100644 --- a/rpxy-lib/Cargo.toml +++ b/rpxy-lib/Cargo.toml @@ -13,8 +13,8 @@ publish.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["sticky-cookie", "cache", "rustls-backend"] -# default = ["http3-quinn", "sticky-cookie", "cache", "rustls-backend"] +# default = ["sticky-cookie", "cache", "rustls-backend"] +default = ["http3-quinn", "sticky-cookie", "cache", "rustls-backend"] http3-quinn = ["socket2", "quinn", "h3", "h3-quinn", "rpxy-certs/http3"] http3-s2n = [ "h3", @@ -83,11 +83,11 @@ x509-parser = "0.16.0" tracing = { version = "0.1.40" } # http/3 -quinn = { version = "0.10.2", optional = true } +quinn = { version = "0.11.1", optional = true } # h3 = { path = "../submodules/h3/h3/", optional = true } # h3-quinn = { path = "../submodules/h3/h3-quinn/", optional = true } -h3 = { version = "0.0.4", optional = true } -h3-quinn = { version = "0.0.5", optional = true } +h3 = { version = "0.0.5", optional = true } +h3-quinn = { version = "0.0.6", optional = true } s2n-quic = { version = "1.37.0", default-features = false, features = [ "provider-tls-rustls", ], optional = true } diff --git a/rpxy-lib/src/backend/backend_main.rs b/rpxy-lib/src/backend/backend_main.rs index 663120e..0282504 100644 --- a/rpxy-lib/src/backend/backend_main.rs +++ b/rpxy-lib/src/backend/backend_main.rs @@ -24,6 +24,9 @@ pub struct BackendApp { /// tls settings: https redirection with 30x #[builder(default)] pub https_redirection: Option, + /// tls settings: mutual TLS is enabled + #[builder(default)] + pub mutual_tls: Option, } impl<'a> BackendAppBuilder { pub fn server_name(&mut self, server_name: impl Into>) -> &mut Self { @@ -56,7 +59,10 @@ impl TryFrom<&AppConfig> for BackendApp { backend_builder.build()? } else { let tls = app_config.tls.as_ref().unwrap(); - backend_builder.https_redirection(Some(tls.https_redirection)).build()? + backend_builder + .https_redirection(Some(tls.https_redirection)) + .mutual_tls(Some(tls.mutual_tls)) + .build()? }; Ok(backend) } diff --git a/rpxy-lib/src/error.rs b/rpxy-lib/src/error.rs index 85c05f4..a775b17 100644 --- a/rpxy-lib/src/error.rs +++ b/rpxy-lib/src/error.rs @@ -16,10 +16,14 @@ pub enum RpxyError { NoServerNameInClientHello, #[error("No TLS serving app: {0}")] NoTlsServingApp(String), - #[error("Failed to update server crypto: {0}")] - FailedToUpdateServerCrypto(String), - #[error("No server crypto: {0}")] - NoServerCrypto(String), + #[error("No default crypto provider")] + NoDefaultCryptoProvider, + #[error("Failed to build server config: {0}")] + FailedToBuildServerConfig(String), + // #[error("Failed to update server crypto: {0}")] + // FailedToUpdateServerCrypto(String), + // #[error("No server crypto: {0}")] + // NoServerCrypto(String), // hyper errors #[error("hyper body manipulation error: {0}")] diff --git a/rpxy-lib/src/globals.rs b/rpxy-lib/src/globals.rs index fec0a53..3582cdb 100644 --- a/rpxy-lib/src/globals.rs +++ b/rpxy-lib/src/globals.rs @@ -157,5 +157,6 @@ pub struct UpstreamUri { /// Configuration parameters on TLS for a single backend application #[derive(PartialEq, Eq, Clone)] pub struct TlsConfig { + pub mutual_tls: bool, pub https_redirection: bool, } diff --git a/rpxy-lib/src/message_handler/handler_manipulate_messages.rs b/rpxy-lib/src/message_handler/handler_manipulate_messages.rs index 2d77d2c..0ee4dab 100644 --- a/rpxy-lib/src/message_handler/handler_manipulate_messages.rs +++ b/rpxy-lib/src/message_handler/handler_manipulate_messages.rs @@ -29,15 +29,15 @@ where { // Manipulate ALT_SVC allowing h3 in response message only when mutual TLS is not enabled // TODO: This is a workaround for avoiding a client authentication in HTTP/3 - if self.globals.proxy_config.http3 && backend_app.crypto_source.as_ref().is_some_and(|v| !v.is_mutual_tls()) { + if self.globals.proxy_config.http3 + && backend_app.https_redirection.is_some() + && backend_app.mutual_tls.as_ref().is_some_and(|v| !v) + { if let Some(port) = self.globals.proxy_config.https_port { add_header_entry_overwrite_if_exist( headers, header::ALT_SVC.as_str(), - format!( - "h3=\":{}\"; ma={}, h3-29=\":{}\"; ma={}", - port, self.globals.proxy_config.h3_alt_svc_max_age, port, self.globals.proxy_config.h3_alt_svc_max_age - ), + format!("h3=\":{}\"; ma={}", port, self.globals.proxy_config.h3_alt_svc_max_age), )?; } } else { diff --git a/rpxy-lib/src/proxy/proxy_h3.rs b/rpxy-lib/src/proxy/proxy_h3.rs index 1e0f24f..998b6ee 100644 --- a/rpxy-lib/src/proxy/proxy_h3.rs +++ b/rpxy-lib/src/proxy/proxy_h3.rs @@ -1,6 +1,5 @@ use super::proxy_main::Proxy; use crate::{ - crypto::CryptoSource, error::*, hyper_ext::body::{IncomingLike, RequestBody}, log::*, @@ -17,10 +16,9 @@ use h3::{quic::BidiStream, quic::Connection as ConnectionQuic, server::RequestSt #[cfg(all(feature = "http3-s2n", not(feature = "http3-quinn")))] use s2n_quic_h3::h3::{self, quic::BidiStream, quic::Connection as ConnectionQuic, server::RequestStream}; -impl Proxy +impl Proxy where T: Connect + Clone + Sync + Send + 'static, - U: CryptoSource + Clone + Sync + Send + 'static, { pub(super) async fn h3_serve_connection( &self, diff --git a/rpxy-lib/src/proxy/proxy_quic_quinn.rs b/rpxy-lib/src/proxy/proxy_quic_quinn.rs index 9c4bf4e..c316ed9 100644 --- a/rpxy-lib/src/proxy/proxy_quic_quinn.rs +++ b/rpxy-lib/src/proxy/proxy_quic_quinn.rs @@ -1,20 +1,17 @@ -use super::proxy_main::Proxy; -use super::socket::bind_udp_socket; -use crate::{ - crypto::{CryptoSource, ServerCrypto}, - error::*, - log::*, - name_exp::ByteName, -}; +use super::{proxy_main::Proxy, socket::bind_udp_socket}; +use crate::{error::*, log::*, name_exp::ByteName}; use hyper_util::client::legacy::connect::Connect; -use quinn::{crypto::rustls::HandshakeData, Endpoint, ServerConfig as QuicServerConfig, TransportConfig}; +use quinn::{ + crypto::rustls::{HandshakeData, QuicServerConfig}, + Endpoint, TransportConfig, +}; +use rpxy_certs::ServerCrypto; use rustls::ServerConfig; use std::sync::Arc; -impl Proxy +impl Proxy where T: Send + Sync + Connect + Clone + 'static, - U: CryptoSource + Clone + Sync + Send + 'static, { pub(super) async fn h3_listener_service(&self) -> RpxyResult<()> { let Some(mut server_crypto_rx) = self.globals.cert_reloader_rx.clone() else { @@ -22,13 +19,14 @@ where }; info!("Start UDP proxy serving with HTTP/3 request for configured host names [quinn]"); // first set as null config server - let rustls_server_config = ServerConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() + // AWS LC provider by default + let provider = rustls::crypto::CryptoProvider::get_default().ok_or(RpxyError::NoDefaultCryptoProvider)?; + let rustls_server_config = ServerConfig::builder_with_provider(provider.clone()) .with_protocol_versions(&[&rustls::version::TLS13]) - .map_err(|e| RpxyError::QuinnInvalidTlsProtocolVersion(e.to_string()))? + .map_err(|e| RpxyError::FailedToBuildServerConfig(format!("TLS 1.3 server config failed: {e}")))? .with_no_client_auth() .with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new())); + let quinn_server_config_crypto = QuicServerConfig::try_from(Arc::new(rustls_server_config)).unwrap(); let mut transport_config_quic = TransportConfig::default(); transport_config_quic @@ -42,20 +40,15 @@ where .map(|v| quinn::IdleTimeout::try_from(v).unwrap()), ); - let mut server_config_h3 = QuicServerConfig::with_crypto(Arc::new(rustls_server_config)); + let mut server_config_h3 = quinn::ServerConfig::with_crypto(Arc::new(quinn_server_config_crypto)); server_config_h3.transport = Arc::new(transport_config_quic); - server_config_h3.concurrent_connections(self.globals.proxy_config.h3_max_concurrent_connections); + server_config_h3.max_incoming(self.globals.proxy_config.h3_max_concurrent_connections as usize); // To reuse address let udp_socket = bind_udp_socket(&self.listening_on)?; - let runtime = quinn::default_runtime() - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "No async runtime found"))?; - let endpoint = Endpoint::new( - quinn::EndpointConfig::default(), - Some(server_config_h3), - udp_socket, - runtime, - )?; + let runtime = + quinn::default_runtime().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "No async runtime found"))?; + let endpoint = Endpoint::new(quinn::EndpointConfig::default(), Some(server_config_h3), udp_socket, runtime)?; let mut server_crypto: Option> = None; loop { @@ -64,8 +57,10 @@ where if server_crypto.is_none() || new_conn.is_none() { continue; } - let mut conn: quinn::Connecting = new_conn.unwrap(); - let Ok(hsd) = conn.handshake_data().await else { + let Ok(mut incoming) = new_conn.unwrap().accept() else { + continue + }; + let Ok(hsd) = incoming.handshake_data().await else { continue }; @@ -84,8 +79,8 @@ where // TODO: 通常のTLSと同じenumか何かにまとめたい let self_clone = self.clone(); self.globals.runtime_handle.spawn(async move { - let client_addr = conn.remote_address(); - let quic_connection = match conn.await { + let client_addr = incoming.remote_address(); + let quic_connection = match incoming.await { Ok(new_conn) => { info!("New connection established"); h3_quinn::Connection::new(new_conn) @@ -114,8 +109,12 @@ where error!("Failed to update server crypto for h3"); break; }; - endpoint.set_server_config(Some(QuicServerConfig::with_crypto(inner.clone().inner_global_no_client_auth.clone()))); - + let rustls_server_config = inner.aggregated_config_no_client_auth.clone(); + let Ok(quinn_server_config_crypto) = QuicServerConfig::try_from(rustls_server_config) else { + error!("Failed to update server crypto for h3"); + break; + }; + endpoint.set_server_config(Some(quinn::ServerConfig::with_crypto(Arc::new(quinn_server_config_crypto)))); } else => break }