Doku: fremde Laufzeiten und Egress-Proxy - Umsetzung, Befunde, Einrichtung (#712)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-25 23:18:19 +02:00
co-authored by Claude Opus 5.5
parent d37fe91b37
commit 036d56ff04
3 changed files with 136 additions and 0 deletions
+56
View File
@@ -317,6 +317,62 @@ Beispiel: Claude Sonnet, typischer Run (5000 in, 1000 out):
---
## 5a. Fremde Laufzeit: ein Agent in Claude Code (#712)
Ein Agent kann statt in unserer Engine in **Claude Code** arbeiten (gedacht für die
Entwicklungsabteilung) oder in einem **Befehl**. Er läuft dann in der Sandbox auf einer
Kopie seines Arbeitsbereichs. Was er ändert, liegt danach als Vorschlag unter
`Agents/Agent-<id>/proposals/<lauf>/` und wird **nicht** übernommen. Taskboard und Kollegen
erreicht er nur über den MCP-Server der Instanz. Hintergrund: Organisation-Konzept,
Abschnitt 7a.
**Voraussetzungen (nur Linux):**
1. `bubblewrap` ist installiert (`apt install bubblewrap`). Die Karte „Laufzeit“ in der
Agentenansicht meldet „Die Sandbox ist bereit“. Ohne Sandbox laufen fremde Laufzeiten
gar nicht, sie fallen auch nicht auf die native Engine zurück.
2. Claude Code ist für den Benutzer installiert, unter dem die Instanz läuft. Mit dem
nativen Installer liegt es unter `~/.local/bin/claude` und wird dort gefunden. Bei einer
npm-Installation kommen das Programm und Node in `claudeCode.executable` bzw.
`claudeCode.readOnlyPaths` der `instance.json`.
3. Das Konto (Karte „Laufzeit“ → „Bearbeiten“, gilt für die ganze Instanz):
- **API-Schlüssel (Voreinstellung):** am besten ein eigener Schlüssel in einem
Anthropic-Workspace mit Ausgabengrenze. Der Schlüssel ist im Lauf für Claude Code
lesbar, denn es muss ihn benutzen.
- **Abo:** ein Token aus `claude setup-token`, erzeugt auf einem Rechner, an dem du
angemeldet bist. Das Abo teilt sein Kontingent mit deiner eigenen Nutzung, und ob es
ein Agentensystem betreiben darf, regeln seine Bedingungen.
**Der Agent** (Karte „Laufzeit“ oder `AgentSettings.json`):
```json
"runtime": {
"kind": "claude-code",
"allowedTools": ["Read", "Edit", "Write", "Glob", "Grep", "mcp__clawd"],
"maxTurns": 30,
"timeoutMinutes": 30,
"egress": ["pypi.org", "files.pythonhosted.org"]
}
```
- `allowedTools` leer lassen heißt: die obige Voreinstellung. `Bash` erlaubt die Shell
**in der Sandbox**. WebFetch und WebSearch sind immer verboten.
- `egress` nennt Hosts, die der Lauf zusätzlich zur API erreichen darf. Es gilt nur HTTPS
und nur an öffentliche Adressen.
- `model` des Agenten wird an Claude Code weitergegeben (`anthropic/claude-sonnet-5` wird
zu `claude-sonnet-5`). Ein Modell eines anderen Anbieters wird ignoriert; dann gilt die
Voreinstellung von Claude Code.
Beim Befehl (`"kind": "command"`, dazu `command` und `arguments`) kommt die Nachricht über
stdin, und was der Befehl ausgibt, ist das Ergebnis. Den MCP-Server erreicht er mit
`$CLAWD_HELPER --mcp-bridge --socket $CLAWD_MCP_SOCKET`; das Token liegt in
`$CLAWD_MCP_TOKEN_FILE`.
**Verbrauch:** Jeder Lauf steht in `RunUsage` mit Laufzeit und Token. Auf dem Abo kostet
er 0 $ und ist als Abo-Lauf markiert, verbraucht aber Kontingent.
---
## 6. Kritische Analyse: Ist die Idee umsetzbar?
Vor dem Aufbau einer Instanz systematisch prüfen:
+56
View File
@@ -828,6 +828,62 @@ verbraucht.
beide Anmeldewege gebaut, Abo und API-Schlüssel, und die Entscheidung bleibt eine
Frage der Konfiguration statt eine des Umbaus.
### Umsetzung (2026-09-25, #712)
Gebaut sind `native`, `claude-code` und `command`. `codex` und `http` sind nicht gebaut
(„nach Bedarf“); die Einstellung lehnt sie ab, statt sie still als native laufen zu lassen.
Ein Agent bekommt seine Laufzeit in der Agentenansicht (Karte „Laufzeit“) oder als
`runtime` in `AgentSettings.json`. Das Claude-Code-Konto gilt für die ganze Instanz
(`claudeCode` in `instance.json`, Schlüssel verschlüsselt wie der OpenRouter-Schlüssel).
- **Ohne Sandbox kein Lauf.** Ein fremder Lauf fällt nie auf die native Engine zurück,
auch nicht ohne Runner. Deren Modell und Kosten wären nicht die konfigurierten.
- **Eigener Arbeitsplatz ist eine Kopie.** Der Lauf arbeitet auf einer Kopie des
Arbeitsbereichs, höchstens 20 000 Dateien und 512 MB. Was sich darin ändert, wird zum
Vorschlag unter `proposals/<lauf>/`: `proposal.json` und die geänderten Dateien.
Übernommen wird nichts; das ist die Sache der Lieferkette (#695). Gelesen werden nur
reguläre Dateien. Ein symbolischer Link, den der Lauf hinterlässt, könnte außerhalb der
Sandbox auf einen Schlüssel zeigen, eine benannte Pipe würde den Leser für immer
blockieren. Beide werden nur als „ignoriert“ genannt.
- **Netz nur über den Proxy.** Die Sandbox behält ihr eigenes Netz ohne alles außer
Loopback. Claude Code erreicht `api.anthropic.com` über einen HTTPS-Proxy im
Hauptprozess, dazu die Hosts, die der Agent einträgt. Der Proxy ist gleich mit gebaut
(#655, Security-Konzept 5.2). Updates und Telemetrie von Claude Code sind abgeschaltet.
- **Nur unsere Einstellungen gelten.** `--setting-sources user` liest nur die Einstellungen,
die vor jedem Lauf ins Home der Laufzeit geschrieben werden, `--strict-mcp-config` nur
unseren MCP-Server. Eine `.claude/settings.json` oder `.mcp.json` in der Kopie gewährt
also nichts. Hooks sind aus. Was ein Lauf im Home hinterlässt und den nächsten anweisen
könnte (Agents, Commands, Skills, `CLAUDE.md`), wird vor dem Start entfernt.
Anmeldestand und Verlauf bleiben.
- **Freigabelisten:** `--allowedTools` (Voreinstellung: Read, Edit, Write, Glob, Grep und
`mcp__clawd`) und `--max-turns`. WebFetch und WebSearch sind immer verboten: Das Web
läuft über unsere Werkzeuge, nicht an ihnen vorbei. `--dangerously-skip-permissions`
gibt es nicht, auch nicht als Werkzeugname verkleidet.
- **Der Rückweg:** Die Anwendung selbst ist in der Sandbox die MCP-Brücke
(`--mcp-bridge --socket /run/mcp.sock`). Der MCP-Socket ist eingebunden, das
Lauf-Token liegt in einer 0600-Datei und endet mit dem Lauf.
- **Schlüssel nie auf der Befehlszeile.** Die Anwendung ist in der Sandbox auch der Starter
(`--sandbox-launch`). Er liest den Schlüssel aus einer eingebundenen Datei und setzt ihn
in die Umgebung des Programms. Er leitet außerdem den Proxy-Port der Sandbox an den
Proxy-Socket weiter.
- **Verbrauch:** `RunUsage` hat die Spalten `Runtime` und `OnSubscription`. Ein Lauf auf
dem Abo kostet 0 $ und trägt seine Token; mit API-Schlüssel gilt der gemeldete Betrag
(`total_cost_usd`). Die Anzeige als Kontingentverbrauch ist Sache von #699.
- **Chat:** Eine fremde Laufzeit hat keinen Gesprächsverlauf, der weitergeführt würde. Jede
Nachricht ist ein eigener Lauf; der Chat zeigt trotzdem beide Seiten.
Geprüft unter Linux auf der Test-VM, in der echten Sandbox: Schlüssel stehen in keiner
Befehlszeile, ein gelisteter Host ist erreichbar, ein anderer nicht, am Proxy vorbei gibt
es kein Netz, der Loopback der Instanz ist nicht der der Sandbox. Ein Programm in der
Sandbox erreicht den MCP-Server als sein Agent. Der ganze Claude-Code-Weg ist mit einem
Stellvertreter für `claude` geprüft, weil das echte Claude Code auf der Test-VM nicht
laufen darf. **Offen:** der Lauf mit dem echten Claude Code und einem echten Schlüssel.
Das macht der Mensch einmal von Hand.
Nicht gebaut: eine Speichergrenze für fremde Laufzeiten. Eine JavaScript-Laufzeit
reserviert Adressraum in Gigabyte-Blöcken und startet unter keiner sinnvollen `ulimit -v`.
Eine echte Grenze bräuchte cgroups.
---
## 8 — Kontrolle: eine Leitung delegiert nicht nur
+24
View File
@@ -197,6 +197,30 @@ Manifest** (Abschnitt 7), und die Freigabe des Skripts umfasst diese Ziele. Durc
wird es vom Egress-Proxy: nur die deklarierten Domains, nur HTTPS, jeder Abruf im Audit.
Ein Skript, das etwas anderes erreichen will, scheitert und erzeugt einen Befund.
*Der Egress-Proxy ist gebaut (2026-09-25, mit #712), die Anbindung an Skript-Manifeste
noch nicht (#655, #695).* So umgesetzt:
- **Die Sandbox teilt nie das Netz des Hosts.** Mit geteiltem Netz sähe sie alles, was der
Host auf Loopback anbietet, darunter den Port der Weboberfläche und eine Datenbank. Sie
sähe außerdem abstrakte Unix-Sockets wie ein X11-Display, denn die gehören zum
Netz-Namensraum, nicht zum Dateisystem. Die Sandbox behält ihr Netz mit nur Loopback. Ein
Starter in der Sandbox (die Anwendung mit `--sandbox-launch`) lauscht dort auf
`127.0.0.1:3128` und reicht jede Verbindung an einen Unix-Socket weiter. Der Socket ist
eingebunden, dahinter steht der Proxy im Hauptprozess.
- **Nur `CONNECT host:port`**, also ein TLS-Tunnel. Klartext-HTTP wird abgewiesen. Der Host
muss auf der Liste stehen (`host`, `host:port`, `*.domain` nur für Subdomains).
Adressliterale stehen nie auf der Liste.
- **Namen löst der Proxy auf, nicht die Sandbox.** Löst ein gelisteter Name auch nur
teilweise auf eine nicht-öffentliche Adresse auf, wird er ganz abgewiesen. Das betrifft
Loopback, private Netze, CGNAT, Link-Local (Metadaten-Dienste), Multicast und
IPv6-Hüllen wie 6to4 und NAT64. So kommt kein DNS-Rebinding auf die Instanz.
- Jede Verbindung und jede Abweisung steht im Log. Im Audit steht sie noch nicht, das kommt
mit den Manifesten.
Geprüft unter Linux in der echten Sandbox (`SandboxLaunchTests`): Ein gelisteter Host
antwortet, ein anderer bekommt 403, `--noproxy` findet kein Netz, und ein Dienst auf dem
Loopback des Hosts ist nicht erreichbar.
## 6 — Befunde und Quarantäne
### 6.1 Zwei Stufen