From 1c2c94d36affbb68cf3f7280a6c3927455296115 Mon Sep 17 00:00:00 2001 From: kaffa Date: Sat, 7 Feb 2026 14:32:41 +0900 Subject: [PATCH] Reduce EWMA false positives with min_pps threshold - Add min_pps (default 20) to skip anomaly detection for low-traffic IPs - Increase threshold_multiplier from 3.0 to 5.0 - Increase rate_limit_after from 1 to 3 violations - Support min_pps in SIGHUP config reload Co-Authored-By: Claude Opus 4.6 --- config/config.yaml | 5 +++-- lib/xdp_defense_daemon.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 7c8a258..bb06ee4 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -32,7 +32,7 @@ rate_limits: escalation: # Violations before escalation - rate_limit_after: 1 # violations before eBPF rate limiting kicks in + rate_limit_after: 3 # violations before eBPF rate limiting kicks in temp_block_after: 5 # violations before temporary block perm_block_after: 999999 # effectively disabled @@ -48,7 +48,8 @@ escalation: ewma: alpha: 0.3 # EWMA smoothing factor (0-1, higher = more reactive) poll_interval: 1 # seconds between rate counter polls - threshold_multiplier: 3.0 # alert when EWMA > multiplier * baseline + threshold_multiplier: 5.0 # alert when EWMA > multiplier * baseline + min_pps: 20 # ignore anomalies below this PPS (reduce false positives) ai: enabled: true diff --git a/lib/xdp_defense_daemon.py b/lib/xdp_defense_daemon.py index 90d08c7..437e1ad 100755 --- a/lib/xdp_defense_daemon.py +++ b/lib/xdp_defense_daemon.py @@ -79,7 +79,8 @@ DEFAULT_CONFIG = { 'ewma': { 'alpha': 0.3, 'poll_interval': 1, - 'threshold_multiplier': 3.0, + 'threshold_multiplier': 5.0, + 'min_pps': 20, }, 'ai': { 'enabled': True, @@ -170,9 +171,10 @@ class ViolationTracker: class EWMAAnalyzer: """Per-IP EWMA calculation for rate anomaly detection.""" - def __init__(self, alpha=0.3, threshold_multiplier=3.0): + def __init__(self, alpha=0.3, threshold_multiplier=5.0, min_pps=20): self.alpha = alpha self.threshold_multiplier = threshold_multiplier + self.min_pps = min_pps self.ewma = {} self.baseline = {} self.lock = threading.Lock() @@ -188,6 +190,9 @@ class EWMAAnalyzer: self.ewma[ip] = self.alpha * current_pps + (1 - self.alpha) * self.ewma[ip] self.baseline[ip] = 0.01 * current_pps + 0.99 * self.baseline[ip] + if current_pps < self.min_pps: + return False + base = max(self.baseline[ip], 1) if self.ewma[ip] > base * self.threshold_multiplier: return True @@ -614,7 +619,8 @@ class DDoSDaemon: self.violation_tracker = ViolationTracker(self.cfg['escalation']) self.ewma_analyzer = EWMAAnalyzer( alpha=self.cfg['ewma'].get('alpha', 0.3), - threshold_multiplier=self.cfg['ewma'].get('threshold_multiplier', 3.0), + threshold_multiplier=self.cfg['ewma'].get('threshold_multiplier', 5.0), + min_pps=self.cfg['ewma'].get('min_pps', 20), ) self.ai_detector = AIDetector(self.cfg['ai']) self.profile_manager = ProfileManager(self.cfg['rate_limits']) @@ -673,7 +679,8 @@ class DDoSDaemon: # Build all new values before swapping anything new_escalation = new_cfg['escalation'] new_alpha = new_cfg['ewma'].get('alpha', 0.3) - new_threshold = new_cfg['ewma'].get('threshold_multiplier', 3.0) + new_threshold = new_cfg['ewma'].get('threshold_multiplier', 5.0) + new_min_pps = new_cfg['ewma'].get('min_pps', 20) new_ai_cfg = new_cfg['ai'] new_rate_cfg = new_cfg['rate_limits'] new_ewma_interval = new_cfg['ewma'].get('poll_interval', 1) @@ -684,6 +691,7 @@ class DDoSDaemon: self.violation_tracker.cfg = new_escalation self.ewma_analyzer.alpha = new_alpha self.ewma_analyzer.threshold_multiplier = new_threshold + self.ewma_analyzer.min_pps = new_min_pps self.ai_detector.cfg = new_ai_cfg self.profile_manager.cfg = new_rate_cfg self._ewma_interval = new_ewma_interval