diff --git a/Cargo.lock b/Cargo.lock index 71e7a490..d9a6a7f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,16 +538,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "assert-json-diff" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "astral-tokio-tar" version = "0.5.6" @@ -2940,24 +2930,6 @@ dependencies = [ "sqlparser", ] -[[package]] -name = "deadpool" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" -dependencies = [ - "deadpool-runtime", - "lazy_static", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" - [[package]] name = "debugid" version = "0.8.0" @@ -8256,18 +8228,15 @@ dependencies = [ name = "rustfs-trusted-proxies" version = "0.0.5" dependencies = [ - "anyhow", "async-trait", "axum", "chrono", "dotenvy", "http 1.4.0", - "http-body-util", "ipnetwork", "lazy_static", "metrics", "moka", - "parking_lot", "regex", "reqwest", "rustfs-utils", @@ -8280,7 +8249,6 @@ dependencies = [ "tracing", "tracing-subscriber", "uuid", - "wiremock", ] [[package]] @@ -11048,29 +11016,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "wiremock" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" -dependencies = [ - "assert-json-diff", - "base64", - "deadpool", - "futures", - "http 1.4.0", - "http-body-util", - "hyper", - "hyper-util", - "log", - "once_cell", - "regex", - "serde", - "serde_json", - "tokio", - "url", -] - [[package]] name = "wit-bindgen" version = "0.46.0" diff --git a/crates/trusted-proxies/Cargo.toml b/crates/trusted-proxies/Cargo.toml index ff6f8ea2..e2b55601 100644 --- a/crates/trusted-proxies/Cargo.toml +++ b/crates/trusted-proxies/Cargo.toml @@ -25,7 +25,6 @@ keywords = ["trusted-proxies", "network-security", "rustfs", "proxy-management"] categories = ["network-programming", "security", "web-programming"] [dependencies] -anyhow = { workspace = true } async-trait = { workspace = true } axum = { workspace = true } chrono = { workspace = true } @@ -43,7 +42,6 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "ti tower = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -parking_lot = { workspace = true } uuid = { workspace = true, features = ["v4"] } regex = { workspace = true } lazy_static = { workspace = true } @@ -52,8 +50,6 @@ dotenvy = "0.15.7" [dev-dependencies] tokio = { workspace = true, features = ["full", "test-util"] } tower = { workspace = true, features = ["util"] } -http-body-util = "0.1" -wiremock = "0.6" [lints] workspace = true diff --git a/crates/trusted-proxies/src/api/handlers.rs b/crates/trusted-proxies/src/api/handlers.rs index 41506a8d..327d9ff4 100644 --- a/crates/trusted-proxies/src/api/handlers.rs +++ b/crates/trusted-proxies/src/api/handlers.rs @@ -25,6 +25,7 @@ use axum::{ use serde_json::{Value, json}; /// Health check endpoint to verify service availability. +#[allow(dead_code)] pub async fn health_check() -> impl IntoResponse { Json(json!({ "status": "healthy", @@ -35,6 +36,7 @@ pub async fn health_check() -> impl IntoResponse { } /// Returns the current application configuration. +#[allow(dead_code)] pub async fn show_config(State(state): State) -> Result, AppError> { let config = &state.config; @@ -66,6 +68,7 @@ pub async fn show_config(State(state): State) -> Result, A } /// Returns information about the client as identified by the trusted proxy middleware. +#[allow(dead_code)] pub async fn client_info(State(_state): State, req: Request) -> impl IntoResponse { // Retrieve the verified client information from the request extensions. let client_info = req.extensions().get::(); @@ -101,6 +104,7 @@ pub async fn client_info(State(_state): State, req: Request) -> impl I } /// Debugging endpoint that returns all proxy-related headers received in the request. +#[allow(dead_code)] pub async fn proxy_test(req: Request) -> Json { // Collect all headers related to proxying. let headers: Vec<(String, String)> = req @@ -130,6 +134,7 @@ pub async fn proxy_test(req: Request) -> Json { } /// Endpoint for retrieving Prometheus metrics. +#[allow(dead_code)] pub async fn metrics(State(state): State) -> impl IntoResponse { if !state.config.monitoring.metrics_enabled { return (StatusCode::NOT_FOUND, "Metrics are not enabled").into_response(); diff --git a/crates/trusted-proxies/src/cloud/detector.rs b/crates/trusted-proxies/src/cloud/detector.rs index d0eaafdd..57297b49 100644 --- a/crates/trusted-proxies/src/cloud/detector.rs +++ b/crates/trusted-proxies/src/cloud/detector.rs @@ -21,7 +21,7 @@ use tracing::{debug, info, warn}; use crate::error::AppError; /// Supported cloud providers. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CloudProvider { /// Amazon Web Services Aws, @@ -161,8 +161,8 @@ impl CloudDetector { return None; } - if let Some(provider) = self.forced_provider { - return Some(provider); + if let Some(provider) = self.forced_provider.as_ref() { + return Some(provider.clone()); } CloudProvider::detect_from_env() @@ -180,17 +180,17 @@ impl CloudDetector { match provider { Some(CloudProvider::Aws) => { info!("Detected AWS environment, fetching metadata"); - let fetcher = crate::cloud::metadata::AwsMetadataFetcher::new(); + let fetcher = crate::cloud::metadata::AwsMetadataFetcher::new(self.timeout); fetcher.fetch_trusted_proxy_ranges().await } Some(CloudProvider::Azure) => { info!("Detected Azure environment, fetching metadata"); - let fetcher = crate::cloud::metadata::AzureMetadataFetcher::new(); + let fetcher = crate::cloud::metadata::AzureMetadataFetcher::new(self.timeout); fetcher.fetch_trusted_proxy_ranges().await } Some(CloudProvider::Gcp) => { info!("Detected GCP environment, fetching metadata"); - let fetcher = crate::cloud::metadata::GcpMetadataFetcher::new(); + let fetcher = crate::cloud::metadata::GcpMetadataFetcher::new(self.timeout); fetcher.fetch_trusted_proxy_ranges().await } Some(CloudProvider::Cloudflare) => { @@ -221,9 +221,9 @@ impl CloudDetector { } let providers: Vec> = vec![ - Box::new(crate::cloud::metadata::AwsMetadataFetcher::new()), - Box::new(crate::cloud::metadata::AzureMetadataFetcher::new()), - Box::new(crate::cloud::metadata::GcpMetadataFetcher::new()), + Box::new(crate::cloud::metadata::AwsMetadataFetcher::new(self.timeout)), + Box::new(crate::cloud::metadata::AzureMetadataFetcher::new(self.timeout)), + Box::new(crate::cloud::metadata::GcpMetadataFetcher::new(self.timeout)), ]; for provider in providers { diff --git a/crates/trusted-proxies/src/cloud/metadata/aws.rs b/crates/trusted-proxies/src/cloud/metadata/aws.rs index a61d898c..3e89c296 100644 --- a/crates/trusted-proxies/src/cloud/metadata/aws.rs +++ b/crates/trusted-proxies/src/cloud/metadata/aws.rs @@ -32,11 +32,14 @@ pub struct AwsMetadataFetcher { impl AwsMetadataFetcher { /// Creates a new `AwsMetadataFetcher`. - pub fn new() -> Self { - let client = Client::builder() - .timeout(Duration::from_secs(2)) - .build() - .unwrap_or_else(|_| Client::new()); + /// + /// # Arguments + /// + /// * `timeout` - Duration to use for HTTP request timeouts. + /// + /// Returns a new instance of `AwsMetadataFetcher`. + pub fn new(timeout: Duration) -> Self { + let client = Client::builder().timeout(timeout).build().unwrap_or_else(|_| Client::new()); Self { client, @@ -45,6 +48,7 @@ impl AwsMetadataFetcher { } /// Retrieves an IMDSv2 token for secure metadata access. + #[allow(dead_code)] async fn get_metadata_token(&self) -> Result { let url = format!("{}/latest/api/token", self.metadata_endpoint); diff --git a/crates/trusted-proxies/src/cloud/metadata/azure.rs b/crates/trusted-proxies/src/cloud/metadata/azure.rs index 19891f79..cea1f6a2 100644 --- a/crates/trusted-proxies/src/cloud/metadata/azure.rs +++ b/crates/trusted-proxies/src/cloud/metadata/azure.rs @@ -33,11 +33,8 @@ pub struct AzureMetadataFetcher { impl AzureMetadataFetcher { /// Creates a new `AzureMetadataFetcher`. - pub fn new() -> Self { - let client = Client::builder() - .timeout(Duration::from_secs(2)) - .build() - .unwrap_or_else(|_| Client::new()); + pub fn new(timeout: Duration) -> Self { + let client = Client::builder().timeout(timeout).build().unwrap_or_else(|_| Client::new()); Self { client, diff --git a/crates/trusted-proxies/src/cloud/metadata/gcp.rs b/crates/trusted-proxies/src/cloud/metadata/gcp.rs index 749e15a6..0ff120a1 100644 --- a/crates/trusted-proxies/src/cloud/metadata/gcp.rs +++ b/crates/trusted-proxies/src/cloud/metadata/gcp.rs @@ -33,11 +33,8 @@ pub struct GcpMetadataFetcher { impl GcpMetadataFetcher { /// Creates a new `GcpMetadataFetcher`. - pub fn new() -> Self { - let client = Client::builder() - .timeout(Duration::from_secs(2)) - .build() - .unwrap_or_else(|_| Client::new()); + pub fn new(timeout: Duration) -> Self { + let client = Client::builder().timeout(timeout).build().unwrap_or_else(|_| Client::new()); Self { client, diff --git a/crates/trusted-proxies/src/logging/middleware.rs b/crates/trusted-proxies/src/logging/middleware.rs index eb4eaf3f..d0c81d6a 100644 --- a/crates/trusted-proxies/src/logging/middleware.rs +++ b/crates/trusted-proxies/src/logging/middleware.rs @@ -14,13 +14,12 @@ //! Logging middleware for the Axum web framework. +use crate::logging::Logger; use std::task::{Context, Poll}; use std::time::Instant; use tower::Service; use uuid::Uuid; -use crate::logging::Logger; - /// Tower Layer for request logging middleware. #[derive(Clone)] pub struct RequestLoggingLayer { @@ -56,6 +55,7 @@ impl Service for RequestLoggingMiddleware where S: Service + Clone + Send + 'static, S::Future: Send, + S::Error: std::error::Error + Send + Sync + 'static, { type Response = S::Response; type Error = S::Error; @@ -144,7 +144,7 @@ where self.inner.poll_ready(cx) } - fn call(&mut self, mut req: axum::extract::Request) -> Self::Future { + fn call(&mut self, req: axum::extract::Request) -> Self::Future { // Log proxy-specific details if available. let peer_addr = req.extensions().get::().copied(); let client_info = req.extensions().get::(); diff --git a/crates/trusted-proxies/src/logging/mod.rs b/crates/trusted-proxies/src/logging/mod.rs index 714c3988..e59b1772 100644 --- a/crates/trusted-proxies/src/logging/mod.rs +++ b/crates/trusted-proxies/src/logging/mod.rs @@ -93,7 +93,7 @@ impl Logger { } /// Logs an incoming HTTP request. - pub fn log_request(&self, req: &axum::http::Request, request_id: &str) { + pub fn log_request(&self, req: &http::Request, request_id: &str) { let method = req.method(); let uri = req.uri(); let version = req.version(); @@ -112,7 +112,7 @@ impl Logger { } /// Logs an outgoing HTTP response. - pub fn log_response(&self, res: &axum::http::Response, request_id: &str, duration: std::time::Duration) { + pub fn log_response(&self, res: &http::Response, request_id: &str, duration: std::time::Duration) { let status = res.status(); let version = res.version(); @@ -130,7 +130,7 @@ impl Logger { } /// Logs HTTP headers, redacting sensitive information. - fn log_headers(&self, headers: &axum::http::HeaderMap, header_type: &str) { + fn log_headers(&self, headers: &http::HeaderMap, header_type: &str) { let mut header_fields = std::collections::HashMap::new(); for (name, value) in headers { diff --git a/crates/trusted-proxies/src/middleware/service.rs b/crates/trusted-proxies/src/middleware/service.rs index c69f66a1..7401ecc1 100644 --- a/crates/trusted-proxies/src/middleware/service.rs +++ b/crates/trusted-proxies/src/middleware/service.rs @@ -29,11 +29,11 @@ use crate::proxy::{ClientInfo, ProxyValidator}; #[derive(Clone)] pub struct TrustedProxyMiddleware { /// The inner service being wrapped. - inner: S, + pub(crate) inner: S, /// The validator used to verify proxy chains. - validator: Arc, + pub(crate) validator: Arc, /// Whether the middleware is enabled. - enabled: bool, + pub(crate) enabled: bool, } impl TrustedProxyMiddleware { diff --git a/crates/trusted-proxies/src/proxy/cache.rs b/crates/trusted-proxies/src/proxy/cache.rs index 2e752d66..05c0ca2b 100644 --- a/crates/trusted-proxies/src/proxy/cache.rs +++ b/crates/trusted-proxies/src/proxy/cache.rs @@ -57,7 +57,7 @@ impl IpValidationCache { /// Clears all entries from the cache. pub async fn clear(&self) { - self.cache.invalidate_all().await; + self.cache.invalidate_all(); metrics::gauge!("proxy.cache.size").set(0.0); } diff --git a/crates/trusted-proxies/src/proxy/chain.rs b/crates/trusted-proxies/src/proxy/chain.rs index 7250a08f..4717af2a 100644 --- a/crates/trusted-proxies/src/proxy/chain.rs +++ b/crates/trusted-proxies/src/proxy/chain.rs @@ -16,7 +16,7 @@ use crate::config::{TrustedProxyConfig, ValidationMode}; use crate::error::ProxyError; -use crate::utils::ip::is_valid_ip_address; +use crate::utils::is_valid_ip_address; use axum::http::HeaderMap; use std::collections::HashSet; use std::net::IpAddr; diff --git a/crates/trusted-proxies/src/proxy/metrics.rs b/crates/trusted-proxies/src/proxy/metrics.rs index af801c41..bf723445 100644 --- a/crates/trusted-proxies/src/proxy/metrics.rs +++ b/crates/trusted-proxies/src/proxy/metrics.rs @@ -71,9 +71,9 @@ impl ProxyMetrics { counter!( "proxy_validation_attempts_total", - 1, "app" => self.app_name.clone() - ); + ) + .increment(1); } /// Records a successful validation. @@ -84,22 +84,22 @@ impl ProxyMetrics { counter!( "proxy_validation_success_total", - 1, "app" => self.app_name.clone(), "trusted" => from_trusted_proxy.to_string() - ); + ) + .increment(1); gauge!( "proxy_chain_length", - proxy_hops as f64, "app" => self.app_name.clone() - ); + ) + .set(proxy_hops as f64); histogram!( "proxy_validation_duration_seconds", - duration.as_secs_f64(), "app" => self.app_name.clone() - ); + ) + .record(duration.as_secs_f64()); } /// Records a failed validation with the specific error type. @@ -123,24 +123,24 @@ impl ProxyMetrics { counter!( "proxy_validation_failure_total", - 1, "app" => self.app_name.clone(), "error_type" => error_type - ); + ) + .increment(1); counter!( "proxy_validation_failure_by_type_total", - 1, "app" => self.app_name.clone(), "error_type" => error_type - ); + ) + .increment(1); histogram!( "proxy_validation_duration_seconds", - duration.as_secs_f64(), "app" => self.app_name.clone(), "error_type" => error_type - ); + ) + .record(duration.as_secs_f64()); } /// Records the validation mode currently in use. @@ -151,14 +151,14 @@ impl ProxyMetrics { gauge!( "proxy_validation_mode", - match mode { - ValidationMode::Lenient => 0.0, - ValidationMode::Strict => 1.0, - ValidationMode::HopByHop => 2.0, - }, "app" => self.app_name.clone(), "mode" => mode.as_str() - ); + ) + .set(match mode { + ValidationMode::Lenient => 0.0, + ValidationMode::Strict => 1.0, + ValidationMode::HopByHop => 2.0, + }); } /// Records cache performance metrics. @@ -167,9 +167,9 @@ impl ProxyMetrics { return; } - counter!("proxy_cache_hits_total", hits, "app" => self.app_name.clone()); - counter!("proxy_cache_misses_total", misses, "app" => self.app_name.clone()); - gauge!("proxy_cache_size", size as f64, "app" => self.app_name.clone()); + counter!("proxy_cache_hits_total", "app" => self.app_name.clone()).increment(hits); + counter!("proxy_cache_misses_total", "app" => self.app_name.clone()).increment(misses); + gauge!("proxy_cache_size", "app" => self.app_name.clone()).set(size as f64); } /// Prints a summary of enabled metrics to the log. diff --git a/crates/trusted-proxies/src/proxy/validator.rs b/crates/trusted-proxies/src/proxy/validator.rs index 12e4914f..bea5de13 100644 --- a/crates/trusted-proxies/src/proxy/validator.rs +++ b/crates/trusted-proxies/src/proxy/validator.rs @@ -163,9 +163,9 @@ impl ProxyValidator { // Prefer RFC 7239 "Forwarded" header if enabled, otherwise fallback to legacy headers. let client_info = if self.config.enable_rfc7239 { self.try_parse_rfc7239_headers(headers, proxy_ip) - .unwrap_or_else(|| self.parse_legacy_headers(headers, proxy_ip)) + .unwrap_or_else(|| self.parse_legacy_headers(headers)) } else { - self.parse_legacy_headers(headers, proxy_ip) + self.parse_legacy_headers(headers) }; // Analyze the integrity and continuity of the proxy chain. @@ -203,7 +203,7 @@ impl ProxyValidator { } /// Parses legacy proxy headers (X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto). - fn parse_legacy_headers(&self, headers: &HeaderMap, proxy_ip: IpAddr) -> ParsedHeaders { + fn parse_legacy_headers(&self, headers: &HeaderMap) -> ParsedHeaders { let forwarded_host = headers .get("x-forwarded-host") .and_then(|h| h.to_str().ok()) @@ -304,10 +304,6 @@ impl ProxyValidator { } Err(err) => { metrics.record_validation_failure(err, duration); - - if self.config.log_failed_validations { - warn!("Proxy validation failed: {}", err); - } } } } diff --git a/crates/trusted-proxies/tests/unit/validation_tests.rs b/crates/trusted-proxies/tests/unit/validation_tests.rs index ac9ca5c0..5567dba9 100644 --- a/crates/trusted-proxies/tests/unit/validation_tests.rs +++ b/crates/trusted-proxies/tests/unit/validation_tests.rs @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use http::HeaderMap; -use rustfs_trusted_proxies::utils::{IpUtils, ValidationUtils}; +use rustfs_trusted_proxies::utils::ValidationUtils; use std::net::IpAddr; #[test]