From d1245ca33cbcee6074760d7840fb421d90aab2b5 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Thu, 9 Jul 2026 01:06:11 +0800 Subject: [PATCH] fix(config): default trusted proxies to loopback-only (#4508) --- crates/config/src/constants/proxy.rs | 6 +- crates/trusted-proxies/README.md | 2 +- .../tests/unit/config_tests.rs | 71 +++++++++++++++++-- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/crates/config/src/constants/proxy.rs b/crates/config/src/constants/proxy.rs index 647f19482..a12f107b9 100644 --- a/crates/config/src/constants/proxy.rs +++ b/crates/config/src/constants/proxy.rs @@ -53,8 +53,10 @@ pub const DEFAULT_TRUSTED_PROXY_LOG_FAILED_VALIDATIONS: bool = true; // ==================== Trusted Proxy Networks ==================== /// Environment variable for the list of trusted proxy networks (comma-separated IP/CIDR). pub const ENV_TRUSTED_PROXY_PROXIES: &str = "RUSTFS_TRUSTED_PROXY_NETWORKS"; -/// Default trusted networks include localhost and common private ranges. -pub const DEFAULT_TRUSTED_PROXY_PROXIES: &str = "127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8"; +/// Default trusted networks are loopback-only. Private/RFC1918 ranges are not +/// trusted by default: an operator must explicitly add the specific proxy +/// addresses in front of this server to have forwarding headers honored. +pub const DEFAULT_TRUSTED_PROXY_PROXIES: &str = "127.0.0.1,::1"; /// Environment variable for additional trusted proxy networks (production specific). pub const ENV_TRUSTED_PROXY_EXTRA_PROXIES: &str = "RUSTFS_TRUSTED_PROXY_EXTRA_NETWORKS"; diff --git a/crates/trusted-proxies/README.md b/crates/trusted-proxies/README.md index e3a3f39d4..c6594383d 100644 --- a/crates/trusted-proxies/README.md +++ b/crates/trusted-proxies/README.md @@ -32,7 +32,7 @@ The module is configured primarily through environment variables: | `RUSTFS_TRUSTED_PROXY_ENABLED` | `true` | Enable the trusted proxy middleware | | `RUSTFS_TRUSTED_PROXY_IMPLEMENTATION` | `simple` | Select `simple` or `legacy` implementation | | `RUSTFS_TRUSTED_PROXY_VALIDATION_MODE` | `hop_by_hop` | Validation strategy (`strict`, `lenient`, `hop_by_hop`) | -| `RUSTFS_TRUSTED_PROXY_NETWORKS` | `127.0.0.1,::1,...` | Comma-separated list of trusted CIDR ranges | +| `RUSTFS_TRUSTED_PROXY_NETWORKS` | `127.0.0.1,::1` | Comma-separated list of trusted CIDR ranges (loopback-only by default) | | `RUSTFS_TRUSTED_PROXY_MAX_HOPS` | `10` | Maximum allowed proxy hops | | `RUSTFS_TRUSTED_PROXY_CACHE_CAPACITY` | `10000` | Max entries in the validation cache | | `RUSTFS_TRUSTED_PROXY_METRICS_ENABLED` | `true` | Enable Prometheus metrics collection | diff --git a/crates/trusted-proxies/tests/unit/config_tests.rs b/crates/trusted-proxies/tests/unit/config_tests.rs index e9d915e02..bf2688da3 100644 --- a/crates/trusted-proxies/tests/unit/config_tests.rs +++ b/crates/trusted-proxies/tests/unit/config_tests.rs @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +use axum::http::HeaderMap; use rustfs_config::{ - DEFAULT_TRUSTED_PROXY_PROXIES, ENV_TRUSTED_PROXY_MAX_HOPS, ENV_TRUSTED_PROXY_PROXIES, ENV_TRUSTED_PROXY_VALIDATION_MODE, + DEFAULT_TRUSTED_PROXY_PROXIES, ENV_TRUSTED_PROXY_EXTRA_PROXIES, ENV_TRUSTED_PROXY_IPS, ENV_TRUSTED_PROXY_MAX_HOPS, + ENV_TRUSTED_PROXY_PROXIES, ENV_TRUSTED_PROXY_VALIDATION_MODE, }; -use rustfs_trusted_proxies::{ConfigLoader, TrustedProxy, TrustedProxyConfig, ValidationMode}; +use rustfs_trusted_proxies::{ConfigLoader, ProxyValidator, TrustedProxy, TrustedProxyConfig, ValidationMode}; use serial_test::serial; -use std::net::IpAddr; +use std::net::{IpAddr, SocketAddr}; #[test] #[serial] @@ -127,8 +129,65 @@ fn test_private_network_check() { #[test] fn test_default_values() { - assert_eq!( - DEFAULT_TRUSTED_PROXY_PROXIES, - "127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8" + // The default trusted set is loopback-only. Private/RFC1918 ranges must not + // be trusted unless an operator explicitly adds them. + assert_eq!(DEFAULT_TRUSTED_PROXY_PROXIES, "127.0.0.1,::1"); +} + +#[test] +#[serial] +fn test_default_config_does_not_trust_private_peer() { + // Under the DEFAULT configuration the trusted set is loopback-only, so a + // peer from a private range must not be treated as a trusted proxy. + temp_env::with_vars_unset( + vec![ + ENV_TRUSTED_PROXY_PROXIES, + ENV_TRUSTED_PROXY_EXTRA_PROXIES, + ENV_TRUSTED_PROXY_IPS, + ], + || { + let config = ConfigLoader::from_env_or_default().proxy; + + let loopback = SocketAddr::new(IpAddr::from([127, 0, 0, 1]), 9000); + assert!(config.is_trusted(&loopback), "loopback must remain trusted by default"); + + let private_peer = SocketAddr::new(IpAddr::from([10, 1, 2, 3]), 9000); + assert!( + !config.is_trusted(&private_peer), + "private-range peer must not be trusted under the default config" + ); + }, + ); +} + +#[test] +#[serial] +fn test_default_config_ignores_spoofed_forwarded_from_private_peer() { + // A peer from a private range (10.1.2.3) sending a spoofed X-Forwarded-For + // must NOT override the socket peer, because private ranges are no longer + // trusted under the default configuration. + temp_env::with_vars_unset( + vec![ + ENV_TRUSTED_PROXY_PROXIES, + ENV_TRUSTED_PROXY_EXTRA_PROXIES, + ENV_TRUSTED_PROXY_IPS, + ], + || { + let config = ConfigLoader::from_env_or_default().proxy; + let validator = ProxyValidator::new(config, None); + + let peer_addr = Some(SocketAddr::new(IpAddr::from([10, 1, 2, 3]), 9000)); + let mut headers = HeaderMap::new(); + headers.insert("x-forwarded-for", "203.0.113.10".parse().unwrap()); + + let info = validator.validate_request(peer_addr, &headers).unwrap(); + + assert!(!info.is_from_trusted_proxy, "private peer must not be a trusted proxy by default"); + assert_eq!( + info.real_ip, + IpAddr::from([10, 1, 2, 3]), + "spoofed X-Forwarded-For must not override the socket peer" + ); + }, ); }