This commit is contained in:
38
.env
Normal file
38
.env
Normal file
@@ -0,0 +1,38 @@
|
||||
TZ=Europe/Berlin
|
||||
|
||||
LISTEN_ADDR=:8080
|
||||
DB_DSN=eventuser:DEINPASSWORT@tcp(mariadb:3306)/eventcollector?parseTime=true&charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&loc=UTC
|
||||
|
||||
DB_MAX_OPEN_CONNS=50
|
||||
DB_MAX_IDLE_CONNS=25
|
||||
DB_CONN_MAX_LIFETIME=3m
|
||||
DB_CONN_MAX_IDLE_TIME=1m
|
||||
|
||||
MAX_BODY_BYTES=10485760
|
||||
HTTP_READ_TIMEOUT=15s
|
||||
HTTP_WRITE_TIMEOUT=30s
|
||||
HTTP_IDLE_TIMEOUT=60s
|
||||
|
||||
DETECTION_INTERVAL=1m
|
||||
OFFLINE_AFTER=10m
|
||||
FAILED_LOGON_WINDOW=5m
|
||||
FAILED_LOGON_THRESHOLD=25
|
||||
REBOOT_WINDOW=15m
|
||||
REBOOT_THRESHOLD=3
|
||||
PASSWORD_SPRAY_WINDOW=5m
|
||||
PASSWORD_SPRAY_MIN_USERS=5
|
||||
PASSWORD_SPRAY_MIN_ATTEMPTS=15
|
||||
SUCCESS_AFTER_FAILURE_WINDOW=10m
|
||||
NEW_SOURCE_IP_LOOKBACK=720h
|
||||
NEW_SOURCE_IP_WINDOW=10m
|
||||
DETECTIONS_LIMIT=100
|
||||
|
||||
MARIADB_DATABASE=eventcollector
|
||||
MARIADB_USER=eventuser
|
||||
MARIADB_PASSWORD=DEINPASSWORT
|
||||
MARIADB_ROOT_PASSWORD=ROOTPASSWORT
|
||||
|
||||
GRAFANA_ADMIN_USER=admin
|
||||
GRAFANA_ADMIN_PASSWORD=BITTE_AENDERN
|
||||
|
||||
ENROLLMENT_KEY=BITTE_SEHR_LANG_UND_ZUFAELLIG
|
||||
51
.gitea/workflows/registry.yml
Normal file
51
.gitea/workflows/registry.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: release-tag
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
jobs:
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_ORG: sendnrw
|
||||
DOCKER_LATEST: latest
|
||||
RUNNER_TOOL_CACHE: /toolcache
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with: # replace it with your local IP
|
||||
config-inline: |
|
||||
[registry."git.send.nrw"]
|
||||
http = true
|
||||
insecure = true
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.send.nrw # replace it with your local IP
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
push: true
|
||||
tags: | # replace it with your local IP and tags
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
||||
FROM golang:1.24 AS builder
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/eventcollector .
|
||||
|
||||
FROM debian:trixie-slim
|
||||
|
||||
ENV LISTEN_ADDR=:8080
|
||||
ENV TZ=Europe/Berlin
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates tzdata wget \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& groupadd --system --gid 10001 app \
|
||||
&& useradd --system --uid 10001 --gid 10001 --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /out/eventcollector /app/eventcollector
|
||||
|
||||
USER 10001:10001
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/app/eventcollector"]
|
||||
118
compose.yml
Normal file
118
compose.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:11.8
|
||||
container_name: siem-mariadb
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
MARIADB_DATABASE: ${MARIADB_DATABASE}
|
||||
MARIADB_USER: ${MARIADB_USER}
|
||||
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
|
||||
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
|
||||
TZ: ${TZ}
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --innodb-buffer-pool-size=512M
|
||||
- --max-connections=300
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
- ./deploy/mariadb/init:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mariadb-admin ping -h 127.0.0.1 -u root -p$$MARIADB_ROOT_PASSWORD --silent"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
siem-backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: siem-backend:latest
|
||||
container_name: siem-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
LISTEN_ADDR: ${LISTEN_ADDR}
|
||||
DB_DSN: ${DB_DSN}
|
||||
DB_MAX_OPEN_CONNS: ${DB_MAX_OPEN_CONNS}
|
||||
DB_MAX_IDLE_CONNS: ${DB_MAX_IDLE_CONNS}
|
||||
DB_CONN_MAX_LIFETIME: ${DB_CONN_MAX_LIFETIME}
|
||||
DB_CONN_MAX_IDLE_TIME: ${DB_CONN_MAX_IDLE_TIME}
|
||||
MAX_BODY_BYTES: ${MAX_BODY_BYTES}
|
||||
HTTP_READ_TIMEOUT: ${HTTP_READ_TIMEOUT}
|
||||
HTTP_WRITE_TIMEOUT: ${HTTP_WRITE_TIMEOUT}
|
||||
HTTP_IDLE_TIMEOUT: ${HTTP_IDLE_TIMEOUT}
|
||||
DETECTION_INTERVAL: ${DETECTION_INTERVAL}
|
||||
OFFLINE_AFTER: ${OFFLINE_AFTER}
|
||||
FAILED_LOGON_WINDOW: ${FAILED_LOGON_WINDOW}
|
||||
FAILED_LOGON_THRESHOLD: ${FAILED_LOGON_THRESHOLD}
|
||||
REBOOT_WINDOW: ${REBOOT_WINDOW}
|
||||
REBOOT_THRESHOLD: ${REBOOT_THRESHOLD}
|
||||
PASSWORD_SPRAY_WINDOW: ${PASSWORD_SPRAY_WINDOW}
|
||||
PASSWORD_SPRAY_MIN_USERS: ${PASSWORD_SPRAY_MIN_USERS}
|
||||
PASSWORD_SPRAY_MIN_ATTEMPTS: ${PASSWORD_SPRAY_MIN_ATTEMPTS}
|
||||
SUCCESS_AFTER_FAILURE_WINDOW: ${SUCCESS_AFTER_FAILURE_WINDOW}
|
||||
NEW_SOURCE_IP_LOOKBACK: ${NEW_SOURCE_IP_LOOKBACK}
|
||||
NEW_SOURCE_IP_WINDOW: ${NEW_SOURCE_IP_WINDOW}
|
||||
DETECTIONS_LIMIT: ${DETECTIONS_LIMIT}
|
||||
TZ: ${TZ}
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8080:8080"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/healthz >/dev/null 2>&1 || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: siem-prometheus
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --config.file=/etc/prometheus/prometheus.yml
|
||||
- --storage.tsdb.path=/prometheus
|
||||
- --storage.tsdb.retention.time=30d
|
||||
- --web.enable-lifecycle
|
||||
depends_on:
|
||||
siem-backend:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./deploy/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
- ./deploy/prometheus/rules:/etc/prometheus/rules:ro
|
||||
- prometheus_data:/prometheus
|
||||
ports:
|
||||
- "9090:9090"
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: siem-grafana
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER}
|
||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
|
||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
||||
GF_SERVER_ROOT_URL: http://localhost:3000
|
||||
TZ: ${TZ}
|
||||
depends_on:
|
||||
- prometheus
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro
|
||||
- ./deploy/grafana/dashboards:/var/lib/grafana/dashboards:ro
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
12
deploy/grafana/provisioning/dashboards/dashboards.yml
Normal file
12
deploy/grafana/provisioning/dashboards/dashboards.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: SIEM Dashboards
|
||||
orgId: 1
|
||||
folder: SIEM
|
||||
type: file
|
||||
disableDeletion: false
|
||||
editable: true
|
||||
updateIntervalSeconds: 30
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
76
deploy/grafana/provisioning/dashboards/siem-overview.json
Normal file
76
deploy/grafana/provisioning/dashboards/siem-overview.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"panels": [
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Active Agents",
|
||||
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "eventcollector_active_agents",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "High Detections (5m)",
|
||||
"gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "increase(eventcollector_detection_hits_total{severity=\"high\"}[5m])",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "HTTP Requests",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(eventcollector_http_requests_total[5m])",
|
||||
"legendFormat": "{{path}} {{status}}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Detection Hits",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "increase(eventcollector_detection_hits_total[5m])",
|
||||
"legendFormat": "{{rule}} {{severity}}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Ingested Events",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 12 },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(eventcollector_ingest_events_total[5m])",
|
||||
"legendFormat": "{{channel}} {{event_id}}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemaVersion": 39,
|
||||
"style": "dark",
|
||||
"tags": ["siem"],
|
||||
"templating": { "list": [] },
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"title": "SIEM Overview",
|
||||
"version": 1
|
||||
}
|
||||
10
deploy/grafana/provisioning/datasources/datasource.yml
Normal file
10
deploy/grafana/provisioning/datasources/datasource.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
uid: prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: true
|
||||
88
deploy/mariadb/init/001-schema.sql
Normal file
88
deploy/mariadb/init/001-schema.sql
Normal file
@@ -0,0 +1,88 @@
|
||||
CREATE DATABASE IF NOT EXISTS eventcollector
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE eventcollector;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS 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),
|
||||
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)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS event_logs (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
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,
|
||||
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_domain VARCHAR(255) NOT NULL DEFAULT '',
|
||||
subject_user 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 '',
|
||||
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;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS detections (
|
||||
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;
|
||||
18
deploy/prometheus/prometheus.yml
Normal file
18
deploy/prometheus/prometheus.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
rule_files:
|
||||
- /etc/prometheus/rules/*.yml
|
||||
|
||||
scrape_configs:
|
||||
- job_name: siem-backend
|
||||
metrics_path: /metrics
|
||||
static_configs:
|
||||
- targets:
|
||||
- siem-backend:8080
|
||||
|
||||
- job_name: prometheus
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost:9090
|
||||
38
deploy/prometheus/rules/siem-alerts.yml
Normal file
38
deploy/prometheus/rules/siem-alerts.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
groups:
|
||||
- name: siem-backend
|
||||
rules:
|
||||
- alert: SiemBackendDown
|
||||
expr: up{job="siem-backend"} == 0
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "SIEM backend nicht erreichbar"
|
||||
description: "Prometheus kann das SIEM-Backend seit mindestens 2 Minuten nicht scrapen."
|
||||
|
||||
- alert: SiemHighDetections
|
||||
expr: increase(eventcollector_detection_hits_total{severity="high"}[5m]) > 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: high
|
||||
annotations:
|
||||
summary: "Neue High-Severity Detection"
|
||||
description: "Es wurde mindestens eine neue High-Severity-Detection in den letzten 5 Minuten erzeugt."
|
||||
|
||||
- alert: SiemRuleErrors
|
||||
expr: increase(eventcollector_rule_errors_total[5m]) > 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Fehler in Detection-Regeln"
|
||||
description: "Mindestens eine Detection-Regel hat in den letzten 5 Minuten einen Fehler erzeugt."
|
||||
|
||||
- alert: SiemTooFewActiveAgents
|
||||
expr: eventcollector_active_agents < 1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Zu wenige aktive Agents"
|
||||
description: "Es wurden weniger aktive Agents erkannt als erwartet."
|
||||
22
go.mod
Normal file
22
go.mod
Normal file
@@ -0,0 +1,22 @@
|
||||
module git.send.nrw/sendnrw/siem-backend
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require github.com/go-sql-driver/mysql v1.9.3
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
)
|
||||
25
go.sum
Normal file
25
go.sum
Normal file
@@ -0,0 +1,25 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
101
schema.sql
Normal file
101
schema.sql
Normal file
@@ -0,0 +1,101 @@
|
||||
CREATE DATABASE IF NOT EXISTS eventcollector
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE eventcollector;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS 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),
|
||||
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)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS event_logs (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
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,
|
||||
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),
|
||||
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;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS detections (
|
||||
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;
|
||||
|
||||
USE eventcollector;
|
||||
|
||||
INSERT INTO agents (hostname, api_key_hash)
|
||||
VALUES
|
||||
('client01.domain.local', SHA2('SUPER-LANGER-AGENT-KEY-01', 256)),
|
||||
('client02.domain.local', SHA2('SUPER-LANGER-AGENT-KEY-02', 256));
|
||||
|
||||
#V2
|
||||
|
||||
ALTER TABLE event_logs
|
||||
ADD COLUMN computer VARCHAR(255) NOT NULL DEFAULT '' AFTER source,
|
||||
ADD COLUMN provider_name VARCHAR(255) NOT NULL DEFAULT '' AFTER computer,
|
||||
ADD COLUMN level_value INT UNSIGNED NOT NULL DEFAULT 0 AFTER provider_name,
|
||||
ADD COLUMN task_value INT UNSIGNED NOT NULL DEFAULT 0 AFTER level_value,
|
||||
ADD COLUMN opcode_value INT UNSIGNED NOT NULL DEFAULT 0 AFTER task_value,
|
||||
ADD COLUMN keywords VARCHAR(255) NOT NULL DEFAULT '' AFTER opcode_value,
|
||||
ADD COLUMN target_user VARCHAR(255) NOT NULL DEFAULT '' AFTER keywords,
|
||||
ADD COLUMN target_domain VARCHAR(255) NOT NULL DEFAULT '' AFTER target_user,
|
||||
ADD COLUMN subject_user VARCHAR(255) NOT NULL DEFAULT '' AFTER target_domain,
|
||||
ADD COLUMN subject_domain VARCHAR(255) NOT NULL DEFAULT '' AFTER subject_user,
|
||||
ADD COLUMN workstation VARCHAR(255) NOT NULL DEFAULT '' AFTER subject_domain,
|
||||
ADD COLUMN src_ip VARCHAR(64) NOT NULL DEFAULT '' AFTER workstation,
|
||||
ADD COLUMN src_port VARCHAR(32) NOT NULL DEFAULT '' AFTER src_ip,
|
||||
ADD COLUMN logon_type VARCHAR(32) NOT NULL DEFAULT '' AFTER src_port,
|
||||
ADD COLUMN process_name VARCHAR(512) NOT NULL DEFAULT '' AFTER logon_type,
|
||||
ADD COLUMN authentication_package VARCHAR(128) NOT NULL DEFAULT '' AFTER process_name,
|
||||
ADD COLUMN logon_process VARCHAR(128) NOT NULL DEFAULT '' AFTER authentication_package,
|
||||
ADD COLUMN status_text VARCHAR(64) NOT NULL DEFAULT '' AFTER logon_process,
|
||||
ADD COLUMN sub_status_text VARCHAR(64) NOT NULL DEFAULT '' AFTER status_text,
|
||||
ADD COLUMN failure_reason VARCHAR(512) NOT NULL DEFAULT '' AFTER sub_status_text;
|
||||
|
||||
ALTER TABLE event_logs
|
||||
ADD KEY ix_event_logs_target_user_ts (target_user, ts),
|
||||
ADD KEY ix_event_logs_src_ip_ts (src_ip, ts),
|
||||
ADD KEY ix_event_logs_target_user_src_ip_ts (target_user, src_ip, ts),
|
||||
ADD KEY ix_event_logs_eventid_srcip_ts (event_id, src_ip, ts),
|
||||
ADD KEY ix_event_logs_eventid_targetuser_ts (event_id, target_user, ts),
|
||||
ADD KEY ix_event_logs_eventid_logontype_ts (event_id, logon_type, ts);
|
||||
Reference in New Issue
Block a user