fix(proxy): skip detection pipeline for bypass CIDR IPs
Trusted IPs (localhost, pod network) now skip the entire DDoS/scanner/ rate-limit pipeline via early return. Fixes buildkitd pushes to Gitea being blocked by the scanner when using host networking. Signed-off-by: Sienna Meridian Satterwhite <sienna@sunbeam.pt>
This commit is contained in:
@@ -365,6 +365,9 @@ fn run_serve(upgrade: bool) -> Result<()> {
|
|||||||
rate_limiter,
|
rate_limiter,
|
||||||
compiled_rewrites,
|
compiled_rewrites,
|
||||||
http_client,
|
http_client,
|
||||||
|
pipeline_bypass_cidrs: crate::rate_limit::cidr::parse_cidrs(
|
||||||
|
&cfg.rate_limit.as_ref().map(|rl| rl.bypass_cidrs.clone()).unwrap_or_default(),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let mut svc = http_proxy_service(&server.configuration, proxy);
|
let mut svc = http_proxy_service(&server.configuration, proxy);
|
||||||
|
|
||||||
|
|||||||
37
src/proxy.rs
37
src/proxy.rs
@@ -43,6 +43,8 @@ pub struct SunbeamProxy {
|
|||||||
pub compiled_rewrites: Vec<(String, Vec<CompiledRewrite>)>,
|
pub compiled_rewrites: Vec<(String, Vec<CompiledRewrite>)>,
|
||||||
/// Shared reqwest client for auth subrequests.
|
/// Shared reqwest client for auth subrequests.
|
||||||
pub http_client: reqwest::Client,
|
pub http_client: reqwest::Client,
|
||||||
|
/// Parsed bypass CIDRs — IPs in these ranges skip the detection pipeline.
|
||||||
|
pub pipeline_bypass_cidrs: Vec<crate::rate_limit::cidr::CidrBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RequestCtx {
|
pub struct RequestCtx {
|
||||||
@@ -278,6 +280,14 @@ impl ProxyHttp for SunbeamProxy {
|
|||||||
// - "scanner" log = traffic that passed DDoS (rate-limit training data)
|
// - "scanner" log = traffic that passed DDoS (rate-limit training data)
|
||||||
// - "rate_limit" log = traffic that passed scanner (validation data)
|
// - "rate_limit" log = traffic that passed scanner (validation data)
|
||||||
|
|
||||||
|
// Skip the detection pipeline for trusted IPs (localhost, pod network).
|
||||||
|
if extract_client_ip(session)
|
||||||
|
.map(|ip| crate::rate_limit::cidr::is_bypassed(ip, &self.pipeline_bypass_cidrs))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
// DDoS detection: check the client IP against the KNN model.
|
// DDoS detection: check the client IP against the KNN model.
|
||||||
if let Some(detector) = &self.ddos_detector {
|
if let Some(detector) = &self.ddos_detector {
|
||||||
if let Some(ip) = extract_client_ip(session) {
|
if let Some(ip) = extract_client_ip(session) {
|
||||||
@@ -1219,6 +1229,33 @@ mod tests {
|
|||||||
assert!(uuid::Uuid::parse_str(&id).is_ok());
|
assert!(uuid::Uuid::parse_str(&id).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pipeline_bypass_cidrs_parsed() {
|
||||||
|
use crate::rate_limit::cidr::{parse_cidrs, is_bypassed};
|
||||||
|
let cidrs = parse_cidrs(&[
|
||||||
|
"10.42.0.0/16".into(),
|
||||||
|
"127.0.0.0/8".into(),
|
||||||
|
"::1/128".into(),
|
||||||
|
]);
|
||||||
|
// Pod network
|
||||||
|
assert!(is_bypassed("10.42.1.5".parse().unwrap(), &cidrs));
|
||||||
|
// Localhost IPv4
|
||||||
|
assert!(is_bypassed("127.0.0.1".parse().unwrap(), &cidrs));
|
||||||
|
// Localhost IPv6
|
||||||
|
assert!(is_bypassed("::1".parse().unwrap(), &cidrs));
|
||||||
|
// External IP should not be bypassed
|
||||||
|
assert!(!is_bypassed("8.8.8.8".parse().unwrap(), &cidrs));
|
||||||
|
assert!(!is_bypassed("192.168.1.1".parse().unwrap(), &cidrs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pipeline_bypass_empty_cidrs_blocks_nothing() {
|
||||||
|
use crate::rate_limit::cidr::{parse_cidrs, is_bypassed};
|
||||||
|
let cidrs = parse_cidrs(&[]);
|
||||||
|
assert!(!is_bypassed("127.0.0.1".parse().unwrap(), &cidrs));
|
||||||
|
assert!(!is_bypassed("10.42.0.1".parse().unwrap(), &cidrs));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compile_rewrites_valid() {
|
fn test_compile_rewrites_valid() {
|
||||||
let routes = vec![RouteConfig {
|
let routes = vec![RouteConfig {
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ fn start_proxy_once(backend_port: u16) {
|
|||||||
}];
|
}];
|
||||||
let acme_routes: AcmeRoutes = Arc::new(RwLock::new(HashMap::new()));
|
let acme_routes: AcmeRoutes = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let compiled_rewrites = SunbeamProxy::compile_rewrites(&routes);
|
let compiled_rewrites = SunbeamProxy::compile_rewrites(&routes);
|
||||||
let proxy = SunbeamProxy { routes, acme_routes, ddos_detector: None, scanner_detector: None, bot_allowlist: None, rate_limiter: None, compiled_rewrites, http_client: reqwest::Client::new() };
|
let proxy = SunbeamProxy { routes, acme_routes, ddos_detector: None, scanner_detector: None, bot_allowlist: None, rate_limiter: None, compiled_rewrites, http_client: reqwest::Client::new(), pipeline_bypass_cidrs: vec![] };
|
||||||
|
|
||||||
let opt = Opt {
|
let opt = Opt {
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user