mirror of
https://github.com/DaLaw2/NetGuardia.git
synced 2026-08-24 22:20:28 +09:00
104 lines
3.5 KiB
Rust
104 lines
3.5 KiB
Rust
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use arc_swap::ArcSwap;
|
|
use macros::log;
|
|
use tokio::sync::broadcast::error::RecvError;
|
|
use tokio::sync::{broadcast, mpsc};
|
|
use tokio::time::interval;
|
|
|
|
use crate::core::correlation::botnet::BotnetDetector;
|
|
use crate::core::correlation::lateral::LateralMovementDetector;
|
|
use crate::core::correlation::scan::ScanDetector;
|
|
use crate::domain::common::config::AppConfig;
|
|
use crate::domain::common::event::DetectionEvent;
|
|
use crate::domain::detection::log::DetectionLog;
|
|
use crate::domain::detection::ml_detection::AlertMessage;
|
|
|
|
/// Coordinates cross-flow correlation detectors (botnet, scan, lateral movement).
|
|
/// Subscribes to ML AlertMessage broadcast and feeds enriched DetectionEvents
|
|
/// to the DetectionOrchestrator for dedup and SOAR routing.
|
|
pub struct CorrelationEngine {
|
|
botnet: BotnetDetector,
|
|
scan: ScanDetector,
|
|
lateral: LateralMovementDetector,
|
|
alert_rx: broadcast::Receiver<AlertMessage>,
|
|
detection_tx: mpsc::Sender<DetectionEvent>,
|
|
cleanup_interval_secs: u64,
|
|
}
|
|
|
|
impl CorrelationEngine {
|
|
pub fn new(
|
|
app_config: &Arc<ArcSwap<AppConfig>>,
|
|
alert_rx: broadcast::Receiver<AlertMessage>,
|
|
detection_tx: mpsc::Sender<DetectionEvent>,
|
|
) -> Self {
|
|
let cfg = app_config.load();
|
|
let correlation = &cfg.correlation;
|
|
let max_tracked = correlation.max_tracked_entries;
|
|
Self {
|
|
botnet: BotnetDetector::new(&correlation.botnet, max_tracked),
|
|
scan: ScanDetector::new(&correlation.scan, max_tracked),
|
|
lateral: LateralMovementDetector::new(&correlation.lateral, max_tracked),
|
|
alert_rx,
|
|
detection_tx,
|
|
cleanup_interval_secs: cfg.detection.cleanup_interval_secs,
|
|
}
|
|
}
|
|
|
|
/// Spawn the correlation engine as a background task.
|
|
pub fn start(self) {
|
|
tokio::spawn(async move { self.run().await });
|
|
}
|
|
|
|
async fn run(mut self) {
|
|
log!(DetectionLog::CorrelationEngineStarted);
|
|
|
|
let mut cleanup_interval = interval(Duration::from_secs(self.cleanup_interval_secs));
|
|
|
|
loop {
|
|
tokio::select! {
|
|
result = self.alert_rx.recv() => {
|
|
match result {
|
|
Ok(alert) => self.process_alert(&alert),
|
|
Err(RecvError::Lagged(_)) => continue,
|
|
Err(RecvError::Closed) => break,
|
|
}
|
|
}
|
|
_ = cleanup_interval.tick() => {
|
|
self.cleanup();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn process_alert(&self, alert: &AlertMessage) {
|
|
if let Some(event) = self.botnet.process(alert) {
|
|
send_or_log(&self.detection_tx, event);
|
|
}
|
|
if let Some(event) = self.scan.process(alert) {
|
|
send_or_log(&self.detection_tx, event);
|
|
}
|
|
if let Some(event) = self.lateral.process(alert) {
|
|
send_or_log(&self.detection_tx, event);
|
|
}
|
|
}
|
|
|
|
fn cleanup(&self) {
|
|
let removed = self.botnet.cleanup() + self.scan.cleanup() + self.lateral.cleanup();
|
|
if removed > 0 {
|
|
log!(DetectionLog::CorrelationCleanup(removed));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn send_or_log(tx: &mpsc::Sender<DetectionEvent>, event: DetectionEvent) {
|
|
if let Err(mpsc::error::TrySendError::Full(dropped)) = tx.try_send(event) {
|
|
log!(DetectionLog::DetectionChannelDrop(
|
|
format!("{:?}", dropped.source),
|
|
dropped.attack_type,
|
|
dropped.source_ip,
|
|
));
|
|
}
|
|
}
|