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

Кейс: smolvm как песочница для ненадёжного Python и JavaScript

Запуск пользовательского кода — одна из самых рискованных задач для любого сервиса. Python-скрипт пользователя может содержать while True, обратиться к файловой системе хоста или отправить данные по сети. Обычные контейнеры Docker не дают полной изоляции: они делят ядро с хостом, и уязвимости ядра потенциально превращаются в побег из контейнера.

В августе 2026 года Саймон Виллисон (simonwillison.net) поручил Claude Fable 5 протестировать smolvm как песочницу для ненадёжного Python и JavaScript. Результат — отчёт из 14 тестов, подтверждающий: аппаратная виртуализация работает, cold start занимает менее секунды, а полный стек изоляции (CPU, RAM, сеть, файловая система, таймауты) — на месте.

Разбираем кейс: зачем hardware-isolated VM вместо контейнеров, как smolvm решает каждую проблему и где у неё ограничения.

Пост Simon Willison о smolvm (19.08.2026)
Simon Willison запустил тесты smolvm 1.8.3 из Claude Code for web. Источник: simonwillison.net, 19.08.2026.

Зачем VM-песочница, а не Docker-контейнер

Основной аргумент smolvm — это уровень изоляции.

Контейнеры Docker используют namespaces и cgroups одного Linux-ядра с хостом. Если в ядре есть уязвимость (а kernel CVE публикуются постоянно), процесс внутри контейнера может выйти за его пределы. Для большинства серверных задач это нормальный риск. Для запуска враждебного пользовательского кода — нет.

smolvm создаёт полноценную виртуальную машину с собственным гостевым ядром. Гипервизор (KVM на Linux, Hypervisor.framework на macOS, WHP на Windows) аппаратно изолирует каждую VM от хоста и друг от друга. Утечка из VM означает взлом гипервизора — это на порядки сложнее, чем побег из контейнера.

smolvm на GitHub — аппаратная изоляция
README проекта smolvm: hardware-isolated VM для запуска ненадёжного кода. Источник: github.com/smol-machines/smolvm.

Как smolvm реализует полный стек изоляции

Исследование Саймона Виллисона показало, что smolvm 1.8.3 предоставляет все нужные механизмы изоляции флагами CLI — без кастомных обёрток и хаков.

Вот ключевые требования и как smolvm их закрывает:

| Ограничение RAM | --mem <MiB> — эластичное выделение через virtio-balloon | 1 ГБ аллокация при --mem 256 → MemoryError внутри гостя, хост не пострадал |
| Ограничение CPU | --cpus <N> | Fork-bomb при --cpus 1 завершился за ~1 с, нагрузка на хост 0.69 |
| Защита от while True | --timeout <dur> (выполняется агентом внутри гостя) | Процесс убит ровно по таймеру, zero leftover VMM-процессов |
| Запрет сети | По умолчанию сеть выключена | wget и DNS-запросы оба завершаются ошибкой |
| Только указанные файлы | -v HOST:GUEST[:ro] — тома только директории через virtiofs | Read-only: строго |
| Защита от заполнения диска | --storage <GiB> | dd остановлен на 2.9 ГБ при --storage 3, fs заполнена на 100% |
| Защита от fork bomb | Ядро гостя управляет PID-пространством | VM завершилась за ~1 с, хост не пострадал |
| Снижение привилегий | --unprivileged | CapEff падает с 1ffffffffff (полные) до a80425fb (стандартный набор) |

Важный нюанс: --overlay не защищает от записи

Саймон обнаружил ошибку при тестировании: флаг --overlay не ограничивает запись в корневую файловую систему гостя. Процесс dd спокойно записал 4 ГБ на 20-ГБ диск storage. Для защиты от заполнения диска нужен именно --storage — корневая FS гостя лежит на этом диске (через overlayfs), и при его заполнении запись корректно завершается с ошибкой ENOSPC.

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

Таймауты: два слоя защиты

--timeout реализован на двух уровнях:

  1. Агент внутри гостя убивает процесс по дедлайну, возвращает захваченный вывод. Это основной механизм.
  2. Клиент на хосте ставит свой таймаут на чтение — подстраховка, если гостевой агент завис.

Для полноты защиты Саймон рекомендует обернуть вызов CLI ещё и в coreutils timeout: если VM «заблокировалась», она всё равно будет демонтирована (эфемерные VM уничтожаются при завершении в любом случае).

In a few runs Claude tried to terminate the malware process once it noticed the compromise, but Auto Mode denied the cleanup command.
— Simon Willison, 27.08.2026

Этот эпизод (Johann Rehberger взломал Claude Code auto mode с 80% успеха) показывает: программные песочницы (auto mode, --restricted) не заменяют аппаратную изоляцию. Автоматическое средство защиты может само стать частью проблемы.

Практический разбор: как использовать smolvm для запуска ненадёжного кода

VM vs контейнер: аппаратная изоляция
smolvm изолирует на уровне аппаратной виртуализации — в отличие от контейнера, гость не делит ядро с хостом. Авторская схема

Установка

curl -sSL https://smolmachines.com/install.sh | bash

Для версии 1.8.3 (на момент публикации) — если installer застрял за прокси:

curl -sSL https://smolmachines.com/install.sh | bash -s -- --version 1.8.3

Устанавливается в ~/.smolvm, симлинк в ~/.local/bin. Без демона.

Требования: macOS 11+ (Apple Silicon или Intel), Linux с /dev/kvm, Windows с WHP. Внутри большинства контейнеров/VM (включая Claude Code for web — Firecracker guest) не работает из-за отсутствия nested virt.

Быстрый старт: запуск скрипта в изолированной VM

# Запуск Python-скрипта пользователя — полностью изолированно
smolvm machine run \
  --image ./python.tar \
  --cpus 1 --mem 512 \
  --timeout 30s \
  --storage 3 \
  --unprivileged \
  -v "$PWD/in:/in:ro" \
  -v "$PWD/out:/out" \
  -- python3 /in/transform.py

Каждый запуск (machine run) — эфемерная VM. После завершения скрипта VM уничтожается. Никакого кросс-задачевого заражения: всё, к чему может обратиться задача — /in (read-only), /out (read-write) и 3-гигабайтный временный диск.

Offline-образы: сеть не нужна

Ключевой момент: без флага --net в VM вообще нет сетевого устройства. Не «сеть отключена на уровне firewall» — устройства нет. Проверено в исходном коде: plan_launch_network возвращает backend None, если не запрошены --net/--ports/политики egress.

Для работы с пользовательским кодом нужен образ Python/Node. Сетевые --image python:3.12-alpine при --net off завершаются ошибкой. Решение — локальные offline-образы:

# Один раз: сохранить образ
docker save python:3.12-alpine -o python.tar
docker save node:20-alpine -o node.tar

# Всегда: запускать без сети
smolvm machine run --image ./python.tar -- python3 /in/script.py

Или упаковать в .smolmachine артефакт:

smolvm pack create --image python:3.12-alpine -o ./python312
./python312 run -- python3 /in/script.py

Постоянная VM для высокой пропускной способности

Эфемерные VM (~0.6–1.5 с на cold start) подходят для одиночных задач. Если нужно выполнять много заданий подряд, выигрывает persistent VM:

# Создать и запустить
smolvm machine create --name sandbox -s Smolfile
smolvm machine start --name sandbox

# Запускать задачи через exec (~50 мс вместо 600+ мс)
smolvm machine exec --name sandbox -- python3 /in/task1.py
smolvm machine exec --name sandbox -- node /in/task2.js

Холодный старт постоянной VM: ~1.5 с (create + start). Warm exec: 48–79 мс. Разница в 30 раз.

Для максимальной производительности — machine fork (Copy-on-Write клон работающей «золотой» VM) с пулом --hold-слотов. Типичный сценарий: одна VM разворачивает среду, форк-пул раздаёт копии задачам.

Архитектура: как выглядит сервис трансформации данных

Саймон предложил референтную архитектуру для задачи «пользователь прислал код и данные — вернуть результат»:

                 ┌──────────────────────── host ───────────────────────┐
 user code ──►   │ orchestrator ──► smolvm machine run                 │
 user data ──►   │   stage task dir:   /tasks/<id>/in  (code + data)   │
                 │   collect results:  /tasks/<id>/out                 │
                 └──────────────────────────────────────────────────────┘
   per task: --cpus 1 --mem 512 --storage 3 --timeout 30s --unprivileged
             -v /tasks/<id>/in:/in:ro  -v /tasks/<id>/out:/out   (no --net)
  • Один machine run на задачу = нулевое кросс-задачевое заражение
  • Exit code, stdout, stderr пробрасываются через CLI
  • Масштабирование: smolvm serve на unix-сокете + persistent VM или fork-пулы, когда bottleneck — boot VM

HTTP API

smolvm предоставляет HTTP API через smolvm serve. На localhost API не аутентифицирован — рекомендуется привязка к unix-сокету с файловыми permissions:

smolvm serve --listen $XDG_RUNTIME_DIR/smolvm.sock

В fleet-режиме (хостинг smol cloud) API использует mTLS, а loopback-дверь ограничена health-эндпоинтами — защита от SSRF-пивота через registry ref на 127.0.0.1.

Нашли баг: timeoutSecs vs timeout_secs

Во время тестирования HTTP API Саймон обнаружил, что поле timeout_secs (snake_case) молча игнорируется — таймаут не срабатывает, запрос выполняется все 300 секунд по умолчанию. Причина: в коде smolvm ExecRequest использует serde(rename_all = "camelCase"), и правильное имя — timeoutSecs.

Это не просто опечатка. Serde по умолчанию не отвергает неизвестные поля: любое поле с «неправильным» именем пропадает без ошибки. В версии 1.8.3 это может удивить разработчиков, которые привыкли к strict validation.

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

Ограничения: что не умеет smolvm

Nested virt

smolvm требует аппаратную поддержку виртуализации на хосте: /dev/kvm (Linux), Hypervisor.framework (macOS), WHP (Windows). Внутри большинства контейнеров и VM (включая среду Claude Code for web, которая сама работает в Firecracker) вложенная виртуализация недоступна. Саймон решал эту проблему через GitHub Actions runners, которые предоставляют KVM.

Это значит: smolvm не подходит как песочница внутри уже запущенной VM (типичный сценарий cloud-платформы). Для этого лучше смотреть на Firecracker или gVisor.

Только Linux-гости

Хост может быть macOS, Linux или Windows. Гость — всегда Linux. Запуск Windows-гостей или macOS-гостей не поддерживается.

Тома только директории

Volume mounts работают на уровне директорий, а не файлов. Чтобы передать один файл — стейджить его в директорию.

Нет ICMP

Сеть (при --net) работает только по TCP/UDP. ICMP-пинги не проходят.

Нет GPU-изоляции

GPU-доступ (через --gpu / --cuda) реализован через API-ремотинг virtio-gpu или CUDA-over-vsock. Это процессорный уровень изоляции, а не аппаратный. Для GPU-задач с полной изоляцией smolvm не подходит.

Сравнение с другими вариантами

Критерий smolvm Docker QEMU Firecracker gVisor
Тип изоляции VM + ядро гостя Namespace + общее ядро Namespace в shared VM VM + ядро гостя User-space ядро
Cold start <200 мс (ephemeral ~600 мс) ~100 мс ~секунды <125 мс ~100 мс
macOS нативно Да Через Docker VM Да (krunkit) Нет Нет
Портативные артефакты .smolmachine Образы (нужен daemon) Нет Нет Нет

smolvm занимает нишу между Docker (быстро, но слабая изоляция) и QEMU/Firecracker (сильная изоляция, но сложнее в настройке). Портативность .smolmachine-артефактов и кроссплатформенность — главные отличия.

Связь с безопасностью агентов

Тема песочниц для ненадёжного кода пересекается с трендом недели: серия атак на Claude Code auto mode. Johann Rehberger показал атаку с 80% успеха: zip-архив с struct.py, который подменяет import base64, — и агент выполняет враждебный код.

Ответ Anthropic — флаг --restricted (убирает доступ к командам/коду и WebFetch). Но программная песочница всегда уступает аппаратной.

The only safe way to run agents if there’s any risk of attracting the attention of an adversarial attack is with a sandbox.
— Simon Willison, 27.08.2026

Simon Willison: «the only safe way … is with a sandbox» (27.08.2026)
Вывод Simon Willison после атаки Rehberger на auto mode: единственная безопасная изоляция — песочница. Источник: simonwillison.net, 27.08.2026.

smolvm решает эту задачу на аппаратном уровне: даже если промпт-инъекция сработала и агент запустил враждебный код, этот код работает в изолированной VM без сети, с лимитами CPU/RAM/диска и read-only входными данными.

Российские реалии

smolvm — open-source проект (Apache-2.0), доступен для скачивания глобально. На момент публикации:

  • Установка: curl -sSL https://smolmachines.com/install.sh | bash работает без VPN из РФ. GitHub-репозиторий smol-machines/smolvm доступен.
  • smol cloud (хостинг managed VM): требует аккаунт на smolmachines.com, оплата картами. Доступность из РФ не проверена — сервис новый и niche.
  • Claude Code for web (среда, из которой проводился тест): доступен с аккаунтом Anthropic, оплата международными картами или через посредников.
  • Альтернативы для РФ: Docker и Firecracker доступны для установки на выделенных серверах; gVisor — через Google Cloud (недоступен напрямую из РФ, но работает через VPN). Для локальной изоляции — любой гипервизор (KVM на bare-metal Linux).
  • Русскоязычное освещение: на момент публикации smolvm в рунете (Habr, vc.ru, Tproger) не освещалось. Проект слишком новый и niche-овый для русскоязычного сообщества.

Выводы

smolvm не заменяет Docker для повседневной разработки. Его ниша — запуск ненадёжного кода с аппаратной изоляцией: пользовательские скрипты, AI-агенты, data transformation tasks. Cold start ~600 мс, warm exec ~50 мс, offline-образы, полный стек изоляции из коробки.

Ключевые уроки из кейса:

  1. Hardware isolation ≠ container isolation. Для ненадёжного кода контейнер — компромисс, а VM — безопасность.
  2. --overlay не защищает от заполнения диска. Используйте --storage.
  3. HTTP API требует camelCase полей. timeoutSecs, не timeout_secs.
  4. Offline-образы — обязательны для безсетевой песочницы. docker save + --image ./file.tar.
  5. Nested virt — главное ограничение. smolvm не работает внутри других VM/контейнеров.

Для сервисов, которые выполняют пользовательский код (независимо, прислал его программист или сгенерировал AI-агент), smolvm — один из самых практичных вариантов hardware isolation на 2026 год.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка / 5. Количество оценок:

Оценок пока нет. Поставьте оценку первым.

Добавить комментарий

Adblock
detector