[2026-09-29] eagle: Research/Gatekeeper Architecture for Agent Tool Calls.md Research/Isolation of Agent Execution - OS and Container Level.md
This commit is contained in:
1 parent
a553538217
commit
afcdd4b866
2 files changed
+315
No files matched your search
@@ -0,0 +1,120 @@
|
||||
---
|
||||
tags:
|
||||
- ai-agents
|
||||
- security
|
||||
- guardrails
|
||||
- mcp
|
||||
- policy-engine
|
||||
title: Gatekeeper Architecture for Agent Tool Calls
|
||||
date: '2026-09-29'
|
||||
status: complete
|
||||
---
|
||||
# Привратник для агентских инструментов: контроль tool calls отдельным слоем (2025–2026)
|
||||
|
||||
> Исследование: как в агентских системах проверяют и одобряют **каждый tool call** отдельным слоем (policy engine / interception / MCP proxy), а не самим агентом.
|
||||
> Дата: 2026-09-29. Все ссылки проверены.
|
||||
|
||||
## Общий вывод
|
||||
|
||||
Единого стандарта «привратника» в 2025–2026 нет. Сложилось **4 архитектурных уровня**, где может сидеть контроль:
|
||||
|
||||
1. **In-harness hooks** (Claude Code PreToolUse, ADK callbacks, LangChain middleware) — контроль внутри runtime агента, ближе к самому агенту.
|
||||
2. **Policy engines вне агента** (OPA/Rego, Cedar/AVP) — решения принимаются детерминированным внешним движком.
|
||||
3. **MCP proxy / gateway** — сетевой слой-посредник, фильтрует трафик tool calls в обе стороны.
|
||||
4. **Guardrail/scan слои** (Invariant, LlamaFirewall, NeMo, Lasso) — ML-классификаторы + правила поверх трафика.
|
||||
|
||||
Ключевой консенсус: **детерминированные проверки — policy engine; семантические («токсично/инъекция») — ML-классификатор, а не policy**. И любое решение должно быть **explainable + логируемым** (для апелляций и итерации правил).
|
||||
|
||||
---
|
||||
|
||||
## Подход 1. Claude Code: permissions + PreToolUse hooks
|
||||
**Зрелость: ВЫСОКАЯ (production, v2.x, ноябрь 2025+)**
|
||||
|
||||
- Трёхуровневая система правил `allow` / `ask` / `deny` с порядком вычисления **deny → ask → allow** (первое совпадение побеждает; специфичность не влияет).
|
||||
- **PreToolUse hook** срабатывает *перед* permission-промптом на **каждый** tool call (кроме `EndConversation`); hook возвращает JSON `{permissionDecision: "deny"|"ask"|"allow", permissionDecisionReason}` и может заблокировать/форсировать промпт/пропустить его.
|
||||
- Hooks — это shell-команды, HTTP-endpoint, MCP tool calls, LLM-промпты или subagents; три уровня конфигурации: event → matcher → handler.
|
||||
- Важно: правила **enforced by Claude Code, not by the model** — инструкции в CLAUDE.md/prompt *не* меняют то, что разрешено.
|
||||
- Deny с bare tool name удаляет инструмент из контекста модели; scoped deny (`Bash(rm *)`) оставляет инструмент, но блокирует вызов.
|
||||
- Источник: https://code.claude.com/docs/en/permissions , https://code.claude.com/docs/en/hooks
|
||||
|
||||
## Подход 2. MCP Authorization Spec (OAuth 2.1) + Security Best Practices
|
||||
**Зрелость: СРЕДНЕ-ВЫСОКАЯ (спек в draft/2025-06-18, OPTIONAL, ломающие ревизии)**
|
||||
|
||||
- Спецификация описывает **transport-level authorization** на основе OAuth 2.1 (RFC6750, RFC8414, RFC7591, RFC8707, RFC9728) только для HTTP-транспорта; STDIO — берёт креды из env, спек не применяет.
|
||||
- Роли: MCP server = OAuth resource server, MCP client = OAuth client, плюс authorization server. Принцип least privilege через `scope` в `WWW-Authenticate`, поддерживается step-up authorization.
|
||||
- Dynamic Client Registration **deprecated** (оставлен для обратной совместимости); вместо него Client ID Metadata Documents.
|
||||
- Отдельный документ **Security Best Practices** покрывает confused deputy, token passthrough, per-client consent, обязательный валидатор `state`, `__Host-` cookies, точный redirect_uri match.
|
||||
- Важное ограничение: авторизация **OPTIONAL** и НЕ покрывает *per-tool-call* политику (какие конкретно инструменты можно звать) — это оставляют приложениям/proxy.
|
||||
- Источник: https://modelcontextprotocol.io/specification/draft/basic/authorization , https://modelcontextprotocol.io/specification/draft/basic/security_best_practices
|
||||
|
||||
## Подход 3. OPA (Open Policy Agent) как policy engine для tool calls
|
||||
**Зрелость: ВЫСОКАЯ (CNCF graduated, но agent-интеграции — 2025+, молодые)**
|
||||
|
||||
- OPA развязывает **policy decision от enforcement**: клиент шлёт JSON input → OPA (Rego + data) → решение. `deny`/`allow`/`requires-approval`/`not-applicable`.
|
||||
- Экспрессивность Rego (Datalog, Тьюринг-полный) позволяет multi-hop решения («может ли агент читать данные проекта из того же отдела»); p99 латентность < 1 ms in-process.
|
||||
- Прямая agent-интеграция: **`@ai-sdk/policy-opa`** (Vercel AI SDK) через `toolApproval` callback — правила вынесены в `.rego`, тестируются `opa test` в CI, меняются без деплоя. Policy `input` несёт `messages` (история tool calls) → правила вида «approval после N записей», «deny второго необратимого действия», бюджет/токен-квоты.
|
||||
- Эмпирическое правило вендора: если можно указать поле в `input` и написать точную проверку — это правильный инструмент; семантику («no toxic content») сюда класть нельзя.
|
||||
- OPA MCP (npm `@orygn/opa-mcp`) даёт агенту 50+ инструментов для authoring/debug Rego.
|
||||
- Источник: https://www.openpolicyagent.org/docs/latest/ , https://ai-sdk.dev/docs/agents/policy-tool-approvals , https://inferensys.com/differences/mcp-security-gateways-and-agent-tool-sandboxes/policy-as-code-engines-for-agents/open-policy-agent-opa-vs-cedar-agent-tool-authorization
|
||||
|
||||
## Подход 4. Cedar (AWS) как policy language
|
||||
**Зрелость: СРЕДНЕ-ВЫСОКАЯ (Apache 2.0, AWS Verified Permissions; agent-специфика ранняя)**
|
||||
|
||||
- Purpose-built язык авторизации: четыре элемента — **principal / action / resource / context** + schema. Схема используется *только* для валидации политик, не при evaluation.
|
||||
- Ключевое преимущество: **ограниченная выразительность намеренно** — делает автоматический анализ политик вычислимо возможным (напр. доказать, что политика никогда не даёт публичный доступ). Это критично для security-аудита.
|
||||
- Интеграция с AWS IAM / Amazon Verified Permissions / Verified Access; p99 < 2 ms (managed). Практический предел ~1000 правил против 10000+ у OPA.
|
||||
- Trade-off: менее выразителен, чем Rego (нет графовых обходов), AWS-центричен; standalone engine требует кастомной интеграции.
|
||||
- MCP-нативной интеграции нет ни у OPA, ни у Cedar — нужен «custom adapter» (отсюда и весь рынок MCP gateway/proxy).
|
||||
- Источник: https://docs.cedarpolicy.com/ , https://inferensys.com/differences/...
|
||||
|
||||
## Подход 5. MCP proxy / security gateway (сетевой привратник)
|
||||
**Зрелость: ВЫСОКАЯ популярность, СРЕДНЯЯ зрелость (Invariant acquired by Snyk 2025; Lasso open-source)**
|
||||
|
||||
- **Invariant Labs: MCP-scan** — два режима: пассивный `scan` (статически ищет tool poisoning / prompt injection в описаниях инструментов) и активный `mcp-scan proxy` (фоновый динамический слой, мониторит и логирует весь MCP-трафик). Guardrails покрывают tool calls/responses, PII/secrets detection, tool restrictions, custom policies. **Tool Pinning** (хэширование) ловит rug-pull атаки; cross-origin escalation → tool shadowing.
|
||||
- **Invariant Guardrails** работает **out-of-band**: ingests execution traces + MCP server definitions → static information-flow analysis над ними, поднимает policy violations (unsafe tool-call sequences). Т.е. контроль отдельным слоем над трейсом, а не в loop.
|
||||
- **Lasso MCP Gateway** (MIT, open source, апр 2025): proxy-and-orchestrator между LLM и другими MCP; перехватывает request/response, mask secrets/PII, блокирует prompt injection и command injection/RCE. Плагины: `basic` (secrets masking), `presidio` (PII), `lasso` (custom policy + injection + harmful content). Унифицированный интерфейс `get_metadata` / `run_tool`.
|
||||
- Паттерн де-факто: **MCP gateway = привратник на сетевом слое**, независимый от агента, применяет фильтры к обеим сторонам трафика.
|
||||
- Источник: https://invariantlabs-ai.github.io/docs/mcp-scan/ , https://www.agentpatternscatalog.org/landing/compositions/invariant-labs/ , https://github.com/lasso-security/mcp-gateway
|
||||
|
||||
## Подход 6. Guardrail-фреймворки (ML-слой поверх tool calls)
|
||||
**Зрелость: ВЫСОКАЯ (Meta/Microsoft/NVIDIA — production, но акцент на content/инъекции, не на per-tool authz)**
|
||||
|
||||
- **LlamaFirewall (Meta)** — «policy engine», оркеструет сканеры: PromptGuard 2 (BERT-классификатор prompt injection), **AlignmentCheck** (аудит chain-of-thought рассуждений агента в реальном времени — ловит goal hijacking / indirect injection), CodeShield (статический анализ LLM-кода, 8 языков), regex/custom. Возвращает `ScanResult {decision, reason, score}`. Поддерживает `scan_replay` по трассам. Реально open source (4.4k★).
|
||||
- **NeMo Guardrails (NVIDIA)** — rails (input/retrieval/dialog/execution/output), Colang flows, YAML. Явно есть **agentic security**: tool call validation (валидирует входы/выходы инструмента до/после вызова), интеграция с LangGraph. Библиотека+микросервис, конфиг переносим.
|
||||
- **Guardrails AI** — validation-фреймворк (Hub валидаторов); в 2026 команда ушла в Harvey (сайт-леденящий), самостоятельный продукт фактически заморожен.
|
||||
- **Prompt Security** — теперь часть **SentinelOne**; предлагает AI Gateway + open-source (ClawSec — integrity/drift detection skills, ps-fuzz — fuzzing промптов). Это скорее enterprise AI gateway, чем tool-level привратник.
|
||||
- Граница: guardrail-фреймворки сильны на *semantic* рисках (инъекции, токсичность, PII), но они **не заменяют** детерминированную авторизацию tool calls.
|
||||
- Источник: https://github.com/meta-llama/PurpleLlama/tree/main/LlamaFirewall , https://docs.nvidia.com/nemo/guardrails/ , https://www.prompt.security/
|
||||
|
||||
## Подход 7. Interception в агентских фреймворках (callbacks / middleware)
|
||||
**Зрелость: ВЫСОКАЯ (first-class API в трёх крупных SDK)**
|
||||
|
||||
- **OpenAI Agents SDK**: tool guardrails выполняются на **каждой** guarded function-tool invocation (input до, output после), включая локальные MCP-инструменты. Tripwire-механизм: `.tripwire_triggered` → `Input/OutputGuardrailTripwireTriggered`. Есть `run_in_parallel` (быстро, но агент может успеть выполнить tool до отмены) vs blocking (расход до tool execution). Отдельно — sanitized placeholder `"Output withheld by an output guardrail."`.
|
||||
- **Google ADK**: 6 типов callbacks, включая **Before Tool / After Tool**. Return `None` = продолжать, return объект = **override и skip** дефолтного поведения (жёсткий interception). Важный совет вендора: для security guardrails использовать **ADK Plugins**, а не callbacks (модульность).
|
||||
- **LangChain**: `HumanInTheLoopMiddleware` — интерцепт tool calls через `after_model` hook + LangGraph `interrupt()`. Pause-Decide-Resume; решения `approve` / `edit` / `reject` / `respond`; настраивается `interrupt_on` (True/False/config) и **`when` predicate** (условный interrupt, напр. только записи вне `/workspace/`). Требует checkpointer (Postgres/Mongo в проде).
|
||||
- Все три — это «in-harness» привратник: контрольный слой в самом рантайме агента, а не внешний сервис.
|
||||
- Источник: https://openai.github.io/openai-agents-python/guardrails/ , https://google.github.io/adk-docs/callbacks/ , https://docs.langchain.com/oss/python/langchain/human-in-the-loop
|
||||
|
||||
## Подход 8. Jev / TypeSafe System One — typed evaluation как гейт
|
||||
**Зрелость: НИЗКО-СРЕДНЯЯ (нишевый, молодой; community-репо 9–18★; сайты-спутники — community)**
|
||||
|
||||
- **Jev — это НЕ LLM и НЕ drop-in замена модели агента.** «System One model»: принимает `state` + набор **typed questions**, возвращает структурированные ответы — `choice` (с вероятностями по опциям), `score` (по рубрике), `noul` (0–1 для true/false утверждения). Не генерирует текст и не вызывает инструменты.
|
||||
- Паттерн tool-approval: между *намерением* агента и *side effect*. Вопросы: `risk` (score 0–3), `injection` (noul), `exfiltration` (noul). Затем **порог и действие — в ВАШЕМ коде**: `if (risk.score >= 1.5 || injection.noul >= 0.8) deny(); if (risk.score >= 0.5 || exfiltration >= 0.5) askUser(); return allow();`
|
||||
- Ключевая идея: судить **действие, а не payload** — JSON schema валидирует форму (`send_email` с валидным списком адресов), а Jev судит intent/scope/proportionality («уместны ли 214 получателей для этой задачи»). Порог варьируют по «blast radius» (read_only / single_target / external_many).
|
||||
- Позиционирование: «strict-typed decisions by construction», замена хрупкому «верни JSON» промпту. Латентность — сотни миллисекунд на каждый tool call (предупреждают о накоплении). Вендор говорит: **один слой defense-in-depth**, не замена sandboxing/least privilege.
|
||||
- Community-реализации: `leepokai/jev-guard` (18★, deny/ask/allow на каждый tool call + injection flagging), `win4r/jev-security-scan` (ревью skills/MCP кода), `jev-mcp` / `typesafe-mcp`.
|
||||
- Источник: https://typesafe-jev.com/en/use-cases/tool-approval/ , https://jevmodel.org/use-cases/tool-call-guardrails/ , https://docs.typesafe.ai/introduction/coding-agents
|
||||
|
||||
---
|
||||
|
||||
## Итоговая карта: кто что даёт
|
||||
|
||||
| Слой | Примеры | Сильная сторона | Слабость |
|
||||
|---|---|---|---|
|
||||
| In-harness hooks/callbacks | Claude Code PreToolUse, ADK, LangChain HITL, OpenAI SDK | Глубокая интеграция, доступ к полному контексту | Привязан к рантайму; «сам агент» часто и решает |
|
||||
| Policy engine | OPA/Rego, Cedar/AVP | Детерминизм, аудируемость, CI-тесты, развязка policy/code | Не понимает семантику; нужен адаптер под MCP |
|
||||
| MCP proxy/gateway | Invariant MCP-scan, Lasso Gateway | Независим от агента, перехват трафика в обе стороны | Доп. hop; статически не видит намерение |
|
||||
| Guardrail/scan | LlamaFirewall, NeMo, Prompt Security | Ловит инъекции/утечки/токсичность | ML-вероятностность; не замена авторизации |
|
||||
| Typed evaluation | Jev / TypeSafe | Судит intent и blast radius, калиброванные вероятности | Молодой, нишевый, добавляет латентность на каждый вызов |
|
||||
|
||||
**Рекомендуемая композиция «привратника»:** deterministic policy engine (OPA для гибкости / Cedar для аудируемости) на сетевом MCP-proxy слое, дополненный ML-сканером для семантических рисков и typed-оценкой (Jev-подобной) для intent/blast-radius — с логированием каждого решения (probabilities + triggering rule) для апелляций и итерации правил.
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: 'Изоляция исполнения агента: OS и контейнерный уровень'
|
||||
tags:
|
||||
- agent-security
|
||||
- sandboxing
|
||||
- seccomp
|
||||
- gvisor
|
||||
- firecracker
|
||||
- bubblewrap
|
||||
- ssh
|
||||
- docker
|
||||
- research
|
||||
created: '2026-09-29'
|
||||
status: complete
|
||||
---
|
||||
# Изоляция исполнения агента: OS и контейнерный уровень
|
||||
|
||||
Исследование: 8 подходов + паттерны ограниченного доступа к хосту.
|
||||
Контекст: дать агенту собирать/тестировать код, но не дать дотянуться до чужих файлов и секретов.
|
||||
|
||||
---
|
||||
|
||||
## 1. gVisor (Google) — application kernel в userspace (`runsc`)
|
||||
|
||||
- gVisor — **не seccomp-фильтр, не обёртка над Linux-примитивами и не VM**. Это «третий подход»: application kernel, реализующий Linux-подобный интерфейс, написанный на **Go (memory-safe)** и работающий в **userspace**. Себя описывают как «seccomp on steroids».
|
||||
- Архитектура: **Sentry** — application kernel на песочницу (перехватывает системные вызовы, сигналы, управление памятью, потоки); **Gofer** — на контейнер, даёт доступ к ФС. Системные вызовы приложения **не проходят** насквозь в ядро хоста — их обрабатывает Sentry.
|
||||
- OCI-runtime `runsc` интегрируется с Docker и Kubernetes (GKE Sandbox, minikube) — можно запускать обычные контейнеры «под gVisor» без переписывания.
|
||||
- Накладные расходы: **эмерджентны** (threads/memory mappings, а не фиксированные guest-ресурсы), быстрый старт, но **плохо для syscall-heavy нагрузок** (компиляция — как раз syscall-heavy, важно учитывать).
|
||||
- Преимущество для агентов: **защита от container-escape через kernel-эксплойты** — единственная поверхность это сам Sentry в Go. Комбинируется с rule-based (seccomp) для defense-in-depth.
|
||||
- Ссылка: https://gvisor.dev/docs/
|
||||
|
||||
## 2. Firecracker microVM (AWS) + jailer
|
||||
|
||||
- Firecracker — lightweight **microVM** (KVM-based, Rust), созданный для multi-tenant serverless (Lambda, Fargate). **Аппаратная виртуализация** = самая сильная граница изоляции при быстром старте (десятки мс) и низком footprint.
|
||||
- Компаньон **`jailer`** создаёт многослойную границу для VMM-процесса:
|
||||
- `chroot` + `pivot_root` в новый mount namespace; копирование бинарника в jail (защита от shared memory с другими Firecracker).
|
||||
- Сброс в **непривилегированный uid/gid** (`--uid`/`--gid`) — VMM не root.
|
||||
- **cgroups v1/v2** — лимиты CPU/памяти (`--cgroup cpuset.cpus=0`, `--parent-cgroup`).
|
||||
- `setrlimit` — **fsize** (ограничение размера создаваемых файлов) и **no-file** (по умолчанию 2048 дескрипторов).
|
||||
- Новый **PID namespace** (`--new-pid-ns` → `clone(CLONE_NEWPID)`, процесс становится init(1)), опционально `--netns`, `setsid()` + redirect I/O в /dev/null при `--daemonize`.
|
||||
- **Вычищает все env-переменные** родителя и закрывает все FD кроме stdin/stdout/stderr.
|
||||
- Каждая microVM изолирована обычными Linux-barriers через jailer + **seccomp-фильтры** для самого VMM-процесса (см. отдельный документ про seccomp filtering).
|
||||
- Ссылки: https://github.com/firecracker-microvm/firecracker/blob/main/docs/jailer.md , https://firecracker-microvm.github.io/
|
||||
|
||||
## 3. E2B (e2b.dev) — sandbox для AI-агентов
|
||||
|
||||
- Managed sandbox-API именно под AI-агентов; под капотом — **Firecracker microVM** (у E2B открытая инфра-стековая часть, Firecracker-основа). Долгоживущие песочницы с **persistence**.
|
||||
- Жизненный цикл: `Sandbox.create({timeoutMs})`, `setTimeout` для продления, `kill()` для остановки. Лимиты **до 24ч (Pro) / 1ч (Base)**, далее pause+resume (состояние сохраняется, окно таймаута сбрасывается).
|
||||
- **Эфемерность на задачу**: таймаут → авто-pause/kill; каждый sandbox — отдельная microVM (свой kernel, своя ФС, свой network). Агент не видит хост.
|
||||
- API: `Sandbox` из JS/TS/Python SDK; getInfo → sandboxId/templateId/startedAt/endAt; metadata. Шаблоны (templates) из Dockerfile.
|
||||
- Для паттерна «собирать/тестировать код»: sandbox = полное Linux-окружение, изолированное от хоста инженера; секреты хоста недостижимы физически.
|
||||
- Ссылка: https://e2b.dev/docs/sandbox
|
||||
|
||||
## 4. Daytona — control/compute plane sandbox-платформа
|
||||
|
||||
- **Три плоскости**: Interface plane (SDK Python/TS/Ruby/Go/Java, CLI, Dashboard, **MCP-сервер**, SSH) → Control plane (NestJS API, Proxy, Snapshot builder, Sandbox manager; Redis/PostgreSQL/Auth0) → Compute plane (runners, sandbox daemon, snapshot store, volumes).
|
||||
- Изоляция: каждый sandbox — **isolated instance с собственными Linux namespaces** (process, network, filesystem mounts, IPC) + **dedicated vCPU, RAM и disk на sandbox**. Multi-tenancy на уровне organization boundary.
|
||||
- **Snapshot builder** собирает снапшоты из **Dockerfile или pre-built image** → пушит в внутренний OCI-registry. Быстрый provisioning эфемерных окружений под задачу.
|
||||
- Network: отдельный HTTP-прокси с **host-based routing** — `{port}-{sandboxId}.{proxy-domain}`; инжектит auth-заголовки, поддерживает WS.
|
||||
- **Sandbox daemon / Toolbox API** внутри sandbox: файловые и git-операции, исполнение кода, computer use, log streaming, терминальные сессии. Volumes — персистентное хранилище между sandbox.
|
||||
- Ссылка: https://www.daytona.io/docs/en/architecture/
|
||||
|
||||
## 5. Modal Sandboxes — контейнеры для untrusted code на лету
|
||||
|
||||
- Явно позиционируются как **secure containers for executing untrusted user or agent code**; определяешь контейнер **в runtime** (`modal.Sandbox.create(...)`), исполняешь произвольный код. Use-case прямо: «execute code generated by a language model», checkout git-репы и запуск тест-сьютов.
|
||||
- **Эфемерность**: default max lifetime **5 минут**, до **24 часов** через `timeout`; `idle_timeout` для авто-остановки (sandbox активен, если есть running command / stdin-write / open TCP over tunnel).
|
||||
- Lifecycle: Created → Scheduled (provision CPU/mem/GPU/volumes) → Started → Ready (readiness probe) → Finished (exit/timeout/OOM/terminate).
|
||||
- **Readiness probes** (TCP или Exec, interval_ms default 100ms, max 5 min) — идеально для «подняли окружение, накатили зависимости, готово к тестам».
|
||||
- Filesystem Snapshots — сохранить состояние и восстановить в новом sandbox (для работ >24ч).
|
||||
- Изоляция: каждое исполнение — отдельный контейнер на worker'е Modal; запуск команд через `sb.exec(...)` с таймаутами.
|
||||
- Ссылка: https://modal.com/docs/guide/sandbox
|
||||
|
||||
## 6. Fly Machines (Fly.io) — быстрые VM с супербыстрым стартом
|
||||
|
||||
- **Быстро-запускаемые VM** с start/stop на **subsecond** скоростях; контроль количества машин, lifecycle каждой машины, ресурсов и region placement через **REST API или flyctl**.
|
||||
- Модель «одна машина = один экземпляр приложения/окружения»: полная VM-граница изоляции, микро-уровень ресурсов. Подходит как эфемерная «машина на задачу» для агента — создать, выполнить сборку/тесты, остановить.
|
||||
- Эфемерность: stop/start по API; отдельные `[machines]` с независимым lifecycle. Для агента удобно: run-per-task VM, старый экземпляр уничтожается.
|
||||
- Интеграция с flyd / hardware-virtualization (Firecracker-подобное) — граница уровня гипервизора, а не просто namespaces.
|
||||
- Ссылки: https://fly.io/docs/machines/ , https://fly.io/machines/overview
|
||||
|
||||
## 7. Anthropic Claude Code sandbox modes (macOS Seatbelt)
|
||||
|
||||
- **Platform support**: sandboxed Bash tool встроен; работает на **macOS, Linux, WSL2**. **Native Windows не поддерживается** (только WSL2).
|
||||
- **macOS**: ничего ставить не надо — используется встроенный фреймворк **Seatbelt** (sandbox-exec / sandbox profiles).
|
||||
- **Linux/WSL2**: требуются пакеты **`bubblewrap`** (ФС-изоляция) и **`socat`** (relay сетевого трафика через sandbox-прокси). Опционально seccomp-фильтр из `@anthropic-ai/sandbox-runtime` (блок AF_UNIX sockets).
|
||||
- **Режимы-аналоги** (у Claude Code — permission modes + sandbox modes): **Manual** стартует read-only (built-in read-only команды `ls/cat/git status` без вопросов); **Accept Edits** авто-одобряет правки и фиксированный набор ФС-команд (`mkdir,touch,rm,mv,cp,sed`) в рабочей директории; **Auto mode** — отдельная classifier-модель ревьюит и блокирует небезопасные действия.
|
||||
- **Working directory boundary**: в Manual режиме запись **только в стартовую папку и подпапки**; чтение вне границы через Read/Grep/Glob требует разрешения. Расширяется `--add-dir` / `permissions.additionalDirectories`, сужается sandbox `denyRead`.
|
||||
- **Network isolation**: первый запрос к новому домену → промпт; `curl`/`wget` **не авто-одобряются**; deny-правила матчат команду «как написано», а независимую от текста сетевую политику даёт именно sandbox network isolation.
|
||||
- Escape hatch: `dangerouslyDisableSandbox` параметр (retry вне sandbox через обычный permission flow); можно запретить `allowUnsandboxedCommands: false` / `failIfUnavailable: true` для managed-деплоя.
|
||||
- Linux нюанс: на Ubuntu 24.04+ с `kernel.apparmor_restrict_unprivileged_userns=1` нужен AppArmor-профиль для `bwrap` (иначе unprivileged userns запрещён).
|
||||
- Ссылки: https://docs.claude.com/en/docs/claude-code/sandboxing , https://docs.claude.com/en/docs/claude-code/security
|
||||
|
||||
## 8. OpenAI Codex CLI sandbox (Seatbelt на macOS, bubblewrap/Landlock на Linux)
|
||||
|
||||
- **Дефолт локально**: сеть **выключена**, write-права **ограничены активным workspace**; OS-enforced sandbox + approval policy сверху.
|
||||
- **Sandbox modes**: `read-only`, `workspace-write` (рабочая директория + оговорённые исключения), `danger-full-access`. Профили прав: built-in `:read-only`, `:workspace`, `:danger-full-access`, кастомные через `[permissions.<name>]`.
|
||||
- **macOS**: **Seatbelt** (как и Chromium). **Linux**: исторически Landlock, теперь **bubblewrap по умолчанию** (Landlock отвергается для ФС-политик, т.к. **не умеет изолировать app-server Unix sockets**; флаг `features.use_legacy_landlock` надо выключать при апгрейде).
|
||||
- Реализация `codex-linux-sandbox` (крейт) поверх bwrap:
|
||||
- **`PR_SET_NO_NEW_PRIVS`** + in-process **seccomp network filter**.
|
||||
- ФС **read-only по умолчанию** (`--ro-bind / /`), writable-корни через `--bind <root> <root>`.
|
||||
- **Защищённые подпути** под writable-корнями (`.git`, resolved `gitdir:`, `.codex`) переоткрываются read-only через `--ro-bind`.
|
||||
- **`--unshare-user`**, `--unshare-pid` (по умолчанию), `--unshare-net` при запрете сети без прокси.
|
||||
- Порядок по path-specificity: `/repo=write`, `/repo/a=none`, `/repo/a/b=write` корректно комбинируются.
|
||||
- Защита от symlink/несуществующих путей: монтирование **`/dev/null`** поверх symlink или первого отсутствующего компонента.
|
||||
- **Managed proxy mode**: `--unshare-net` + внутренний TCP→UDS→TCP bridge; после старта bridge seccomp **блокирует создание AF_UNIX/socketpair**.
|
||||
- `bwrap` ищется на PATH вне CWD; при отсутствии — bundled fallback (`codex-resources/bwrap`); WSL1 не поддерживается (нет user namespaces).
|
||||
- **Two-phase в Codex cloud**: *setup phase* (сеть есть, ставим зависимости) → *agent phase* (по умолчанию offline). **Секреты доступны только в setup и удаляются до старта agent phase** — это ровно паттерн «дать собирать, но не дать украсть секреты».
|
||||
- Managed: `requirements.toml` может запретить `approval_policy = "never"` и `sandbox_mode = "danger-full-access"`.
|
||||
- Ссылки: https://developers.openai.com/codex/sandbox/ , https://github.com/openai/codex/blob/main/codex-rs/linux-sandbox/README.md
|
||||
|
||||
## 9. Chromium / Chrome seccomp-bpf sandbox (эталон многослойности)
|
||||
|
||||
- **Multi-process security model**: процессы, обрабатывающие недоверенные данные (renderer, utility, GPU), ограничены; к привилегированным ресурсам — только через **медиацию Browser-процесса** (broker).
|
||||
- **Linux**: `namespaces` + **seccomp-BPF** (+ опционально legacy SELinux/AppArmor/Landlock в зависимости от доступных фич ядра). Несколько слоёв: confinement процесса **и** сокращение exposed kernel attack surface.
|
||||
- Ключевая проблема seccomp-bpf: фильтрация на уровне syscall даёт трудно-понятную семантику; если процесс A под одним seccomp-bpf policy, нужно **гарантировать, что он не может повлиять на целостность процесса B** под другим policy (иначе sandbox escape).
|
||||
- **macOS**: `sandbox/mac` использует **Seatbelt**; **Windows**: restricted tokens + job objects + alternate desktops + integrity levels. Один код-бейз, три разных OS-механизма.
|
||||
- **Broker-паттерн**: GPU/utility не ходят в ядро напрямую, а просят Browser-процесс выполнить операцию — переносится на любые агентские архитектуры (агент просит «runner» выполнить привилегированное действие).
|
||||
- Ссылки: https://github.com/chromium/chromium/tree/main/sandbox , https://chromium.googlesource.com/chromium/src.git/+/master/sandbox/linux/
|
||||
|
||||
## 10. bubblewrap (bwrap) — unprivileged sandboxing (упоминался в DeepSeek Harness как sandbox seam)
|
||||
|
||||
- **Low-level unprivileged sandboxing tool** (Flatpak, rpm-ostree, и теперь Claude Code / Codex на Linux). Ключ: использует **Linux user namespaces**, поэтому доступен **непривилегированному пользователю** — в отличие от docker/systemd-nspawn, которые админам.
|
||||
- Всегда создаёт **новый mount namespace**: root на **tmpfs**, невидимый с хоста, автоочистка при выходе последнего процесса. Опции конструируют ФС: `--ro-bind /usr /usr`, `--proc /proc`, `--dev /dev`, `--bind`.
|
||||
- Namespaces: `CLONE_NEWUSER` (`--unshare-user`, маппинг uid/gid), `CLONE_NEWIPC`, `CLONE_NEWPID` (`--unshare-pid` + trivial pid1 reaps zombies — решает docker pid-1 problem), `CLONE_NEWNET` (`--unshare-net`, только loopback), `CLONE_NEWUTS`; плюс **seccomp-фильтры**.
|
||||
- Использует **`PR_SET_NO_NEW_PRIVS`** — выключает setuid-бинарники, классический способ выйти из chroot.
|
||||
- ⚠️ Важно: bwrap **не готовый sandbox с политикой** — уровень защиты целиком определяется переданными аргументами. Владельцы могут повысить риск DoS.
|
||||
- ⚠️ Подводные камни: без фильтрации `TIOCSTI` нужен `--new-session` (CVE-2017-5226); **всё, что смонтировано, потенциально эскалирует привилегии** (напр. bind D-Bus socket = исполнение через systemd → используйте `xdg-dbus-proxy`); nested sandboxing: браузеры со своим seccomp требуют разрешить syscall `seccomp`, иначе не смогут применить собственные ограничения.
|
||||
- **Установка на Ubuntu 24.04+**: `apparmor_restrict_unprivileged_userns=1` блокирует unprivileged userns → нужен AppArmor-профиль `profile bwrap /usr/bin/bwrap flags=(unconfined) { userns }`.
|
||||
- Ссылка: https://github.com/containers/bubblewrap
|
||||
|
||||
---
|
||||
|
||||
# Доступ к хосту с ограниченным набором команд (изоляция машины инженера)
|
||||
|
||||
## Паттерн A: SSH forced command (`authorized_keys command=`)
|
||||
|
||||
- В `~/.ssh/authorized_keys` перед ключом — опции: `command="..."` заставляет **sshd игнорировать любую команду/шелл клиента и выполнить серверную команду**. Формат: `options keytype base64 comment`.
|
||||
- В паре с **`restrict`** (новый агрегат) — отключает PTY, agent forwarding, X11 forwarding, TCP forwarding и user rc processing **одним словом**. Эквивалент набора: `no-pty, no-port-forwarding, no-agent-forwarding, no-X11-forwarding, no-user-rc`.
|
||||
- Дополнительно: `from="IP"` — ограничение источника; `environment="..."` (только если `PermitUserEnvironment`); `permitopen`/`permitlisten` для точечного туннелирования.
|
||||
- **Wrapper-паттерн**: `command="/usr/local/bin/agent-wrapper.sh"` — скрипт читает `$SSH_ORIGINAL_COMMAND`, валидирует по **whitelist** допустимых команд, и только затем исполняет через `exec`. Это и есть command-whitelist wrapper: агент шлёт `ssh host "git status"`, wrapper сверяет с allowlist, остальное → reject + лог.
|
||||
- Реальный пример в природе: **`git-shell`** — логин-шелл только для git-операций; тот же принцип в GitHub/GitLab-деплой-ключах.
|
||||
- Отдельно можно задать **`ForceCommand`** в `sshd_config` (глобально/по Match-блоку) — серверная сторона, независимая от клиента.
|
||||
- Ссылки: https://man.openbsd.org/sshd.8 , https://www.simplified.guide/ssh/restrict-authorized-keys-command , https://superuser.com/questions/299927/
|
||||
|
||||
## Паттерн B: bastion / jump host (граница «хост-инженера ↔ агент»)
|
||||
|
||||
- Отдельный **bastion-пользователь** (`agentrunner`) на отдельном хосте/VM: агент аутентифицируется ключом **только** к bastion, не к машине инженера. С бастиона — либо `ProxyJump`, либо жёсткий wrapper к целевому хосту.
|
||||
- `ssh -J bastion target` / `ProxyJump` в `~/.ssh/config` — единая точка аудита и rate-limit; удобно логировать весь трафик агента.
|
||||
- Bastion в **непривилегированном** аккаунте, `rbash`/restricted shell, chroot-джейл (см. паттерн A + `sshd_config Match`), все forward'ы запрещены кроме разрешённых.
|
||||
- Машина инженера полностью закрыта снаружи; даже скомпрометированный ключ агента ведёт лишь на узкий bastion с whitelist.
|
||||
- Ссылка: https://man.openbsd.org/ssh.1 (раздел про `-J`, port forwarding, agent forwarding)
|
||||
|
||||
## Паттерн C: restricted shells (rbash) — как дополнение, не как основная защита
|
||||
|
||||
- `rbash`/`bash --restricted`: нельзя `cd`, менять `$PATH`/`$SHELL`, использовать `/` в именах команд, редиректы, `exec`. Работает только в связке с корректно настроенным `PATH` и неизменяемыми переменными.
|
||||
- ⚠️ `rbash` **легко обходится** (интерпретаторы, `vi`, `less`, `awk`, `find -exec`, git hooks) — общеизвестен как слабая защита. Использовать **только как второй слой**; основной — forced command + wrapper + опционально контейнер/VM.
|
||||
- Ссылка: https://man.openbsd.org/sshd.8 (login process), https://oneuptime.com/blog/post/2026-03-02-how-to-restrict-user-ssh-access-to-specific-commands-on-ubuntu/view
|
||||
|
||||
---
|
||||
|
||||
# Docker socket proxy: как дать `docker build` без доступа к `docker.sock`
|
||||
|
||||
## Tecnativa/docker-socket-proxy
|
||||
|
||||
- **Проблема**: дать сервису доступ к `/var/run/docker.sock` = дать **root на хосте** (даже на весь swarm). Прокси блокирует то, что сервису не нужно, возвращая **HTTP 403 Forbidden**.
|
||||
- Реализация: официальный Alpine-based **HAProxy** + маленький конфиг; **plain HTTP proxy** к хосту-unix-socket (TLS нет by design — ограничивать доступ сетью/Docker firewall).
|
||||
- Управление — **env-переменные по URL-префиксу** (напр. `AUTH` → блокирует `/auth/*`), значения **`0`=revoke, `1`=grant**:
|
||||
- Разрешены по умолчанию: `EVENTS`, `PING`, `VERSION` (почти безвредны).
|
||||
- **Запрещены по умолчанию (критично)**: `AUTH`, `SECRETS`, **`POST`** (при `POST=0` разрешены только GET/HEAD → весь API read-only).
|
||||
- Прочее по умолчанию off: `BUILD`, `CONTAINERS`, `EXEC`, `IMAGES`, `NETWORKS`, `VOLUMES`, `ALLOW_START/STOP/RESTARTS/PAUSE/UNPAUSE`, `COMMIT`, `CONFIGS`, `DISTRIBUTION`, `GRPC`, `INFO`, `NODES`, `PLUGINS`, `SERVICES`, `SESSION`, `SWARM`, `SYSTEM`, `TASKS`.
|
||||
- **Для `docker build`**: включить `IMAGES=1` и `BUILD=1` (или положиться на BuildKit-эндпоинты), но `POST` нужен разрешённым — иначе build не пройдёт. Точечно: НЕ давать `EXEC` (иначе агент влезет в чужой контейнер), `SECRETS`, `AUTH`.
|
||||
- Запуск: `docker run -d --privileged --name dockerproxy -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:2375:2375 tecnativa/docker-socket-proxy` → клиент через `DOCKER_HOST=tcp://localhost:2375`.
|
||||
- **Обязательно**: не публиковать порт в публичную сеть — только во внутренний Docker network с прокси и потребителем. Использовать GitHub Container Registry образ, а не Docker Hub (остальные теги deprecated).
|
||||
- **Более сильные альтернативы для build**: **rootless docker**, **BuildKit без root** (`buildkitd` как непривилегированный демон, `buildctl`), **kaniko**/**buildah** (сборка образа без демона вообще), **sysbox**, **Podman** с user-namespaced rootless containers. Для «агенту надо собрать образ» лучше не давать docker.sock вообще, а дать rootless `buildah`/`kaniko` или удалённый build-сервис.
|
||||
- Ссылка: https://github.com/tecnativa/docker-socket-proxy
|
||||
|
||||
---
|
||||
|
||||
# Сводка: как собрать «сборка/тесты — да, чужие файлы/секреты — нет»
|
||||
|
||||
| Слой | Механизм | Инструмент |
|
||||
|---|---|---|
|
||||
| Граница железной изоляции | microVM | Firecracker (+jailer), Fly Machines |
|
||||
| Сильная граница, но дешевле VM | userspace kernel | **gVisor** (`runsc`) |
|
||||
| Unprivileged ФС-песочница | user/mount/pid/net namespaces + `NO_NEW_PRIVS` | **bubblewrap** |
|
||||
| ФС-политика в ядре | Landlock / seccomp-bpf | Chromium, Codex (legacy) |
|
||||
| macOS-граница | Seatbelt / sandbox-exec | Claude Code, Codex, Chromium |
|
||||
| Ограничение syscall | seccomp-bpf | Chromium, Firecracker VMM, Codex net-filter |
|
||||
| Непривилегированный юзер | uid/gid drop, rootless | jailer `--uid/--gid`, rootless docker |
|
||||
| Эфемерность | timeout + авто-kill + снапшоты | E2B, Modal, Daytona, Fly |
|
||||
| Секреты | доступ только в setup-фазе, удаляются до agent phase | **Codex two-phase** |
|
||||
| Доступ к хосту | forced command + whitelist wrapper + bastion | `authorized_keys command=`/`restrict`, `ForceCommand`, `-J` |
|
||||
| Docker без docker.sock | API-прокси с точечными правами | tecnativa/docker-socket-proxy |
|
||||
|
||||
**Рекомендуемый минимум для агента-сборщика**: непривилегированный отдельный пользователь → bubblewrap/gVisor sandbox с read-only корнем и одним writable workspace → `--unshare-net`/прокси для сети → секреты только на этапе setup → отдельный bastion-пользователь с forced command + whitelist wrapper для доступа к хосту инженера → docker build через socket-proxy или (лучше) rootless BuildKit/buildah/kaniko.
|
||||
Reference in new issue
Block a user