Start / Uncategorized / Einbrüche erkennen und blockieren

Einbrüche erkennen und blockieren

Zur weiteren Härtung des eigenen Netzwerkes, sei es nun gewerblich oder auch ein Homelab, kommt man an IDS bzw. IPS nicht vorbei. Das eine ist ein Intrusion Detection System, das zweite ein Intrusion Prevention System. Das sagt eigentlich auch schon alles aus, was man in der Unterscheidung wissen muss: Ein IDS schreibt mit und meldet, ein IPS tut das auch, blockiert aber automatisch schädliche Pakete.

Als ein weiteres Projekt, was sich für einen Raspberry Pi eignet, möchte ich diese Systeme auch dort ansiedeln: Auf isolierten Geräten. Kaum einer wird beide Varianten in seinem Netzwerk laufen lassen, daher sollte vorab geklärt werden, was du selbst vor hast.

Die Hardware
Ein IPS wird als Durchfluss-Gerät arbeiten, d. h. es benötigt mindestens zwei LAN-Schnittstellen – besser noch eine dritte als Wartungs-Konsole, da die beiden Adapter zum reinen Durchfluss keine IP-Adressen bekommen sollen. Dies ist zwar auch möglich, um einen Adapter zu sparen, aber Best Practice ist das nicht. Ein IPS blockiert also den böswilligen Verkehr, noch bevor dieser das Ziel überhaupt erreicht.

Ein IDS wiederum kommt mit einer einzelnen Schnittstelle aus, muss aber zwingend an einen Port des Routers gehängt werden, der als Mirror arbeitet: Der Verkehr aller definierten Anschlüsse am Router wird dann gespiegelt zum IDS weitergeleitet zwecks Auswertung. Einige Switches können einen solchen Port Mirror auch bereitstellen. Wähle dabei die zu spiegelnden Ports mit Bedacht: Läuft in deinem Netzwerk ein TV- oder Streaming-Dienst? Dies bringt sehr viele Pakete mit sich, die das IDS nie benötigen würde. Angriffe hierüber sind sehr exotisch bis unrealistisch und sollten daher nicht in die Auswertung einbezogen werden.

Da eine solche IDS- oder IPS-Instanz Milliarden von Paketen durchsuchen und protokollieren wird im Laufe der Zeit – also sehr viele Schreibzugriffe erfolgen – ist die Installation des Systems auf einer SD-Karte nicht besonders sinnvoll. Unter der permanenten Schreiblast gehen diese Karten dann zügig kaputt. Eine M.2 SSD, mit einem Renkforce-Adapter zum Beispiel, lässt sich „huckepack“ auf einen Raspberry verladen und in ein gemeinsames Gehäuse packen.

Derlei Hardware wird wohlgemerkt auch recht warm; zwar habe ich das alles auf einem Raspberry 4 laufen mit einer 256 GB M.2 SSD und ohne Lüfter – allerdings befindet sich dieser in einem gut durchlüfteten Server-Rack, welches die Wärme insgesamt abführt. Anders platziert, kommst du hier um eine aktive Kühlung des Raspis nicht herum.

Auf einem Raspi 3B+ gilt die Software auch als „noch lauffähig“ – darunter keinesfalls mehr sinnvoll. Ob ein 3B+ nicht doch an seine Grenzen stößt, konnte ich mangels Gerät nicht testen.

Die Software auf dem Raspi
Da die SSD über den Renkforce-Adapter bereits einen USB-Anschluss hat: An den lokalen Rechner anschließen, mit Balena Etcher das Image der neuesten Raspberry Pi OS Lite draufbringen und an den Raspberry wieder anschließen. Starten mit Bildschirm und Tastatur, einrichten, fertig. Ab jetzt kann der Zugriff über SSH erfolgen.

Der Kern der Software ist meistens Suricata oder Snort. Diese Software untersucht die Pakete im Netzwerk in Echtzeit, gleicht diese mit bekannten Angriffsmustern ab und analysiert Anomalien im Verhalten zur Identifizierung böswilliger Absichten.

Das Ganze lässt sich dann mit zum Beispiel Loki in einen Container mit Grafana importieren, wo die Ergebnisse aufbereitet und grafisch dargestellt werden können. Es gibt zu Grafana auch Alternativen – da sich in diesem Netzwerk allerdings noch weitere Dienste später einmal mit Grafana verknüpfen sollen, ist das für diesen Beitrag der Software-Stack.

Auch wenn 256 GB als SSD nun erst einmal nach sehr viel klingt: Ist es nicht. Da alles protokolliert und in Logfiles geschrieben wird, läuft dieser Speicher mit der Zeit auch voll – und im schlimmsten Fall stellt die Instanz dann den Dienst ein.

Ein Log-Rotate einzubauen und zum Beispiel nur die Protokolle der letzten 25 Tage überhaupt aufzubewahren, ist die bessere Idee. Lesen wirst du die ganzen Einträge vermutlich nur selten, selbst die Grafana-Instanz schaut sich dann nur die Einträge der letzten xx Stunden an, je nach Konfiguration.

Loki als der Transporteur der Logdateien und Grafana selbst habe ich übrigens nicht mit auf dem Raspi installiert, sondern in eine VM unter Proxmox. Da gibt’s genug Rechenleistung und Platz. So kann sich der Raspi selbst denn ausschließlich um die Echtzeit-Analyse der Pakete kümmern.Zur weiteren Härtung des eigenen Netzwerkes, sei es nun gewerblich oder auch ein Homelab, kommt man an IDS bzw. IPS nicht vorbei. Das eine ist ein Intrusion Detection System, das zweite ein Intrusion Prevention System. Das sagt eigentlich auch schon alles aus, was man in der Unterscheidung wissen muss: Ein IDS schreibt mit und meldet, ein IPS tut das auch, blockiert aber automatisch schädliche Pakete.Loki als der Transporteur der Logdateien und Grafana selbst habe ich übrigens nicht mit auf dem Raspi installiert, sondern in eine VM unter Proxmox. Da gibt’s genug Rechenleistung und Platz. So kann sich der Raspi selbst denn ausschließlich um die Echtzeit-Analyse der Pakete kümmern.

Suricata installieren
In dem Standard-Repository des Raspberry Pi OS Lite ist die Version von Suricata oft veraltet; es empfiehlt sich daher, die Version aus den „Backports“ zu installieren, um aktuelle Sicherheitsregeln zu gewährleisten.

System aktualisieren und Abhängigkeiten installieren:

bash

Kopieren
sudo apt update && sudo apt upgrade -y
sudo apt install -y suricata jq ethtool
Aktuelle Version aus Backports installieren:

bash

Kopieren
echo „deb http://deb.debian.org/debian bookworm-backports main“ | sudo tee /etc/apt/sources.list.d/backports.list
sudo apt update
sudo apt install -y suricata -t bookworm-backports
Da ein Raspi nur begrenzte Ressourcen mitbringt, muss die Konfiguration von Suricata angepasst werden – sonst könnte es zu einer Überlastung kommen.

bash

Kopieren
nano /etc/suricata/suricata.yaml
Die YAML-Datei ist riesig, allerdings handelt es sich mehr um eine gute Dokumentation innerhalb der YAML als um Konfigurationen. Suche daher einfach diese jeweiligen Punkte und ersetze den Wert durch den hier gezeigten:

yaml

Kopieren
af-packet:

  • interface: eth0 # bzw. Name deines Adapters am Raspi
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    ring-size: 30000
    Durch die Aktivierung dieses Bereiches „af-packet“ wird Suricata als IDS eingerichtet. Soll der Raspi hingegen als IPS konfiguriert werden, so geschieht dies im Abschnitt „nfqueue“.

Nun kann Suricata gestartet und die Regeln geladen werden:

bash

Kopieren
sudo suricata-update
sudo systemctl enable suricata
sudo systemctl start suricata
Suricata sollte nun bereits laufen.

Logfiles rotieren
Wie bereits erwähnt, sammeln sich mit der Zeit die Logdateien. Linux kennt hier von Haus aus ein Logrotate, also ein zyklisches Zusammenfassen und Verwerfen alter Logfiles. Da wir die Daten aus der Logdatei ohnehin mit Loki auslesen und abholen werden, ist hier ein recht aggressiver Kurs möglich, um die Datei einmal pro Stunde zu kürzen.

Ebenfalls unter /etc abgelegt, folgende Befehle:

bash

Kopieren
sudo touch /etc/logrotate.d/suricata
sudo nano /etc/logrotate.d/suricata
Alles, was da drin steht: Löschen. Dies hier hinzufügen:

conf

Kopieren
/var/log/suricata/eve.json {
hourly
rotate 2
compress
delaycompress
missingok
notifempty
create 640 suricata suricata
sharedscripts
postrotate
/bin/kill -USR2 $(cat /var/run/suricata.pid 2>/dev/null) 2>/dev/null || true
endscript
}
Im Anschluss das erste Mal testen:

bash

Kopieren
sudo logrotate -d /etc/logrotate.d/suricata
Die Ausgabe sollte fehlerfrei sein. Sollte es hierbei zu Fehlern kommen, bitte individuell auf die Suche nach der Lösung im Web gehen; hier alle möglichen Fehler zu behandeln, ist nicht möglich und würde den Beitrag unlesbar machen.

Der Software-Stack zur Auswertung
Die Auswertung folgt über mehrere Bausteine, die ineinandergreifen. Da diese ebenfalls einiges an Ressourcen verbrauchen, empfiehlt sich hier die Platzierung auf einer Docker-VM. Zwar ließe sich dies auch lokal an einem Arbeitsrechner aufsetzen, allerdings läuft dieser dann nicht rund um die Uhr – was ein Abholen der Logs vor Löschung durch das eben beschriebene Logrotate unmöglich macht.

Der Stack hier besteht aus Loki, Grafana und Alloy. Ich habe das Ganze in einer VM unter Proxmox verfrachtet – der Server läuft ohnehin 24/7 und holt die Logs somit in-time ab. Alloy gehört zwar so nicht zum notwendigen Stack auf der VM, allerdings installiere ich diesen gleich mit hierhin, um für spätere Ausbauten den Collector bereits auf der VM laufen zu haben.

Lege wie gewohnt eine docker-compose.yml in zum Beispiel /home/[nutzername]/grafana-stack an und füge diesen Inhalt ein:

yaml

Kopieren
services:
loki:
image: grafana/loki:3.0.0
container_name: loki
command: -config.file=/etc/loki/local-config.yaml
ports:
– „3100:3100“
volumes:
– ./loki-config.yaml:/etc/loki/local-config.yaml
– loki-data:/loki
restart: unless-stopped
networks:
– logging-net
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 512M

grafana:
image: grafana/grafana:11.0.0
container_name: grafana
ports:
– „3000:3000“
environment:
– GF_SECURITY_ADMIN_USER=admin
– GF_SECURITY_ADMIN_PASSWORD=admin
– GF_USERS_ALLOW_SIGN_UP=false
volumes:
– grafana-data:/var/lib/grafana
– ./grafana-provisioning:/etc/grafana/provisioning
restart: unless-stopped
depends_on:
– loki
networks:
– logging-net

alloy:
image: grafana/alloy:latest
container_name: alloy
command: [„run“, „/etc/alloy/config.alloy“]
volumes:
– ./alloy-config.alloy:/etc/alloy/config.alloy:ro
– alloy-data:/var/lib/alloy
– /var/run/docker.sock:/var/run/docker.sock:ro
– /var/log:/var/log:ro
restart: unless-stopped
depends_on:
– loki
networks:
– logging-net

networks:
logging-net:
driver: bridge

volumes:
loki-data:
grafana-data:
alloy-data:
Grafana selbst ist das Web-Frontend für einfachere Visualisierung von Logfiles, Loki ist das Aggregationssystem, welches die Logdateien entgegennimmt und komprimiert, und Alloy ist der Agent, der die Logs von Containern und Anwendungen sammelt und an Loki sendet.

Du benötigst für Loki und Alloy noch weitere Konfigurationsdaten in dem gleichen Verzeichnis.

Die loki-config.yaml:

yaml

Kopieren
auth_enabled: false

server:
http_listen_port: 3100
grpc_listen_port: 9096

common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory

query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100

schemaconfig:
configs:
– from: 2025-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index

period: 24h

ruler:
alertmanager_url: http://localhost:9093

limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
ingestion_rate_mb: 16
ingestion_burst_size_mb: 24
max_streams_per_user: 10000
max_line_size: 256kb
retention_period: 720h
Die alloy-config.alloy (für die VM):

alloy

Kopieren
// Entdecke alle Docker Container auf diesem Host
discovery.docker „containers“ {
host = „unix:///var/run/docker.sock“
}

// Lies die Logs und sende sie an Loki
loki.source.docker „all_containers“ {
host = „unix:///var/run/docker.sock“
targets = discovery.docker.containers.target
forward_to = [loki.process.local.receiver]
}

loki.process „local“ {
forward_to = [loki.write.local.receiver]
stage.static_labels {
values = {
source = „docker-vm“,
}
}
}

loki.write „local“ {
endpoint {
url = „http://loki:3100/loki/api/v1/push“
}
}
Diesen Stack startest du nun ebenfalls wie gewohnt mit:

bash

Kopieren
docker compose up -d
Der Agent auf dem Raspberry
Der Agent „Alloy“ muss dazu auf dem Raspberry installiert werden.

bash

Kopieren

Grafana Key und Repository hinzufügen

sudo mkdir -p /etc/apt/keyrings
wget -q -O – https://apt.grafana.com/gpg.key | gpg –dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo „deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main“ | sudo tee /etc/apt/sources.list.d/grafana.list

sudo apt-get update
sudo apt-get install alloy
Die Konfiguration findest du im Anschluss unter /etc/alloy/config.alloy.

Lösche den Inhalt komplett und füge dies hier ein; solltest du unsicher sein, kopiere zunächst die config.alloy zur config.alloy.old – dann hast du ein sauberes Fallback.

alloy

Kopieren
// 1. Datei entdecken
local.file_match „suricata_files“ {
path_targets = [
{
path = „/var/log/suricata/eve.json“,
job = „suricata“,
host = „raspi-ids“,
},
]
}

// 2. Logs lesen
loki.source.file „suricata“ {
targets = local.file_match.suricata_files.targets
forward_to = [loki.process.suricata_labels.receiver]
}

// 3. Logs verarbeiten (Labeln & JSON Parsen)
loki.process „suricata_labels“ {
forward_to = [loki.write.local.receiver]

stage.static_labels {
values = {
type = „ids“,
}
}

stage.json {
expressions = {
event_type = „event_type“,
src_ip = „src_ip“,
dest_ip = „dest_ip“,
alert_severity = „alert.severity“,
signature = „alert.signature“,
}
}
}

// 4. Logs an Loki senden
loki.write „local“ {
endpoint {
// HIER DIE LOKI IP EINTRAGEN: http://192.168.x.xx:3100/loki/api/v1/push
url = „http://192.168.x.xx:3100/loki/api/v1/push“
}
}
Damit ist Alloy auf dem Raspi eingerichtet und kann gestartet werden:

bash

Kopieren
sudo systemctl enable alloy
sudo systemctl start alloy
sudo systemctl status alloy

Der Alloy-Port hat die 12345 auf dem Raspberry. Dieser ist jedoch nur für den Push Dienst relevant und nicht wirklich erreichbar.

Nun sind die Dienste miteinander verknüpft und Grafana wird mit Daten gefüttert.

Wie es weiter geht

Das WebUI von Grafana erreichst du nun auf Port 3000 und Loki auf 3100. Loki gibt dabei lediglich ein Lebenszeichen von sich auf dem Port und ist sonst nicht großartig erreichbar. Während Grafana nun deine Benutzeroberfläche wird.

Die reine Bedienung dieser Oberfläche ist vom Anbieter selbst gut dokumentiert und ändert sich von Zeit zu Zeit mit gewissen Updates. Da ich diese nicht vorhersehen kann, möchte ich an die online verfügbare Dokumentation verweisen zur Bedienung.

Ist jedoch die Verbindung zu Loki einmal eingerichtet, wird Zeile für Zeile das Logfile auf deinem Bildschirm erscheinen, gefiltert nach „Alerts“ – wobei die wenigsten davon hoffentlich echte Alerts sind. Diese sind abgestuft in die Unterscheidungen nach 1, 2 und 3. In der Regel solltest du fast nur die 3. Alarme zu sehen bekommen – das können allerlei Meldungen sein, die Suricata produziert, aber auch zum Beispiel eine DNS-Abfrage von deinem Unbound-Server etc.

Nimm dir ein wenig Zeit und lasse dir die Logs der letzten sechs Stunden anzeigen. Du kannst diese Zeile für Zeile überprüfen und dann, wenn es kein echter Alarm ist, mit einem Filter belegen – dass Alarme mit dieser Bezeichnung nicht wieder angezeigt werden sollen. Die Inhalte einer jeden Zeile sind komplex und schwer zu lesen. Bevor du dich an die Syntax gewöhnt hast, hilft dir hier eine Cloud-KI weiter. Diese legt dir einfach dar, was dahinter steckt – und ob du diese filtern kannst bzw. sogar solltest.

Ich habe aktuell rund zwei Dutzend Filter aktiv und bislang noch keinerlei Einbrüche verzeichnet hier. Doch bedenke: Selbst, wenn an deinem ISP-Router alle Ports geschlossen sind nach außen hin, gibt es immer noch genug Szenarien, die dennoch böswillig sind, wie zum Beispiel Malware etc. Auch diese werden dann angezeigt. Zusätzlich kannst du für die Einstufungen 1 oder 2 auch umgehende Alarme auslösen lassen, die dich per Telegram, Email oder Slack erreichen.

Fazit

Ich habe lange damit gehardert, so ein IDS aufzusetzen und zu betreiben, die Logfiles zu sichten und daraus Rückschlüsse zu ziehen. Zwar fühlte ich mich subjektiv mit einem komplett geschlossenen Router und mehreren Firewalls schon recht sicher eingegraben – tatsächlich. All diese Dinge folgen beim Filtern aber lediglich ein paar Regeln, nicht jedoch den Inhalten der Pakete selbst.

Und da liegt der Hase im Pfeffer:
Ein IDS ist eine weitere Sicherheitsschicht, die du so nicht hattest bislang. Sie hilft dir zunächst einmal zu verstehen, welche Vorgänge überhaupt als ein Alarm zu sehen sind, und diese dann mittels Filter auszublenden. So stellst du deinen Türsteher immer schärfer ein, bis du nur noch die „echten“ Alarme angezeigt bekommst. Dies würde ich als eine Voraussetzung dafür sehen, überhaupt ein IPS zu betreiben später – mit klaren Vorstellungen, was überhaupt automatisch gesperrt werden muss und darf.

Das IDS selbst verstärkt mein subjektives Sicherheitsgefühl nicht wirklich, aber es erlaubt mir einen Einblick in die tiefen Vorgänge hier – welche zu Stunde lediglich bestätigen, dass alles in Ordnung ist. In Kombination mit einer Pi-hole & Unbound-Instanz wird das LAN also immer weiter gehärtet und für die Anforderungen der Zukunft bereits jetzt sauber aufgestellt. Und dabei sind Raspis nach wie vor kleine, unscheinbare Helferlein, die sich mit wenig Energie begnügen und nicht allzu anspruchsvoll an die Kühlung sind.

Die Konfiguration in diesem Beitrag mag erschlagend wirken – ist sie aber eigentlich nicht wirklich. Hier hast du alles Handwerkszeug, auf Funktionalität getestet und vor Ort am Laufen.

Jetzt bist du dran!

Thinking ‚bout’…

Hinterlasse einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert