From 1f9841b16904f5c4d8eea6a74ca45f21e6d0b7f1 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 2 May 2026 18:20:32 +0000 Subject: [PATCH] deploy/mariadb/init/001-schema.sql aktualisiert --- deploy/mariadb/init/001-schema.sql | 991 ++++++++++++++++++----------- 1 file changed, 616 insertions(+), 375 deletions(-) diff --git a/deploy/mariadb/init/001-schema.sql b/deploy/mariadb/init/001-schema.sql index b623b82..99ed382 100644 --- a/deploy/mariadb/init/001-schema.sql +++ b/deploy/mariadb/init/001-schema.sql @@ -1,120 +1,637 @@ -CREATE DATABASE IF NOT EXISTS eventcollector - CHARACTER SET utf8mb4 - COLLATE utf8mb4_unicode_ci; +-- SIEM-lite vollständiges Datenbankschema +-- Ziel: schneller Test-Restore mit partitionierten Tabellen, Buckets, UEBA, Baseline, SOC und UI-Bewertungen. +-- Engine: MySQL 8.x / MariaDB mit InnoDB und RANGE COLUMNS Partitionierung. +-- +-- WICHTIG: +-- 1. Dieses Script löscht bestehende Tabellen. +-- 2. Partitionierung erfolgt auf UTC-DATETIME-Spalten. +-- 3. event_logs und event_logs_raw starten mit pmax und werden danach per Stored Procedure +-- mit 3-Stunden-Partitionen von -6h bis +24h vorbereitet. +-- 4. Im Go-Code sollte der Partition-Maintenance-Job weiterlaufen und Partitionen fortlaufend erzeugen. +-- 5. Für time.ParseDuration in Go: 30 Tage als 720h konfigurieren, nicht als 30d. -USE eventcollector; +SET NAMES utf8mb4; +SET time_zone = '+00:00'; -CREATE TABLE IF NOT EXISTS agents ( +SET FOREIGN_KEY_CHECKS = 0; + +DROP TABLE IF EXISTS event_count_buckets; +DROP TABLE IF EXISTS ueba_context_buckets; +DROP TABLE IF EXISTS host_risk_scores; +DROP TABLE IF EXISTS baseline_exclusions; +DROP TABLE IF EXISTS baseline_event_stats; +DROP TABLE IF EXISTS detection_suppressions; +DROP TABLE IF EXISTS detections; +DROP TABLE IF EXISTS detection_rules; +DROP TABLE IF EXISTS user_privilege_baseline; +DROP TABLE IF EXISTS user_source_ip_seen; +DROP TABLE IF EXISTS ueba_user_baseline; +DROP TABLE IF EXISTS privileged_users; +DROP TABLE IF EXISTS event_logs_raw; +DROP TABLE IF EXISTS event_logs; +DROP TABLE IF EXISTS agents; + +SET FOREIGN_KEY_CHECKS = 1; + +-- --------------------------------------------------------------------- +-- Agents +-- --------------------------------------------------------------------- + +CREATE TABLE agents ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, hostname VARCHAR(255) NOT NULL, api_key_hash CHAR(64) NOT NULL, - first_seen DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - last_seen DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + first_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + last_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), last_ip VARCHAR(64) NOT NULL DEFAULT '', is_enabled TINYINT(1) NOT NULL DEFAULT 1, + PRIMARY KEY (id), - UNIQUE KEY ux_agents_hostname (hostname), - KEY ix_agents_last_seen (last_seen) + UNIQUE KEY uq_agents_hostname (hostname), + KEY idx_agents_enabled_last_seen (is_enabled, last_seen), + KEY idx_agents_last_seen (last_seen) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -CREATE TABLE IF NOT EXISTS event_logs ( +-- --------------------------------------------------------------------- +-- Event Logs, normalisierte Haupttabelle +-- --------------------------------------------------------------------- + +CREATE TABLE event_logs ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + ts DATETIME(6) NOT NULL, agent_id BIGINT UNSIGNED NOT NULL, hostname VARCHAR(255) NOT NULL, channel_name VARCHAR(128) NOT NULL, event_id INT UNSIGNED NOT NULL, - source VARCHAR(255) NOT NULL, + source VARCHAR(255) NOT NULL DEFAULT '', + computer VARCHAR(255) NOT NULL DEFAULT '', provider_name VARCHAR(255) NOT NULL DEFAULT '', + level_value INT UNSIGNED NOT NULL DEFAULT 0, task_value INT UNSIGNED NOT NULL DEFAULT 0, opcode_value INT UNSIGNED NOT NULL DEFAULT 0, keywords VARCHAR(255) NOT NULL DEFAULT '', + target_user VARCHAR(255) NOT NULL DEFAULT '', + target_user_norm VARCHAR(255) NOT NULL DEFAULT '', target_domain VARCHAR(255) NOT NULL DEFAULT '', + subject_user VARCHAR(255) NOT NULL DEFAULT '', + subject_user_norm VARCHAR(255) NOT NULL DEFAULT '', subject_domain VARCHAR(255) NOT NULL DEFAULT '', + workstation VARCHAR(255) NOT NULL DEFAULT '', src_ip VARCHAR(64) NOT NULL DEFAULT '', src_port VARCHAR(32) NOT NULL DEFAULT '', logon_type VARCHAR(32) NOT NULL DEFAULT '', - process_name VARCHAR(512) NOT NULL DEFAULT '', + process_name VARCHAR(1024) NOT NULL DEFAULT '', authentication_package VARCHAR(128) NOT NULL DEFAULT '', logon_process VARCHAR(128) NOT NULL DEFAULT '', - status_text VARCHAR(64) NOT NULL DEFAULT '', - sub_status_text VARCHAR(64) NOT NULL DEFAULT '', - failure_reason VARCHAR(512) NOT NULL DEFAULT '', - ts DATETIME(6) NOT NULL, - received_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - msg LONGTEXT NOT NULL, - msg_sha256 CHAR(64) NOT NULL, - PRIMARY KEY (id), - KEY ix_event_logs_ts (ts), - KEY ix_event_logs_received_at (received_at), - KEY ix_event_logs_agent_ts (agent_id, ts), - KEY ix_event_logs_eventid_ts (event_id, ts), - KEY ix_event_logs_hostname_ts (hostname, ts), - KEY ix_event_logs_channel_event_ts (channel_name, event_id, ts), - KEY ix_event_logs_target_user_ts (target_user, ts), - KEY ix_event_logs_src_ip_ts (src_ip, ts), - KEY ix_event_logs_target_user_src_ip_ts (target_user, src_ip, ts), - KEY ix_event_logs_eventid_srcip_ts (event_id, src_ip, ts), - KEY ix_event_logs_eventid_targetuser_ts (event_id, target_user, ts), - KEY ix_event_logs_eventid_logontype_ts (event_id, logon_type, ts), - CONSTRAINT fk_event_logs_agent - FOREIGN KEY (agent_id) REFERENCES agents(id) - ON DELETE RESTRICT - ON UPDATE RESTRICT -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + status_text VARCHAR(128) NOT NULL DEFAULT '', + sub_status_text VARCHAR(128) NOT NULL DEFAULT '', + failure_reason VARCHAR(1024) NOT NULL DEFAULT '', -CREATE TABLE IF NOT EXISTS detections ( + received_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + msg_sha256 CHAR(64) NOT NULL, + + -- msg ist optional als kurzer/kompatibler Rest vorhanden. + -- Wenn Raw XML ausgelagert ist, kann hier leerer String oder kurzer Auszug gespeichert werden. + msg MEDIUMTEXT NULL, + + PRIMARY KEY (id, ts), + + KEY idx_event_logs_id (id), + KEY idx_event_logs_agent_ts (agent_id, ts), + KEY idx_event_logs_ts_host_channel_event (ts, hostname, channel_name, event_id), + KEY idx_event_logs_hostname_ts (hostname, ts), + KEY idx_event_logs_channel_event_ts (channel_name, event_id, ts), + KEY idx_event_logs_target_user_norm_ts (target_user_norm, ts), + KEY idx_event_logs_subject_user_norm_ts (subject_user_norm, ts), + KEY idx_event_logs_src_ip_ts (src_ip, ts), + + KEY idx_event_logs_security_logon_user ( + channel_name, + event_id, + ts, + target_user_norm, + hostname, + src_ip + ), + + KEY idx_event_logs_ueba_context ( + channel_name, + event_id, + ts, + hostname, + target_user_norm, + src_ip, + workstation + ), + + KEY idx_event_logs_password_spray ( + channel_name, + event_id, + ts, + src_ip, + target_user_norm, + hostname + ), + + KEY idx_event_logs_dedupe_lookup ( + hostname, + channel_name, + event_id, + ts, + msg_sha256 + ) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +PARTITION BY RANGE COLUMNS(ts) ( + PARTITION pmax VALUES LESS THAN (MAXVALUE) +); + +-- --------------------------------------------------------------------- +-- Raw XML Tabelle +-- --------------------------------------------------------------------- + +CREATE TABLE event_logs_raw ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - rule_name VARCHAR(128) NOT NULL, - severity VARCHAR(32) NOT NULL, - hostname VARCHAR(255) NOT NULL, - channel_name VARCHAR(128) NOT NULL DEFAULT '', - event_id INT UNSIGNED NOT NULL DEFAULT 0, - score DOUBLE NOT NULL DEFAULT 0, - window_start DATETIME(6) NOT NULL, - window_end DATETIME(6) NOT NULL, - summary VARCHAR(512) NOT NULL, - details_json JSON NOT NULL, - created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - PRIMARY KEY (id), - UNIQUE KEY ux_detection_dedupe (rule_name, hostname, channel_name, event_id, window_start, window_end), - KEY ix_detections_created (created_at), - KEY ix_detections_rule_host_time (rule_name, hostname, created_at), - KEY ix_detections_severity_time (severity, created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + event_log_id BIGINT UNSIGNED NOT NULL, + ts DATETIME(6) NOT NULL, + msg_sha256 CHAR(64) NOT NULL, + msg LONGBLOB NOT NULL, + is_gzip TINYINT(1) NOT NULL DEFAULT 0, + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + + PRIMARY KEY (id, ts), + + KEY idx_event_logs_raw_id (id), + KEY idx_event_logs_raw_event_log_id (event_log_id), + KEY idx_event_logs_raw_ts (ts), + KEY idx_event_logs_raw_sha (msg_sha256), + UNIQUE KEY uq_event_logs_raw_event_ts (event_log_id, ts) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +PARTITION BY RANGE COLUMNS(ts) ( + PARTITION pmax VALUES LESS THAN (MAXVALUE) +); + +-- --------------------------------------------------------------------- +-- Detection-Regeln +-- --------------------------------------------------------------------- CREATE TABLE detection_rules ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL UNIQUE, - description TEXT, - severity VARCHAR(16) NOT NULL DEFAULT 'medium', + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + description TEXT NULL, + severity ENUM('info','low','medium','high','critical') NOT NULL DEFAULT 'medium', + channel VARCHAR(512) NOT NULL DEFAULT 'Security', + event_ids VARCHAR(1024) NOT NULL, - channel VARCHAR(255) NOT NULL DEFAULT 'Security', - event_ids VARCHAR(255) NOT NULL, - - match_field VARCHAR(255) DEFAULT '', - match_operator VARCHAR(16) DEFAULT '', - match_value TEXT, + match_field VARCHAR(128) NOT NULL DEFAULT '', + match_operator ENUM('', 'eq', 'contains', 'in') NOT NULL DEFAULT '', + match_value TEXT NULL, threshold_count INT NOT NULL DEFAULT 1, threshold_window_seconds INT NOT NULL DEFAULT 0, - suppress_for_seconds INT NOT NULL DEFAULT 3600, + + enabled TINYINT(1) NOT NULL DEFAULT 1, + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY (id), + UNIQUE KEY uq_detection_rules_name (name), + KEY idx_detection_rules_enabled (enabled) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Detections +-- --------------------------------------------------------------------- + +CREATE TABLE detections ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + rule_name VARCHAR(255) NOT NULL, + severity ENUM('info','low','medium','high','critical') NOT NULL DEFAULT 'medium', + hostname VARCHAR(255) NOT NULL DEFAULT '', + channel_name VARCHAR(128) NOT NULL DEFAULT '', + event_id INT UNSIGNED NOT NULL DEFAULT 0, + score DOUBLE NOT NULL DEFAULT 0, + + window_start DATETIME(6) NOT NULL, + window_end DATETIME(6) NOT NULL, + + summary TEXT NOT NULL, + details_json JSON NULL, + + status ENUM( + 'open', + 'acknowledged', + 'investigating', + 'plausible', + 'legitimate', + 'false_positive', + 'resolved', + 'suppressed', + 'confirmed_incident' + ) NOT NULL DEFAULT 'open', + + analyst_note TEXT NULL, + reviewed_by VARCHAR(255) NOT NULL DEFAULT '', + reviewed_at DATETIME(6) NULL, + is_false_positive TINYINT(1) NOT NULL DEFAULT 0, + is_legitimate TINYINT(1) NOT NULL DEFAULT 0, + + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + + PRIMARY KEY (id), + + UNIQUE KEY uq_detection_dedupe ( + rule_name, + hostname, + channel_name, + event_id, + window_start, + window_end + ), + + KEY idx_detections_created (created_at), + KEY idx_detections_status_created (status, created_at), + KEY idx_detections_severity_created (severity, created_at), + KEY idx_detections_host_created (hostname, created_at), + KEY idx_detections_rule_host_created (rule_name, hostname, created_at), + KEY idx_detections_created_status_severity_host ( + created_at, + status, + severity, + hostname + ), + KEY idx_detections_window ( + hostname, + window_start, + window_end + ), + KEY idx_detections_soc ( + status, + severity, + hostname, + created_at + ) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Detection Suppressions +-- --------------------------------------------------------------------- + +CREATE TABLE detection_suppressions ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + rule_name VARCHAR(255) NOT NULL, + hostname VARCHAR(255) NOT NULL DEFAULT '', + channel_name VARCHAR(128) NOT NULL DEFAULT '', + event_id INT UNSIGNED NOT NULL DEFAULT 0, + + reason TEXT NULL, + created_by VARCHAR(255) NOT NULL DEFAULT '', + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + expires_at DATETIME(6) NULL, enabled TINYINT(1) NOT NULL DEFAULT 1, - created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) -); + PRIMARY KEY (id), + KEY idx_detection_suppressions_lookup ( + enabled, + rule_name, + hostname, + channel_name, + event_id, + expires_at + ), + KEY idx_detection_suppressions_expires (expires_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -CREATE INDEX idx_detection_rules_enabled -ON detection_rules (enabled); +-- --------------------------------------------------------------------- +-- Event Count Buckets für Baseline +-- --------------------------------------------------------------------- -CREATE INDEX idx_event_logs_dynamic_rules -ON event_logs (channel_name, event_id, ts, hostname); +CREATE TABLE event_count_buckets ( + bucket_start DATETIME(6) NOT NULL, + bucket_end DATETIME(6) NOT NULL, + + hostname VARCHAR(255) NOT NULL, + channel_name VARCHAR(128) NOT NULL, + event_id INT UNSIGNED NOT NULL, + + cnt BIGINT UNSIGNED NOT NULL DEFAULT 0, + + first_event_ts DATETIME(6) NULL, + last_event_ts DATETIME(6) NULL, + + finalized TINYINT(1) NOT NULL DEFAULT 0, + anomaly_checked_at DATETIME(6) NULL, + learned_at DATETIME(6) NULL, + + updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY (bucket_start, hostname, channel_name, event_id), + + KEY idx_event_count_buckets_lookup ( + hostname, + channel_name, + event_id, + bucket_start + ), + + KEY idx_event_count_buckets_anomaly ( + finalized, + anomaly_checked_at, + bucket_start + ), + + KEY idx_event_count_buckets_learn ( + finalized, + learned_at, + bucket_start + ), + + KEY idx_event_count_buckets_time ( + bucket_start, + bucket_end + ) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Baseline Stats +-- --------------------------------------------------------------------- + +CREATE TABLE baseline_event_stats ( + hostname VARCHAR(255) NOT NULL, + channel_name VARCHAR(128) NOT NULL, + event_id INT UNSIGNED NOT NULL, + hour_of_day TINYINT UNSIGNED NOT NULL, + day_of_week TINYINT UNSIGNED NOT NULL, + + avg_count DOUBLE NOT NULL DEFAULT 0, + m2_count DOUBLE NOT NULL DEFAULT 0, + stddev_count DOUBLE NOT NULL DEFAULT 0, + sample_count INT NOT NULL DEFAULT 0, + + first_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + last_updated DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY ( + hostname, + channel_name, + event_id, + hour_of_day, + day_of_week + ), + + KEY idx_baseline_event_stats_event ( + channel_name, + event_id, + hour_of_day, + day_of_week + ), + + KEY idx_baseline_event_stats_updated (last_updated) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE baseline_exclusions ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + hostname VARCHAR(255) NOT NULL DEFAULT '', + channel_name VARCHAR(128) NOT NULL DEFAULT '', + event_id INT UNSIGNED NOT NULL DEFAULT 0, + + reason TEXT NULL, + created_by VARCHAR(255) NOT NULL DEFAULT '', + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + expires_at DATETIME(6) NULL, + enabled TINYINT(1) NOT NULL DEFAULT 1, + + PRIMARY KEY (id), + + KEY idx_baseline_exclusions_lookup ( + enabled, + hostname, + channel_name, + event_id, + expires_at + ), + KEY idx_baseline_exclusions_expires (expires_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- UEBA Tabellen +-- --------------------------------------------------------------------- + +CREATE TABLE ueba_user_baseline ( + username VARCHAR(255) NOT NULL, + hostname VARCHAR(255) NOT NULL, + src_ip VARCHAR(64) NOT NULL, + workstation VARCHAR(255) NOT NULL, + + first_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + last_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + seen_count BIGINT UNSIGNED NOT NULL DEFAULT 0, + + PRIMARY KEY (username, hostname, src_ip, workstation), + + KEY idx_ueba_user_baseline_last_seen (last_seen), + KEY idx_ueba_user_baseline_user_last_seen (username, last_seen), + KEY idx_ueba_user_baseline_host_last_seen (hostname, last_seen) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE user_source_ip_seen ( + username VARCHAR(255) NOT NULL, + src_ip VARCHAR(64) NOT NULL, + hostname VARCHAR(255) NOT NULL, + + first_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + last_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + seen_count BIGINT UNSIGNED NOT NULL DEFAULT 0, + + PRIMARY KEY (username, src_ip, hostname), + + KEY idx_user_source_ip_seen_last_seen (last_seen), + KEY idx_user_source_ip_seen_user_last_seen (username, last_seen), + KEY idx_user_source_ip_seen_src_ip_last_seen (src_ip, last_seen) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE user_privilege_baseline ( + username VARCHAR(255) NOT NULL, + + first_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + last_seen DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + seen_count BIGINT UNSIGNED NOT NULL DEFAULT 0, + + PRIMARY KEY (username), + KEY idx_user_privilege_baseline_last_seen (last_seen) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE ueba_context_buckets ( + bucket_start DATETIME(6) NOT NULL, + bucket_end DATETIME(6) NOT NULL, + + username VARCHAR(255) NOT NULL, + hostname VARCHAR(255) NOT NULL, + src_ip VARCHAR(64) NOT NULL, + workstation VARCHAR(255) NOT NULL, + + cnt BIGINT UNSIGNED NOT NULL DEFAULT 0, + first_event_ts DATETIME(6) NULL, + last_event_ts DATETIME(6) NULL, + + finalized TINYINT(1) NOT NULL DEFAULT 0, + checked_at DATETIME(6) NULL, + learned_at DATETIME(6) NULL, + + updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY ( + bucket_start, + username, + hostname, + src_ip, + workstation + ), + + KEY idx_ueba_context_check ( + finalized, + checked_at, + bucket_start + ), + + KEY idx_ueba_context_learn ( + finalized, + learned_at, + bucket_start + ), + + KEY idx_ueba_context_lookup ( + username, + hostname, + src_ip, + workstation, + bucket_start + ) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Privileged Users +-- --------------------------------------------------------------------- + +CREATE TABLE privileged_users ( + username VARCHAR(255) NOT NULL, + reason TEXT NULL, + enabled TINYINT(1) NOT NULL DEFAULT 1, + created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), + updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY (username), + KEY idx_privileged_users_enabled (enabled) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Host Risk Scores / SOC +-- --------------------------------------------------------------------- + +CREATE TABLE host_risk_scores ( + hostname VARCHAR(255) NOT NULL, + risk_score DOUBLE NOT NULL DEFAULT 0, + severity ENUM('info','low','medium','high','critical') NOT NULL DEFAULT 'info', + + open_detections INT NOT NULL DEFAULT 0, + high_detections INT NOT NULL DEFAULT 0, + critical_detections INT NOT NULL DEFAULT 0, + confirmed_incidents INT NOT NULL DEFAULT 0, + + last_detection_at DATETIME(6) NULL, + updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)) ON UPDATE UTC_TIMESTAMP(6), + + PRIMARY KEY (hostname), + KEY idx_host_risk_scores_risk (risk_score), + KEY idx_host_risk_scores_severity_risk (severity, risk_score), + KEY idx_host_risk_scores_updated (updated_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- --------------------------------------------------------------------- +-- Partition Management Stored Procedure +-- --------------------------------------------------------------------- + +DROP PROCEDURE IF EXISTS ensure_siem_partitions; + +DELIMITER $$ + +CREATE PROCEDURE ensure_siem_partitions() +BEGIN + DECLARE v_start DATETIME; + DECLARE v_end DATETIME; + DECLARE v_current DATETIME; + DECLARE v_part_end DATETIME; + DECLARE v_part_name VARCHAR(32); + DECLARE v_exists INT DEFAULT 0; + + -- UTC now auf 3-Stunden-Grenze abrunden. + SET v_start = FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(UTC_TIMESTAMP()) / 10800) * 10800); + SET v_start = DATE_SUB(v_start, INTERVAL 6 HOUR); + SET v_end = DATE_ADD(FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(UTC_TIMESTAMP()) / 10800) * 10800), INTERVAL 27 HOUR); + + SET v_current = v_start; + + WHILE v_current < v_end DO + SET v_part_end = DATE_ADD(v_current, INTERVAL 3 HOUR); + SET v_part_name = CONCAT('p', DATE_FORMAT(v_current, '%Y%m%d%H')); + + -- event_logs + SELECT COUNT(*) + INTO v_exists + FROM information_schema.PARTITIONS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'event_logs' + AND PARTITION_NAME = v_part_name; + + IF v_exists = 0 THEN + SET @sql_event_logs = CONCAT( + 'ALTER TABLE event_logs REORGANIZE PARTITION pmax INTO (', + 'PARTITION ', v_part_name, ' VALUES LESS THAN (''', DATE_FORMAT(v_part_end, '%Y-%m-%d %H:%i:%s'), '''),', + 'PARTITION pmax VALUES LESS THAN (MAXVALUE))' + ); + + PREPARE stmt_event_logs FROM @sql_event_logs; + EXECUTE stmt_event_logs; + DEALLOCATE PREPARE stmt_event_logs; + END IF; + + -- event_logs_raw + SELECT COUNT(*) + INTO v_exists + FROM information_schema.PARTITIONS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'event_logs_raw' + AND PARTITION_NAME = v_part_name; + + IF v_exists = 0 THEN + SET @sql_event_logs_raw = CONCAT( + 'ALTER TABLE event_logs_raw REORGANIZE PARTITION pmax INTO (', + 'PARTITION ', v_part_name, ' VALUES LESS THAN (''', DATE_FORMAT(v_part_end, '%Y-%m-%d %H:%i:%s'), '''),', + 'PARTITION pmax VALUES LESS THAN (MAXVALUE))' + ); + + PREPARE stmt_event_logs_raw FROM @sql_event_logs_raw; + EXECUTE stmt_event_logs_raw; + DEALLOCATE PREPARE stmt_event_logs_raw; + END IF; + + SET v_current = v_part_end; + END WHILE; +END$$ + +DELIMITER ; + +CALL ensure_siem_partitions(); + +-- --------------------------------------------------------------------- +-- Initiale Beispielregeln +-- Optional. Kann gelöscht werden, wenn Regeln nur über UI gepflegt werden sollen. +-- --------------------------------------------------------------------- INSERT INTO detection_rules ( @@ -1290,162 +1807,7 @@ VALUES 0, 86400, 0 -); - -UPDATE detection_rules -SET description = '' -WHERE description IS NULL; - -UPDATE detection_rules -SET match_field = '' -WHERE match_field IS NULL; - -UPDATE detection_rules -SET match_operator = '' -WHERE match_operator IS NULL; - -UPDATE detection_rules -SET match_value = '' -WHERE match_value IS NULL; - -ALTER TABLE detection_rules -MODIFY description TEXT NULL, -MODIFY match_value TEXT NULL, -MODIFY match_field VARCHAR(64) NOT NULL DEFAULT '', -MODIFY match_operator VARCHAR(16) NOT NULL DEFAULT ''; - - - -CREATE TABLE baseline_event_stats ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - - hostname VARCHAR(255) NOT NULL, - channel_name VARCHAR(255) NOT NULL, - event_id INT NOT NULL, - - hour_of_day TINYINT NOT NULL, - day_of_week TINYINT NOT NULL, - - avg_count DOUBLE NOT NULL DEFAULT 0, - m2_count DOUBLE NOT NULL DEFAULT 0, - stddev_count DOUBLE NOT NULL DEFAULT 0, - sample_count INT NOT NULL DEFAULT 0, - - last_updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - - UNIQUE KEY uniq_baseline_event ( - hostname, - channel_name, - event_id, - hour_of_day, - day_of_week - ) -); - -CREATE INDEX idx_baseline_event_lookup -ON baseline_event_stats ( - hostname, - channel_name, - event_id, - hour_of_day, - day_of_week, - sample_count -); - -ALTER TABLE detections -ADD COLUMN status VARCHAR(32) NOT NULL DEFAULT 'open', -ADD COLUMN analyst_note TEXT NULL, -ADD COLUMN reviewed_by VARCHAR(128) NULL, -ADD COLUMN reviewed_at TIMESTAMP(6) NULL, -ADD COLUMN is_false_positive TINYINT(1) NOT NULL DEFAULT 0, -ADD COLUMN is_legitimate TINYINT(1) NOT NULL DEFAULT 0; - -CREATE INDEX idx_detections_status_created -ON detections (status, created_at); - -CREATE INDEX idx_detections_rule_status -ON detections (rule_name, status); - -CREATE TABLE detection_suppressions ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - rule_name VARCHAR(128) NOT NULL, - hostname VARCHAR(255) DEFAULT '', - channel_name VARCHAR(255) DEFAULT '', - event_id INT DEFAULT 0, - reason TEXT NULL, - created_by VARCHAR(128) DEFAULT '', - created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - expires_at TIMESTAMP(6) NULL, - enabled TINYINT(1) NOT NULL DEFAULT 1 -); - -CREATE INDEX idx_suppressions_lookup -ON detection_suppressions (enabled, rule_name, hostname, channel_name, event_id); - -CREATE TABLE baseline_exclusions ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - hostname VARCHAR(255) NOT NULL DEFAULT '', - channel_name VARCHAR(255) NOT NULL DEFAULT '', - event_id INT NOT NULL DEFAULT 0, - reason TEXT NULL, - created_by VARCHAR(128) NOT NULL DEFAULT '', - created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - expires_at TIMESTAMP(6) NULL, - enabled TINYINT(1) NOT NULL DEFAULT 1 -); - -CREATE INDEX idx_baseline_exclusions_lookup -ON baseline_exclusions (enabled, hostname, channel_name, event_id, expires_at); - -CREATE TABLE host_risk_scores ( - hostname VARCHAR(255) PRIMARY KEY, - risk_score DOUBLE NOT NULL DEFAULT 0, - severity VARCHAR(16) NOT NULL DEFAULT 'info', - open_detections INT NOT NULL DEFAULT 0, - high_detections INT NOT NULL DEFAULT 0, - critical_detections INT NOT NULL DEFAULT 0, - confirmed_incidents INT NOT NULL DEFAULT 0, - last_detection_at TIMESTAMP(6) NULL, - updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) -); - -CREATE TABLE IF NOT EXISTS ueba_user_baseline ( - username VARCHAR(255) NOT NULL, - hostname VARCHAR(255) NOT NULL, - src_ip VARCHAR(64) NOT NULL, - workstation VARCHAR(255) NOT NULL, - first_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6), - last_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6), - seen_count BIGINT NOT NULL DEFAULT 1, - PRIMARY KEY (username, hostname, src_ip, workstation) -); - -CREATE INDEX idx_ueba_user_baseline_user -ON ueba_user_baseline (username, last_seen); - -CREATE INDEX idx_host_risk_score -ON host_risk_scores (risk_score, severity); - -INSERT INTO detection_rules -( - name, - description, - severity, - channel, - event_ids, - match_field, - match_operator, - match_value, - threshold_count, - threshold_window_seconds, - suppress_for_seconds, - enabled -) -VALUES - --- ============================================================ --- KRITISCHE MANIPULATIONEN --- ============================================================ +), ( 'v2_security_log_cleared', @@ -1763,154 +2125,33 @@ VALUES '', '', '', 1, 0, 1800, 1 -); +) +ON DUPLICATE KEY UPDATE + description = VALUES(description), + severity = VALUES(severity), + channel = VALUES(channel), + event_ids = VALUES(event_ids), + match_field = VALUES(match_field), + match_operator = VALUES(match_operator), + match_value = VALUES(match_value), + threshold_count = VALUES(threshold_count), + threshold_window_seconds = VALUES(threshold_window_seconds), + suppress_for_seconds = VALUES(suppress_for_seconds), + enabled = VALUES(enabled); UPDATE detection_rules SET enabled = 0 WHERE name LIKE 'v1_%'; -CREATE TABLE user_host_baseline ( - username VARCHAR(128), - hostname VARCHAR(128), - first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (username, hostname) -); - -CREATE TABLE IF NOT EXISTS user_privilege_baseline ( - username VARCHAR(255) NOT NULL PRIMARY KEY, - first_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6), - last_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6), - seen_count BIGINT NOT NULL DEFAULT 1 -); - -CREATE TABLE IF NOT EXISTS privileged_users ( - username VARCHAR(255) PRIMARY KEY, - reason TEXT NULL, - enabled TINYINT(1) NOT NULL DEFAULT 1, - created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) -); - -CREATE INDEX idx_privileged_users_enabled -ON privileged_users (enabled); - -INSERT INTO privileged_users (username, reason, enabled) -VALUES -('administrator', 'Built-in Administrator', 1), -('admin', 'Generic admin account', 1) -ON DUPLICATE KEY UPDATE -reason = VALUES(reason); - -CREATE TABLE IF NOT EXISTS user_source_ip_seen ( - username VARCHAR(255) NOT NULL, - src_ip VARCHAR(64) NOT NULL, - hostname VARCHAR(255) NOT NULL, - first_seen DATETIME(6) NOT NULL, - last_seen DATETIME(6) NOT NULL, - seen_count BIGINT NOT NULL DEFAULT 1, - PRIMARY KEY (username, src_ip, hostname) -); - -### - -ALTER TABLE detections - ADD INDEX idx_detections_created_at (created_at), - ADD INDEX idx_detections_status_created (status, created_at), - ADD INDEX idx_detections_severity_created (severity, created_at), - ADD INDEX idx_detections_host_created (hostname, created_at), - ADD INDEX idx_detections_rule_created (rule_name, created_at), - ADD INDEX idx_detections_host_rule_created (hostname, rule_name, created_at), - ADD INDEX idx_detections_host_status_created (hostname, status, created_at), - ADD INDEX idx_detections_risk_window (created_at, status, hostname, severity), - ADD INDEX idx_detections_window_lookup (hostname, window_start, window_end); - -ALTER TABLE ueba_user_baseline - ADD UNIQUE KEY uq_ueba_user_context (username, hostname, src_ip, workstation), - ADD INDEX idx_ueba_user_last_seen (username, last_seen), - ADD INDEX idx_ueba_host_last_seen (hostname, last_seen); - -ALTER TABLE user_source_ip_seen - ADD UNIQUE KEY uq_user_source_ip_host (username, src_ip, hostname), - ADD INDEX idx_user_source_ip_last_seen (username, src_ip, last_seen); - -ALTER TABLE user_privilege_baseline - ADD UNIQUE KEY uq_user_privilege_username (username); - -ALTER TABLE baseline_event_stats - ADD UNIQUE KEY uq_baseline_bucket ( - hostname, - channel_name, - event_id, - hour_of_day, - day_of_week - ); - -ALTER TABLE detection_suppressions - ADD INDEX idx_detection_suppressions_lookup ( - enabled, - rule_name, - hostname, - channel_name, - event_id, - expires_at - ); - -ALTER TABLE baseline_exclusions - ADD INDEX idx_baseline_exclusions_lookup ( - enabled, - hostname, - channel_name, - event_id, - expires_at - ); - -ALTER TABLE detections - ADD INDEX idx_detections_status_created2 (status, created_at), - ADD INDEX idx_detections_severity_created2 (severity, created_at); - -### - - CREATE TABLE IF NOT EXISTS event_log_raw ( - event_log_id BIGINT UNSIGNED NOT NULL, - msg MEDIUMTEXT NOT NULL, - msg_sha256 CHAR(64) NOT NULL, - created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)), - PRIMARY KEY (event_log_id), - INDEX idx_event_log_raw_sha256 (msg_sha256), - INDEX idx_event_log_raw_created_at (created_at) -); - -ALTER TABLE event_log_raw - ADD CONSTRAINT fk_event_log_raw_event - FOREIGN KEY (event_log_id) - REFERENCES event_logs(id) - ON DELETE CASCADE; - -######################## MIGRATION ############################ -INSERT INTO event_log_raw (event_log_id, msg, msg_sha256, created_at) -SELECT id, msg, msg_sha256, COALESCE(received_at, UTC_TIMESTAMP(6)) -FROM event_logs -WHERE msg IS NOT NULL - AND msg <> '' -ON DUPLICATE KEY UPDATE - msg = VALUES(msg), - msg_sha256 = VALUES(msg_sha256); -######################## MIGRATION ############################ - - -######################## TEST ################################# -SELECT COUNT(*) AS raw_rows FROM event_log_raw; -SELECT COUNT(*) AS event_rows_with_msg FROM event_logs WHERE msg IS NOT NULL AND msg <> ''; -######################## TEST ################################# - -######################## MIGRATION ############################ -UPDATE event_logs -SET msg = '' -WHERE msg IS NOT NULL - AND msg <> '' -LIMIT 10000; -######################## MIGRATION ############################ - -######################## TEST ################################# -SELECT COUNT(*) FROM event_logs WHERE msg IS NOT NULL AND msg <> ''; -######################## TEST ################################# \ No newline at end of file +-- --------------------------------------------------------------------- +-- Diagnose-Queries nach Restore +-- --------------------------------------------------------------------- +-- SELECT TABLE_NAME, PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS +-- FROM information_schema.PARTITIONS +-- WHERE TABLE_SCHEMA = DATABASE() +-- AND TABLE_NAME IN ('event_logs', 'event_logs_raw') +-- ORDER BY TABLE_NAME, PARTITION_ORDINAL_POSITION; +-- +-- SHOW INDEX FROM event_logs; +-- SHOW INDEX FROM event_logs_raw; +-- SHOW INDEX FROM detections;