Hermes Agent einsperren
Auf dem gehärteten Raspberry Pi 5 zieht ein autonomer KI-Agent ein, der selbstständig Shell-Befehle ausführt, Web-Inhalte liest und Zugangsdaten verwaltet. Das verschiebt das Bedrohungsmodell: Die größte Gefahr kommt jetzt von innen. Die Antwort heißt Least Privilege.
Die Grundhärtung des Pi habe ich im Security Setup dokumentiert: SSH nur per Key, Firewall, Fail2ban, automatische Updates. Das schützt gegen Angreifer von außen. Für den Hermes Agent reicht das nicht, denn der Agent soll auf dem Pi autonom Kurzvideos produzieren und hochladen. Er führt dafür eigenständig Befehle aus, und genau das ist die neue Risikoklasse.
Das Bedrohungsmodell dreht sich um
Ein klassischer Server wird von außen angegriffen: Portscans, Brute-Force, Exploits. Ein autonomer Agent kann dagegen selbst zur Gefahr werden. Er liest Web-Inhalte, und eine präparierte Seite kann versuchen, ihm per Prompt Injection Befehle unterzuschieben. Oder er macht schlicht einen Fehler. In beiden Fällen gilt: Was der Agent nicht darf, kann er auch nicht kaputt machen. Jede Entscheidung in diesem Setup folgt aus diesem Prinzip.
Warum trotzdem kein einziger Port aufgeht
Der Agent braucht nur ausgehende Verbindungen: API-Aufrufe zum Modell, der YouTube-Upload, die Websuche über ein lokales SearXNG auf dem Loopback. Eingehend muss nichts erreichbar sein. Der Router bleibt dicht, die Firewall-Regeln aus der Grundhärtung bleiben unverändert.
Ein User, der nichts darf
Der Agent bekommt einen eigenen Benutzer ohne Passwort, ohne sudo und ohne SSH-Zugang. Zugriff gibt es nur über den Admin-Account.
sudo adduser --disabled-password --gecos '' hermes
sudo -l -U hermes
→ User hermes is not allowed to run sudo on raspberrypi5
Die Gegenprobe ist der wichtigste Teil. Erst wenn sudo -l -U die Ablehnung bestätigt, ist der User wirklich eingesperrt. Der Root-Account selbst ist ohnehin gesperrt.
curl pipe bash? Erst lesen
Der offizielle Installationsweg ist curl | bash. Stattdessen habe ich das Skript nach /tmp geladen und gelesen. Ergebnis: Der Installer schreibt nur ins Home-Verzeichnis, sudo will er ausschließlich für Systempakete. Also habe ich jede sudo-Abfrage mit n beantwortet und Systempakete separat und nachvollziehbar über den Admin-Account installiert. Erfreulich: arm64 wird first-class unterstützt, alle Python-Pakete kamen als fertige Wheels, kein einziges Build-Tool nötig.
Die npx-als-Root-Falle
Ein Detail aus dem Installer verdient einen eigenen Abschnitt. Für die Browser-Bibliotheken schlägt er vor:
sudo npx playwright install-deps chromium
(from /home/hermes/.hermes/hermes-agent, ...)
Klingt harmlos, wäre aber: Root führt Code aus einem Verzeichnis aus, das dem eingesperrten Agent-User gehört. Manipulierter Inhalt dort liefe mit Root-Rechten, und das ganze Least-Privilege-Konzept wäre an einer Stelle ausgehebelt, an die später niemand mehr denkt. Also abgelehnt. Die Pointe auf Debian 13: hermes doctor meldete die Chromium-Bibliotheken als bereits vorhanden, der Vorschlag wäre nicht nur riskant, sondern auch überflüssig gewesen.
Blank Slate: alles aus, dann gezielt an
Beim Setup bietet Hermes drei Wege an. Ich habe Blank Slate gewählt: Alles startet ausgeschaltet, jedes Toolset wird bewusst aktiviert. Am Ende sind es acht: Dateien, Terminal, Websuche, Browser, Vision, Rückfragen, Sprachausgabe und Bildgenerierung. Skills, Messaging, MCP und alles andere bleiben aus. Was nicht aktiv ist, kann nicht missbraucht werden.
Docker rootless: root, der keiner ist
Shell-Befehle des Agenten laufen nicht direkt auf dem Host, sondern in einem Docker-Container. Entscheidend ist das Wie: Der Agent-User kommt nicht in die docker-Gruppe, denn die ist faktisch Root. Stattdessen läuft ein rootless Daemon als Prozess des Agent-Users. Bricht etwas aus dem Container aus, landet es beim rechtlosen User, nicht bei Root.
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-ce-rootless-extras uidmap dbus-user-session
sudo systemctl disable --now docker.service docker.socket containerd.service
sudo loginctl enable-linger hermes
# als Agent-User:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
dockerd-rootless-setuptool.sh install
systemctl --user enable --now docker
Der Stolperstein: In einer sudo -u hermes -i Shell fehlt die systemd-User-Session. Ohne die beiden Exports schlägt systemctl --user fehl. Alle Exports stehen deshalb mit $(id -u) in der .bashrc. Linger sorgt dafür, dass der Daemon beim Boot startet, ohne dass sich jemand einloggt.
Der Pi-Stolperstein: ein unsichtbares cgroup_disable
Auf dem Pi kam eine Überraschung dazu, die es auf normalen Servern nicht gibt. Die Container bekommen Ressourcen-Limits, damit ein Amok laufender Agent nicht die ganze Maschine frisst. Aber docker info meldete: No memory limit support. Die Ursache steckt in der Firmware: Sie stellt der Kernel-Cmdline ein cgroup_disable=memory voran, das in keiner Konfigurationsdatei sichtbar ist. Das RAM-Limit würde still ignoriert.
# ans Ende der einen Zeile anhängen (es bleibt EINE Zeile!):
cgroup_enable=memory cgroup_memory=1
# nach dem Reboot der Härtetest:
docker run --rm --memory=256m alpine cat /sys/fs/cgroup/memory.max
→ 268435456
Der Nachtrag in der Cmdline wird nach dem Firmware-Teil verarbeitet und gewinnt. Erst der Härtetest mit den 256 MB beweist, dass das Limit wirklich greift. Danach laufen die Agent-Container mit 2 CPU-Kernen, 3 GB RAM und 20 GB Disk.
Die Überraschung: Approval ODER Container
Hermes hat ein Approval-System: Bei gefährlichen Befehlsmustern hält der Agent an und fragt nach. Mein ursprünglicher Plan war Docker plus Approval, doppelte Absicherung. Die Doku und die Praxis sagen: Das gibt es so nicht. Die Befehlsprüfung greift nur, wenn Befehle direkt auf dem Host laufen. Beim Docker-Backend wird sie übersprungen, der Container selbst ist die Sicherheitsgrenze.
Die Konsequenz ist eine klare Entscheidung pro Betriebsart: Wer daneben sitzt, fährt lokal mit Approval und bestätigt jeden heiklen Befehl. Wer autonom fährt, braucht den Container mit Limits, denn nachts drückt niemand auf Approve. Eine Hardline-Blocklist für die ganz groben Sachen wie rm -rf / oder Fork-Bomben gilt bei Hermes übrigens immer und lässt sich nicht abschalten.
Der Beweis
Zum Abschluss die Aufgabe an den Agenten: Führe hostname und whoami aus.
hostname: 9a2a41dbddc4 ← Container-ID statt raspberrypi5
whoami: root ← root im Container
Das root sieht gefährlicher aus, als es ist: Per User-Namespace-Mapping ist dieses root auf dem Host nur der rechtlose Agent-User. Im Container darf der Agent alles, auf dem Host ist er niemand.
Least Privilege in der Praxis
Zwei bewusste Lockerungen zeigen, wie das Prinzip im Alltag funktioniert. Für den YouTube-Upload bekommt der Container die Zugangsdaten eines dedizierten Kontos read-only gemountet, Scope nur Upload, Token jederzeit widerrufbar. Der Mount bewährte sich sofort: Beim ersten Lauf versuchte der Agent, ins Credentials-Verzeichnis zu schreiben, und wurde vom Read-only-Mount abgewiesen. Später kam ein zweiter Read-only-Mount für den Bilder-Cache dazu, und zwar bewusst nur dieser eine Unterordner, nicht das ganze Konfigurationsverzeichnis mit den Tokens.
Das Muster ist immer gleich: Zugriff nur auf das, was die Aufgabe wirklich braucht, nur in die Richtung, die nötig ist, und jede Grenze einmal aktiv testen.
Meine Checkliste
[x] Agent-User ohne Passwort, ohne sudo, ohne SSH-Login
[x] Root-Account gesperrt
[x] Installer gelesen statt curl | bash, sudo-Abfragen abgelehnt
[x] Systempakete separat über den Admin-Account
[x] Kein npx/Installer-Code als Root aus dem Agent-Home
[x] Blank-Slate-Setup, nur benötigte Toolsets aktiv
[x] Docker rootless, Agent-User NICHT in der docker-Gruppe
[x] Root-Daemon und System-containerd deaktiviert
[x] memory-cgroup aktiviert, RAM-Limit im Härtetest verifiziert
[x] Container-Limits: 2 CPU, 3 GB RAM, 20 GB Disk
[x] Approval vs. Container verstanden, Betriebsart bewusst gewählt
[x] Zugangsdaten 600, dediziertes Upload-Konto, Mounts read-only
[x] Kein offener Port, alles ausgehend
Damit ist der Agent produktionsbereit eingesperrt. Wie die eigentliche Kurzvideo-Pipeline darauf aufsetzt, von der Recherche bis zum automatischen Upload, ist Thema des nächsten Beitrags: Shorts-Pipeline mit Hermes Agent.