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:
2026-03-10 23:38:20 +00:00
parent bc82ca2961
commit 2660ee974c
3 changed files with 41 additions and 1 deletions

View File

@@ -365,6 +365,9 @@ fn run_serve(upgrade: bool) -> Result<()> {
rate_limiter,
compiled_rewrites,
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);

View File

@@ -43,6 +43,8 @@ pub struct SunbeamProxy {
pub compiled_rewrites: Vec<(String, Vec<CompiledRewrite>)>,
/// Shared reqwest client for auth subrequests.
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 {
@@ -278,6 +280,14 @@ impl ProxyHttp for SunbeamProxy {
// - "scanner" log = traffic that passed DDoS (rate-limit training 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.
if let Some(detector) = &self.ddos_detector {
if let Some(ip) = extract_client_ip(session) {
@@ -1219,6 +1229,33 @@ mod tests {
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]
fn test_compile_rewrites_valid() {
let routes = vec![RouteConfig {

View File

@@ -108,7 +108,7 @@ fn start_proxy_once(backend_port: u16) {
}];
let acme_routes: AcmeRoutes = Arc::new(RwLock::new(HashMap::new()));
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 {
upgrade: false,