diff --git a/CHANGELOG.md b/CHANGELOG.md index d26098b..f9c6b81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixed + +- Avoid using dropped handlers for HTTP proxies. + ## 0.9.2 (2026-03-28) ### Added diff --git a/Cargo.lock b/Cargo.lock index a47af6d..d87f7ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,9 +371,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -382,9 +382,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.38.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" dependencies = [ "cc", "cmake", @@ -545,7 +545,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" dependencies = [ - "hybrid-array", + "hybrid-array 0.4.5", ] [[package]] @@ -931,17 +931,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-models" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" -dependencies = [ - "hax-lib", - "pastey", - "rand 0.9.2", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1068,7 +1057,7 @@ version = "0.2.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "919bd05924682a5480aec713596b9e2aabed3a0a6022fab6847f85a99e5f190a" dependencies = [ - "hybrid-array", + "hybrid-array 0.4.5", ] [[package]] @@ -1929,43 +1918,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "hax-lib" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" -dependencies = [ - "hax-lib-macros", - "num-bigint", - "num-traits", -] - -[[package]] -name = "hax-lib-macros" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" -dependencies = [ - "hax-lib-macros-types", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "hax-lib-macros-types" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_json", - "uuid", -] - [[package]] name = "heck" version = "0.5.0" @@ -2105,6 +2057,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9" +dependencies = [ + "typenum", +] + [[package]] name = "hybrid-array" version = "0.4.5" @@ -2571,6 +2532,25 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kem" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f" +dependencies = [ + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "kqueue" version = "1.1.1" @@ -2606,72 +2586,6 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" -[[package]] -name = "libcrux-intrinsics" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" -dependencies = [ - "core-models", - "hax-lib", -] - -[[package]] -name = "libcrux-ml-kem" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-secrets", - "libcrux-sha3", - "libcrux-traits", - "rand 0.9.2", - "tls_codec", -] - -[[package]] -name = "libcrux-platform" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" -dependencies = [ - "libc", -] - -[[package]] -name = "libcrux-secrets" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" -dependencies = [ - "hax-lib", -] - -[[package]] -name = "libcrux-sha3" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-traits", -] - -[[package]] -name = "libcrux-traits" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" -dependencies = [ - "libcrux-secrets", - "rand 0.9.2", -] - [[package]] name = "libm" version = "0.2.15" @@ -2853,6 +2767,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ml-kem" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de49b3df74c35498c0232031bb7e85f9389f913e2796169c8ab47a53993a18f" +dependencies = [ + "hybrid-array 0.2.3", + "kem", + "rand_core 0.6.4", + "sha3", +] + [[package]] name = "mockall" version = "0.14.0" @@ -3263,12 +3189,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -3517,28 +3437,6 @@ dependencies = [ "elliptic-curve", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -3990,9 +3888,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.58.0" +version = "0.58.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f6ce4f5d5105b934cfb4b8b3028aab4d5dcdff863cb8dda9edd06d39b8c4e8" +checksum = "68d53bd2e1d6c49e32ae183c09bdc710ace41e7c8c564cc8a2286aad3bffe10d" dependencies = [ "aes", "aws-lc-rs", @@ -4019,9 +3917,9 @@ dependencies = [ "hmac", "inout", "internal-russh-forked-ssh-key", - "libcrux-ml-kem", "log", "md5", + "ml-kem", "num-bigint", "p256", "p384", @@ -4533,6 +4431,16 @@ dependencies = [ "digest 0.11.0-rc.4", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -5006,27 +4914,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.50.0" @@ -6159,20 +6046,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zerotrie" diff --git a/Cargo.toml b/Cargo.toml index cc81a3d..6dc42ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ reqwest = { version = "0.13.2", optional = true, default-features = false, featu "json", "rustls", ] } -russh = "0.58.0" +russh = "0.58.1" rustls = "0.23.37" rustls-acme = { version = "0.15.1", optional = true, default-features = false, features = [ "tokio", diff --git a/src/entrypoint.rs b/src/entrypoint.rs index 172f3d3..e6a37b0 100644 --- a/src/entrypoint.rs +++ b/src/entrypoint.rs @@ -57,10 +57,7 @@ use crate::{ droppable_handle::DroppableHandle, error::ServerError, fingerprints::FingerprintsValidator, - http::{ - DomainRedirect, Protocol, ProxyData, ProxyType, http2::https_2_handler, - http11::https_11_handler, proxy_handler, - }, + http::{DomainRedirect, Protocol, ProxyData, ProxyType, proxy_handler}, ip::{IpFilter, IpFilterConfig}, quota::{DummyQuotaHandler, QuotaHandler, QuotaMap}, reactor::{AliasReactor, HttpReactor, SniReactor, SshReactor, TcpReactor}, @@ -1040,13 +1037,7 @@ async fn handle_https_connection( // Create a Hyper service and serve over the accepted TLS connection. let io = TokioIo::new(stream); let service = service_fn(move |req: Request| { - https_2_handler( - req, - address, - Arc::clone(&proxy_data), - Arc::clone(&tunnel_handler), - sni.clone(), - ) + proxy_handler(req, address, None, Arc::clone(&proxy_data)) }); let server = auto::Builder::new(TokioExecutor::new()); let conn = server.serve_connection_with_upgrades(io, service); @@ -1070,13 +1061,7 @@ async fn handle_https_connection( // Create a Hyper service and serve over the accepted TLS connection. let io = TokioIo::new(stream); let service = service_fn(move |req: Request| { - https_11_handler( - req, - address, - Arc::clone(&proxy_data), - Arc::clone(&tunnel_handler), - sni.clone(), - ) + proxy_handler(req, address, None, Arc::clone(&proxy_data)) }); let mut server = auto::Builder::new(TokioExecutor::new()); server.http1().pipeline_flush(true); @@ -1097,7 +1082,7 @@ async fn handle_https_connection( }; }; } else { - match TlsAcceptor::from(http11_server_config).accept(stream).await { + match TlsAcceptor::from(http2_server_config).accept(stream).await { Ok(stream) => { // Create a Hyper service and serve over the accepted TLS connection. let io = TokioIo::new(stream); diff --git a/src/http/http11.rs b/src/http/http11.rs index bf0ea4c..67b4961 100644 --- a/src/http/http11.rs +++ b/src/http/http11.rs @@ -6,130 +6,25 @@ use crate::{ connection_handler::ConnectionHandler, connections::ConnectionGetByHttpHost, http::{ - ArcProxyData, HttpError, HttpLog, Protocol, ProxyData, ProxyResponse, ProxyType, - TimedResponse, X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PORT, X_FORWARDED_PROTO, - append_to_header, http_log, proxy_handler_inner, + ArcProxyData, HttpError, HttpLog, ProxyData, ProxyResponse, ProxyType, TimedResponse, + http_log, }, keepalive::KeepaliveAlias, - telemetry::{TELEMETRY_COUNTER_HTTP_REQUESTS, TELEMETRY_KEY_HOSTNAME}, }; -use axum::{body::Body as AxumBody, response::IntoResponse}; +use http::Uri; use http::header::COOKIE; -use http::{Uri, Version}; use hyper::{ - Request, Response, StatusCode, + Request, StatusCode, body::Body, header::{HOST, UPGRADE}, }; use hyper_util::rt::TokioIo; -use metrics::counter; use tokio::{ io::{AsyncRead, AsyncWrite, copy_bidirectional_with_sizes}, time::timeout, }; -#[cfg_attr( - not(coverage_nightly), - tracing::instrument(skip(proxy_data, handler), level = "debug") -)] -pub(crate) async fn https_11_handler( - request: Request, - tcp_address: SocketAddr, - proxy_data: Arc>, - handler: Arc, - host: String, -) -> color_eyre::Result> -where - M: ConnectionGetByHttpHost> + Send + Sync + 'static, - H: ConnectionHandler + Send + Sync + 'static, - T: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static, - B: Body + Debug + Send + Unpin + 'static, - ::Data: Send + Sync + 'static, - ::Error: Error + Send + Sync + 'static, -{ - match https_11_handler_inner(request, tcp_address, proxy_data, handler, host).await { - Ok(response) => Ok(match response { - ProxyResponse::Axum(response) => response, - ProxyResponse::Proxy(response) => response.into_response(), - }), - Err(error) => Ok(error.into_response()), - } -} - -#[inline] -async fn https_11_handler_inner( - mut request: Request, - tcp_address: SocketAddr, - proxy_data: Arc>, - handler: Arc, - host: String, -) -> Result -where - M: ConnectionGetByHttpHost> + Send + Sync + 'static, - H: ConnectionHandler + Send + Sync + 'static, - T: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static, - B: Body + Debug + Send + Unpin + 'static, - ::Data: Send + Sync + 'static, - ::Error: Error + Send + Sync + 'static, -{ - let timer = Instant::now(); - let host_header_or_uri = match request.version() { - Version::HTTP_2 => request.uri().host().ok_or(HttpError::MissingUriHost)?, - Version::HTTP_11 => match request.headers().get(HOST) { - Some(header_value) => match header_value.to_str() { - Ok(header) => header - .split(':') - .next() - .ok_or(HttpError::InvalidHostHeader)?, - Err(_) => return Err(HttpError::InvalidHostHeader), - }, - None => return Err(HttpError::MissingHost), - }, - version => return Err(HttpError::InvalidHttpVersion(version)), - }; - if host != host_header_or_uri { - // Fallback to legacy behavior - return proxy_handler_inner(request, tcp_address, None, proxy_data).await; - } - - let ip = tcp_address.ip().to_canonical(); - - // Read protocol information for X-Forwarded headers - let Protocol::Https { port } = proxy_data.protocol else { - unreachable!("HTTPS-only handler"); - }; - - // Add proxied info to the proper headers, but don't overwrite any existing proxy headers - let headers = request.headers_mut(); - append_to_header(headers, &X_FORWARDED_FOR, ip.to_string().as_bytes()); - append_to_header(headers, &X_FORWARDED_HOST, host.as_bytes()); - append_to_header(headers, &X_FORWARDED_PROTO, b"https"); - append_to_header(headers, &X_FORWARDED_PORT, port.to_string().as_bytes()); - - // Add this request to the telemetry for the host - counter!(TELEMETRY_COUNTER_HTTP_REQUESTS, TELEMETRY_KEY_HOSTNAME => host.clone()).increment(1); - - let host_clone = host.clone(); - let http_data = handler.http_data(); - let request_host = http_data - .as_ref() - .and_then(|data| data.host.as_deref()) - .unwrap_or(host_clone.as_str()); - - let key = KeepaliveAlias(host.clone(), ip, None); - handle_http11_request( - request, - tcp_address, - proxy_data, - handler, - request_host, - key, - timer, - ) - .await -} - pub(crate) async fn handle_http11_request( mut request: Request, tcp_address: SocketAddr, diff --git a/src/http/http2.rs b/src/http/http2.rs index 1ff67e1..0912fae 100644 --- a/src/http/http2.rs +++ b/src/http/http2.rs @@ -4,131 +4,20 @@ use crate::{ connection_handler::ConnectionHandler, connections::ConnectionGetByHttpHost, http::{ - ArcProxyData, HttpError, HttpLog, Protocol, ProxyData, ProxyResponse, ProxyType, - TimedResponse, X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PORT, X_FORWARDED_PROTO, - append_to_header, http_log, http11::handle_http11_request, proxy_handler_inner, + ArcProxyData, HttpError, HttpLog, ProxyData, ProxyResponse, ProxyType, TimedResponse, + http_log, }, keepalive::KeepaliveAlias, - telemetry::{TELEMETRY_COUNTER_HTTP_REQUESTS, TELEMETRY_KEY_HOSTNAME}, }; -use axum::{body::Body as AxumBody, response::IntoResponse}; -use http::{Uri, Version, header::CONNECTION, uri::Authority}; -use hyper::{Request, Response, StatusCode, body::Body}; +use http::{Uri, header::CONNECTION, uri::Authority}; +use hyper::{Request, StatusCode, body::Body}; use hyper_util::rt::{TokioExecutor, TokioIo}; -use metrics::counter; use tokio::{ io::{AsyncRead, AsyncWrite}, time::timeout, }; -#[cfg_attr( - not(coverage_nightly), - tracing::instrument(skip(proxy_data, handler), level = "debug") -)] -pub(crate) async fn https_2_handler( - request: Request, - tcp_address: SocketAddr, - proxy_data: Arc>, - handler: Arc, - host: String, -) -> color_eyre::Result> -where - M: ConnectionGetByHttpHost> + Send + Sync + 'static, - H: ConnectionHandler + Send + Sync + 'static, - T: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static, - B: Body + Debug + Send + Unpin + 'static, - ::Data: Send + Sync + 'static, - ::Error: Error + Send + Sync + 'static, -{ - match https_2_handler_inner(request, tcp_address, proxy_data, handler, host).await { - Ok(response) => Ok(match response { - ProxyResponse::Axum(response) => response, - ProxyResponse::Proxy(response) => response.into_response(), - }), - Err(error) => Ok(error.into_response()), - } -} - -#[inline] -async fn https_2_handler_inner( - mut request: Request, - tcp_address: SocketAddr, - proxy_data: Arc>, - handler: Arc, - host: String, -) -> Result -where - M: ConnectionGetByHttpHost> + Send + Sync + 'static, - H: ConnectionHandler + Send + Sync + 'static, - T: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static, - B: Body + Debug + Send + Unpin + 'static, - ::Data: Send + Sync + 'static, - ::Error: Error + Send + Sync + 'static, -{ - let timer = Instant::now(); - let Some(host_uri) = request.uri().host() else { - return Err(HttpError::MissingHost); - }; - if host != host_uri { - // Fallback to legacy behavior - return proxy_handler_inner(request, tcp_address, None, proxy_data).await; - }; - - let ip = tcp_address.ip().to_canonical(); - - // Read protocol information for X-Forwarded headers - let Protocol::Https { port } = proxy_data.protocol else { - unreachable!("HTTPS-only handler"); - }; - - // Add proxied info to the proper headers, but don't overwrite any existing proxy headers - let headers = request.headers_mut(); - append_to_header(headers, &X_FORWARDED_FOR, ip.to_string().as_bytes()); - append_to_header(headers, &X_FORWARDED_HOST, host.as_bytes()); - append_to_header(headers, &X_FORWARDED_PROTO, b"https"); - append_to_header(headers, &X_FORWARDED_PORT, port.to_string().as_bytes()); - - // Add this request to the telemetry for the host - counter!(TELEMETRY_COUNTER_HTTP_REQUESTS, TELEMETRY_KEY_HOSTNAME => host.clone()).increment(1); - - let host_clone = host.clone(); - let http_data = handler.http_data(); - let request_host = http_data - .as_ref() - .and_then(|data| data.host.as_deref()) - .unwrap_or(host_clone.as_str()); - - let key = KeepaliveAlias(host.clone(), ip, None); - match request.version() { - Version::HTTP_11 => { - handle_http11_request( - request, - tcp_address, - proxy_data, - handler, - request_host, - key, - timer, - ) - .await - } - Version::HTTP_2 => { - handle_http2_request( - request, - tcp_address, - proxy_data, - handler, - request_host, - key, - timer, - ) - .await - } - _ => panic!(), - } -} - pub(crate) async fn handle_http2_request( mut request: Request, tcp_address: SocketAddr,