mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
fix(config): default trusted proxies to loopback-only (#4508)
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user