Vorwort – Warum ein Thin-Client mit KI?
Meine Motivation
Die Ausgangslage in vielen kleinen und mittelständischen Betrieben, also genau dem Umfeld, in dem ich und die meisten anderen sich bewegen, sieht meistens so aus: begrenztes Budget, überschaubares IT-Personal (oft genau eine Person, nämlich ich), aber trotzdem die Erwartung, dass Infrastruktur und Security auf Enterprise-Niveau laufen. Klassische Monitoring- und SIEM-Lösungen? Teuer, komplex, und sie erzeugen am Ende vor allem eins: Alerts, die niemand auswertet, weil die Zeit dafür fehlt.
Gleichzeitig ist KI gerade überall – aber ehrlich, ich wollte nicht einfach nur ChatGPT irgendwo draufkleben und es „KI-gestützte Automatisierung" nennen. Was mich wirklich interessiert hat, war die Frage: Kann ein LLM als lokaler, isolierter Agent tatsächlich operative Arbeit abnehmen? Nicht als Spielzeug, sondern als Werkzeug, das Telemetrie analysiert, Anomalien erkennt und mir fundierte Handlungsvorschläge macht – während ich die Kontrolle behalte.
Ein paar Beweggründe, die mich konkret getrieben haben:
- Security Operations ohne Security-Team. Angriffe wie der Mirai-Botnetz-Scan aus meinem Szenario passieren ständig, vollautomatisch, rund um die Uhr. Ich schlafe aber nachts. Ein Agent, der eBPF-Telemetrie auswertet und mir morgens sagt „Host X hat 300 Telnet-Verbindungsversuche gemacht, hier ist mein Vorschlag", ist realistischer als ein 24/7-SOC – und eine spannende technische Herausforderung.
- Hardware-Separation aus Prinzip. Ein 32B-LLM braucht eine dicke GPU. Mein Edge-Node soll aber 5 Watt verbrauchen und unauffällig im Netzwerk sitzen. Die Lösung – Inferenz auf dem Desktop mit LM Studio, Ausführung auf dem Wyse 5070 – ist nicht nur pragmatisch, sondern auch ein Security-Feature: Der Thin-Client mit den Credentials und Tools redet mit dem LLM nur über eine strikt begrenzte API. Kein Internetzugang fürs Modell, keine Secrets im Prompt. Das war für mich eine der wichtigsten Lektionen: Architektur ist Datenschutz.
- Lernen durch Bauen (und Scheitern). Ich hätte auch einfach die Doku zu Ansible, eBPF und der NVD-API lesen können. Habe ich auch – aber richtig verstanden habe ich die Konzepte erst, als ich sie in einem Gesamtsystem zusammenzwingen musste. Function Calling, Tool-Whitelists, LXC-Sandboxes für Patch-Tests: Das alles ergibt erst Sinn, wenn man sieht, warum man es braucht.
- Der Realitätscheck: Mensch bleibt im Loop. Ein Punkt, der mir wichtig ist, weil er oft unterschlagen wird: Ich lasse die KI nichts eigenständig auf produktive Systeme los. Jedes Ansible-Playbook, jede Switch-Isolation geht durch meine Freigabe. Das ist kein Mangel des Systems – das ist Design. Automatisierung ohne Kontrolle ist keine Lösung, sondern ein Incident mit Verzögerung.
Was dich in diesem Tutorial erwartet
Wir nehmen einen frisch mit Ubuntu Server 24.04 installierten Dell Wyse 5070 und bauen Schritt für Schritt einen KI-Orchestrator, der zwei reale Szenarien abdeckt: die verhaltensbasierte Netzwerkanalyse mit eBPF (inklusive Erkennung eines Mirai-ähnlichen Scans) und ein automatisiertes Security-Audit eines vServers mit CVE-Abgleich und Patch-Vorschlag. Am Ende hast du ein lauffähiges System – und hoffentlich, genau wie ich, deutlich mehr Fragen als am Anfang. Das ist gut so.
Voraussetzungen
Grundkenntnisse in Linux, Netzwerken und Python schaden nicht, Motivation schadet definitiv nicht. Einen zweiten Rechner brauchst du übrigens auch – konkret sieht die Einkaufsliste so aus:
- Ein Dell Wyse 5070 (oder ein vergleichbarer stromsparender x86-Mini-PC – ein alter Thin-Client aus der Bucht tut's auch) mit frisch installiertem Ubuntu Server 24.04
- Eine Workstation mit GPU – für das Qwen2.5-32B in Q4_K_M sollten es schon ~24 GB VRAM sein (bei mir: RTX 4090). Weniger VRAM? Nimm ein kleineres Modell, das Prinzip bleibt gleich.
- Beide Kisten im selben LAN – idealerweise über WireGuard verbunden, dazu kommen wir in Teil 6
- Optional, aber nice to have: ein managed Switch (für die Port-Isolation im Angriffsszenario) und ein Test-vServer oder eine VM als „Opfer" für das Audit
- Zeit und Neugier – beides wird strapaziert, versprochen
Noch ein Wort der Warnung
Alle IP-Adressen, Hostnamen und Netzbereiche in diesem Tutorial (10.10.99.0/24, vserver-alpha und Co.) sind Beispiele aus meinem Labor. Du musst sie auf deine Umgebung anpassen – und bitte teste das Ganze nicht zuerst im produktiven Netz deines Ausbildungsbetriebs. Genau dafür sind Homelabs da.
So ist das Tutorial aufgebaut
- Teil 1: LLM-Server mit LM Studio auf dem Desktop einrichten
- Teil 2: Basis-Setup des Wyse (Python, eBPF, Ansible, LXC)
- Teil 3: Die Hermes-CLI bauen – Function Calling gegen das lokale LLM
- Teil 4: MCP-Tool-Server anbinden (eBPF, NVD, Switch-API)
- Teil 5: Die Szenarien nachspielen – Mirai-Erkennung & vServer-Audit
- Teil 6: Härtung, WireGuard, systemd-Service – damit das Ding auch dauerhaft läuft
Genug der Vorrede – schnappen wir uns den Thin-Client und fangen an. Der 5-Watt-Kasten wird dir zeigen, dass er mehr kann, als man ihm ansieht.
Ziel dieses Tutorials: Du baust aus einem Dell Wyse 5070 (Ubuntu Server 24.04, headless) einen KI-gesteuerten SOC-Orchestrator. Die LLM-Inferenz läuft auf einem separaten Desktop-PC mit GPU via LM Studio. Am Ende kannst du Befehle wie analyze local-network --behavioral und audit vserver-alpha --deep-scan ausführen.
Architektur-Überblick
Der Wyse 5070 führt kein lokales LLM aus. Die Inferenz läuft auf einem separaten Desktop-PC mit dedizierter GPU (≥24 GB VRAM), auf dem LM Studio das Modell Qwen2.5-32B-Instruct-Q4_K_M (~19,9 GB GGUF) lädt und über den integrierten Server im LAN bereitstellt. Der Hermes-Agent auf dem Wyse nutzt die OpenAI-kompatible API (base_url=http://<desktop-ip>:1234/v1) für alle Inferenz-Aufgaben.
Teil 1: LLM-Server auf dem Desktop-PC einrichten
- LM Studio installieren von lmstudio.ai auf dem Desktop-PC mit GPU (≥24 GB VRAM empfohlen).
- Modell laden: Qwen2.5-32B-Instruct als Q4_K_M GGUF (~19,9 GB) herunterladen und in LM Studio laden.
- Server starten:
- LM Studio → Tab „Developer" → Modell auswählen → Server aktivieren.
- Port: 1234, Haken bei „Serve on local network" setzen (bind 0.0.0.0).
- Verbindung testen (vom Wyse aus):
curl http://DESKTOP-IP:1234/v1/models
Wichtig: LM Studio hat keine echte Authentifizierung — der API-Key wird ignoriert. Daher muss der Zugriff netzseitig gehärtet werden:
# Zugriff nur aus dem KI-Subnetz erlauben
sudo ufw allow from 10.10.99.0/24 to any port 1234 proto tcp
sudo ufw deny 1234
sudo ufw enable
Optimalerweise läuft der Traffic über WireGuard (siehe Teil 5) und LM Studio bindet nur an das wg0-Interface.
Teil 2: Basis-Setup auf dem Wyse
Nach der Ubuntu-Server-24.04-Installation auf dem Dell Wyse 5070:
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip git curl jq \
bpfcc-tools linux-headers-$(uname -r) ansible lxd lxd-client
# Statische IP vergeben (netplan)
sudo netplan apply
# SSH-Zugriff von deinem Admin-Client
sudo systemctl enable --now ssh
mkdir -p ~/edge-brain && cd ~/edge-brain
python3 -m venv venv && source venv/bin/activate
pip install openai requests aiohttp mcp
Teil 3: Die Hermes-CLI bauen (Function Calling gegen LM Studio)
Die Kernidee: Das LLM sieht nur Tool-Schemas, die Ausführung passiert lokal auf dem Wyse. Erstelle ~/edge-brain/hermes.py:
#!/usr/bin/env python3
import json, subprocess, sys
from openai import OpenAI
client = OpenAI(base_url="http://10.10.99.10:1234/v1", api_key="lm-studio")
MODEL = "qwen2.5-32b-instruct"
TOOLS = [
{"type": "function", "function": {
"name": "run_command",
"description": "Führt ein erlaubtes Shell-Kommando auf dem Edge-Node aus",
"parameters": {"type": "object", "properties": {
"cmd": {"type": "string"}}, "required": ["cmd"]}}},
{"type": "function", "function": {
"name": "ebpf_flow_metrics",
"description": "Liefert aggregierte eBPF-Netzwerk-Flow-Metriken",
"parameters": {"type": "object", "properties": {}}}},
{"type": "function", "function": {
"name": "cve_lookup",
"description": "Fragt die NVD API nach CVEs für ein Produkt/Version ab",
"parameters": {"type": "object", "properties": {
"keyword": {"type": "string"}}, "required": ["keyword"]}}},
{"type": "function", "function": {
"name": "ansible_run",
"description": "Führt ein Ansible-Playbook aus (erst nach Freigabe!)",
"parameters": {"type": "object", "properties": {
"playbook": {"type": "string"}}, "required": ["playbook"]}}},
]
WHITELIST = ["ss ", "ip ", "sudo bpftrace", "docker ps", "dpkg -l",
"cat ", "ls ", "nmap "]
def run_command(cmd):
if not any(cmd.startswith(w) for w in WHITELIST):
return "[BLOCKIERT] Kommando nicht in Whitelist."
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=60)
return (r.stdout + r.stderr)[:4000]
def ebpf_flow_metrics():
r = subprocess.run("sudo timeout 10 tcpconnect-bpfcc -t",
shell=True, capture_output=True, text=True)
return r.stdout[:4000]
def cve_lookup(keyword):
import requests
r = requests.get("https://services.nvd.nist.gov/rest/json/cves/2.0",
params={"keywordSearch": keyword, "resultsPerPage": 5})
data = r.json()
return json.dumps([{"id": v["cve"]["id"],
"desc": v["cve"]["descriptions"][0]["value"][:300]}
for v in data.get("vulnerabilities", [])])
def ansible_run(playbook):
with open("/tmp/proposal.yml", "w") as f:
f.write(playbook)
print(playbook)
if input("Playbook ausführen? [y/N] ") == "y":
r = subprocess.run(["ansible-playbook", "/tmp/proposal.yml"],
capture_output=True, text=True)
return r.stdout + r.stderr
return "Abgebrochen durch Operator."
HANDLERS = {"run_command": run_command,
"ebpf_flow_metrics": lambda **_: ebpf_flow_metrics(),
"cve_lookup": cve_lookup,
"ansible_run": ansible_run}
SYSTEM = """Du bist Hermes, ein IT-Operations-Agent auf einem Ubuntu Server.
Analysiere Infrastruktur-Telemetrie, erkenne Anomalien (Scans, C2-Patterns),
und schlage konkrete Remediation vor. Antworte auf Deutsch, präzise, im Format:
[ERKENNUNG] ... [PROPOSAL] ... Führe Aktionen NUR über Tools aus."""
def chat(user_msg):
messages = [{"role": "system", "content": SYSTEM},
{"role": "user", "content": user_msg}]
while True:
resp = client.chat.completions.create(model=MODEL,
messages=messages, tools=TOOLS)
msg = resp.choices[0].message
messages.append(msg)
if msg.tool_calls:
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
print(f"[TOOL] {tc.function.name}({args})")
result = HANDLERS[tc.function.name](**args)
messages.append({"role": "tool", "tool_call_id": tc.id,
"content": str(result)})
else:
print(msg.content)
break
if __name__ == "__main__":
chat(" ".join(sys.argv[1:]))
Test:
python hermes.py "Analysiere die eBPF-Flows und melde verdächtige Verbindungen."
Teil 4: Netzwerk-Analyse mit eBPF (Use Case „Mirai-Scan")
Die bpfcc-Tools liefern eBPF-basierte Connect-Metriken ohne nennenswerte CPU-Last auf dem Wyse. Ein Kernel-Hook auf tcp_connect protokolliert alle ausgehenden Verbindungen.
# Live-Verbindungen beobachten (eBPF, Kernel-Hook auf tcp_connect)
sudo tcpconnect-bpfcc -t
Oder als Cron/Skript-Aggregator, der speziell auf Telnet-Scans (Port 23) prüft:
#!/bin/bash
sudo timeout 60 tcpconnect-bpfcc | awk '$NF==23 {print $4, $5}' \
| sort | uniq -c | awk '$1 > 10 {print "[WARNUNG] " $0}'
Ausführbar machen und testen:
chmod +x ~/edge-brain/watch_telnet.sh
python hermes.py "Hier sind Telemetriedaten: $(./watch_telnet.sh). \
Bewerte: Handelt es sich um einen Mirai-ähnlichen C2-Scan? Schlage Isolation vor."
Switch-Isolation (als REST-API-Tool erweiterbar):
curl -X POST https://core-switch/api/ports/14 \
-H "Authorization: Bearer $SWITCH_TOKEN" \
-d '{"admin_state": "down"}'
Teil 5: vServer-Audit mit NVD, LXC-Sandbox und Ansible
a) WireGuard-Tunnel zum vServer
sudo apt install wireguard -y
wg genkey | tee privatekey | wg pubkey > publickey
# /etc/wireguard/wg0.conf konfigurieren (Peer = vServer)
sudo systemctl enable --now wg-quick@wg0
b) LXC initialisieren
sudo lxd init # Defaults akzeptieren, Storage: dir
sudo lxc launch ubuntu:24.04 patch-test
sudo lxc exec patch-test -- apt update
c) Der Audit-Workflow
Ein einziger Prompt triggert die gesamte Kette:
python hermes.py "Führe ein Deep-Audit auf vserver-alpha durch: \
1) ssh vserver-alpha 'dpkg -l && docker ps' \
2) Prüfe gefundene Versionen via cve_lookup gegen die NVD \
3) Bei Befund: generiere ein Ansible-Playbook mit Patch, Health-Check \
(GET https://app.kunde.de/api/health) und Rollback-Strategie."
Das LLM ruft nacheinander run_command → cve_lookup → ansible_run auf. Die Freigabe bleibt bei dir (Y/n-Prompt im ansible_run-Handler).
Teil 6: Absicherung (Security-Härtung)
Da die LM-Studio-API standardmäßig keine echte Authentifizierung hat, sind folgende Maßnahmen essenziell:
- LLM-Isolation: LM Studio bindet nur an wg0; Desktop hat UFW-Regel (siehe Teil 1). Das LLM hat keinen Internetzugriff (kein Proxy, Firewall-Outbound-Regel).
- Keine Secrets ans LLM: SSH-Keys und API-Tokens liegen nur auf dem Wyse (~/.ssh, Environment-Variablen). Das LLM sieht nur Tool-Schemas und aggregierte Ausgaben.
- Command-Whitelist in hermes.py verhindert destruktive Befehle (rm, dd u. a. sind blockiert).
- Human-in-the-Loop: Jede schreibende Aktion (Ansible-Playbook, Switch-Port) erfordert eine manuelle Bestätigung über den Y/n-Prompt.
- Wyse selbst härten:
sudo ufw allow from DEIN-ADMIN-NETZ to any port 22
sudo ufw enable
sudo apt install unattended-upgrades -y
Systemd-Service für den Agenten
Damit Hermes als Daemon autark startet und bei Abstürzen neu hochkommt:
[Unit]
Description=Hermes Edge-Brain Agent
After=network-online.target
[Service]
User=lukas
WorkingDirectory=/home/lukas/edge-brain
ExecStart=/home/lukas/edge-brain/venv/bin/python hermes_daemon.py
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now hermes
Ergebnis
Du hast jetzt:
- Einen 5-Watt-Thin-Client als headless KI-Orchestrator
- LLM-Inferenz ausgelagert auf die GPU-Workstation (Client-Server-Trennung)
- eBPF-basierte Verhaltensanalyse des lokalen Netzes
- CVE-Automatching via NVD API
- Patch-Simulation in LXC-Sandbox + Ansible-Rollout mit Freigabe
- Strikte Trennung: Das LLM sieht nie Credentials, nur Tool-Aufrufe
In Planung: MCP-Server in dedizierte Prozesse auslagern (statt Inline-Handler), ein PowerShell-MCP-Modul für Windows Server / Active Directory-Anomalieerkennung, und langfristig vollautomatische Remediation für Low-Risk-Findings.