Sandbox-Backends
Überblick über die Backends
Abschnitt betitelt „Überblick über die Backends“| Backend | Wert | Plattform | Verfügbarkeit |
|---|---|---|---|
| Gekapselte VM | microvm |
macOS, Linux, Windows (microVM-Hypervisor) | An die Prüfung der microVM-Laufzeit gebunden; gilt für interaktive Terminals — die Shell läuft in einem Rig, nicht auf dem Host |
| Native sandbox | native |
macOS (Seatbelt), Linux und WSL2 (bubblewrap) | Beim Start geprüft; Linux verweigert einen Lauf, der Schreibverbote per Dateinamen-Glob verlangt, die bubblewrap nicht durchsetzen kann |
| No isolation | none |
Alle | Der Standard beim Parsen, der Standard des Terminals und der Rückfall, wenn kein Backend verfügbar ist oder --sandbox off gesetzt ist |
SandboxBackend.fromName liefert none für einen Nullwert oder einen unbekannten gespeicherten Wert. Ein gespeichertes "docker" wird auf native abgebildet.
microvm ist das einzige Backend mit einer Kernel-Grenze — die Arbeit
läuft wirklich nicht auf dem Host. Es ist in keine Richtung ein stiller
Rückfall: Wer es auf einem Host ausdrücklich verlangt, der keine VM starten
kann, scheitert laut („Ich habe eine VM verlangt und meinen Laptop bekommen“
ist die eine Verschlechterung, die sich die Kapsel nicht leisten kann), und
nur ein nicht festgelegter Standard wird auf native herabgestuft, mit
einer Logzeile, die das sagt. Das tatsächliche Backend der Sitzung wird an
den Client gemeldet und mit einem Badge versehen, ein Terminal kann also nie
als gekapselt gelten, während es auf dem Host läuft. Lebenszyklus, Images und
der Ausgang der VM selbst stehen in der
Rigs-Referenz.
Windows hat keine native Sandbox. NativeSandboxAdapter.probe liefert available: false auf jedem Host, der nicht macOS oder Linux/WSL2 ist. Der Agenten-Dispatch unter Windows nutzt deshalb none. Das Backend microvm ist davon getrennt: Es hängt an der microVM-Laufzeit und gilt für interaktive Terminals und Rigs, nicht für die Umhüllung von bash auf dem Host.
Prüfergebnis
Abschnitt betitelt „Prüfergebnis“Die Prüfung entscheidet, ob durchgesetzt wird. Sie läuft einmal, beim Start des Servers.
| Host | available |
Notiz / Hinweis |
|---|---|---|
| macOS | true |
sandbox-exec (Seatbelt) — Namespace-Isolation, keine Kernel-Grenze |
Linux oder WSL2 mit bwrap und socat |
true |
bubblewrap — Namespace-Isolation, keine Kernel-Grenze |
| Linux oder WSL2, eines der Werkzeuge fehlt | false |
requiresInstall: true, mit einem Installationshinweis, der die fehlenden Pakete nennt |
| Jede andere Plattform | false |
Eine native Sandbox wird auf dieser Plattform nicht angeboten |
Der Server schreibt das Ergebnis in eine Startzeile: agent sandbox ON plus die Notiz, no OS-native agent sandbox on this host plus den Hinweis, oder agent sandbox DISABLED by --sandbox off. GET /healthz trägt kein Sandbox-Feld.
--sandbox / CC_SERVER_SANDBOX nimmt on (Standard) oder off an. Es ist ein Abwählen, kein Einschalten: on heißt „umhüllen, wenn der Host ein Backend anbietet“, nie „eines verlangen“.
Natives Backend
Abschnitt betitelt „Natives Backend“macOS: Seatbelt
Abschnitt betitelt „macOS: Seatbelt“Nutzt Apples Seatbelt-Sandbox (dasselbe System wie die macOS App Sandbox).
Das erzeugte Profil ist standardmäßig permissiv ((allow default)) und schneidet Verbote aus, weil ein Profil, das standardmäßig alles verweigert, eine interaktive Shell unbenutzbar macht — es gibt zu viele dyld-, XPC- und Mach-Aufrufe, als dass man sie aufzählen könnte. Die praktische Folge: Ein Agent kann den größten Teil des Hosts weiterhin lesen; deine registrierten Checkouts sind durch ausdrückliche Verbote für Schreiben und Umbenennen geschützt, nicht durch Unsichtbarkeit.
- Dateisystem, Lesen: verweigert für die geheimen Pfade (
~/.ssh,~/.aws,~/.gnupg,~/.config/gh,~/Library/Keychainsund der Rest der Leseverbotsmenge) - Dateisystem, Schreiben: zuerst auf Verweigern zurückgesetzt, dann ausdrückliche Erlaubnisse für Teilpfade; die Pfade mit zwingendem Verbot (Shell-rc-Dateien,
.git/hooks, Claude-Konfiguration, geschützte Checkouts) bleiben auch innerhalb einer beschreibbaren Wurzel gesperrt.file-write-unlinkist auf jedem verweigerten Pfad und seinen Vorfahren verboten, ein Umbenennen kann ein Schreibverbot also nicht umgehen - Prozess:
process-execverweigert die dauerhaft gefährlichen Binaries, dazu eine Sperre für alles, was aus einem beschreibbaren Verzeichnis ausgeführt wird - Netzwerk: beschränkt auf die Ports der prozessinternen Proxys plus DNS — Loopback ist nicht pauschal erlaubt, und Unix-Sockets sind nicht erlaubt. Die Proxys setzen die Domain-Zulassungsliste je Sitzung durch (siehe Netzwerkausgang)
- Konfiguration: Profile werden je Start aus der
SandboxSpecerzeugt
Voraussetzungen:
sandbox-exec(/usr/bin/sandbox-exec), Teil jeder macOS-Installation — kein zusätzliches Werkzeug nötig
Linux: bubblewrap
Abschnitt betitelt „Linux: bubblewrap“Nutzt bubblewrap (bwrap), um Mount- und PID-Namespaces anzulegen.
- Dateisystem: legt einen Mount-Namespace an, in dem nur die gebundenen Pfade sichtbar sind
- Netzwerk: läuft in einem nicht geteilten Netzwerk-Namespace; der Verkehr erreicht die Host-Proxys über Unix-Socket-Brücken von
socat - Prozess: Isolation im PID-Namespace
Voraussetzungen:
bubblewrapundsocatinstalliert (sudo apt-get install bubblewrap socatunter Debian/Ubuntu)- Unterstützung für Kernel-Namespaces (Standard auf modernem Linux)
Bubblewrap kann einen bestehenden Pfad überlagern, aber keine neue Datei
.env, *.key oder *.pem in einem beschreibbaren Bind verbieten. Die
Standardrichtlinie des Dispatch verlangt genau diese Schreibverbote per
Dateinamen-Glob, deshalb schlägt der native Agenten-Dispatch unter
Linux/WSL2 derzeit fail-closed fehl, auch wenn die Prüfung bubblewrap findet. Er
läuft nicht mit einer stillschweigend schwächeren Richtlinie. Um den nativen
Dispatch unter Linux wiederherzustellen, braucht es ein Backend mit
Dateinamenregeln zum Erzeugungszeitpunkt. Das Sandboxing abzuschalten ist ein
ausdrückliches, unbeschränktes Abwählen, kein gleichwertiger Ersatz.
Backend ohne Isolation
Abschnitt betitelt „Backend ohne Isolation“Keine Sandbox. Der Prozess läuft direkt auf dem Host, mit Dateisystem, Netzwerk und Prozesszugriff des Hosts. ACP und das Werkzeug bash des Harness bekommen weiterhin die Bereinigung der Umgebung und die Befehlsrichtlinie; Claude Code über NoSandboxAdapter erbt die Umgebung des Hosts. Darauf fällt der Agenten-Dispatch zurück, wenn kein Backend verfügbar ist oder --sandbox off gesetzt ist, und das ist der Standard des Terminals, wenn ein Client none nennt.
Servergehostetes Terminal
Abschnitt betitelt „Servergehostetes Terminal“Das Terminal wird nach eigenen Regeln in die Sandbox gelegt, nicht durch die Prüfung des Dispatch. defaultBackend von TerminalSessionService ist none, und cc_server überschreibt das nicht. Eine Terminalsitzung führt also die Host-Shell aus, außer der Client verlangt ausdrücklich native — das Terminal der Desktop-App tut das nicht.
Wird native verlangt, umhüllt SandboxManager die Shell mit einem festen Terminalprofil: $HOME und das Arbeitsverzeichnis der Sitzung sind beschreibbar, das Netzwerk ist offen, und ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh und ~/Library/Keychains sind leseverboten. Das Forge-Token, das nur lesen darf, und das Push-Gateway gelten nicht — das sind Sachen des Dispatch. Ist die Umhüllung nicht unterstützt, schlägt die Sitzung fehl, statt ein natives Badge auf einem PTY der Host-Shell zu zeigen.
Netzwerkausgang
Abschnitt betitelt „Netzwerkausgang“Wird eine Sandbox gestartet, setzen prozessinterne Proxys den ausgehenden Netzwerkzugriff durch, nicht Regeln einer Kernel-Firewall. Auf einem Host ohne verfügbares Backend gilt davon nichts, und ein gestarteter Prozess erreicht das Netzwerk genau so, wie es der Server-Benutzer täte.
- Die Sandbox-Laufzeit betreibt einen HTTP/HTTPS-Proxy und einen SOCKS5-Proxy auf
127.0.0.1im Prozess voncc_server, ein Paar je eingeschränkter Sitzung (http_proxy.dart,socks_proxy.dart). Gleichzeitige Läufe teilen sich kein Paar. - Sandbox-Prozesse bekommen Proxy-Umgebungsvariablen (
HTTP_PROXY/HTTPS_PROXY→ der HTTP-Proxy,ALL_PROXY→ der SOCKS-Proxy;GIT_SSH_COMMANDleitet Git über SSH durch den SOCKS-Proxy). Unter Linux ist der Netzwerk-Namespace nicht geteilt, die Proxys sind also der einzige Weg nach draußen; unter macOS teilt die Sandbox das Loopback des Hosts und spricht die Proxys direkt an. - Der Zielhost jeder Verbindung wird gegen die Domain-Zulassungsliste der Sitzung geprüft; alles andere wird abgelehnt. Zwei Platzhalterformen, jede mit genau einem
*: ein führendes*.example.comtrifft jede Subdomain (einschließlicha.b.example.com), nicht aber die Apex selbst; ein mittleres Labelbedrock.*.amazonaws.comtrifft genau ein Label (bedrock.us-east-1.amazonaws.com, nichtbedrock.evil.attacker.amazonaws.com). - Die Zulassungsliste wird je Sitzung aus der kuratierten Basis aufgelöst (
kBaselineAllowedDomains: LLM-Anbieter, Git-Hosting, Paketregistries), plus die GitHub-Domains (kGithubDomains), plusSandboxSpec.egressAllowlist. Der Ausführungspfad übergibt nie zusätzliche Einträge inegressAllowlist, in der Praxis bekommt also jeder Lauf die Basis plus GitHub. - Eine feste Verbotsliste (
kBaselineDeniedDomains: Endpunkte für Cloud-Metadaten, Senken für Telemetrie) hat Vorrang vor allen Erlaubnissen. networkEnabled: falsestartet die Proxys je Sitzung trotzdem, die Zulassungsliste ist aber leer, also wird jede ausgehende Verbindung abgelehnt. Der Dispatch setzt den Wert beim Start des Laufs aus der Regel Auf das Netzwerk zugreifen: Verweigern schaltet das Netzwerk ab, Zulassen und Zuerst fragen lassen es an.
SandboxSpec
Abschnitt betitelt „SandboxSpec“Die Spezifikation, die an SandboxPort.launch geht:
| Feld | Typ | Beschreibung |
|---|---|---|
sessionId |
String |
Stabile Sitzungskennung (typischerweise die Bereichs-ID) |
workspaceId |
String |
ID des zugehörigen Arbeitsbereichs |
agentId |
String? |
Agent, der gerade an die Sandbox gebunden ist, falls vorhanden |
bindMounts |
List<SandboxBindMount> |
Mounts Host→Gast; die Sandbox sieht nur die hier aufgeführten Host-Pfade |
networkEnabled |
bool (Standard true) |
Beim Dispatch aus der Regel für ausgehenden Netzwerkverkehr gesetzt (nur aus, wenn sie zu Verweigern auflöst). Ist es aus, starten die Proxys je Sitzung trotzdem, die Zulassungsliste bleibt aber leer, also wird jede ausgehende Verbindung abgelehnt |
egressAllowlist |
List<String> (Standard []) |
Zusätzliche erlaubte Ausgangsdomains; Platzhalter sind möglich (*.example.com, dazu eine Form mit genau einem Label in der Mitte). Der Dispatch füllt das nie, ein umhüllter Lauf bekommt also die kuratierte Basis plus die GitHub-Domains |
guestWorkdir |
String? |
Standard-Arbeitsverzeichnis in der Sandbox; einzelne exec-Aufrufe können es überschreiben |
mode |
Mode (Standard chat) |
Schneidet die Schreibregeln des Dateisystems: chat lässt Bind-Mounts beschreibbar; review, plan und orchestrate sind nur lesend |
protectedPaths |
List<String> (Standard []) |
Host-Pfade, die in der Sandbox in keinem Modus beschreibbar sein dürfen |
runnerStateDirs |
List<String> (Standard []) |
Host-Verzeichnisse, in denen der Runner seinen eigenen Zustand hält, in jedem Modus beschreibbar (ein von Control Center verwaltetes CLAUDE_CONFIG_DIR ist derzeit das eine) |
execGrantRoots |
List<String> (Standard []) |
Von dir freigegebene Verzeichnisbäume, die unter macOS die Exec-Sperre für beschreibbare Verzeichnisse wieder öffnen. Linux ignoriert sie — bwrap hat keine $HOME-Exec-Sperre, die sich wieder öffnen ließe |
protectedPaths enthält die ursprünglich registrierten Repo-Checkouts, je Dispatch aus den Repos des Arbeitsbereichs aufgelöst. Agenten arbeiten ausschließlich in Copy-on-Write-Worktrees je Unterhaltung, die Originale dürfen aus der Sandbox heraus also nie beschreibbar sein — unter macOS ließe die pauschale Schreibfreigabe von Seatbelt für $HOME sie sonst über bash beschreibbar. Sie gehören in jedem Modus zur Menge der Schreibverbote für Geheimnisse, chat eingeschlossen.
Jeder Leseverbots- und Schreibverbotspfad wird in beiden Schreibweisen ausgegeben, der wörtlichen und der über den Symlink aufgelösten. Der Kernel prüft eine Verbotsregel gegen den aufgelösten Pfad, eine Regel gegen einen Symlink-Pfad greift also nie — und unter macOS ist das der Normalfall, nicht die Ausnahme, denn /tmp löst sich zu /private/tmp auf und /var/folders/… zu /private/var/folders/…. Einträge mit * oder ? gelten als Globs und bleiben unverändert; ein Pfad, der sich nicht auflöst, bleibt, wie er geschrieben wurde.
SandboxBindMount
Abschnitt betitelt „SandboxBindMount“| Feld | Typ | Beschreibung |
|---|---|---|
hostPath |
String |
Absoluter Pfad auf dem Host |
guestPath |
String |
Pfad in der Sandbox (bei der nativen Sandbox gleich hostPath; bleibt wegen der Symmetrie der Adapter) |
readOnly |
bool (Standard false) |
Nur lesend einhängen, wenn wahr |
SandboxHandle
Abschnitt betitelt „SandboxHandle“Zurückgegeben von SandboxPort.launch:
| Feld | Typ | Beschreibung |
|---|---|---|
sessionId |
String |
Sitzungskennung |
backend |
SandboxBackend |
Welches Backend gerade läuft |
state |
SandboxState |
Zustand im Lebenszyklus |
error |
Object? |
Gesetzt, wenn state den Wert error hat |
details |
Map<String, Object?> |
Interne Verwaltungsdaten des Adapters (Arbeitsverzeichnis, PID, Profilpfad usw.) |
SandboxState
Abschnitt betitelt „SandboxState“| Zustand | Beschreibung |
|---|---|
created |
Spezifikation angelegt, die VM ist noch nicht gestartet |
warm |
Läuft und bereit für exec-Befehle |
active |
Führt gerade einen Befehl aus |
suspended |
Pausiert und als Checkpoint auf die Platte geschrieben, um RAM zu sparen |
destroyed |
Abgebaut; das Handle ist nicht mehr gültig |
error |
Start fehlgeschlagen — siehe SandboxHandle.error |
Sandbox-Ereignisse
Abschnitt betitelt „Sandbox-Ereignisse“| Ereignistyp | Beschreibung |
|---|---|
stdout |
Standardausgabe der Sandbox |
stderr |
Standardfehler der Sandbox |
exit |
Der Sandbox-Prozess ist beendet (trägt exitCode) |
starting |
Die Sandbox startet |
ready |
Die Sandbox nimmt exec-Aufrufe an |
killed |
Vom Host beendet (OOM, Zeitüberschreitung, manueller Stopp) |
violation |
Eine Sandbox-Richtlinie wurde verletzt (trägt eine SandboxViolation) |
SandboxViolation
Abschnitt betitelt „SandboxViolation“| Feld | Typ | Beschreibung |
|---|---|---|
action |
String |
Das verweigerte Verb (z. B. file-read*, network-outbound) |
target |
String |
Pfad oder Host, der Ziel der verweigerten Operation war |
raw |
String? |
Rohe Logzeile, nützlich zur Fehlersuche |
Siehe auch
Abschnitt betitelt „Siehe auch“- Sandbox und Sicherheit
- Sandbox-Richtlinien konfigurieren
- Schutzregeln
- cc_server CLI: das Flag
--sandbox