Песочницы для AI-агентов: Docker, gVisor, Firecracker

Каждый AI-агент рано или поздно просит об одном и том же: «дай мне запустить программу». Иногда это безобидный pytest. Иногда — pip install sketchy-package && python run.py. Так или иначе, стоит агенту получить право выполнять код, и вы уже запускаете чужие байты на машине, которая вам дорога.

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

AI-агенты воссоздают ту же проблему, только теперь «образец малвари» — это, как правило:

  • код, сгенерированный моделью;

  • код, вставленный пользователем;

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

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

И «побег» — не единственный режим отказа. Даже без эксплойта ядра недоверенный код может:

  • утащить секреты (SSH-ключи, облачные креды, API-токены);

  • отправить домой код приватного репозитория;

  • сделать разворот во внутренние сети;

  • или просто спалить ваши деньги (майнинг, форк-бомбы, безудержные сборки).

Запрос простой: запусти эту программу и не дай ей превратиться в проблему захвата машины.

Это уже не нишевая тема. Vercel, Cloudflare и Google за последний год выпустили продукты с песочницами для выполнения кода. Но выбор базовой технологии по-прежнему понимают плохо — и получаются «песочницы», которые либо слабее ожидаемого, либо дороже необходимого.

Часть путаницы в том, что выполнение кода агентами бывает разной формы:

  • Удалённый devbox / кодовый агент: долгоживущее рабочее пространство, доступ к шеллу, пакетные менеджеры, иногда GPU.

  • Stateless-интерпретатор кода: выполнить фрагмент, вернуть вывод, отбросить состояние.

  • Вызов инструментов (tool calling): запуск небольших компонентов (например, «прочитай этот файл», «вызови этот API») с явными правами.

  • RL-среды: множество параллельных прогонов, быстрый сброс, иногда снапшот/восстановление.

Разным формам нужны разный жизненный цикл и разные границы.

Есть здесь и «железная» подоплёка. В 2010-м «просто возьми VM» обычно означало секунды загрузки и накладные расходы, убивающие плотность. Контейнеры победили, потому что были дёшевы.

В 2026-м микро-VM можно загрузить достаточно быстро, чтобы ощущалось как контейнер, а снапшот/восстановление делают «сброс» почти бесплатным. Пространство компромиссов изменилось, а словарь — нет.

Другая часть путаницы — само слово песочница. На практике им называют как минимум четыре разные границы:

Контейнер делит хостовое ядро. Каждый системный вызов по-прежнему попадает в то же ядро, что обслуживает всё остальное. Баг ядра в любом разрешённом пути syscall — это баг хоста.

Микро-VM запускает гостевое ядро за аппаратной виртуализацией. Нагрузка общается со своим ядром. Хостовое ядро видит в основном ioctl-ы KVM и I/O virtio-устройств, а не полный ABI системных вызовов Linux.

gVisor вставляет ядро в пространстве пользователя. Системные вызовы приложения обрабатывает Sentry, а не хостовое ядро напрямую; сам Sentry использует небольшой список разрешённых хостовых syscall-ов.

WebAssembly / изоляты ограничивают код внутри рантайма. Нет доступа к файловой системе или сети по умолчанию; гость получает только те возможности хоста, которые вы явно предоставили.

Это не взаимозаменяемые вещи. У них разные стоимости старта, истории совместимости и режимы отказа. Выберете не то — получите либо «песочницу», которая течёт, либо песочницу, которая не может запустить нужный вам софт.

Как каждая граница опосредует доступ к хостовому ядру

Как каждая граница опосредует доступ к хостовому ядру. Слева направо прямое воздействие на ABI системных вызовов уменьшается.

Диаграмма — это вся суть. Контейнеры открывают ABI системных вызовов хостового ядра (отфильтрованный политикой, но всё то же ядро). gVisor вставляет ядро в пространстве пользователя. Микро-VM ставят гостевое ядро за аппаратной виртуализацией. Wasm-модули вообще не получают syscall ABI — им дают явные хостовые функции.

Но бесплатно ничего не бывает. Каждая «более сильная» граница добавляет свой доверенный компонент: ядро в пространстве пользователя (gVisor), гостевое ядро + VMM (микро-VM) или рантайм + встраиватель (Wasm). Сильнее не значит проще. Это значит, что вы ставите на другой код.

Если это всё ещё кажется странным — потому что индустрия приучила нас считать контейнеры ответом по умолчанию.

Середина 2010-х. Docker взрывается. Kubernetes делает контейнер единицей планирования. VM тяжелее, дольше грузятся, сложнее в эксплуатации. Контейнеры решают реальную проблему: быстрый старт, плотная упаковка, один и тот же артефакт везде.

Для доверенных нагрузок эта ставка часто нормальна: вы и так доверяете хостовому ядру.

AI-агенты меняют модель угроз: теперь вы выполняете произвольные пути кода, часто сгенерированные моделью, иногда предоставленные пользователем, внутри своей инфраструктуры.

Это не значит «никогда не используйте контейнеры». Это значит, что нужна более чёткая процедура принятия решения, чем «то, что мы уже используем».

Дальше я дам простую ментальную модель для оценки песочниц, а затем пройдусь по границам, которые встречаются в реальных системах выполнения кода AI: контейнеры, gVisor, микро-VM и рантайм-песочницы.

Модель трёх вопросов

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

Граница (boundary) — где обеспечивается изоляция. Она определяет, что стоит по каждую сторону линии.

  • Граница контейнера: процессы в отдельных namespace-ах, но всё то же одно хостовое ядро.

  • Граница gVisor: системные вызовы нагрузки сначала обслуживает ядро в пространстве пользователя (Sentry).

  • Граница микро-VM: syscall-ы идут в гостевое ядро; хостовое ядро видит активность гипервизора/VMM, а не гостевой syscall ABI.

  • Граница рантайма: гостевой код не имеет syscall ABI; он может вызывать только явные хостовые API.

Граница — это линия, про которую вы ставите, что атакующий её не пересечёт.

Политика (policy) — чего код может касаться внутри границы:

  • пути файловой системы (чтение/запись/выполнение);

  • сетевые назначения и протоколы;

  • создание процессов / сигналы;

  • доступ к устройствам (включая GPU);

  • квоты времени/памяти/CPU/диска;

  • и сама поверхность интерфейса (syscall-ы, ioctl-ы, импорты).

Жёсткая политика в слабой границе — всё равно слабая песочница. Сильная граница с разрешительной политикой — упущенная возможность.

Жизненный цикл (lifecycle) — что сохраняется между прогонами. Для агентов и RL это очень важно:

  • Свежий прогон: ничего не сохраняется. Отлично для враждебного кода. Плохо для UX «рабочего пространства агента».

  • Рабочее пространство: долгоживущая ФС/сессия. Отлично для агентов; опасно, если утекают секреты или злоупотребляют персистентностью.

  • Снапшот/восстановление: быстрый сброс через чекпоинт состояния VM или рантайма. Отлично для RL-роллаутов и «прогретых» агентов.

Жизненный цикл меняет и операционные выборы. Нужны снапшоты — нужна снапшотируемая граница (микро-VM, некоторые рантаймы). Нужно рабочее пространство — нужны надёжное хранилище и продуманная политика для секретов.

Три вопроса

Оценивая любую песочницу, спросите:

  1. Что разделяется между этим кодом и хостом?

  2. Чего код может касаться (файлы, сеть, устройства, syscall-ы)?

  3. Что переживает прогоны?

Если можете ответить — вы понимаете свою песочницу. Если нет — вы гадаете.

Быстрый пример делает разделение нагляднее:

  • Многоарендный кодовый агент

    • Граница: микро-VM (гостевое ядро)

    • Политика: разрешить ФС рабочего пространства, запретить хостовые монтирования, allowlist исходящего трафика, никаких сырых устройств

    • Жизненный цикл: снапшот базового образа, клон на сессию, уничтожение при закрытии

Та же продуктовая идея, другие ограничения:

  • Вызов инструмента (например, «отформатируй этот код»)

    • Граница: Wasm-компонент

    • Политика: preopen одной директории, сеть по умолчанию запрещена

    • Жизненный цикл: свежий на каждый вызов

Словарь

Песочница: граница + политика + жизненный цикл.

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

Виртуальная машина: гостевое ядро ОС, работающее на виртуальном железе.

Микро-VM: минимальная VM, оптимизированная под быстрый старт и малый след.

Рантайм-песочница: изоляция, обеспечиваемая рантаймом (Wasm, V8-изоляты), а не ОС.

Строительные блоки Linux

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

Анатомия системного вызова Linux

Он работает в пространстве пользователя. Когда ему нужно что-то реальное — прочитать файл, создать сокет, выделить память, породить процесс — он делает системный вызов. Этот syscall входит в ядро. Код ядра выполняется с полными привилегиями.

Если есть баг в любом достижимом пути syscall, коде файловой системы, сетевом коде или обработчике ioctl — можно получить локальное повышение привилегий. Это корень истории про «побег из контейнера».

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

Контейнеры Linux — это «политический бутерброд» из четырёх примитивов:

Namespaces

Namespace-ы дают процессу изолированное представление определённых ресурсов, предоставляя отдельные экземпляры подсистем ядра:

  • PID namespace: изолированное дерево процессов / нумерация PID

  • Mount namespace: изолированная таблица монтирований / вид ФС

  • Network namespace: изолированный сетевой стек (интерфейсы, маршруты, состояние netfilter)

  • IPC/UTS namespaces: изоляция System V IPC и изоляция hostname

  • User namespace: маппинги UID/GID и скоупинг capabilities

User namespace-ы стоит выделить. Они позволяют смапить «root внутри контейнера» на непривилегированный UID на хосте. Это меняет смысл привилегии. Rootless-контейнеры получают здесь реальный выигрыш в безопасности: случайный «root в контейнере» не означает автоматически «root на хосте».

Но граница ядра всё та же. Баг ядра — это баг ядра.

Capabilities

Linux дробит «root» на capabilities (мелкогранулярные привилегии). Контейнеры обычно стартуют с урезанным набором, но часто в нём всё ещё достаточно силы, чтобы навредить, если раздать не те.

Печально известная — CAP_SYS_ADMIN. Она гейтит огромный, слабо связанный набор привилегированных операций и на практике часто открывает доступ к опасным интерфейсам ядра. Поэтому говорят: «SYS_ADMIN — это новый root».

В реальных песочницах считайте выдачу capabilities частью бюджета атакующей поверхности. Самый простой выигрыш — просто убрать ненужные.

Cgroups

Cgroups (control groups) ограничивают и учитывают ресурсы:

  • квоты/доли CPU и наборы аффинности

  • лимиты памяти

  • троттлинг пропускной способности I/O / IOPS

  • максимальное число процессов (смягчает форк-бомбы)

Cgroups прежде всего про предотвращение исчерпания ресурсов. Они существенно не уменьшают атакующую поверхность ядра.

Seccomp

Seccomp — это фильтрация системных вызовов. Процесс устанавливает BPF-программу, которая срабатывает на входе в syscall; она может проверить номер вызова (а во многих профилях — и аргументы) и решить: разрешить, запретить, залогировать, trap, убить или уведомить супервизор.

Жёсткий seccomp-профиль блокирует syscall-ы, расширяющие атакующую поверхность ядра или дающие эскалацию (ptrace, mount, kexec_load, bpf, perf_event_open, userfaultfd и т. п.). Он же обычно блокирует легаси-интерфейсы, которые трудно безопасно изолировать.

Как игрушечный пример, правило seccomp «запретить опасные syscall-ы» часто выглядит так:

{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    { "names": ["bpf", "perf_event_open", "kexec_load"], "action": "SCMP_ACT_ERRNO" }
  ]
}

В реальных песочницах вы также фильтруете аргументы (флаги clone3, номера запросов ioctl) и используете allowlist, а не denylist.

Есть ещё одна фича seccomp, о которой стоит знать, потому что она встречается в песочницах: пользовательские уведомления seccomp (SECCOMP_RET_USER_NOTIF). Вместо простого «разрешить/запретить» ядро может приостановить syscall и отправить его процессу-супервизору для решения. Это позволяет строить «брокерские syscall-ы» (например, разрешать open(), только если путь соответствует политике, или проксировать сетевые подключения через движок политик).

Это мощно, но не бесплатно: брокерские syscall-ы добавляют задержку и сложность, а ваш брокер становится частью доверенной вычислительной базы.

Но итог тот же: syscall-ы, которые вы всё же разрешаете, по-прежнему выполняются в хостовом ядре.

Как контейнеры это комбинируют

«Контейнер» — это просто обычный процесс, настроенный набором политик ядра плюс корневая ФС:

  1. Namespace-ы скоупят/виртуализируют ресурсы.

  2. Capabilities урезаются.

  3. Cgroups ограничивают потребление ресурсов.

  4. Seccomp фильтрует syscall-ы на входе.

  5. Корневая ФС даёт контейнеру его вид / (часто слоями через overlayfs).

  6. AppArmor/SELinux могут применять дополнительную политику.

Строительные блоки песочницы в Linux: syscall-ы

Концептуально: syscall-ы входят в хостовое ядро. Seccomp может блокировать их до диспетчеризации. Namespace-ы скоупят ресурсы. Cgroups обеспечивают квоты. Но ядро всё то же, хостовое.

Это ограничение на основе политики внутри общей границы ядра. Вы уменьшаете, что процесс видит (namespaces), ограничиваете, что он потребляет (cgroups), и ограничиваете, какие syscall-ы он вызывает (seccomp). Вы не вставляете более сильную границу изоляции.

Так почему же контейнеры считают границей безопасности? Потому что «изолированное» смешивают с «безопасным», и потому что контейнеры операционно удобны.

Но если вы выбираете песочницу, граница важнее удобства.

Где контейнеры не справляются

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

Чаще всего я вижу такие режимы отказа: неправильная конфигурация и баги ядра/рантайма — плюс третий, который вылезает в AI-системах: утечка политики.

Побеги через мисконфигурацию

Многие побеги из контейнеров — самонанесённые. Рантайм предлагает способы ослабить изоляцию, и люди ими пользуются.

--privileged убирает большинство защит. По сути превращает контейнер в «root на хосте с парой лишних шагов». Если вашей песочнице нужен privileged-режим — у вас нет песочницы.

Docker-сокет (/var/run/docker.sock) — ещё одна классика. Смонтируйте его — и можно попросить хостовый Docker-демон создать новый привилегированный контейнер, смонтировать хостовую ФС и так далее. На практике доступ к Docker-сокету — это доступ к root на хосте.

Чувствительные монтирования и широкие capabilities — остальная часть обычного списка:

  • доступный на запись /sys или /proc/sys

  • хостовые пути, примонтированные на запись

  • добавление широких capabilities (особенно CAP_SYS_ADMIN)

  • присоединение к хостовым namespace-ам (--pid=host, --net=host)

  • проброс устройств, открывающий сырые интерфейсы ядра

Это решаемые проблемы: конфиги можно аудировать и банить очевидные «грабли».

Баги ядра и рантайма

Правильно настроенный контейнер всё равно делит хостовое ядро. Если в ядре есть баг, достижимый через разрешённый syscall, путь ФС, поведение сетевого стека или ioctl, код внутри контейнера может его триггернуть.

Примеры релевантных контейнерам локальных повышений привилегий:

  • Dirty COW (CVE-2016-5195): гонка copy-on-write в подсистеме памяти.

  • Dirty Pipe (CVE-2022-0847): баг обработки pipe, позволяющий перезаписывать данные в read-only отображениях.

  • Переполнение fs_context (CVE-2022-0185): баг разбора контекста ФС, эксплуатируемый в контейнерных сценариях.

Seccomp снижает воздействие, блокируя syscall-ы, но те, что вы разрешаете, — всё равно код ядра. Дефолтный seccomp-профиль Docker — это allowlist с уклоном в совместимость: он блокирует часть высокорисковых вызовов, но всё равно разрешает сотни.

И дело не только в ядре. Достаточно бывает бага рантайма контейнеров (например, перезапись runC: CVE-2019-5736).

Утечка политики (специфичная для AI)

Многие сбои «песочницы агента» — вовсе не побеги из ядра. Это сбои политики.

Если ваша песочница может читать репозиторий и имеет исходящий доступ в сеть — агент может слить репозиторий. Если она может читать ~/.aws или монтировать хостовые тома — может слить креды. Если может достучаться до внутренних сервисов — может стать инструментом бокового перемещения.

Поэтому проектирование песочницы для агентов чаще про явный дизайн возможностей, чем про «самую сильную доступную границу». Граница важна, но политика — это то, чем вы контролируете радиус поражения, когда модель делает что-то глупое или её направляют вредоносные промпты.

Две практические заметки:

  • Rootless/user namespaces помогают. Они уменьшают ущерб от случайной привилегии. Но не убирают баги ядра.

  • Многоарендность меняет всё. Если вы запускаете код из разных доменов доверия на одном ядре — считайте, что кто-то попробует ударить по багам ядра и по side-channel-ам. «Да это же просто билд-код» перестаёт утешать.

Ещё одна контейнерная засада: ioctl — это огромная поверхность. Даже если вы блокируете опасные syscall-ы, множество реальных интерфейсов ядра живут за ioctl() на файловых дескрипторах (ФС, устройства, сеть). Если вы пробрасываете устройства (особенно GPU), вы открываете большие пути драйверного кода недоверенному вводу.

Поэтому многие «AI-песочницы», выглядящие как контейнеры, в итоге тихо добавляют снизу одну из более сильных границ: либо gVisor, чтобы уменьшить хостовые syscall-ы, либо микро-VM, чтобы поставить гостевое ядро перед хостовым.

Ничто из этого не значит, что контейнеры «плохие». Это значит, что контейнеры — отличный инструмент, когда весь код внутри контейнера в том же домене доверия, что и хост. Если вы запускаете свои сервисы — часто так и есть.

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

Опции хардинга

Хардинг ужесточает политику. Он не меняет границу.

Мера хардинга Что делает Чего не делает

Кастомный seccomp

блокирует больше syscall-ов/аргументов, чем дефолт

не защищает от багов в разрешённых путях ядра

AppArmor/SELinux

ограничивает ФС/procfs и чувствительные операции

не чинит баги ядра; лишь уменьшает достижимые пути

Сброс capabilities

убирает привилегированные интерфейсы (избегайте SYS_ADMIN)

не меняет границу общего ядра

Read-only rootfs

запрещает запись в корень контейнера

не мешает эксплуатации в памяти/ядре

User namespaces

мапят root контейнера на непривилегированный UID хоста

баги ядра всё ещё могут дать эскалацию

На пределе контейнер можно захарденить почти до неюзабельности. Границу вы всё равно не поменяли. Немногие разрешённые syscall-ы — всё ещё привилегированный код ядра. Ядро всё ещё общее.

Если эксплойт ядра имеет значение, нужна другая граница:

  • gVisor: перехват syscall-ов

  • микро-VM: гостевое ядро за виртуализацией

  • Wasm/изоляты: вообще нет syscall ABI

Более сильные границы

У контейнеров общее фундаментальное ограничение: syscall-ы нагрузки идут в хостовое ядро. Чтобы реально сменить границу, есть два основных подхода:

  1. Перехват syscall-ов: перехватывать вызовы до того, как они достигнут хостового ядра, реализовать достаточную часть Linux в пространстве пользователя.

  2. Аппаратная виртуализация: запускать гостевое ядро за VMM/гипервизором, снизив воздействие на хост до VM-exit-ов и I/O устройств.

gVisor

gVisor — это «прикладное ядро», которое перехватывает syscall-ы (и часть исключений) из контейнера и обрабатывает их в ядре пространства пользователя, называемом Sentry.

Если контейнеры — это «процессы в namespace-ах», то gVisor — это «процессы в namespace-ах, но их syscall-ы не идут прямо в хостовое ядро».

Пара деталей реализации важна при выборе:

  • gVisor интегрируется как OCI-рантайм (runsc), поэтому встраивается в Docker/Kubernetes.

  • Sentry реализует логику ядра на Go: syscall-ы, сигналы, части /proc, сетевой стек и т. д.

  • Перехват делает платформа gVisor. Сегодня по умолчанию — systrap; старые развёртывания использовали ptrace, есть опциональный режим KVM.

Две подсистемы стоит понимать, потому что они доминируют в производительности и поведении безопасности:

Медиация ФС (Gofer / lisafs). gVisor обычно разделяет обязанности: Sentry обеспечивает семантику syscall-ов, но доступ к ФС может опосредоваться отдельным компонентом, который выполняет операции с хостовой ФС и отдаёт их Sentry по протоколу (исторически «Gofer»; новее — lisafs). Так gVisor держит хостовый интерфейс Sentry маленьким и аудируемым.

Сеть (netstack vs host). gVisor может использовать собственный сетевой стек в пространстве пользователя («netstack») и не взаимодействовать с хостовым сетевым стеком так, как это делает контейнер. Есть и режимы с более прямой интеграцией в хостовую сеть — зависит от ограничений развёртывания.

Суть безопасности в том, что нагрузка больше не выбирает, какие хостовые syscall-ы вызывать. Это делает Sentry, а сам Sentry можно ограничить небольшим allowlist-ом хостовых syscall-ов. gVisor публиковал цифры: 53 хостовых syscall-а без сети плюс ещё 15 с сетью (итого 68) в одной конфигурации, обеспечиваемые seccomp-ом на процессе Sentry.

Это совсем другой интерфейс, чем «какие угодно syscall-ы, которые может сделать ваша контейнерная нагрузка».

Компромиссы предсказуемы:

  • Совместимость: не каждый syscall и не каждое поведение ядра идентичны. Есть таблица совместимости syscall-ов, и рано или поздно вы в неё упрётесь.

  • Накладные расходы: перехват syscall-ов не бесплатен. Syscall-heavy нагрузки платят больше. ФС-heavy нагрузки платят за медиацию и лишние копии/IPC.

  • Отлаживаемость: режимы отказа включают ENOSYS для нереализованных вызовов или тонкие семантические несовпадения.

Моё мнение: gVisor лучше всего подходит, когда можно терпеть «Linux, но с матрицей совместимости» и хочется существенно меньший интерфейс к хостовому ядру, чем у стандартного контейнера.

Архитектура gVisor: Sentry перехватывает syscall-ы приложения

Микро-VM

Альтернатива перехвату syscall-ов — аппаратная изоляция. Запускать гостевое ядро за аппаратной виртуализацией (KVM на Linux, Hypervisor.framework на macOS). Хостовое ядро видит VM-exit-ы и I/O virtio-устройств, а не отдельные syscall-ы нагрузки.

Поэтому микро-VM — ответ по умолчанию для «запусти произвольный Linux-код для незнакомцев». Вы получаете полную семантику Linux, не переписывая syscall ABI.

Что на самом деле видит хостовое ядро

Микро-VM всё ещё использует хостовое ядро, но интерфейс меняет форму:

  • VMM делает ioctl-ы /dev/kvm, чтобы создавать vCPU, отображать гостевую память и запускать VM.

  • Гость взаимодействует с виртуальными устройствами (virtio-net, virtio-blk, vsock). Эти устройства реализует VMM (или бэкенды вроде vhost-user).

  • Выполнение вызывает VM-exit-ы (переходы в хост) на привилегированных событиях, I/O устройств, прерываниях и т. п.

Хостовое ядро всё ещё опосредует доступ, но через более узкий, структурированный интерфейс, чем полный syscall ABI Linux.

Чего микро-VM сами по себе не решают

Граница гостевого ядра не означает автоматически «безопасно».

Политику всё равно нужно решать:

  • Есть ли у гостя исходящий доступ в сеть?

  • Монтирует ли он секреты или креды?

  • Есть ли доступ к внутренним сервисам?

  • Разделяет ли он какое-либо состояние ФС между прогонами?

Микро-VM — сильная граница, но нужна ещё сильная политика, чтобы не превратить её в мощный ящик для эксфильтрации данных.

На масштабе песочницы на микро-VM сходятся к небольшому набору паттернов жизненного цикла:

  • Эфемерная сессионная VM: загрузка → выполнение команд → уничтожение. Просто и надёжно.

  • Клонирование из снапшота: один раз загрузить «золотую» VM (рантаймы языков, кэш пакетов) → снапшот → клон на сессию. Быстрый холодный старт и быстрый сброс.

  • Стиль fork-and-exec: держать пул приостановленных VM, возобновлять по требованию. Операционно сложнее, но снижает хвостовую задержку.

Инъекция состояния — тоже выбор дизайна, а не данность:

  • Образы блочных устройств (ext4 внутри virtio-blk): просто, портативно, дружелюбно к снапшотам.

  • virtio-fs / 9p-подобные шары: пробросить хостовую директорию в гостя (полезно для «зеркал рабочего пространства», но снова вводит хостовую ФС в поверхность политики).

  • Сетевая загрузка: тянуть код в гостя из объектного хранилища/Git-ремоута. Держит хостовую ФС вне границы гостя, но требует сетевой политики.

И всё равно нужна сетевая история. Частые паттерны:

  • NAT + egress-allowlist: самый распространённый для SaaS-агентов.

  • Нет прямого интернета: весь трафик через прокси, обеспечивающий политику и логирование.

  • Выделенный VPC/подсеть: изолировать «недоверенное выполнение» от внутренних сервисов.

Наконец, если важно защитить гостя от хоста (например, «пользователи не доверяют оператору»), смотрите в сторону конфиденциальных вычислений (SEV-SNP/TDX) и проектов вроде Confidential Containers. Это другая модель угроз, но всё более актуальная для хостинга выполнения агентов.

Что такое VMM?

На Linux «VM» — это два слоя:

  • KVM (в ядре): превращает Linux в гипервизор и открывает примитивы виртуализации через ioctl-ы /dev/kvm.

  • VMM (пространство пользователя): выделяет гостевую память, настраивает vCPU и предоставляет виртуальные устройства, которые использует гость.

QEMU — классический VMM общего назначения: масса устройств, масса легаси-путей, масса кода. Эта гибкость полезна — и одновременно это атакующая поверхность и операционная стоимость.

VMM для микро-VM намеренно это урезают: меньше устройств, меньше путей эмуляции, меньший след, быстрее загрузка.

Модель устройств — это новый интерфейс

Переход от «syscall-ов контейнера» к «микро-VM» смещает атакующую поверхность, а не устраняет её.

Вместо тревоги о каждом обработчике syscall в хостовом ядре вы тревожитесь о:

  • обработке ioctl-ов KVM в хостовом ядре,

  • процессе VMM (его разбор конфигурации устройств, его циклы событий),

  • и реализациях виртуальных устройств (virtio-net, virtio-blk, virtio-fs, vsock).

Поэтому VMM для микро-VM агрессивно минимизируют устройства. Каждое добавленное устройство — это больше разбора, больше конечных автоматов, больше краевых случаев.

Это меняет и вашу ответственность за патчинг. С микро-VM у вас теперь два ядра, которые надо держать здоровыми:

  • хостовое ядро (KVM и связанные подсистемы),

  • и гостевое ядро (то, что ваш недоверенный код атакует первым).

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

Virtio, но какой вариант?

Virtio-устройства можно экспонировать через разные транспорты. Firecracker использует virtio-mmio (простой, минимальный), другие VMM обычно используют virtio-pci (форма «обычной VM», более широкая экосистема устройств). Обычно это не важно, пока не станет важно: часть гостевого тулинга предполагает PCI, часть фич производительности предполагает определённый стек, а работа с проброса тяготеет к PCI.

«Микро-VM» описывает дизайн-стойку: убрать легаси-устройства и держать модель устройств маленькой.

Сравнение: syscall-ы контейнера идут в общее хостовое ядро; syscall-ы микро-VM — в гостевое ядро

Firecracker

Firecracker — минималистичный VMM от AWS для многоарендного serverless (Lambda, Fargate). Он специально сделан для запуска множества маленьких VM с плотным конфайнментом хоста.

Архитектура намеренно скучная:

  • один процесс Firecracker на микро-VM,

  • минимальная модель virtio-устройств (net, block, vsock, console),

  • и «jailer», который настраивает изоляцию для процесса VMM до того, как гость вообще запустится.

Внутри можно думать о трёх типах потоков:

  • поток API (control plane),

  • поток VMM (модель устройств и I/O),

  • потоки vCPU (крутят цикл KVM).

Модель безопасности — защита в глубину вокруг VMM:

  1. Jailer настраивает chroot + namespaces + cgroups, сбрасывает привилегии, затем делает exec VMM.

  2. Жёсткий seccomp-профиль ограничивает VMM. Статья Firecracker на NSDI описывает whitelist из 24 syscall-ов (с фильтрацией аргументов) и 30 ioctl-ов.

Микро-VM Firecracker настраивается через небольшую API-поверхность (REST API или сокет). Типичные продукты «удалённый devbox» строят вокруг этого API высокоуровневый жизненный цикл: загрузка, снапшот, восстановление, пауза, возобновление, сбор логов.

Пример: VMM не «монтирует ваш репозиторий». Вы решаете, как инъецировать состояние: подключить блочное устройство, смонтировать virtio-fs-шару или тянуть код по сети внутри гостя. Это выборы политики.

Снапшот/восстановление — практический выигрыш для агентов и RL:

  • Агенты: можно прогреть базовый образ (рантаймы языков, кэш пакетов) и быстро клонировать.

  • RL: можно сбросить к известному состоянию без переигрывания долгой инициализации.

Firecracker поддерживает снапшоты, но их производительность и корректность чувствительны к нагрузке (размер памяти, состояние устройств, источники энтропии). Считайте, что это «хорошо работает во многих случаях», а не «бесплатно».

Несколько прагматичных ограничений:

  • Firecracker ориентирован на современных Linux-гостей. Он намеренно избегает многого «PC-совместимого» железа.

  • Набор устройств минимален по замыслу. Если ваша нагрузка зависит от малоизвестных устройств или модулей ядра, придётся адаптировать гостя или выбрать другой VMM.

  • Отладка больше похожа на «отладку крохотной VM», чем «отладку контейнера». Пригодятся хорошие логи serial-консоли и метрики VMM.

И нюанс, который важен: «загрузка 125 мс» — это обычно путь загрузки VMM и гостя. Если вы запускаете Firecracker через более тяжёлый control plane (containerd, настройка сети, оркестрация хранилища), сквозной холодный старт может быть выше. Измеряйте в своём стеке.

Архитектура Firecracker: одно-процессный VMM с virtio-устройствами и jailer

cloud-hypervisor

cloud-hypervisor — тоже VMM на Rust, из той же экосистемы rust-vmm, но нацелен на более широкий класс сценариев «современной облачной VM».

Отличие от Firecracker — фичи и модель устройств:

  • virtio-устройства на базе PCI (без virtio-mmio)

  • горячее подключение CPU/памяти/устройств

  • опциональные vhost-user бэкенды (вынос бэкендов устройств из процесса)

  • проброс VFIO (включая GPU, с обычными ограничениями IOMMU/VFIO)

  • поддержка Windows-гостей

Если нужна «граница VM и проброс GPU» — это частая точка приземления. Проект заявляет загрузку до пространства пользователя менее 100 мс при прямой загрузке ядра.

Осторожно с пробросом GPU: VFIO даёт гостю куда более прямой доступ к железу. Это бывает необходимо и всё ещё безопасно, но меняет режимы отказа. Теперь вас волнуют прошивка устройства, изоляция IOMMU и конфигурация гипервизора так, как не волновали в «CPU-only микро-VM». Многие системы выбирают гибрид: CPU-only микро-VM для общего выполнения кода и отдельная граница GPU-сервиса для исполнения моделей.

libkrun

libkrun выбирает другой подход: это минимальный VMM, встроенный как библиотека с C-API. Он использует KVM на Linux и Hypervisor.framework на macOS/ARM64.

Это встречается в тулинге «запусти контейнеры внутри лёгких VM на моём ноутбуке», и это релевантно, потому что локальные агенты всё чаще первоклассный сценарий. У libkrun интересный macOS-угол: virtio-gpu + Mesa Venus для форвардинга Vulkan-вызовов на Apple Silicon (Vulkan→MoltenVK→Metal). Во многих сетапах так некоторые контейнерные стеки получают GPU-ускорение на macOS без «полновесных» менеджеров VM.

Загвоздка в том, что граница доверия может быть иной. С встроенным VMM нужно считать сам процесс VMM частью TCB и изолировать его, как любой привилегированный хелпер.

Опции микро-VM с высоты птичьего полёта

VMM Хорош в Не очень в

Firecracker

многоарендная плотность, плотный конфайнмент хоста, снапшоты

общие фичи VM, проброс GPU

cloud-hypervisor

более широкий набор фич VM; VFIO/hotplug; поддержка Windows

минимально возможная поверхность

libkrun

лёгкие VM на dev-машинах (особенно macOS/ARM64)

крупномасштабные многоарендные control plane-ы

gVisor против микро-VM

Если выбираете между «перехватом syscall-ов» и «аппаратной виртуализацией», практические компромиссы такие:

  • Совместимость: микро-VM — полный Linux; gVisor — «в основном Linux», и нужно валидировать.

  • Накладные расходы: gVisor избегает запуска гостевого ядра; микро-VM платят за гостевое ядро и VMM (но всё ещё могут быть быстрыми).

  • Атакующая поверхность: gVisor ставит на реализацию ядра в пространстве пользователя и маленький allowlist хостовых syscall-ов; микро-VM ставят на поверхность KVM + устройств VMM.

Универсального победителя нет. Обычно решают модель угроз и профиль нагрузки.

Kata Containers

Kata решает частое операционное ограничение: «хочу сохранить контейнерный воркфлоу, но нужна изоляция уровня VM».

Kata Containers — OCI-совместимый рантайм, запускающий контейнеры внутри лёгкой sandbox-VM (часто на границе пода в Kubernetes: несколько контейнеров пода делят одну VM).

Архитектура в одном абзаце:

  • containerd/CRI создаёт sandbox пода через shim-рантайм Kata,

  • shim запускает лёгкую VM на настроенном бэкенде гипервизора (QEMU, Firecracker, cloud-hypervisor и т. д.),

  • kata-agent внутри гостя запускает и управляет процессами контейнера,

  • rootfs пробрасывается в гостя (обычно через virtio-fs),

  • сеть и vsock предоставляются через virtio-устройства.

Привлекательность — эргономика контейнеров с границей гостевого ядра. Цена — накладные расходы: каждая sandbox-VM несёт гостевое ядро плюс медиацию VMM/устройств, а загрузка добавляет задержку.

Kata имеет смысл, когда вы запускаете нагрузки смешанного доверия на одном кластере Kubernetes и хотите границу VM без переписывания платформы.

Операционно Kata обычно вводят через Kubernetes RuntimeClass: часть подов использует дефолтный рантайм runc; недоверенные поды — рантайм-класс Kata. Так можно смешивать доверенные и недоверенные нагрузки без отдельного кластера.

Если вы исследуете конфиденциальные вычисления, Kata — одна из частых точек интеграции для Confidential Containers (аппаратная изоляция вроде SEV-SNP/TDX). Для «песочницы враждебного кода» это не обязательно, но релевантно, если нужна изоляция арендаторов и более сильные гарантии против оператора инфраструктуры.

Рантайм-песочницы

Теперь про кое-что действительно иное. Контейнеры, gVisor и микро-VM все запускают «код как процесс», поэтому гость видит какой-то syscall ABI (хостового ядра, ядра в пространстве пользователя или гостевого ядра).

Рантайм-песочницы переворачивают эту модель. Граница живёт внутри самого рантайма. Код в песочнице никогда не получает хостовый syscall ABI. Он получает только то, что рантайм (и встраиватель) явно предоставляет.

Граница рантайм-песочницы: эффекты опосредованы явными хостовыми API

WebAssembly

WebAssembly — это байткод-песочница с чистой моделью возможностей. Модули не могут касаться внешнего мира, если хост не предоставит импорты.

Это фундаментально иной дефолт, чем «вот тебе POSIX-процесс, веди себя прилично».

Wasm-рантаймы обеспечивают:

  • изоляцию памяти: модули работают внутри линейной памяти; чтение/запись за границами вызывают trap.

  • ограниченный поток управления: нет произвольных переходов на сырые адреса.

  • отсутствие доступа к ОС по умолчанию: хостовые вызовы — это явные импорты.

WASI (WebAssembly System Interface) расширяет это capability-ориентированным API. Ключевой ход — preopened-директории: вместо разрешения модулю открывать произвольные пути вы даёте ему хендл директории, представляющий «это поддерево», и он может резолвить только относительные пути внутри него.

Вот как это ощущается на практике (псевдокод):

// Дать модулю вид ./workspace, а не хостовую ФС.
wasi.preopenDir("./workspace", "/work");

// Разрешить исходящий HTTPS к конкретному хосту.
wasi.allowNet(["api.github.com:443"]);

Если вы никогда не сделаете preopen ~/.ssh, модуль не сможет её прочитать. Нет бага «упс, забыл проверить префикс пути» в вашем приложении, случайно дающего доступ, — потому что возможность вообще не выдавалась.

Производительность часто отличная, потому что нет гостевой ОС для загрузки. Инстанцирование модуля может занимать микросекунды–миллисекунды в зависимости от рантайма и режима компиляции (AOT vs JIT). Поэтому edge-платформы любят Wasm и изоляты: высокая плотность, низкий холодный старт.

Одна деталь, важная в реальных песочницах: учёт ресурсов. Рантайм-песочница волшебно не остановит бесконечные циклы или экспоненциальные алгоритмы. Всё равно нужны лимиты CPU/времени. Многие рантаймы дают «топливо» или метрирование инструкций (например, fuel в Wasmtime), чтобы детерминированно прерывать безудержное выполнение.

Ограничения часто проявляются как «мне нужна ещё одна хостовая функция»:

  • Настоящая граница безопасности — ваша поверхность импортов. Если вы экспонируете мощный хостовый вызов runCommand(), вы заново изобрели шелл.

  • Держите импорты узкими и типизированными. Предпочитайте структурированные операции («прочитать файл X из preopened-директории») общим («открыть произвольный путь»).

Одна из причин, почему Wasm постоянно всплывает в тулинге агентов, — направление component model: вместо «вот модуль, вызывай экспортированные функции» вы получаете типизированные интерфейсы и лучшую композицию. Это подталкивает к меньшим инструментам с явными входами/выходами — именно то, что нужно для capability-скоупленных AI-инструментов.

Обратная сторона — хосту всё равно нужно быть осторожным. Проще всего испортить чистую Wasm-песочницу, экспонировав одну чрезмерно мощную хостовую функцию. Системы возможностей ломаются через случайную ambient-полномочность.

Практические ограничения:

  • Потоки существуют, но поддержка зависит от рантайма и платформы.

  • Сеть WASI была в движении (Preview 1 vs Preview 2; экосистема догоняет).

  • Динамические языки обычно требуют интерпретаторов (Pyodide и т. п.).

  • Всё, что ждёт «нормального Linux» (шеллы, пакетные менеджеры, произвольные бинарники), не портируется чисто.

Основные рантаймы, если оцениваете:

  • Wasmtime (Bytecode Alliance): фокус на безопасности, близко к WASI/component-model.

  • Wasmer: рантайм плюс экосистема тулинга, с WASIX для более POSIX-подобных API.

  • WasmEdge: фокус на edge/cloud-native.

V8-изоляты (и deny-by-default рантаймы)

V8-изоляты — это изолированные экземпляры JavaScript-движка V8 внутри процесса. Платформы вроде Cloudflare Workers используют изоляты для запуска многих арендаторов с высокой плотностью и низкими накладными на старт.

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

Продакшн-системы всё равно накладывают защиты уровня ОС для защиты в глубину (namespaces/seccomp вокруг процесса рантайма, брокер-процессы для I/O, смягчения тайминговых side-channel-ов). Граница рантайма мощная, но не замена изоляции ОС, если ваша модель угроз включает побеги из движка.

Модель разрешений Deno — тот же паттерн, поданный разработчикам: V8 плюс deny-by-default модель возможностей (--allow-read, --allow-net, --allow-run).

Ограничение — область применения: изоляты для JS/TS (и встроенного Wasm). Если песочнице нужны произвольные ELF-бинарники — это не тот инструмент.

Кто использует рантайм-песочницы для AI?

Постоянно всплывающий паттерн — Wasm для изоляции инструментов, а не «Wasm как целое dev-окружение»:

  • Microsoft Wassette: запускает Wasm-компоненты через MCP с deny-by-default моделью разрешений.

  • NVIDIA: описывает использование Pyodide (CPython-в-Wasm) для запуска сгенерированного LLM Python на клиенте внутри песочницы браузера.

  • Extism: Wasm-фреймворк плагинов, по сути «capability-скоупленное выполнение недоверенного кода».

Когда рантайм-песочниц достаточно

Рантайм-песочницы подходят под определённый профиль:

Ограничение Сила рантайм-песочницы

Stateless-выполнение

отлично

Холодный старт / плотность

отлично

Полная совместимость с Linux

нет (только явные хостовые API)

Гибкость языков

ограничена (Wasm-языки / JS)

Доступ к GPU

только через хостовые API (например, WebGPU), не сырые устройства

Если продукту нужны полное пространство пользователя и произвольные бинарники — вы вернётесь к микро-VM или gVisor. Если продукт — «безопасно запускать маленькие инструменты», рантайм-песочницы могут быть самым чистым вариантом.

Как выбрать песочницу

Большинство ошибок выбора — от пропуска вопроса о границе и прыжка сразу к деталям реализации.

Вот как я на самом деле решаю:

  1. Модель угроз: этот код доверенный, полудоверенный или враждебный? Важен ли эксплойт ядра?

  2. Совместимость: нужна полная семантика Linux или можно жить внутри capability-API?

  3. Жизненный цикл: нужны быстрый сброс/снапшоты или долгоживущие рабочие пространства?

  4. Эксплуатация: можете ли вы запускать KVM и управлять гостевыми ядрами, или ограничены контейнерами?

Таблица решений:

Нагрузка Модель угроз Требования к совместимости Рекомендуемая граница

AI-кодовый агент (многоарендный SaaS)

враждебная (код от пользователей)

полный Linux, шелл, пакетные менеджеры

микро-VM (Firecracker / cloud-hypervisor)

AI-кодовый агент (одноарендный / self-hosted)

полудоверенная

полный Linux

захарденный контейнер или gVisor

RL-роллауты (параллельные, много сбросов)

в основном доверенный код, нужна изоляция на прогон

быстрый сброс, снапшот/восстановление

микро-VM с поддержкой снапшотов

Интерпретатор кода (stateless-фрагменты)

враждебная

скоупленные возможности, без шелла

gVisor или рантайм-песочница (если язык подходит)

Вызов инструментов / плагины

смешанная

явная поверхность возможностей

Wasm / изоляты

Две частые ошибки:

Ошибка 1: «Наш код доверенный». В агентных системах код, который вы запускаете, формируется недоверенным вводом (prompt injection, dependency confusion, supply chain). Считайте «полудоверенным» по умолчанию, если нет веской причины иначе.

Ошибка 2: «Мы просто заблокируем сеть». Сетевые ограничения важны, но это не граница. Если вы запускаете враждебный код на общем ядре, «песочница без сети» всё равно может стать песочницей для эксплойта ядра.

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

  • Default-deny исходящей сети, затем allowlist. (Или маршрутизировать всё через политиковый прокси.)

  • Никаких долгоживущих кредов в песочнице. Используйте короткоживущие скоупленные токены.

  • Доступ к ФС только рабочего пространства. Никаких хостовых монтирований, кроме явно задуманных.

  • Лимиты ресурсов: CPU, память, диск, таймауты и PID.

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

Конкретные дефолты, если начинаете с нуля:

  • Многоарендное выполнение AI-агентов: микро-VM. Firecracker для плотности и узкой поверхности VMM. cloud-hypervisor, если нужны VFIO/hotplug/GPU.

  • «У меня уже есть Kubernetes»: gVisor — хорошая золотая середина, если совместимость приемлема.

  • Доверенная/внутренняя автоматизация: захарденные контейнеры обычно норм.

  • Capability-скоупленные инструменты: Wasm или изоляты.

Правило большого пальца, которое держится на удивление хорошо:

  • Если нужны шелл + пакетные менеджеры и вы не полностью доверяете коду: начинайте с микро-VM.

  • Если можете жить внутри матрицы совместимости ради экономии накладных: рассмотрите gVisor.

  • Если задачу можно смоделировать как capability-скоупленные операции: предпочтите Wasm/изолят.

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

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

Приложение: локальные песочницы ОС

Всё выше предполагает запуск нагрузок на сервере. Локальные агенты (Claude Code, Codex CLI и т. п.) — другая проблема:

Агент работает на вашем ноутбуке. Он видит вашу файловую систему. Здесь режим отказа часто не «0-day ядра». Это «prompt injection заставляет агента прочитать ~/.ssh или удалить домашнюю директорию».

У каждой крупной ОС есть механизм «лёгкой, попроцессной» песочницы. Это всё ещё обеспечение политики внутри общей границы ядра, но они спроектированы быть быстрыми и вызываемыми пользователем.

Политика песочницы

macOS Seatbelt (App Sandbox)

Seatbelt — система политик песочницы в macOS, обеспечиваемая ядром (профили SBPL). Инструменты могут сгенерировать профиль, разрешающий рабочую и временные директории и запрещающий чувствительные пути вроде ~/.ssh. Когда агент всё же пробует, ядро возвращает EPERM. Шелл не может «сбежать» от проверки ядра.

Заметка про тулинг: sandbox-exec уже годы как deprecated, но базовый механизм всё ещё используется на платформах Apple. Современные инструменты применяют песочницу через API/entitlements, а не через CLI-обёртку.

Крохотный пример SBPL:

(version 1)
(deny default)
(allow file-read* (subpath "/Users/alice/dev/myrepo"))
(allow network-outbound (remote tcp "api.github.com:443"))

Linux Landlock (+ seccomp)

Landlock — непривилегированный Linux Security Module для самоизоляции: процесс может ограничить свой собственный доступ к ФС (а на новых ядрах — и часть TCP-операций), и ограничение необратимо и наследуется потомками.

В большинстве сетапов Landlock хорошо сочетается с seccomp: Landlock контролирует пути ФС; seccomp блокирует высокорисковые syscall-ы (ptrace, mount и т. п.).

На уровне API Landlock — это несколько syscall-ов: создать ruleset → добавить правила путей → обеспечить. Минимальный поток:

int ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &rule, 0);
landlock_restrict_self(ruleset_fd, 0);

Важное поведение — ограничение одностороннее (нельзя отключить) и наследуется потомками. Это делает его пригодным для «запусти этот инструмент, но не дай ему касаться чего-либо ещё».

Если хотите практичный инструмент для старта — landrun оборачивает Landlock в CLI. Требует Linux 5.13+ для правил ФС и 6.7+ для ограничений bind/connect TCP.

Windows AppContainer

AppContainer — изоляция процессов на основе возможностей в Windows (SID-ы/токены). Широко используется для изоляции рендереров браузера. Процессы AppContainer работают с ограниченным токеном и только теми возможностями, что вы выдали (сеть, места ФС и т. п.).

Для кодовых агентов сегодня встречается реже — в основном потому, что настройка тяжела на Win32-API по сравнению с «написать профиль / применить ruleset». Но если нужна сильная нативная изоляция ОС на Windows без поднятия VM — это тот примитив, который стоит изучить.

Сравнение

Аспект macOS Seatbelt Linux Landlock + seccomp Windows AppContainer

Требуемая привилегия

нет

нет

нет (но настройка явная)

Контроль ФС

правила профиля

ruleset-ы путей

выдача возможностей

Контроль сети

профиль + паттерны прокси

namespaces/seccomp (или ограниченный Landlock)

выдача возможностей

Чего не решает

уязвимости ядра

уязвимости ядра

уязвимости ядра

Одна операционная ловушка для всех локальных песочниц: нужно разрешить достаточно, чтобы программа работала. Динамические компоновщики, рантаймы языков, хранилища сертификатов и временные директории — всё это «реальные» зависимости. Deny-by-default песочница, забывшая /usr/lib (или его аналог в Windows), упадёт запутанным образом.

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

Я лично запускаю своих кодовых агентов только с включённой песочницей и советую другим делать так же.

© 2026 meganuke