Всем привет!

Это Андрей Яковлев, исследователь в области кибербезопасности. Сегодня разбираем, как изолировать ИИ-агент в виртуальной среде с помощью Docker Sandbox.

У меня в ~/.config/secretkeys/ лежит очень важный OAuth-токен. У вас на машине, скорее всего, тоже есть аналогичные штуки: ключи от облака, GitHub PAT в ~/.config/gh/, SSH-ключи в ~/.ssh/. И когда ИИ-агент запущен от имени моего пользователя, он видит все это по тем же путям, что и я. Не гипотетически может увидеть, а буквально читает и может распорядиться властью, данной ему, не самым лучшим образом, о чем я рассказывал в предыдущей статье. Поэтому я долго не пользовался агентами всерьез.

Больше всего меня пугали не риски промпт-инъекции или намеренной атаки. Давила постоянная тревожность: запущу агент не в той директории, а он перепутает токены и сходит туда, куда не надо, либо снесет диск одной командой. Я делал отдельные виртуалки под каждый проект и они работали, но ели время и ресурсы. Хотелось иметь инструмент, которому можно доверять и который не потребует ставить дома серверную стойку под вычислительные ресурсы для виртуальных машин.

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

Содержание

В предыдущей статье я разбирал шесть классов угроз для кодовых ИИ-агентов: случайный rm -rf, промпт-инъекцию, малварь, использующую агент как инструмент разведки, атаку на цепочку поставок через установку стороннего пакета, кражу токенов доступа и каскадную CI/CD-компрометацию. Общий знаменатель здесь — процесс агента, имеющего полные права того пользователя, который его запустил. Сейчас изоляция на уровне среды представляется не просто прогрессивным инструментом безопасности, а обязательным условием использования агента.

Эта статья про то, как и с помощью чего собирается изоляция, без глубокого технического разбора виртуализации и без воспроизведения атак на практике; этот материал я оставлю для следующих заходов. Конкретно я рассматриваю инструмент от Docker под названием Docker Sandbox — микровиртуалку с собственным ядром, сетью с файрволом «из коробки» и приватным Docker-демоном. Docker Sandbox ставится в три команды и заменяет отдельные виртуальные машины (далее — VM), созданные вручную под каждый проект с агентом.

Сразу скажу про источники информации для статьи: это официальная документация Docker по sandbox (на момент написания полная), включая архитектурный раздел и Network Policies. Я опираюсь на нее и размещаю рядом свой опыт, который накопил за несколько месяцев ежедневного использования агентов с Docker Sandbox. Если что-то в тексте у меня не совпадает с документацией, я опираюсь на практику. Где это важно, оговариваюсь явно.

Инструмент активно развивается: к примеру, за несколько месяцев успела появиться и устареть версия, включаемая в Docker Desktop, а официальная документация на сайте Docker несколько раз переписывалась. То есть на момент вашего, дорогой читатель, знакомства со статьей некоторые детали могут отличаться от описанных ниже. Поэтому настоятельно рекомендую сверяться с актуальной версией документации на официальном сайте Docker [1] перед началом использования.

Уточнение терминов

Прежде чем перейти к командам, определим терминологию. Под Docker Sandbox я понимаю продукт в целом: концепцию microVM-изоляции для ИИ-агентов от Docker. sbx — это CLI-инструмент, через который продукт сейчас живет. Раньше у Docker была другая имплементация в виде команды docker sandbox в Docker Desktop, с нее все начиналось. Но в актуальной документации она помечена как устаревшая и заменена на отдельный CLI-инструмент sbx. Дальше в статье я говорю про sbx.

Архитектура изоляции

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

Контейнер делит ядро с хостом, а sandbox нет

Контейнер — это набор специфичных для Linux механизмов: namespaces изолируют процессы и сеть, cgroups ограничивают ресурсы. Но ядро у контейнера и у хоста общее, поэтому любой системный вызов из контейнера проходит через то же ядро, что и системные вызовы пользователя. Sandbox устроен иначе: он запускается как отдельная microVM с собственным ядром, а границей между агентом и хостом выступает гипервизор, а не namespaces [2]. Это принципиально другой уровень изоляции.

Агент не видит ваш Docker

Внутри microVM работает собственный Docker-демон. Агент может собирать образы, запускать контейнеры, поднимать docker-compose, и ничего из этого не доходит до хостового Docker Engine. Сокет хоста (/var/run/docker.sock) недоступен для монтирования внутрь sandbox [3]. Это снимает целый класс эскалаций, где потенциальная вредоносная нагрузка дотягивается до Docker socket и через него управляет хостом. [Docker Security Cheat Sheet]

Агент не видит ваши файлы — кроме тех, что вы сами показали

В sandbox видна только рабочая директория (workspace в терминологии Docker), которую вы передали при sbx run. Другие директории хоста — ~/.ssh/, ~/.config/cloud/, /etc/ — недоступны фундаментально [2]. Символическая ссылка за пределы области не разрешается [2]. Важная оговорка: если вы передаете домашнюю директорию целиком (sbx run claude ~), агент получает доступ ко всему, что лежит в домашнем каталоге, в том числе к ключам. Естественно, это ожидаемое поведение, так и задумано, поэтому рабочую директорию агента стоит ограничивать минимально необходимым.

Sandbox живет между запусками, в отличие от обычного контейнера

Привычный контейнер одноразовый: упал, перезапустил и состояние стерто.

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

Sandbox устроен иначе. Пакеты, конфиги и скилы агента остаются между запусками, пока вы не сделаете sbx rm. У меня скилы лежат в двух местах: частично в ~/.claude внутри sandbox (живут с конкретным sandbox), частично — в репозитории проекта (живут с кодом и попадают внутрь sandbox вместе с рабочей директорией). Второй паттерн устойчивее: репозиторий переносится между sandbox, скилы переезжают вместе с кодом.

Сеть через прокси на вашем хосте

Из sandbox наружу идет только HTTP/HTTPS [5] и только через прокси на стороне хоста (host-side proxy в документации Docker [5]). Просто TCP, UDP, ICMP, DNS заблокированы на сетевом уровне [5]. Прокси делает TLS-терминацию и заодно инжектирует заголовки авторизации из ваших секретов, которые лежат вне sandbox, так что настоящее значение API-ключа в sandbox вообще не попадает. Заодно блокируются обращения к private IP, loopback и link-local [5].

Итог: изнутри Sandbox трафик идет наружу через локальный MITM прокси на хосте, все остальное на сетевом уровне отрезано.

Граница изоляции понятна. Издержки изоляции — это следующий вопрос.

Установка

Перед установкой Docker sbx стоит учесть два момента.

Первый: согласно документации Docker, на момент написания статьи поддерживаются macOS Sonoma 14 и новее, Windows 11, а также Ubuntu 24.04 и новее с поддержкой KVM.[4]. Несколько месяцев назад картина была другая: когда я ставил sbx в macOS в феврале 2026-го, требовалась именно Tahoe (версия 26). Пришлось обновляться с Sequoia.

Второй: команды установки на разных платформах разные. В macOS через Homebrew, в Windows через winget, в Linux — через apt. Это видно в блоках ниже. А вот команды самого sbx после установки везде одинаковые: sbx run, sbx ls, sbx policy работают на любой ОС без модификаций. Дальше в статье приведены команды установки для каждой платформы отдельно, а примеры запуска и настройки без разделения, потому что они идентичны для всех платформ.

# macOS
brew install docker/tap/sbx
sbx login
# Windows
winget install -h Docker.sbx
sbx login
# Linux (Ubuntu)
curl -fsSL https://get.docker.com | sudo REPO_ONLY=1 sh
sudo apt-get install docker-sbx
sudo usermod -aG kvm $USER
newgrp kvm
sbx login

На Linux чуть больше шагов. Сначала подключается APT-репозиторий Docker (флаг REPO_ONLY=1 ставит только репозиторий, без самого Docker Engine). Потом из него ставится пакет docker-sbx, и ваш пользователь добавляется в группу kvm. sbx использует KVM как гипервизор, и без доступа к нему microVM не поднимется.

Если что-то пошло не так, sbx diagnose покажет, где именно. Команда проверяет бинарь CLI, daemon, версии, состояние директорий и аутентификации, умеет выводить данные в JSON и в Markdown для репорта в issue Docker.

sbx login запускает OAuth в браузере и записывает сессию локально. Да, и мне не до конца понятно, зачем логиниться. Докер объясняет это довольно пространными формулировками о необходимости идентификации и удобстве управления политиками внутри команды. После авторизации CLI просит выбрать сетевую политику по умолчанию: это часть онбординга и необходимый шаг [4]. О пресетах политики я расскажу ниже в отдельном разделе.

Выбор сетевой политики при первом sbx login. Три варианта (Open, Balanced, Locked Down). Базового варианта из коробки нет, осознанный выбор обязателен.
Выбор сетевой политики при первом sbx login. Три варианта (Open, Balanced, Locked Down). Базового варианта из коробки нет, осознанный выбор обязателен.

Docker Desktop при работе с sbx не нужен. Управление политиками на уровне организации возможно, но это отдельный платный продукт Docker AI Governance, подробнее о нем я рассказываю в разделе «Сетевые политики». Без подписки на Docker AI Governance sbx работает локально, без ограничений администратора. Это нормальный режим для индивидуального разработчика.

CLI поставлен. Что с ним реально делают каждый день?

Основные команды

Сценарий, который я демонстрирую, простой: создаем sandbox, запускаем агент, заходим в shell параллельно. Один сплошной фрагмент, как это выглядит у меня в реальности:

████████  ~  ♥️ 13:18  cd ~/myProject
████████  myProject  ♥️ 13:19  sbx run claude
Creating new sandbox 'claude-myProject'...
a8531b2e5fc6: Download complete
0e4f92455740: Download complete
c2407f5c2e01: Download complete
2417a2836e92: Download complete
Digest: sha256:a8531b2e5fc6fea3a9bb88c598cde60b3b1a3ce50589fd3e7fb6c282735c31bc
Status: Downloaded newer image for docker/sandbox-templates:claude-code-docker

INFO: Started Docker daemon in 0.5s
Starting claude agent in sandbox 'claude-myProject'...
Workspace: C:\Users\████████\myProject
╭─── Claude Code v2.1.162 ─────────────────────────────────────────────────────────────────────────────╮
│                                               │ Tips for getting started                             │
│                 Welcome back!                 │ Ask Claude to create a new app or clone a repository │
│                                               │ ──────────────────────────────────────────────────── │
│                    ▐▛███▜▌                    │ What's new                                           │
│                   ▝▜█████▛▘                   │ Check the Claude Code changelog for updates          │
│                     ▘▘ ▝▝                     │                                                      │
│                                               │                                                      │
│   Opus 4.8 (1M context) · API Usage Billing   │                                                      │
│         /c/Users/████████/myProject         │                                                      │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯

 ▎ Opus 4.8 is here! Now defaults to high effort · /effort xhigh for your hardest tasks

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯ 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ⏵⏵ bypass permissions on (shift+tab to cycle) · ← for agents                                                                                                              Not logged in · Run /login
Интерфейс Claude Code сразу после запуска sbx run claude. Видно скриншот терминала с pull-выводом Docker образа и запущенным Claude code
Интерфейс Claude Code сразу после запуска sbx run claude. Видно скриншот терминала с pull-выводом Docker образа и запущенным Claude code

Образ агента подтягивается на первом запуске и дальше идет из кэша. Следующий старт укладывается в секунды две-три. sbx run работает и без аргументов: открывается интерактивный TUI picker для выбора агента и рабочей директории, удобно для первой пробы.

TUI-dashboard sbx (открывается командой sbx без аргументов). Показывает карточки всех запущенных и остановленных sandbox с CPU, memory, портами плюс панель network governance с allowlist и логом обращений. Визуально демонстрирует, что sbx не голый CLI
TUI-dashboard sbx (открывается командой sbx без аргументов). Показывает карточки всех запущенных и остановленных sandbox с CPU, memory, портами плюс панель network governance с allowlist и логом обращений. Визуально демонстрирует, что sbx не голый CLI

В соседнем терминале можно открыть параллельную сессию:

$ sbx ls
NAME            AGENTSTATUS    WORKSPACE
claude-my-project   claude   running   /Users/andrew/my-project
$ sbx exec -ti claude-my-project bash
agent@claude-my-project:~$ whoami
agent
agent@claude-my-project:~$ cat /etc/os-release | head -1
PRETTY_NAME="Ubuntu 25.10"
agent@claude-my-project:~$ exit

Флаг -ti для sbx exec обязателен, если нужна интерактивная сессия. Без него shell стартует в неинтерактивном режиме.

Деталь для более полного понимания: sbx stop сохраняет состояние VM и можно выполнить команду sbx run claude-my-project и поднять с того же места, sbx rm удаляет microVM совсем. Рабочая директория на хосте не удаляется ни в одном из случаев. Где лежала, там и осталась. Sandbox живет именно между запусками, а не представляет собой контейнер на одну сессию.

Несколько вариантов запуска, которые я использую чаще всего:

sbx run claude ~/my-project                # Явное указание рабочей директории

sbx run claude . /path/to/docs:ro          # Дополнительные read-only-директории

sbx run claude -- --continue               # Передача флагов агенту через --

sbx run claude --branch feature/x ~/my-project # Изолированный git-worktree под ветку [https://docs.docker.com/ai/sandboxes/usage/#branch-mode]

sbx create claude ~/project                # Создать sandbox без старта агента

sbx run claude-my-project                  # Подключиться к существующему Sandbox

sbx ports claude-my-project --publish 8080:3000 # Пробросить порт sandbox на хост

Пояснения по каждой неочевидной команде:

🔻 -- --continue — всё после -- уходит агенту как есть. Без оберток и хитрых скриптов можно передавать любые CLI-флаги конкретного агента.

🔻 --branch feature/x — создает git-worktree в .sbx/<sandbox>-worktrees/<branch> и изолирует работу агента от main [4]. Полезный прием, если не хочется давать ИИ-агенту трогать основную ветку.

🔻 sbx ports — пробрасывает порт из sandbox на хост, когда агент собрал dev server или API. В этом случае сервис внутри sandbox должен слушать на 0.0.0.0, не на 127.0.0.1, иначе проброс не сработает.

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

Два агента в одном shell sandbox. Если переключаюсь между Claude и Codex в одном проекте, держу один shell sandbox (sbx run shell ~/my-project) и из него поочередно запускаю агентов локально внутри VM. Окружение не дублируется: npm-пакеты, Python venv, конфиги делятся на оба агента. В этом сценарии на хосте только одна microVM вместо двух — ресурсы экономятся, и проще синхронизировать скилы и инструкции между агентами. Подходит, когда хочется запустить разные агенты в одном проекте.

Git: read внутри sandbox, write с хоста. Принцип наименьших привилегий в чистом виде: агент внутри sandbox читает репозиторий, может смотреть разницу между коммитами и ветками, формулирует сообщение к коммиту, ставит изменения в индекс. Финальные git commit и git push я делаю с хоста, где лежат SSH- и GPG-ключи. Ключи внутри sandbox не нужны. Если агент случайно подменит коммит-хук или попытается переписать историю, инцидент останется внутри VM, репозиторий на удаленном сервере не пострадает. Один и тот же каталог .git/ в рабочей директории работает в двух разных контурах операций.

СРОЧНО В НОМЕР! Пока готовилась статья, Docker Sandbox научился подписывать коммиты ключами с хоста. Но в моем случае агент не делает коммит и пуш, поэтому для меня все остается по-старому.

«Залипший» sandbox. Один раз Claude Code отказался подхватывать новую модель, когда вышла версия Opus 4.7. Разбираться с состоянием VM было нерационально долго. Я попросил агент сложить плагины, скилы и важные конфиги в .backup/ в директории проекта. Это папка внутри рабочей директории, она доступна и на хосте, и в sandbox одновременно — изменения с одной стороны видны сразу с другой. Потом я сделал sbx rm claude-my-project, запустил sbx run claude заново, и агент сам, под моим чутким руководством, подтянул файлы из .backup/ обратно. Заняло несколько минут. Рабочая директория тут работает как мост между поколениями sandbox: никаких специальных команд для миграции данных не нужно, потому что директория доступна с обеих сторон.

Контейнеры живут между запусками, но хранить конфиги внутри sandbox (в ~/.claude/ внутри VM) все равно рискованно: такие файлы переживут sbx stop, но не переживут sbx rm. То, что должно сохраниться, кладется в репозиторий. Он остается доступен и на хосте.

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

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

Запуск агента уложился в одну команду. Какие обращения он отправит во внешнюю сеть?

Сетевые политики

В sbx есть три пресета сетевой политики, и при первом sbx login CLI заставляет выбрать один пресет: по умолчанию опции «из коробки» нет [4].

Open. Весь исходящий трафик HTTP/HTTPS разрешен. Подходит для прототипа или быстрой разработки, когда стоимость ошибки маленькая.

Balanced. Deny by default плюс allowlist популярных ресурсов: пакетные реестры (npm, PyPI), GitHub, основные CDN, API крупных провайдеров моделей. Точный список не привожу: он эволюционирует. За актуальной выкладкой следите в документации Docker [6]. Это стартовая точка для повседневной деятельности, и документация Docker рекомендует начать с режима Balanced.

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

Базовые команды управления политикой:

sbx policy set-default balanced                           # Пресет по умолчанию
sbx policy allow network -g api.anthropic.com             # Разрешить домен
sbx policy allow network -g "*.npmjs.org,*.pypi.org"      # Несколько доменов
sbx policy deny network -g ads.tracking.com               # Запретить домен
sbx policy ls                                             # Активные правила
sbx policy rm network -g --resource ads.tracking.com      # Снять правило
sbx policy reset                                          # Сбросить и переспросить
sbx policy log                                            # Лог запросов прокси (все sandbox)
sbx policy log my-sandbox                                 # Лог одного конкретного
sbx policy log --json                                     # Машиночитаемый вывод
sbx policy log --limit 50                                 # Последние 50 записей

Флаг -g делает правило глобальным для всех ваших sandbox. Если его не указать, политика привязывается к конкретному sandbox по имени. Например, sbx policy allow network my-sandbox api.example.com разрешит доступ только из my-sandbox. В примерах ниже я везде использую -g, потому что собираю общий профиль доступа на машину, но можно и точечный.

Несколько свойств, на которых я успел споткнуться или которые мне пригодились [6]:

  • Wildcard поддерживается. *.npmjs.org покрывает все поддомены, включая registry.npmjs.org, cdn.npmjs.org.

  • Deny beats allow. Если домен подпадает под оба правила, побеждает запрет. Это удобно для точечного отключения: добавляешь общий wildcard-allow на корпоративный домен и точечный deny на ad-домен поверх.

  • HTTP/HTTPS only. sbx policy allow влияет только на HTTP/HTTPS-домены. Другой TCP-трафик можно разрешить, прямо указав адрес и порт [5][6]. UDP- и ICMP-трафик заблокирован на уровне сети и не может быть разблокирован сетевыми политиками. Это намеренное архитектурное свойство, и именно оно закрывает DNS-туннелирование, описанное в предыдущей статье, — в случае если на вашем DNS-сервере в инфраструктуре настроена фильтрация по доменам… Ведь настроена? Да?

  • Трафик на локальные адреса и в частные сети блокируется и требует от пользователя целенаправленного разрешения в политиках. Зачем? Потому что бывает и такое.

За два месяца активного использования я сделал несколько практических наблюдений.

По умолчанию у меня установлен режим Balanced или Open. Пресет Locked Down включаю отдельно, под конкретную задачу, когда исходящий трафик нежелателен сам по себе и проще выставить «Все запрещено», а потом точечно разрешить только то, что понадобится для конкретных действий. Для рабочих проектов Balanced дает нужный список разрешенных доменов «из коробки» без раздельной настройки под каждый репозиторий.

Агенты конструктивно реагируют на блокировку. Claude, упершись в закрытый домен, не уходит в retry loop и не пытается обойти политику. Он возвращается к пользователю с сообщением вида: «Для этого шага нужен доступ к api.example.com, разрешишь?». Решение о расширении доверенного списка принимаю я, не агент. Это и есть human in the loop, ради которого выстраивается изоляция: контроль над тем, куда агент дотягивается, остается на пользователе, а не растекается по спискам доступа и базовых политик.

Логи прокси информативные. В них видно, что запрошено, что пропущено по доверенному списку, что заблокировано: домен, время и sandbox, из которого пришел запрос. Это бесплатный аудит сетевой активности агента: не нужно отдельно настраивать tcpdump или подключать SIEM-систему, чтобы увидеть, что Claude хотел сходить в api.openai.com, а вы его туда не пускали.

Вывод sbx policy log с двумя секциями (Blocked requests / Allowed requests), таблицей. Визуальный пример «бесплатного аудита».
Вывод sbx policy log с двумя секциями (Blocked requests / Allowed requests), таблицей. Визуальный пример «бесплатного аудита».

Попыток «выйти» из sandbox не было. За два месяца работы ни разу не видел в логах попытки агента дотянуться до запрещенного домена самостоятельно: все блокировки были по легитимным запросам, которым в моей политике не нашлось разрешения. Это слабая выборка для статистики, но как ориентир она успокаивает: базовые установки Balanced не создают постоянного фона ложноположительных алертов.

И еще один практический штрих про режим Locked Down. Когда я запускаю недоверенный код, то ставлю sandbox в Locked Down и явно разрешаю один-два домена для конкретной задачи (например, *.virustotal.com для проверки хешей). Любая попытка достучаться изнутри sandbox куда-то еще видна в логах и блокируется на уровне сети, а не на уровне правил детекта. Это не полное лабораторное окружение, и не рекомендуется так делать, хотя этот подход и лучше, чем просто открыть подозрительный архив на хосте без изоляции.

С исходящим трафиком разобрались. Но прежде чем разбирать ключи, которые агент будет подкладывать в такой трафик, остановлюсь на решении Docker для организации, а потом перейду к одной поведенческой детали, которая в sandbox работает иначе, чем на голом хосте.

Управление политиками безопасности песочниц на уровне организации

Это платный продукт. На момент написания у Docker есть Docker AI Governance — отдельная подписка, через которую администратор задает сетевые и файловые политики напрямую из консоли администратора. Эти правила имеют приоритет над локальными sbx policy на машине разработчика. Администратор получает централизованный допуск, возможность запретить определенные домены (для всех sandbox организации одновременно), возможность управлять тем, какие пути на хосте разработчикам разрешено монтировать как рабочие директории (нельзя смонтировать /etc или ~/.ssh в sandbox, если админ так настроил), и опциональную делегацию: админ может позволить разработчикам добавлять дополнительные allow-правила локально, но не снимать его запрет.

Это решает классическую корпоративную задачу: «У меня в инфраструктуре сто разработчиков, и я хочу, чтобы никто из них не мог сам разрешить sandbox ходить в любой произвольный домен или смонтировать чувствительный путь к рабочей директории».

Соседние enterprise-инструменты Docker, которые работают в том же сегменте Docker Business, но решают другие задачи:

  • Hardened Docker Desktop с Enhanced Container Isolation — ECI запускает контейнеры в Linux user namespaces, это защита самого Docker Desktop от эскалации привилегий (неспецифично для sbx);

  • Settings Management — с помощью него админ навязывает конфигурацию Docker Desktop всем разработчикам (через JSON-файл или Admin Console);

  • Registry Access Management — для централизованного контроля доступа к реестрам образов;

  • Image Access Management — для контроля того, какие образы из Docker Hub можно скачивать.

Docker AI Governance — AI/sbx-специфичная часть стека, не интегрированная с остальными в единый продукт и живущая в отдельной подписке, но адресующая ту же enterprise-картину: безопасное использование Docker и его агентов в организации [10]. На обычной подписке Personal, Pro, Team или Business этой возможности нет: политика sbx остается локальной для каждой машины, и для enterprise-кейса есть только внешние механизмы: governance-процессы и MDM, policy push на установку. Ниша локального аудита и контроля действий ИИ-агента — отдельный разговор, к которому мы еще вернемся за рамками этой статьи.

--dangerously-skip-permissions

У многих агентов есть флаг вида --dangerously-skip-permissions (или --yolo, в зависимости от агента), который отключает необходимость подтверждения каждого действия у пользователя. На голом хосте такая опция выглядит как минимум подозрительно. Имя флага не зря содержит слово dangerously: за ним стоит реальный риск — агент запускает все что угодно, не спрашивая. При негативном сценарии вы сможете только наблюдать, как агент быстро и по кусочкам аннигилирует вашу систему.

Внутри sandbox этот флаг включен по умолчанию у большинства поддерживаемых агентов, и Docker в FAQ обосновывает это прямо: радиус поражения уже определен архитектурой. Сам sandbox — это и есть граница безопасности. Поскольку агенты крутятся в изолированной microVM с сетевыми политиками, с изоляцией секретов и без доступа к хосту за пределами рабочей директории, обычные причины запрашивать подтверждение каждой команды (защита от деструктивных действий, контроль сетевых обращений, ограничение модификации файлов) уже закрыты слоями изоляции [9].

На практике это снимает с меня целый пласт задач, связанных с микроконтролем, которые раньше съедали ощутимое время. Без sandbox каждое npm install, cargo build, перемещение файла или sed-замена в большом проекте либо подтверждается вручную, либо вызывает тревогу: «А вдруг агент сейчас сделает что-нибудь не то?». Внутри sandbox этой тревоги нет: если ошибется, ошибка ограничена пределами VM, рабочая директория восстанавливается из Git, sandbox пересоздается командой sbx rm + sbx run. Я могу держать несколько агентов параллельно на разных проектах и не отвлекаться на согласование каждого действия.

Одно исключение, о котором стоит помнить. Если вы запускаете агент через shell sandbox (то есть выполняя команду sbx run shell ~/my-project), а потом внутри руками поднимаете, например, claude, то в таком случае флаг --dangerously-skip-permissions нужно указать явно: shell sandbox не знает, какой агент в нем живет, и мод YOLO по умолчанию не включается.

Теперь — к ключам, которые агент будет подкладывать в исходящие запросы.

Секреты

Чаще всего API-ключ передается напрямую в API-запросе через ANTHROPIC_API_KEY=... в окружении агента, однако этот способ устарел. Проблема в том, что переменная окружения внутри VM доступна любому процессу, в том числе запущенному вредоносным пакетом, который агент только что подтянул. sbx решает эту задачу через прокси на стороне хоста [7][8]:

  1. Хост хранит учетные данные через sbx secret set.

  2. При запросе, например, к api.anthropic.com прокси перехватывает HTTP-вызов.

  3. Прокси добавляет Authorization: Bearer sk-ant-... в заголовки.

  4. Агент внутри VM получает только ответ API. Реальное значение ключа в VM не попадает.

sbx secret set anthropic       # CLI запросит значение ключа с маской ввода

sbx secret set github          # GitHub PAT, npm token и т. п.

sbx secret ls                  # Список без значений

sbx secret rm anthropic        # Удалить

Секреты можно ставить глобально или для конкретного sandbox.

Важно учитывать ограничение sbx secret: команда работает с фиксированным набором провайдеров (Anthropic, OpenAI, GitHub и другими). Для кастомных переменных окружения вроде BRAVE_API_KEY или внутреннего корпоративного токена путь идет через /etc/sandbox-persistent.sh внутри sandbox: это shell script, который sbx подгружает при каждом входе в Bash внутри sandbox. Переменные оттуда становятся доступны интерактивным сессиям и агентам, запущенным через sbx run [9]. Один нюанс: если вы вызываете команду напрямую через sbx exec <name> <command>, sbx запускает ее без shell — и файл /etc/sandbox-persistent.sh не подхватывается. В этих случаях оборачивайте команду в bash -c "...", чтобы окружение подгрузилось [9].

Лично я sbx secret не использовал: с Claude Code авторизация идет через webflow, сессия обновляется в необходимых интервалах, и отдельный токен в локальном хранилище мне не нужен. Когда я буду работать с провайдерами, требующими открытого API-ключа внутри запроса, сценарий поменяется, но пока такого не было.

Сокрытие секретов — лишь частный случай. Возвращаюсь к шести классам угроз из первой статьи цикла: что из них изоляция реально закрывает?

Что закрывает изоляция

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

Класс угрозы из первой части статьи

Закрывает ли ее sbx

Почему

Случайный rm -rf

✅ Полностью

Файловая система вне рабочей директории недоступна

Промпт-инъекция

❌ Нет

Модель получит ту же инструкцию, sbx ограничивает только blast radius

Малварь, использующая агент

⚠️ Частично

Вредонос крутится в microVM, до хост-секретов не дотянется

Атака на цепочку поставок через агент

⚠️ Частично

Зависимость отравлена, но не выходит за периметр sandbox

Кража токенов

⚠️ Частично

Хост-секреты закрыты, секреты в рабочей директории — нет

Компрометация CI/CD

⚠️ Частично

Если CI-токен попал в sandbox, то он доступен изнутри. Sandbox — не магия

Один ✅, один ❌, и четыре ⚠️. Это и есть центральный тезис статьи: sbx помогает снизить радиус потенциального поражения, а не предлагает универсальное решение. Он действительно делает невозможным случайный rm. Ту часть промпт-инъекции, которую обрабатывает LLM, он принципиально не закрывает: это происходит на уровне модели и приема данных. Все остальное нужно для ограничения последствий, а не для предотвращения события.

«Частично» дает драматическую разницу между сценарием без изоляции и с sbx. Для большинства угроз, описанных в первой статье цикла, разница между «Без изоляции» и «Использование через sbx» сводится к разнице между «Хост скомпрометирован» и «Sandbox скомпрометирован, хост целый». В первом случае атакующий получает рабочую станцию разработчика с SSH-ключами, токенами и доступом к корпоративной VPN; во втором — изолированный sandbox размером в один проект, без хост-секретов, с мониторингом исходящего канала и без Docker-демона хоста. Для большинства моделей угроз этого хватает, чтобы переместить инцидент на уровень ниже.

В следующей статье цикла я планирую поставить вопрос иначе: не «закрывает или нет», а «насколько именно сужается радиус поражения относительно запуска на хосте без песочницы и обычного контейнера и какие сценарии все равно проходят насквозь». Для этого нужны эксперименты: DNS-туннелирование при разных пресетах, тестирование поведения под Locked Down, попытки доступа к сетевым ресурсам разной степени подозрительности и т. п. Всему этому место в отдельном материале.

Куда дальше

На этом я закончил с базовыми настройками и основными рычагами управления защитой. За несколько месяцев использования sbx заменил мне отдельные виртуальные машины, которые я раньше держал под ИИ-агенты: он стартует быстрее, ставится проще и при этом дает аналогичную изоляцию.

Если резюмировать, базовый путь выглядит так: вы ставите sbx из своего пакетного менеджера (команды для macOS, Windows и Linux я приводил выше, в разделе «Установка»), делаете sbx login, на начальном этапе выбираете пресет Balanced, дальше в проектной директории запускаете sbx run claude или <Имя вашего агента>. Агент работает в изолированной microVM с сетью в режиме доступа только к разрешенным ресурсам и MITM-прокси на стороне хоста для секретов. Хост при этом остается в большей безопасности, чем при запуске агента на нем. Сам переход занимает несколько минут. После него для пяти из шести классов угроз из моей предыдущей статьи потенциальный импакт сужается до периметра sandbox VM. Реализацию шестой угрозы, промпт-инъекции, изоляция не предотвращает. Инъекция приходит в модель, как и раньше, однако то, что скомпрометированный агент способен сделать далее, ограничено.

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

  • Cursor — CLI-режим Сursor называется agent (не IDE Cursor с GUI), запускающийся в моде YOLO по умолчанию;

  • Droid — AI-агент Factory (требуется Factory-аккаунт), которого в изначальной версии не было.

За те месяцы, которые прошли между моими первыми проверками (Docker Sandbox в феврале, sbx — в апреле) и моментом, когда вы это читаете, у Docker неоднократно менялись набор поддерживаемых агентов, синтаксис отдельных команд и статус параллельной имплементации в Docker Desktop. Архитектура и принципы, о которых я пишу (microVM, прокси на стороне хоста, deny by default), стабильны и переживут текущий цикл переименований. Меняется набор CLI-флагов и эргономика команд — это вопрос пользовательского опыта, а не модели изоляции. Конкретные имена команд, флагов и подкоманд сверяйте с актуальной docs.docker.com перед запуском.

Заметка про устаревшую имплементацию: у docker sandbox в Docker Desktop был флаг

--bypass-host, который выводил конкретные домены из MITM-инспекции прокси. В начале 2026 года этим флагом я чинил странное поведение Codex в связке с docker sandbox: Codex обнаруживал атаку и отказывался отправлять запросы через MITM-прокси, пока docker sandbox не был запущен с командой --bypass-host *.openai.com. Поведение касалось конкретной устаревшей имплементации, к sbx как продукту оно отношения не имеет. В актуальном sbx эквивалентного флага я не нашел. Воспроизвести ситуацию в рамках статьи не успел. Если столкнетесь с похожим — пишите в комментариях, разберемся вместе.

Статья посвящена рабочему сценарию — применению агента в sandbox для проекта. Но sbx можно использовать и для персонального ассистента с доступом к личным данным (к документам, заметкам, почте) через локальную интеграцию. Например, OpenClaw (в первой статье, в разделе «Кража данных и токенов», я разбирал инцидент, в котором конфиги OpenClaw стали целью инфостилер-кампании). Сценарий с персональным ассистентом требует иной конфигурации образа: другого набора инструментов, политик сети, другой модели доверия к рабочей директории.

Docker Sandbox позволяет собирать кастомные образы. Желающих поупражняться приглашаю в комментарии и предлагаю составить конфиг для личного ассистента (желательно с аргументацией, почему так, а не иначе).

За кадром остались три вопроса:

  1. Как именно работает гипервизор-изоляция изнутри?

  2. В ходе каких экспериментов можно определить реальные границы защиты?

  3. Как собрать кастомный образ под конкретный сценарий?

Это материал для следующей части серии. Если тема зайдет и в комментариях будет интерес, соберу третью часть.

Источники

На этом пока всё!

Если хотите больше такого, заходите в мой Telegram-канал, который я веду совместно с замечательными людьми. Там авторское мнение по теме ИИ и кибербеза, мемы из частной коллекции и многое другое. Буду рад!