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