Agent Sandboxing

Sandboxing — изолированная среда исполнения для агента/инструментов, чтобы взломанный через инъекцию агент не вырвался за периметр. Спектр: Docker (быстрый, слабая изоляция — общее ядро) → VM (сильная, медленная) → Firecracker / E2B microVM (золотой стандарт 2026: скорость контейнера + изоляция VM, старт ~150 мс).

status/volatile «Стандарт 2026», список технологий (Firecracker, E2B, Docker Sandboxes) и тайминги (~150 мс) быстро устаревают. Ревизия раз в квартал.

Суть

Любая защита на уровне промпта пробиваема, поэтому последний барьер — аппаратная граница. Если агенту дали инструменты execute_code / bash, изоляция исполнения обязательна: даже при успешном Tool Hijacking ущерб ограничен изолированной машиной.

Зачем это нужно

Это «ограничение последствий» — то, на что упирается даже «сорвавшаяся» после Jailbreak Attacks модель. Для computer-use агентов (Computer Use) изоляция — часть базовой защиты (никогда не на хосте, не маунтить docker.sock).

Как работает (спектр изоляции)

Технология Изоляция Старт Когда
Docker-контейнеры Слабая — все делят одно ядро ОС хоста Медленнее VM-микро Обычный веб-сервис (Next.js)
VM Сильная Секунды, большой overhead Когда нужна полная изоляция, скорость неважна
E2B / Firecracker microVM Сильная (своё ядро через KVM) ~150 мс Агент с execute_code / bash — золотой стандарт

Альтернативная линия: gVisor и WASM

Продакшен-факультатив заходит с той же посылки — голого Docker недостаточно, — но предлагает другую замену. Вместо микро-VM там два варианта:

  • gVisor — перехватывает системные вызовы приложения в пользовательском пространстве и не пускает их напрямую в ядро хоста. Изоляция без запуска отдельного ядра.
  • WASM-песочницы — исполнение Python внутри WebAssembly через Pyodide. Здесь изолируется не машина, а сам рантайм языка: код физически не имеет доступа к файловой системе и сети, если их явно не пробросили.

При этом Docker у него не отменяется совсем: одноразовые контейнеры с жёсткими лимитами CPU и памяти и отключённой сетью остаются рабочим вариантом для менее рискованных сценариев. Формулировка, которую он выносит в правило: никогда не запускать exec() напрямую в хост-ОС приложения.

Как это соотносится с Firecracker. Оба автора сходятся в диагнозе и расходятся в выборе лечения, причём выбор объясняется разным профилем задач:

Firecracker / microVM (базовый разбор) gVisor и WASM (продакшен-вариант)
Что изолирует Машину целиком, своё ядро через KVM Системные вызовы (gVisor) или рантайм языка (WASM)
Цена Старт ~150 мс, нужна аппаратная виртуализация Легче и быстрее, виртуализация не требуется
Слабое место Оверхед на каждый запуск Поверхность атаки больше: ядро хоста рядом
Естественный домен Долгие сессии computer-use, произвольный bash Короткие вычисления, исполнение сгенерированного кода в цикле рефлексии

Практический ориентир: чем длиннее живёт песочница и чем шире права внутри неё, тем сильнее аргумент за микро-VM. Для паттерна рефлексии, где код исполняется десятки раз подряд короткими итерациями (см. Multi Agent Patterns), оверхед старта становится определяющим, и WASM выигрывает.

Третий слой, который факультатив добавляет поверх изоляции при работе с MCP-серверами: доступ к базе только через read-only учётные записи с тайм-аутом и лимитом вывода (порядка пятидесяти строк), а любые мутирующие операции — через обязательное подтверждение оператором (Human in the Loop).

Firecracker (от AWS, та же технология, что AWS Lambda):

  • Каждый microVM получает изолированное ядро через KVM (аппаратная виртуализация) — побег из песочницы требует уязвимости в гипервизоре, а не в разделяемом ядре.

Не «невозможно», а «сильно дороже» Формулировку «выйти за периметр невозможно» стоит считать рекламной. Замер 2026 года (arXiv:2607.17518) подтверждает градиент изоляции container → gVisor → микро-VM, но снимает абсолют: side-channel через общий page cache не устраняется и микро-VM. Изоляция здесь — вопрос стоимости атаки, а не бинарной гарантии.

  • Написан на Rust — исключает целые классы уязвимостей (buffer overflow, memory safety).
  • Минималистичная модель устройств; изоляция процесса через cgroups, namespaces, seccomp BPF; сам процесс заперт через утилиту Jailer.
  • microVM даёт полноценную среду (включая Docker внутри) без доступа к хосту.
  • Scoped permissions — каждый microVM получает только нужные для задачи права.

Тренд 2026: в апреле 2026 Docker выпустил Sandboxes — каждый агент в microVM с приватным Docker внутри (Docker признал, что собственной изоляции контейнеров для агентных нагрузок недостаточно).

Спорный/устаревающий тезис Ранее Docker считался достаточной основой деплоя агента. Для агентов с доступом к коду это устаревает: «докер-контейнеры — это медленно… их изоляции недостаточно для агентных нагрузок». Для обычного веб-сервиса Docker по-прежнему ок.

Самый дешёвый уровень: разбор вместо исполнения

Не всякая задача, выглядящая как «выполни код», требует песочницы. Классический пример — арифметика: агенту нужно посчитать выражение, и соблазн решить это через eval() велик, потому что одна строка вместо тридцати.

eval() здесь недопустим в любом виде, включая «отфильтрованный». Фильтрация строки по запрещённым подстрокам — чёрный список, который обходится вложенностью, кодировками и обращением к атрибутам; за eval("__import__('os').system(...)") стоит вся стандартная библиотека.

Рабочий вариант — не исполнять строку, а разобрать её в дерево и обойти белый список разрешённых узлов:

_ALLOWED_OPS = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
                ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg}

def _eval_expr(node):
    if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
        return node.value
    if isinstance(node, ast.BinOp) and type(node.op) in _ALLOWED_OPS:
        return _ALLOWED_OPS[type(node.op)](_eval_expr(node.left), _eval_expr(node.right))
    if isinstance(node, ast.UnaryOp) and type(node.op) in _ALLOWED_OPS:
        return _ALLOWED_OPS[type(node.op)](_eval_expr(node.operand))
    raise ValueError("Unsupported expression")     # всё неперечисленное — отказ

Ключевое — последняя строка. Функция знает конечный список того, что умеет, и на всём остальном отказывает; вызовы функций, обращения к атрибутам и импорты просто не имеют ветки в разборе. Это тот же принцип белого списка, что и в реестре инструментов (Tool Calling), только на уровне выражения.

Правило выбора уровня: если множество допустимых операций конечно и перечислимо — хватает разбора, и песочница не нужна. Песочница начинается там, где агент выполняет произвольный код, который вы заранее не описали.

Альтернативный взгляд: управляемая песочница вместо своей

Разбор выше строит изоляцию самостоятельно — Docker SDK, лимиты CPU/RAM, отключённая сеть, при необходимости gVisor или WASM. Облачный заход предлагает другой ответ: взять готовый managed-рантайм, спроектированный именно под код от LLM.

Разница видна при сравнении обычного serverless-контейнера и специализированной песочницы (пример — Azure Container Apps Sandboxes):

Критерий Serverless-контейнер Managed sandbox
Назначение Хостинг сервисов и фоновых задач Выполнение недоверенного кода в реальном времени
Жизненный цикл Длительный Эфемерный: создаётся под сессию и уничтожается
Скорость запуска Секунды (холодный старт) < 100 мс за счёт пула прогретых инстансов
Изоляция Стандартная контейнерная, ядро Linux общее Аппаратная, Hyper-V microVM на пользователя
Рантайм Собираешь образ сам Готовый Python REPL с DS-библиотеками

Два аргумента, которых нет в самосборной схеме. Первый — скорость старта как продуктовое требование: пока песочница поднимается секунды, стриминг ответа стоит, и пользователь видит паузу. Второй — граница изоляции проходит по железу, а не по namespace ядра, что снимает целый класс побегов из контейнера.

Цена — вендор-лок и невозможность тонко настроить рантайм под себя.

Где проходит граница между managed и своей песочницей

Из сравнения выше граница читается по трём признакам, а не по цене за час.

Managed берут, когда песочница нужна эфемерная и часто: холодный старт под 100 мс достижим только пулом прогретых инстансов, а поддерживать такой пул самому — отдельная инфраструктурная работа. Плюс готовый рантайм снимает сборку образа под каждую задачу.

Свою строят, когда критичны две вещи, которых managed не даёт: полный контроль над содержимым образа (свои библиотеки, свои версии, отсутствие чужого агента внутри) и отсутствие выхода данных за периметр — недоверенный код обрабатывает ваши данные внутри чужого рантайма, и для регулируемого контура это стоп-фактор сам по себе, независимо от стоимости (DLP for LLM).

Стоимость становится решающей только на третьем шаге, когда обе стороны технически подходят: managed дороже за вызов, но не требует дежурства и обновления ядра, поэтому переход к своей окупается на постоянной нагрузке и не окупается на редкой.

Три вопроса про песочницу, которые легко перепутать

Заметка отвечает на первый из трёх, и границу стоит держать явно:

Вопрос Где
чем изолировать — контейнер, микровиртуальная машина, gVisor, WASM здесь
какой контракт между инструментами и средой, чтобы среду можно было подменить Sandbox Abstraction
как эксплуатировать арендованную среду: состояния, стоимость простоя, снимки Sandbox Lifecycle

Оси независимы: локальная реализация не изолирует вовсе, но контракту удовлетворяет; облачная изолирует полностью и удовлетворяет тому же контракту. Выбирать технологию изоляции и проектировать контракт — разные решения, и принимаются они в разное время.

Отдельное измерение: как права объявляются

Всё выше — про то, чем изолировать: контейнер, микровиртуальная машина, gVisor, WASM. Есть второй вопрос, независимый от первого: в какой форме права записаны.

Обвязка Codex объявляет политику перечислимым значением — readOnly, workspaceWrite, dangerFullAccess, externalSandbox — и уточняет полями writableRoots, readableRoots, networkAccess (Agent Harness).

Разница с рассыпанными по коду проверками практическая, а не эстетическая. Объявленное значение читается из конфигурации, попадает в лог, сравнивается между запусками и запрещается политикой централизованно; условия в коде ничего из этого не позволяют, и ответ на вопрос «а что этому запуску вообще разрешено» приходится собирать чтением (Agent Control Plane).

Отдельного упоминания стоит externalSandbox — значение, которое честно говорит «изоляцию обеспечивает вызывающая сторона». Явное «здесь не я» лучше умолчания: без него окружение, где изоляции нет вовсе, неотличимо от окружения, где она есть снаружи.

Три уровня, которые называют одним словом «песочница»

Архитектурно песочница — не контейнер, а режим ограничений: она отвечает на вопрос «что произойдёт, если инструмент поведёт себя хуже, чем мы ожидали». Реализаций много, уровней изоляции — три, и команды регулярно считают, что у них есть песочница, имея только первый:

Уровень Что это Чем останавливает
Логическая Проверки политик, контракты возможностей, списки разрешений Логикой приложения
Процессная Отдельный процесс, таймаут, лимиты ресурсов Операционной системой
Средой исполнения Отдельное окружение, урезанная файловая система, ограниченный исходящий трафик, минимум секретов Самой средой

Проверочный вопрос, отделяющий одно от другого: если возможность начнёт вести себя хуже нормы, что именно её остановит — логика, процесс или среда? Для чтения с низким риском иногда хватает первого уровня; для исполнения с высоким риском почти всегда нужен третий.

Ограничивает песочница шесть вещей: доступ к сети, к файловой системе, к секретам, бюджет процессора и памяти, разрешённые системные вызовы, время жизни операции.

Связано с

  • Sandbox Abstraction — контракт со средой, позволяющий её подменить
  • Sandbox Lifecycle — как эксплуатировать арендованную среду: состояния, стоимость, снимки
  • Agent Harness — как права объявляются, в отличие от того, чем изолировать
  • Capability Containment — сдерживание возможностей как принцип над этими уровнями
  • Tool Catalog — где записан профиль изоляции конкретной возможности
  • Capability Containment — зачем изоляция сильнее надзора за поведением
  • Tool Hijacking — что именно ограничивает sandbox
  • Jailbreak Attacks — «ограничение последствий» сорвавшейся модели
  • Guardrails — sandbox как Action-слой защиты
  • Computer Use — Firecracker/gVisor для агента, управляющего экраном
  • Agent Security — место изоляции в модели угроз
  • Tool Calling — реестр с allowlist как тот же принцип уровнем выше