mirror of
https://github.com/rustfs/rustfs.git
synced 2026-05-06 22:28:16 +08:00
fix(cache): wire trusted-proxy cache and remove stale cache traces (#2581)
This commit is contained in:
@@ -237,15 +237,6 @@ println!("最大并发读: {}", config.scheduler.max_concurrent_reads);
|
|||||||
|
|
||||||
## 🔧 配置
|
## 🔧 配置
|
||||||
|
|
||||||
### 环境变量
|
|
||||||
|
|
||||||
| 变量名 | 描述 | 默认值 |
|
|
||||||
|--------|------|--------|
|
|
||||||
| `RUSTFS_CACHE_MAX_CAPACITY` | 缓存最大容量 | 10000 |
|
|
||||||
| `RUSTFS_CACHE_TTL_SECS` | 缓存 TTL 秒数 | 300 |
|
|
||||||
| `RUSTFS_CACHE_MAX_MEMORY` | 缓存最大内存 | 104857600 |
|
|
||||||
| `RUSTFS_ADAPTIVE_TTL_ENABLED` | 启用自适应 TTL | true |
|
|
||||||
|
|
||||||
### 代码配置
|
### 代码配置
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ axum = { workspace = true }
|
|||||||
http = { workspace = true }
|
http = { workspace = true }
|
||||||
ipnetwork = { workspace = true }
|
ipnetwork = { workspace = true }
|
||||||
metrics = { workspace = true }
|
metrics = { workspace = true }
|
||||||
moka = { workspace = true, features = ["future"] }
|
moka = { workspace = true, features = ["future", "sync"] }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
rustfs-config = { workspace = true }
|
rustfs-config = { workspace = true }
|
||||||
rustfs-utils = { workspace = true, features = ["net"] }
|
rustfs-utils = { workspace = true, features = ["net"] }
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pub fn init() {
|
|||||||
.expect("Trusted proxy metrics already initialized");
|
.expect("Trusted proxy metrics already initialized");
|
||||||
|
|
||||||
// Initialize the trusted proxy layer.
|
// Initialize the trusted proxy layer.
|
||||||
let layer = TrustedProxyLayer::new(config.proxy.clone(), metrics, enabled);
|
let layer = TrustedProxyLayer::with_cache_config(config.proxy.clone(), config.cache.clone(), metrics, enabled);
|
||||||
PROXY_LAYER.set(layer).expect("Trusted proxy layer already initialized");
|
PROXY_LAYER.set(layer).expect("Trusted proxy layer already initialized");
|
||||||
|
|
||||||
tracing::info!("Trusted Proxies module initialized");
|
tracing::info!("Trusted Proxies module initialized");
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower::Layer;
|
use tower::Layer;
|
||||||
|
|
||||||
use crate::ProxyMetrics;
|
|
||||||
use crate::ProxyValidator;
|
use crate::ProxyValidator;
|
||||||
use crate::TrustedProxyConfig;
|
use crate::TrustedProxyConfig;
|
||||||
use crate::TrustedProxyMiddleware;
|
use crate::TrustedProxyMiddleware;
|
||||||
|
use crate::{CacheConfig, ProxyMetrics};
|
||||||
|
|
||||||
/// Tower Layer for the trusted proxy middleware.
|
/// Tower Layer for the trusted proxy middleware.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -34,12 +34,22 @@ pub struct TrustedProxyLayer {
|
|||||||
impl TrustedProxyLayer {
|
impl TrustedProxyLayer {
|
||||||
/// Creates a new `TrustedProxyLayer`.
|
/// Creates a new `TrustedProxyLayer`.
|
||||||
pub fn new(config: TrustedProxyConfig, metrics: Option<ProxyMetrics>, enabled: bool) -> Self {
|
pub fn new(config: TrustedProxyConfig, metrics: Option<ProxyMetrics>, enabled: bool) -> Self {
|
||||||
let validator = ProxyValidator::new(config, metrics);
|
Self::with_cache_config(config, CacheConfig::default(), metrics, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
/// Creates a new `TrustedProxyLayer` with explicit cache configuration.
|
||||||
validator: Arc::new(validator),
|
pub fn with_cache_config(
|
||||||
enabled,
|
config: TrustedProxyConfig,
|
||||||
|
cache_config: CacheConfig,
|
||||||
|
metrics: Option<ProxyMetrics>,
|
||||||
|
enabled: bool,
|
||||||
|
) -> Self {
|
||||||
|
let validator = Arc::new(ProxyValidator::with_cache_config(config, cache_config.clone(), metrics));
|
||||||
|
if enabled {
|
||||||
|
validator.spawn_cache_maintenance_task(cache_config.cleanup_interval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self { validator, enabled }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `TrustedProxyLayer` that is enabled by default.
|
/// Creates a new `TrustedProxyLayer` that is enabled by default.
|
||||||
|
|||||||
@@ -14,62 +14,114 @@
|
|||||||
|
|
||||||
//! High-performance cache implementation for proxy validation results using Moka.
|
//! High-performance cache implementation for proxy validation results using Moka.
|
||||||
|
|
||||||
use moka::future::Cache;
|
use moka::sync::Cache;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::ProxyMetrics;
|
||||||
|
|
||||||
/// Cache for storing IP validation results.
|
/// Cache for storing IP validation results.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IpValidationCache {
|
pub struct IpValidationCache {
|
||||||
/// The underlying Moka cache.
|
/// The underlying Moka cache.
|
||||||
cache: Cache<IpAddr, bool>,
|
cache: Cache<IpAddr, bool>,
|
||||||
|
/// Configured capacity.
|
||||||
|
capacity: usize,
|
||||||
/// Whether the cache is enabled.
|
/// Whether the cache is enabled.
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
|
/// Optional metrics collector for cache activity.
|
||||||
|
metrics: Option<ProxyMetrics>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IpValidationCache {
|
impl IpValidationCache {
|
||||||
/// Creates a new `IpValidationCache` using Moka.
|
/// Creates a new `IpValidationCache` using Moka.
|
||||||
pub fn new(capacity: usize, ttl: Duration, enabled: bool) -> Self {
|
pub fn new(capacity: usize, ttl: Duration, enabled: bool, metrics: Option<ProxyMetrics>) -> Self {
|
||||||
let cache = Cache::builder().max_capacity(capacity as u64).time_to_live(ttl).build();
|
let cache = Cache::builder().max_capacity(capacity as u64).time_to_live(ttl).build();
|
||||||
|
let this = Self {
|
||||||
Self { cache, enabled }
|
cache,
|
||||||
|
capacity,
|
||||||
|
enabled,
|
||||||
|
metrics,
|
||||||
|
};
|
||||||
|
this.update_cache_size_metric();
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an IP is trusted, using the cache if available.
|
/// Checks if an IP is trusted, using the cache if available.
|
||||||
pub async fn is_trusted(&self, ip: &IpAddr, validator: impl FnOnce(&IpAddr) -> bool) -> bool {
|
pub fn is_trusted(&self, ip: &IpAddr, validator: impl FnOnce(&IpAddr) -> bool) -> bool {
|
||||||
if !self.enabled {
|
if !self.enabled {
|
||||||
return validator(ip);
|
return validator(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to get the result from cache.
|
// Attempt to get the result from cache.
|
||||||
if let Some(is_trusted) = self.cache.get(ip).await {
|
if let Some(is_trusted) = self.cache.get(ip) {
|
||||||
metrics::counter!("rustfs_trusted_proxy_cache_hits").increment(1);
|
self.record_cache_hit();
|
||||||
return is_trusted;
|
return is_trusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss: perform validation and update cache.
|
// Cache miss: perform validation. Only positive trust decisions are cached
|
||||||
metrics::counter!("rustfs_trusted_proxy_cache_misses").increment(1);
|
// to avoid polluting the cache with one-off untrusted client IPs.
|
||||||
|
self.record_cache_miss();
|
||||||
let is_trusted = validator(ip);
|
let is_trusted = validator(ip);
|
||||||
self.cache.insert(*ip, is_trusted).await;
|
if is_trusted {
|
||||||
|
self.cache.insert(*ip, is_trusted);
|
||||||
|
self.update_cache_size_metric();
|
||||||
|
}
|
||||||
|
|
||||||
is_trusted
|
is_trusted
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears all entries from the cache.
|
/// Clears all entries from the cache.
|
||||||
pub async fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.cache.invalidate_all();
|
self.cache.invalidate_all();
|
||||||
metrics::gauge!("rustfs_trusted_proxy_cache_size").set(0.0);
|
self.cache.run_pending_tasks();
|
||||||
|
self.update_cache_size_metric();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs pending cache maintenance tasks and refreshes size metrics.
|
||||||
|
pub fn run_maintenance(&self) {
|
||||||
|
if !self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.run_pending_tasks();
|
||||||
|
self.update_cache_size_metric();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns statistics about the current state of the cache.
|
/// Returns statistics about the current state of the cache.
|
||||||
pub fn stats(&self) -> CacheStats {
|
pub fn stats(&self) -> CacheStats {
|
||||||
|
if self.enabled {
|
||||||
|
self.cache.run_pending_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
let entry_count = self.cache.entry_count();
|
let entry_count = self.cache.entry_count();
|
||||||
|
|
||||||
CacheStats {
|
CacheStats {
|
||||||
size: entry_count as usize,
|
size: entry_count as usize,
|
||||||
// Moka doesn't expose max_capacity directly in a simple way after build,
|
capacity: self.capacity,
|
||||||
// but we can track it if needed.
|
}
|
||||||
capacity: 0,
|
}
|
||||||
|
|
||||||
|
/// Returns whether the cache is enabled.
|
||||||
|
pub fn is_enabled(&self) -> bool {
|
||||||
|
self.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_cache_hit(&self) {
|
||||||
|
if let Some(metrics) = &self.metrics {
|
||||||
|
metrics.record_cache_hit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_cache_miss(&self) {
|
||||||
|
if let Some(metrics) = &self.metrics {
|
||||||
|
metrics.record_cache_miss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_cache_size_metric(&self) {
|
||||||
|
if let Some(metrics) = &self.metrics {
|
||||||
|
metrics.set_cache_size(self.cache.entry_count() as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ impl ProxyChainAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an IP address is trusted based on the configuration.
|
/// Checks if an IP address is trusted based on the configuration.
|
||||||
fn is_ip_trusted(&self, ip: &IpAddr) -> bool {
|
pub(crate) fn is_ip_trusted(&self, ip: &IpAddr) -> bool {
|
||||||
if self.trusted_ip_cache.contains(ip) {
|
if self.trusted_ip_cache.contains(ip) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,33 @@ impl ProxyMetrics {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Records a cache hit.
|
||||||
|
pub fn record_cache_hit(&self) {
|
||||||
|
if !self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter!("rustfs_trusted_proxy_cache_hits_total", "app" => self.app_name.clone()).increment(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records a cache miss.
|
||||||
|
pub fn record_cache_miss(&self) {
|
||||||
|
if !self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter!("rustfs_trusted_proxy_cache_misses_total", "app" => self.app_name.clone()).increment(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates only the cache size gauge.
|
||||||
|
pub fn set_cache_size(&self, size: usize) {
|
||||||
|
if !self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gauge!("rustfs_trusted_proxy_cache_size", "app" => self.app_name.clone()).set(size as f64);
|
||||||
|
}
|
||||||
|
|
||||||
/// Records cache performance metrics.
|
/// Records cache performance metrics.
|
||||||
pub fn record_cache_metrics(&self, hits: u64, misses: u64, size: usize) {
|
pub fn record_cache_metrics(&self, hits: u64, misses: u64, size: usize) {
|
||||||
if !self.enabled {
|
if !self.enabled {
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::time::Instant;
|
use std::sync::Arc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{ProxyChainAnalyzer, ProxyError, ProxyMetrics, TrustedProxyConfig, ValidationMode};
|
use crate::{
|
||||||
|
CacheConfig, CacheStats, IpValidationCache, ProxyChainAnalyzer, ProxyError, ProxyMetrics, TrustedProxyConfig, ValidationMode,
|
||||||
|
};
|
||||||
|
|
||||||
/// Information about the client extracted from the request and proxy headers.
|
/// Information about the client extracted from the request and proxy headers.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -95,6 +98,8 @@ pub struct ProxyValidator {
|
|||||||
config: TrustedProxyConfig,
|
config: TrustedProxyConfig,
|
||||||
/// Analyzer for verifying the integrity of the proxy chain.
|
/// Analyzer for verifying the integrity of the proxy chain.
|
||||||
chain_analyzer: ProxyChainAnalyzer,
|
chain_analyzer: ProxyChainAnalyzer,
|
||||||
|
/// Cache for repeated direct-peer trusted proxy decisions.
|
||||||
|
validation_cache: Arc<IpValidationCache>,
|
||||||
/// Metrics collector for observability.
|
/// Metrics collector for observability.
|
||||||
metrics: Option<ProxyMetrics>,
|
metrics: Option<ProxyMetrics>,
|
||||||
}
|
}
|
||||||
@@ -102,11 +107,24 @@ pub struct ProxyValidator {
|
|||||||
impl ProxyValidator {
|
impl ProxyValidator {
|
||||||
/// Creates a new `ProxyValidator` with the given configuration and metrics.
|
/// Creates a new `ProxyValidator` with the given configuration and metrics.
|
||||||
pub fn new(config: TrustedProxyConfig, metrics: Option<ProxyMetrics>) -> Self {
|
pub fn new(config: TrustedProxyConfig, metrics: Option<ProxyMetrics>) -> Self {
|
||||||
|
Self::with_cache_config(config, CacheConfig::default(), metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `ProxyValidator` with explicit cache configuration.
|
||||||
|
pub fn with_cache_config(config: TrustedProxyConfig, cache_config: CacheConfig, metrics: Option<ProxyMetrics>) -> Self {
|
||||||
let chain_analyzer = ProxyChainAnalyzer::new(config.clone());
|
let chain_analyzer = ProxyChainAnalyzer::new(config.clone());
|
||||||
|
let cache_enabled = cache_config.capacity > 0 && cache_config.ttl_seconds > 0;
|
||||||
|
let validation_cache = Arc::new(IpValidationCache::new(
|
||||||
|
cache_config.capacity,
|
||||||
|
cache_config.ttl_duration(),
|
||||||
|
cache_enabled,
|
||||||
|
metrics.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
chain_analyzer,
|
chain_analyzer,
|
||||||
|
validation_cache,
|
||||||
metrics,
|
metrics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,21 +148,33 @@ impl ProxyValidator {
|
|||||||
|
|
||||||
/// Internal logic for request validation.
|
/// Internal logic for request validation.
|
||||||
fn validate_request_internal(&self, peer_addr: Option<SocketAddr>, headers: &HeaderMap) -> Result<ClientInfo, ProxyError> {
|
fn validate_request_internal(&self, peer_addr: Option<SocketAddr>, headers: &HeaderMap) -> Result<ClientInfo, ProxyError> {
|
||||||
// Fallback to unspecified address if peer address is missing.
|
let Some(peer_addr) = peer_addr else {
|
||||||
let peer_addr = peer_addr.unwrap_or_else(|| SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0));
|
debug!("SocketAddr extension is missing; skipping trusted proxy evaluation");
|
||||||
|
return Ok(ClientInfo::direct(SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer_ip = peer_addr.ip();
|
||||||
|
if peer_ip.is_unspecified() {
|
||||||
|
debug!("Peer address is unspecified; skipping trusted proxy evaluation");
|
||||||
|
return Ok(ClientInfo::direct(peer_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_trusted_proxy = self
|
||||||
|
.validation_cache
|
||||||
|
.is_trusted(&peer_ip, |ip| self.chain_analyzer.is_ip_trusted(ip));
|
||||||
|
|
||||||
// Check if the direct peer is a trusted proxy.
|
// Check if the direct peer is a trusted proxy.
|
||||||
if self.config.is_trusted(&peer_addr) {
|
if is_trusted_proxy {
|
||||||
debug!("Request received from trusted proxy: {}", peer_addr.ip());
|
debug!("Request received from trusted proxy: {}", peer_ip);
|
||||||
|
|
||||||
// Parse and validate headers from the trusted proxy.
|
// Parse and validate headers from the trusted proxy.
|
||||||
self.validate_trusted_proxy_request(&peer_addr, headers)
|
self.validate_trusted_proxy_request(&peer_addr, headers)
|
||||||
} else {
|
} else {
|
||||||
// Log a warning if the request is from a private network but not trusted.
|
// Log a warning if the request is from a private network but not trusted.
|
||||||
if self.config.is_private_network(&peer_addr.ip()) {
|
if self.config.is_private_network(&peer_ip) {
|
||||||
warn!(
|
warn!(
|
||||||
"Request from private network but not trusted: {}. This might indicate a configuration issue.",
|
"Request from private network but not trusted: {}. This might indicate a configuration issue.",
|
||||||
peer_addr.ip()
|
peer_ip
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +183,31 @@ impl ProxyValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns cache statistics for direct-peer validation decisions.
|
||||||
|
pub fn cache_stats(&self) -> CacheStats {
|
||||||
|
self.validation_cache.stats()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn spawn_cache_maintenance_task(self: &Arc<Self>, cleanup_interval: Duration) {
|
||||||
|
if cleanup_interval.is_zero() || !self.validation_cache.is_enabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(handle) = tokio::runtime::Handle::try_current() else {
|
||||||
|
tracing::debug!("No Tokio runtime available; trusted proxy cache maintenance is disabled");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache = self.validation_cache.clone();
|
||||||
|
handle.spawn(async move {
|
||||||
|
let mut interval = tokio::time::interval(cleanup_interval);
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
cache.run_maintenance();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Validates a request that originated from a trusted proxy.
|
/// Validates a request that originated from a trusted proxy.
|
||||||
fn validate_trusted_proxy_request(&self, proxy_addr: &SocketAddr, headers: &HeaderMap) -> Result<ClientInfo, ProxyError> {
|
fn validate_trusted_proxy_request(&self, proxy_addr: &SocketAddr, headers: &HeaderMap) -> Result<ClientInfo, ProxyError> {
|
||||||
let proxy_ip = proxy_addr.ip();
|
let proxy_ip = proxy_addr.ip();
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use rustfs_trusted_proxies::{ClientInfo, ProxyChainAnalyzer, ProxyValidator, TrustedProxy, TrustedProxyConfig, ValidationMode};
|
use rustfs_trusted_proxies::{
|
||||||
|
CacheConfig, ClientInfo, ProxyChainAnalyzer, ProxyValidator, TrustedProxy, TrustedProxyConfig, ValidationMode,
|
||||||
|
};
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -77,3 +79,56 @@ fn test_proxy_chain_too_long() {
|
|||||||
_ => panic!("Expected ChainTooLong error"),
|
_ => panic!("Expected ChainTooLong error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validator_caches_trusted_direct_peer_decision() {
|
||||||
|
let validator = ProxyValidator::with_cache_config(create_test_config(), CacheConfig::default(), None);
|
||||||
|
let peer_addr = Some(SocketAddr::new(IpAddr::from_str("192.168.1.100").unwrap(), 8080));
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
|
||||||
|
assert_eq!(validator.cache_stats().size, 0);
|
||||||
|
|
||||||
|
let first = validator.validate_request(peer_addr, &headers).unwrap();
|
||||||
|
assert!(first.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 1);
|
||||||
|
|
||||||
|
let second = validator.validate_request(peer_addr, &headers).unwrap();
|
||||||
|
assert!(second.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validator_caches_untrusted_direct_peer_decision() {
|
||||||
|
let validator = ProxyValidator::with_cache_config(create_test_config(), CacheConfig::default(), None);
|
||||||
|
let peer_addr = Some(SocketAddr::new(IpAddr::from_str("203.0.113.8").unwrap(), 8080));
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
|
||||||
|
let first = validator.validate_request(peer_addr, &headers).unwrap();
|
||||||
|
assert!(!first.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 0);
|
||||||
|
|
||||||
|
let second = validator.validate_request(peer_addr, &headers).unwrap();
|
||||||
|
assert!(!second.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validator_skips_cache_when_peer_addr_is_missing() {
|
||||||
|
let validator = ProxyValidator::with_cache_config(create_test_config(), CacheConfig::default(), None);
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
|
||||||
|
let client_info = validator.validate_request(None, &headers).unwrap();
|
||||||
|
assert!(!client_info.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validator_skips_cache_for_unspecified_peer_addr() {
|
||||||
|
let validator = ProxyValidator::with_cache_config(create_test_config(), CacheConfig::default(), None);
|
||||||
|
let peer_addr = Some(SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0));
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
|
||||||
|
let client_info = validator.validate_request(peer_addr, &headers).unwrap();
|
||||||
|
assert!(!client_info.is_from_trusted_proxy);
|
||||||
|
assert_eq!(validator.cache_stats().size, 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ services:
|
|||||||
- RUSTFS_SECRET_KEY=rustfsadmin # CHANGEME
|
- RUSTFS_SECRET_KEY=rustfsadmin # CHANGEME
|
||||||
- RUSTFS_OBS_LOGGER_LEVEL=info
|
- RUSTFS_OBS_LOGGER_LEVEL=info
|
||||||
- RUSTFS_TLS_PATH=/opt/tls
|
- RUSTFS_TLS_PATH=/opt/tls
|
||||||
# Object Cache
|
|
||||||
- RUSTFS_OBJECT_CACHE_ENABLE=true
|
|
||||||
- RUSTFS_OBJECT_CACHE_TTL_SECS=300
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- rustfs_data_0:/data/rustfs0
|
- rustfs_data_0:/data/rustfs0
|
||||||
|
|||||||
@@ -2077,7 +2077,6 @@ impl DefaultObjectUsecase {
|
|||||||
opts,
|
opts,
|
||||||
} = request_context;
|
} = request_context;
|
||||||
|
|
||||||
// Try to get from cache for small, frequently accessed objects
|
|
||||||
let manager = get_concurrency_manager();
|
let manager = get_concurrency_manager();
|
||||||
|
|
||||||
let prepared_read = Self::prepare_get_object_read_execution(
|
let prepared_read = Self::prepare_get_object_read_execution(
|
||||||
|
|||||||
@@ -159,9 +159,6 @@ $env:RUSTFS_NS_SCANNER_INTERVAL = "60"
|
|||||||
$env:RUSTFS_SCANNER_ENABLED = "false"
|
$env:RUSTFS_SCANNER_ENABLED = "false"
|
||||||
$env:RUSTFS_HEAL_ENABLED = "false"
|
$env:RUSTFS_HEAL_ENABLED = "false"
|
||||||
|
|
||||||
# Object cache configuration
|
|
||||||
$env:RUSTFS_OBJECT_CACHE_ENABLE = "true"
|
|
||||||
|
|
||||||
# Profiling configuration
|
# Profiling configuration
|
||||||
$env:RUSTFS_ENABLE_PROFILING = "false"
|
$env:RUSTFS_ENABLE_PROFILING = "false"
|
||||||
|
|
||||||
|
|||||||
@@ -241,9 +241,6 @@ export RUSTFS_SCANNER_ENABLED=true
|
|||||||
|
|
||||||
export RUSTFS_HEAL_ENABLED=true
|
export RUSTFS_HEAL_ENABLED=true
|
||||||
|
|
||||||
# Object cache configuration
|
|
||||||
export RUSTFS_OBJECT_CACHE_ENABLE=true
|
|
||||||
|
|
||||||
# Profiling configuration
|
# Profiling configuration
|
||||||
export RUSTFS_ENABLE_PROFILING=false
|
export RUSTFS_ENABLE_PROFILING=false
|
||||||
# Memory profiling periodic dump
|
# Memory profiling periodic dump
|
||||||
|
|||||||
Reference in New Issue
Block a user