From d93151746ce74785e0d4c4e27b0a6b59a2d15ea3 Mon Sep 17 00:00:00 2001 From: groot Date: Wed, 29 Apr 2026 15:02:27 +0200 Subject: [PATCH] Bugfix Problem Metrics --- main.go | 94 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/main.go b/main.go index 79a3e72..72ac893 100644 --- a/main.go +++ b/main.go @@ -1688,6 +1688,7 @@ func main() { s.templates = tmpl go s.runSOCLoop() + go s.runBaselineLoop() go s.runDetectionLoop() mux := http.NewServeMux() @@ -1756,6 +1757,53 @@ func (s *server) runSOCLoop() { } } +func (s *server) runBaselineLoop() { + ticker := time.NewTicker(s.cfg.DetectionInterval) + defer ticker.Stop() + + s.runBaselineOnce() + + for range ticker.C { + s.runBaselineOnce() + } +} + +func (s *server) runBaselineOnce() { + if !s.cfg.BaselineEnabled { + return + } + + rules := []struct { + name string + timeout time.Duration + fn func(context.Context) error + }{ + {"baseline_anomaly", 120 * time.Second, s.detector.runBaselineAnomalyRule}, + {"baseline_update", 120 * time.Second, s.detector.runBaselineUpdate}, + } + + for _, rule := range rules { + start := time.Now() + + ctx, cancel := context.WithTimeout(context.Background(), rule.timeout) + err := rule.fn(ctx) + cancel() + + dur := time.Since(start) + + if err != nil { + s.logger.Printf("baseline rule %s error after %s: %v", rule.name, dur, err) + s.detector.ruleErrorsTotal.WithLabelValues(rule.name).Inc() + continue + } + + s.logger.Printf("baseline rule %s completed in %s", rule.name, dur) + + s.detector.ruleLastRunGauge.WithLabelValues(rule.name).Set(float64(time.Now().Unix())) + s.detector.ruleRuntimeHist.WithLabelValues(rule.name).Observe(dur.Seconds()) + } +} + func (s *server) runSOCOnce() { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -3941,17 +3989,18 @@ func (d *detector) runBaselineUpdate(ctx context.Context) error { windowEnd := time.Now().UTC() windowStart := windowEnd.Add(-d.cfg.BaselineWindow) + hour := windowEnd.Hour() + dayOfWeek := int(windowEnd.Weekday()+6) % 7 // Go: Sonntag=0, MySQL WEEKDAY: Montag=0 + rows, err := d.db.QueryContext(ctx, ` SELECT hostname, channel_name, event_id, - HOUR(ts) AS hour_of_day, - WEEKDAY(ts) AS day_of_week, COUNT(*) AS cnt FROM event_logs WHERE ts >= ? AND ts < ? -GROUP BY hostname, channel_name, event_id, HOUR(ts), WEEKDAY(ts) +GROUP BY hostname, channel_name, event_id `, windowStart, windowEnd) if err != nil { return err @@ -3964,13 +4013,14 @@ GROUP BY hostname, channel_name, event_id, HOUR(ts), WEEKDAY(ts) &b.Hostname, &b.Channel, &b.EventID, - &b.Hour, - &b.DayOfWeek, &b.Count, ); err != nil { return err } + b.Hour = hour + b.DayOfWeek = dayOfWeek + excluded, err := d.isBaselineExcluded(ctx, b.Hostname, b.Channel, b.EventID) if err != nil { return err @@ -3995,6 +4045,12 @@ GROUP BY hostname, channel_name, event_id, HOUR(ts), WEEKDAY(ts) return rows.Err() } +func mysqlWeekday(t time.Time) int { + // MySQL WEEKDAY(): Monday=0 ... Sunday=6 + // Go Weekday(): Sunday=0 ... Saturday=6 + return (int(t.UTC().Weekday()) + 6) % 7 +} + func (d *detector) hasConfirmedIncidentInWindow(ctx context.Context, hostname, channel string, eventID uint32, windowStart, windowEnd time.Time) (bool, error) { var count int @@ -4129,35 +4185,34 @@ func (d *detector) runBaselineAnomalyRule(ctx context.Context) error { windowEnd := time.Now().UTC() windowStart := windowEnd.Add(-d.cfg.BaselineWindow) + hour := windowEnd.Hour() + dayOfWeek := mysqlWeekday(windowEnd) + rows, err := d.db.QueryContext(ctx, ` SELECT e.hostname, e.channel_name, e.event_id, - HOUR(e.ts) AS hour_of_day, - WEEKDAY(e.ts) AS day_of_week, COUNT(*) AS cnt, - b.avg_count, - b.stddev_count, - b.sample_count + COALESCE(b.avg_count, 0), + COALESCE(b.stddev_count, 0), + COALESCE(b.sample_count, 0) FROM event_logs e -JOIN baseline_event_stats b +LEFT JOIN baseline_event_stats b ON b.hostname = e.hostname AND b.channel_name = e.channel_name AND b.event_id = e.event_id - AND b.hour_of_day = HOUR(e.ts) - AND b.day_of_week = WEEKDAY(e.ts) + AND b.hour_of_day = ? + AND b.day_of_week = ? WHERE e.ts >= ? AND e.ts < ? GROUP BY e.hostname, e.channel_name, e.event_id, - HOUR(e.ts), - WEEKDAY(e.ts), b.avg_count, b.stddev_count, b.sample_count -`, windowStart, windowEnd) +`, hour, dayOfWeek, windowStart, windowEnd) if err != nil { return err } @@ -4167,8 +4222,6 @@ GROUP BY var host string var channel string var eventID uint32 - var hour int - var dayOfWeek int var count int var avg float64 var stddev float64 @@ -4178,8 +4231,6 @@ GROUP BY &host, &channel, &eventID, - &hour, - &dayOfWeek, &count, &avg, &stddev, @@ -4769,9 +4820,6 @@ func (s *server) runDetectionsOnce() { {"new_source_ip_for_user", s.detector.runNewSourceIPForUserRule}, {"dynamic_rules", s.detector.runDynamicRules}, - {"baseline_anomaly", s.detector.runBaselineAnomalyRule}, - {"baseline_update", s.detector.runBaselineUpdate}, - {"ueba_admin_new_host", s.detector.runAdminNewHostRule}, {"ueba_offhours_login", s.detector.runOffHoursLoginRule}, {"ueba_first_privileged_use", s.detector.runFirstTimePrivilegedRule},