Ein Coding-Agent liest Quelltext, Anweisungen und Webseiten und handelt auf dieser Grundlage. Er bearbeitet Dateien, führt Befehle aus und ruft Dienste im Netz auf. Jeder Text, den er liest, kann damit sein Handeln beeinflussen, auch ein Kommentar in einer Abhängigkeit oder eine präparierte Webseite. Dieses Risiko wird als Prompt-Injection beschrieben (OWASP LLM01 ). Kritisch wird es, wenn ein Agent zugleich private Daten liest, fremden Inhalten ausgesetzt ist und nach außen kommunizieren kann (Willison ). Auf einem Entwicklerrechner treffen alle drei Bedingungen zu.

Ziel und Abgrenzung

Das hier beschriebene Werkzeug cjx  führt einen Agenten in einem Container aus und begrenzt dabei drei Dinge. Das Projekt ist standardmäßig nur lesbar und wird erst mit --rw beschreibbar. Netzverbindungen sind nur zu Hosts möglich, die auf einer Allowlist stehen. Der Container läuft ohne Privilegien. Unterstützt werden Claude Code, OpenAI Codex und pi.dev, weitere Agenten lassen sich über eine Konfigurationsdatei ergänzen.

Die Sandbox ist eine praktische Grenze für das Verhalten eines Agenten. Gegen eine Schwachstelle im Kernel, die aus einem Container ausbricht, schützt sie nicht.

Aufbau

Home-VoluimneternesNetzPIrASnogqtjeueenirkrtCd4ntoO4e/N3trN,wECATllowlist

Jeder Start erzeugt einen Agent-Container, dem alle Linux-Capabilities entzogen sind. Er läuft mit no-new-privileges, einem schreibgeschützten Root-Dateisystem und begrenzter Zahl an CPUs, Speicher und Prozessen. Beschreibbar sind nur flüchtige tmpfs-Verzeichnisse wie /tmp, das Home-Volume und, mit --rw, das Projekt sowie per --add-dir eingebundene Verzeichnisse. Das Home-Volume gehört zu einem Agenten und dessen Modus und enthält Anmeldung und Verlauf. Es bleibt über Sitzungen hinweg erhalten und liegt im Volume-Speicher der Engine, nicht in einem Verzeichnis des Benutzers.

Die Images entstehen in Schichten. Ein Basis-Image enthält Debian, Node.js und einen Benutzer für den Agenten, darauf installiert ein Image pro Agent dessen offizielles Installationsskript. Für Claude Code legt das Basis-Image zusätzlich eine verwaltete Richtlinie an, die Claude Code das Lesen von .env-Dateien und secrets/-Verzeichnissen verbietet (Claude Code Permissions ). Sie gilt für die Werkzeuge von Claude Code, nicht für Skripte, die der Agent ausführt. Ein Hash über Build-Dateien und Agent-Definition, gespeichert als Label am Image, legt fest, wann ein Image neu gebaut wird.

Firewall pro Sitzung

Zu jedem Start gehören ein eigenes Netz und ein eigener Squid-Container. Das Netz ist intern und hat keine Route nach außen. Unter Podman fehlt ihm zusätzlich ein DNS-Server, und Docker leitet DNS-Anfragen aus internen Netzen seit einer Sicherheitskorrektur nicht mehr nach außen weiter (CVE-2024-29018 ). Der Agent kann damit weder Namen im Internet auflösen noch Verbindungen dorthin aufbauen. Squid ist sowohl mit diesem Netz als auch mit dem Standardnetz der Engine verbunden und dient dem Agenten als Proxy.

Squid erlaubt ausschließlich die Methode CONNECT (RFC 9110 ) auf Port 443 und nur zu Zielen auf der Allowlist.

acl allowed_dst dstdomain "/etc/cjx/allowlist"
http_access allow CONNECT SSL_ports allowed_dst
http_access deny all

Die Allowlist setzt sich aus den Hosts des Agenten und den Einträgen des Benutzers zusammen. Squid sieht dabei nur den Zielnamen, der Inhalt der TLS-Verbindung bleibt unberührt. Ist in der Shell ein Proxy gesetzt und vom Host aus erreichbar, leitet Squid über diesen weiter. Am Ende der Sitzung werden beide Container, das Netz und die Sitzungsdaten entfernt.

Anmeldung in der Sandbox

Die Agenten melden sich per OAuth beim jeweiligen Anbieter an. Auf einem Desktop öffnet der Agent dazu den Browser und nimmt die Antwort über einen lokalen Rückruf-Server entgegen (RFC 8252 ). In der Sandbox läuft dieser Server im Container, wo der Browser des Hosts ihn nicht erreicht.

Die Hersteller sehen für diesen Fall eigene Wege vor. Claude Code und pi.dev zeigen den Anmeldelink an und nehmen den Code oder die Ziel-URL anschließend per Eingabe entgegen. Codex bietet die Anmeldung per Gerätecode an (RFC 8628 ), die einmal in den ChatGPT-Sicherheitseinstellungen freigeschaltet wird. Dabei wird ein Code im Browser eingegeben. Die Anmeldung landet im Home-Volume und gilt damit für alle weiteren Sitzungen. Variablen, die das Terminal beschreiben, wie TERM_PROGRAM und COLORTERM, reicht cjx in den Container weiter, damit ein Agent anklickbare Links und Farben wie außerhalb verwendet. Weitere Variablen der Shell erreichen die eingebauten Agenten nicht.

Podman und Docker

cjx ist für Podman ohne Root-Rechte gedacht und unterstützt außerdem einen Docker-Daemon mit Root-Rechten. Rootless Podman ist die bevorzugte Grundlage, da die Container-Laufzeit keine Rechte über die des Benutzers hinaus besitzt. Unter Docker läuft der Daemon als root, und wer ihn erreicht, erlangt damit Root-Rechte auf dem Host (Docker Security ).

Der Agent muss das eingebundene Projekt unter der Kennung des Benutzers lesen und schreiben. Podman bildet die eigene uid mit --userns keep-id auf den Benutzer im Container ab (podman run ), unter Docker übergibt cjx die uid und gid mit --user. Zwei Docker-Varianten lehnt cjx ab. Rootless Docker bildet den Benutzer auf root im Container ab und alle übrigen Kennungen in einen Bereich untergeordneter IDs (Rootless mode ). Mit userns-remap verschiebt der Daemon sämtliche Kennungen aller Container, root eingeschlossen, in einen solchen Bereich (userns-remap ). In beiden Fällen gehört das eingebundene Projekt im Container nicht dem Agenten.

Grenzen

Die Allowlist begrenzt die Ziele, nicht die Daten. Ein erlaubter Host bleibt ein möglicher Kanal nach außen, darunter die API des Modellanbieters selbst. Die Richtlinie gegen das Lesen von .env-Dateien gilt nur für die Werkzeuge von Claude Code. Skripte, die ein Agent ausführt, und die übrigen Agenten lesen, was die Einbindung freigibt. Mit --no-firewall entfällt die Netzbeschränkung vollständig. Unter Docker bleibt der Daemon mit Root-Rechten Teil der Vertrauensbasis, und Codex läuft dort ohne Seccomp-Filter, weil seine bubblewrap-Sandbox einen User-Namespace braucht.

Fazit

cjx trennt das Handeln eines Agenten vom Rechner, auf dem er läuft. Schreiben erfordert eine Freigabe, das Netz ist auf benannte Hosts beschränkt, und die Anmeldung bleibt im Container. Der Agent arbeitet damit weiter wie gewohnt, und seine Möglichkeiten reichen nicht über diese Freigaben hinaus.