Перейти к содержимому

Бэкенды песочницы

Бэкенд Значение Платформа Доступность
Изолированная VM microvm macOS, Linux, Windows (гипервизор microVM) Допускается только после проверки среды выполнения microVM; применяется к интерактивным терминалам — оболочка работает внутри rig, а не на хосте
Нативная песочница native macOS (Seatbelt), Linux и WSL2 (bubblewrap) Проверяется при загрузке; Linux отказывает запуску, который требует запретов записи по glob-шаблонам имён файлов, — их bubblewrap обеспечить не может
Без изоляции none Все Значение по умолчанию при разборе, значение по умолчанию терминала и запасной вариант, когда бэкенд недоступен или задан --sandbox off

SandboxBackend.fromName возвращает none для пустого или нераспознанного сохранённого значения. Сохранённое "docker" отображается на native.

microvm — единственный бэкенд с границей ядра — рабочая нагрузка действительно не выполняется на хосте. Он никогда не бывает тихим запасным вариантом ни в одну, ни в другую сторону: явный запрос его на хосте, который не может загрузить VM, завершается громкой ошибкой («я просил VM, а получил свой ноутбук» — единственная деградация, которую изолирующая среда не может себе позволить), и только неуказанное значение по умолчанию опускается до native, о чём сообщает строка журнала. Фактический бэкенд сессии сообщается обратно клиенту и помечается значком, поэтому терминал никогда не может заявить, что он изолирован, работая на хосте. Жизненный цикл, образы и исходящий трафик самой VM описаны в справочнике по rig.

В Windows нет нативной песочницы. NativeSandboxAdapter.probe возвращает available: false на любом хосте, который не является macOS или Linux/WSL2. Поэтому диспетчеризация агента в Windows использует none. Бэкенд microvm отдельный: он допускается только после проверки среды выполнения microVM и применяется к интерактивным терминалам и rig, а не к обёртке bash на хосте.

Именно проверка управляет принуждением. Она выполняется один раз, при загрузке сервера.

Хост available Заметка / подсказка
macOS true sandbox-exec (Seatbelt) — изоляция пространств имён, без границы ядра
Linux или WSL2 с bwrap и socat true bubblewrap — изоляция пространств имён, без границы ядра
Linux или WSL2, где нет одного из инструментов false requiresInstall: true, с подсказкой об установке, называющей недостающие пакеты
Любая другая платформа false Нативная песочница на этой платформе не предлагается

Сервер записывает результат в одну строку при запуске: agent sandbox ON плюс заметка, no OS-native agent sandbox on this host плюс подсказка или agent sandbox DISABLED by --sandbox off. GET /healthz не содержит поля песочницы.

--sandbox / CC_SERVER_SANDBOX принимает on (по умолчанию) или off. Это отказ, а не включение: on означает «обернуть, когда хост предлагает бэкенд», а не «требовать его».

Использует механизм песочницы Seatbelt от Apple (ту же систему, что и macOS App Sandbox).

Создаваемый профиль по умолчанию разрешающий ((allow default)) и вырезает из него запреты, потому что полностью запрещающий по умолчанию профиль делает интерактивную оболочку непригодной — вызовов dyld, XPC и Mach слишком много, чтобы их перечислить. Практическое следствие: агент по-прежнему может читать большую часть хоста; ваши зарегистрированные рабочие копии защищены явными запретами записи и переименования, а не невидимостью.

  • Чтение файловой системы: запрещено для секретных путей (~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, ~/Library/Keychains и остального набора запрета чтения)
  • Запись в файловую систему: сбрасывается в запрет, затем следуют явные разрешения для подпутей, а пути с обязательным запретом (файлы rc оболочки, .git/hooks, конфигурация Claude, защищённые рабочие копии) заблокированы даже внутри записываемого корня. file-write-unlink запрещён для каждого запрещённого пути и его предков, поэтому переименование не может обойти запрет записи
  • Процессы: запреты process-exec для всегда опасных бинарников плюс блокировка выполнения чего угодно из записываемого каталога
  • Сеть: ограничена портами внутрипроцессных прокси плюс DNS — loopback не разрешён огульно, а Unix-сокеты не разрешены. Прокси обеспечивают доменный список разрешённых для каждой сессии (см. Исходящий сетевой трафик)
  • Конфигурация: профили создаются при каждом запуске из SandboxSpec

Требования:

  • sandbox-exec (/usr/bin/sandbox-exec), входит в любую установку macOS — дополнительный инструментарий не нужен

Использует bubblewrap (bwrap) для создания пространств имён монтирования и PID.

  • Файловая система: создаёт пространство имён монтирования, в котором видны только привязанные пути
  • Сеть: работает в отдельном пространстве имён сети; трафик достигает хостовых прокси через мосты socat на Unix-сокетах
  • Процессы: изоляция пространства имён PID

Требования:

  • Установленные bubblewrap и socat (sudo apt-get install bubblewrap socat в Debian/Ubuntu)
  • Поддержка пространств имён ядром (стандартна для современного Linux)

Bubblewrap может заслонить существующий путь, но не может запретить новый файл .env, *.key или *.pem внутри записываемого bind-монтирования. Стандартная политика диспетчеризации требует таких запретов по glob-шаблонам имён файлов, поэтому нативная диспетчеризация агента сейчас завершается ошибкой в Linux/WSL2, даже когда проверка находит bubblewrap. Она не работает с молча ослабленной политикой. Чтобы вернуть нативную диспетчеризацию в Linux, нужен бэкенд с правилами имён файлов на момент создания. Отключение песочницы — это явный отказ от изоляции, а не равноценный обходной путь.

Никакой изоляции. Процесс выполняется напрямую на хосте с доступом хоста к файловой системе, сети и процессам. ACP и инструмент bash харнесса всё равно получают очистку окружения и политику команд; Claude Code через NoSandboxAdapter наследует окружение хоста. К этому диспетчеризация агента откатывается на хосте без доступного бэкенда или при --sandbox off, и это значение по умолчанию терминала, когда клиент не называет ни одного бэкенда.

Терминал изолируется по своим собственным правилам, а не по проверке диспетчеризации. defaultBackend у TerminalSessionService равен none, и cc_server его не переопределяет, поэтому сессия терминала запускает оболочку хоста, если клиент явно не запросит native, — а панель терминала настольного приложения этого не делает.

Когда запрошен native, оболочка оборачивается через SandboxManager по фиксированному профилю терминала: $HOME и рабочий каталог сессии доступны для записи, сеть открыта, а ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh и ~/Library/Keychains запрещены для чтения. Токен хоста кода только для чтения и шлюз push не применяются — это забота диспетчеризации. Если обёртка не поддерживается, сессия завершается ошибкой, а не показывает значок нативной песочницы на PTY оболочки хоста.

Когда песочница запускается, исходящий сетевой доступ обеспечивается внутрипроцессными прокси, а не правилами межсетевого экрана ядра. На хосте без доступного бэкенда ничего из этого не применяется, и порождённый процесс выходит в сеть точно так же, как это делал бы пользователь сервера.

  • Среда выполнения песочницы запускает прокси HTTP/HTTPS и прокси SOCKS5 на 127.0.0.1 внутри процесса cc_server, по одной паре на ограниченную сессию (http_proxy.dart, socks_proxy.dart). Параллельные запуски не делят одну пару.
  • Изолированные процессы получают переменные окружения прокси (HTTP_PROXY/HTTPS_PROXY → прокси HTTP, ALL_PROXY → прокси SOCKS; GIT_SSH_COMMAND направляет git-over-SSH через прокси SOCKS). В Linux пространство имён сети отделено, поэтому прокси — единственный выход; в macOS песочница делит с хостом loopback и обращается к ним напрямую.
  • Целевой хост каждого подключения сверяется с доменным списком разрешённых для сессии; всё остальное отклоняется. Есть две формы шаблонов, в каждой ровно один *: ведущий *.example.com совпадает с любым поддоменом (включая a.b.example.com), но не с самим доменом верхнего уровня; bedrock.*.amazonaws.com со звёздочкой в середине совпадает ровно с одной меткой (bedrock.us-east-1.amazonaws.com, но не bedrock.evil.attacker.amazonaws.com).
  • Список разрешённых собирается для каждой сессии из подобранной базовой линии (kBaselineAllowedDomains: провайдеры LLM, хостинг Git, реестры пакетов), плюс домены GitHub (kGithubDomains), плюс SandboxSpec.egressAllowlist. Путь диспетчеризации никогда не передаёт дополнительные записи egressAllowlist, поэтому на практике каждый запуск получает базовую линию плюс GitHub.
  • Фиксированный список запрещённых (kBaselineDeniedDomains: конечные точки метаданных облака, приёмники телеметрии) имеет приоритет над всеми разрешениями.
  • networkEnabled: false всё равно запускает прокси для каждой сессии, но список разрешённых пуст, поэтому каждое исходящее подключение отклоняется. Диспетчеризация задаёт это значение при старте запуска по правилу Обращаться к сети: запрет выключает сеть, разрешить и спросить сначала оставляют её включённой.

Спецификация, передаваемая в SandboxPort.launch:

Поле Тип Описание
sessionId String Стабильный идентификатор сессии (обычно id пространства)
workspaceId String Id владеющего рабочего пространства
agentId String? Агент, привязанный к песочнице в данный момент, если есть
bindMounts List<SandboxBindMount> Монтирования хост→гость; песочница видит только перечисленные здесь пути хоста
networkEnabled bool (по умолчанию true) Задаётся при диспетчеризации по правилу исходящего сетевого трафика (выключено, только когда оно разрешается в запрет). Когда выключено, прокси для каждой сессии всё равно запускаются, но список разрешённых пуст, поэтому каждое исходящее подключение отклоняется
egressAllowlist List<String> (по умолчанию []) Дополнительные разрешённые домены исходящего трафика; поддерживаются шаблоны (*.example.com, а также форма со звёздочкой в середине на одну метку). Диспетчеризация никогда его не заполняет, поэтому обёрнутый запуск получает подобранную базовую линию плюс домены GitHub
guestWorkdir String? Рабочий каталог по умолчанию внутри песочницы; отдельные вызовы exec могут его переопределить
mode Mode (по умолчанию chat) Определяет правила записи в файловую систему: chat оставляет bind-монтирования доступными для записи; review, plan и orchestrate — только для чтения
protectedPaths List<String> (по умолчанию []) Пути хоста, которые никогда не должны быть доступны для записи внутри песочницы, ни в каком режиме
runnerStateDirs List<String> (по умолчанию []) Каталоги хоста, в которых раннер хранит собственное состояние, доступные для записи в любом режиме (сейчас это CLAUDE_CONFIG_DIR, управляемый Control Center)
execGrantRoots List<String> (по умолчанию []) Одобренные оператором деревья каталогов, которые вновь открывают блокировку exec из записываемого каталога в macOS. Linux их игнорирует — у bwrap нет блокировки exec по $HOME, которую можно вновь открыть

protectedPaths содержит исходные зарегистрированные рабочие копии репозиториев, определяемые при каждой диспетчеризации из репозиториев рабочего пространства. Агенты работают исключительно в worktree с копированием при записи для каждого разговора, поэтому оригиналы никогда не должны быть доступны для записи изнутри песочницы — иначе в macOS огульное разрешение записи в $HOME у Seatbelt оставило бы их доступными для записи через bash. Они присоединяются к набору запрета записи для секретов в любом режиме, включая chat.

Каждый путь запрета чтения и запрета записи выдаётся в двух написаниях — буквальном и после разрешения символических ссылок. Ядро сопоставляет правило запрета с разрешённым путём, поэтому правило, написанное для пути через символическую ссылку, никогда не срабатывает — а в macOS это обычный случай, а не экзотический, поскольку /tmp разрешается в /private/tmp, а /var/folders/… — в /private/var/folders/…. Записи, содержащие * или ?, считаются glob-шаблонами и не трогаются; путь, который не разрешается, сохраняется как написан.

Поле Тип Описание
hostPath String Абсолютный путь на хосте
guestPath String Путь внутри песочницы (в нативной песочнице совпадает с hostPath; сохранён для единообразия адаптеров)
readOnly bool (по умолчанию false) Монтировать только для чтения, когда истинно

Возвращается методом SandboxPort.launch:

Поле Тип Описание
sessionId String Идентификатор сессии
backend SandboxBackend Какой бэкенд работает
state SandboxState Состояние жизненного цикла
error Object? Заполняется, когда state равен error
details Map<String, Object?> Служебные данные, приватные для адаптера (рабочий каталог, PID, путь к профилю и т. д.)
Состояние Описание
created Спецификация создана, но VM ещё не запущена
warm Работает и готова выполнять команды
active В данный момент выполняет команду
suspended Приостановлена / сохранена на диск контрольной точкой для экономии ОЗУ
destroyed Свёрнута; дескриптор больше недействителен
error Запуск не удался — см. SandboxHandle.error
Тип события Описание
stdout Стандартный вывод из песочницы
stderr Стандартный вывод ошибок из песочницы
exit Процесс песочницы завершился (несёт exitCode)
starting Песочница запускается
ready Песочница готова принимать вызовы exec
killed Процесс убит хостом (OOM, тайм-аут, ручная остановка)
violation Нарушена политика песочницы (несёт SandboxViolation)
Поле Тип Описание
action String Запрещённый глагол (например, file-read*, network-outbound)
target String Путь или хост, бывший целью запрещённой операции
raw String? Необработанная строка журнала, полезная для отладки