Files
opencode-sandbox/README.ru.md
T
k.denisov 01fc1627c1 Protect host-run files from the agent; trust registry; git audit
- read-only in the sandbox: .opencode-sandbox/, .cc-safety-net/, .git/hooks, .git/config,
  an in-project core.hooksPath, the egress rules and the safety-net home; .git is pinned
  by a bind onto itself so it cannot be swapped
- only trusted projects are loaded (init / trust): a .opencode-sandbox/ in a worktree or a
  subdirectory is no longer sourced on the host
- run (before and after the session) and doctor report planted .git entries, redirected
  worktrees and per-worktree overrides
- hide Firefox, Claude, VS Code, Cursor and Codex configs
2026-10-30 01:31:36 -07:00

26 KiB
Raw Blame History

opencode-sandbox

English · Русский

Дайте OpenCode 2 работать над проектом с полными правами — без вопросов «разрешить этот путь?», — а его ошибки пусть остаются внутри песочницы.

cd my-project
opencode-sandbox init     # один раз на проект
opencode-sandbox run      # фильтр → netlock → самопроверки → веб-интерфейс OpenCode

Что это даёт

Изоляция файлов bubblewrap: агент пишет только в проект, его worktree и разрешённые вами пути. Остальная система — только чтение; SSH-ключи, облачные учётки, ~/.kube/config, профили браузеров и конфиги других агентов скрыты
Сетевой фильтр весь HTTP(S) идёт через squid с правилами проекта; прод и внутренние сети недоступны, пока вы их не разрешите
Netlock nftables на уровне ядра отклоняет всё, что идёт в обход фильтра: прямой TCP, IP-адреса, ping, обычный SSH
Без подтверждений внутри песочницы агенту разрешено всё, кроме sudo и systemd-run
Worktree одна сессия — одно дерево; создаются и удаляются командой opencode-sandbox wt, с хуками проекта (порты, .env, запуск стека)
Дополнительно панель «Токены» в веб-интерфейсе (состав контекста, расход, скорость модели), скилы после compaction возвращаются, cc-safety-net против разрушительных git/rm, уведомления на рабочий стол

Только Linux: bubblewrap, nftables, systemd, Docker.

Содержание

Требования

  • Linux с непривилегированными user namespaces (bubblewrap должен работать от обычного пользователя)
  • docker, bwrap (bubblewrap), sg (shadow-utils), curl, jq, ncat (nmap-ncat), git
  • sudo один раз — для установки netlock
  • OpenCode 2: npm i -g @opencode/cli или локальная установка, путь — в OCSB_OPENCODE_BIN
  • по желанию: xdg-dbus-proxy — уведомления из песочницы

Установка

git clone --recursive https://git.neotokai.ru/public/opencode-sandbox.git ~/opencode-sandbox
cd ~/opencode-sandbox
make install                      # ссылка opencode-sandbox в ~/.local/bin, плагины
opencode-sandbox doctor           # проверка зависимостей и настроек хоста

Первый run ставит netlock через sudo (или вручную: opencode-sandbox netlock setup). Вас добавят в группу opencode-sandbox; перелогиниваться не нужно.

Обновление: git pull && make plugins.

Быстрый старт

cd my-project
opencode-sandbox init             # создаст .opencode-sandbox/ и .cc-safety-net/policy.json
$EDITOR .opencode-sandbox/egress.conf   # разрешите хосты, которые нужны проекту
ssh-add                           # только если агенту нужен git по SSH
opencode-sandbox run

run поднимает фильтр, проверяет netlock, прогоняет самопроверки и запускает OpenCode. Браузер откроется на http://127.0.0.1:4196/: логин opencode, пароль — в ~/.local/share/opencode-sandbox/<проект>/password (выводится при первом запуске).

Закоммитьте .opencode-sandbox/ (кроме local/) и .cc-safety-net/ — каждый, кто склонирует проект, получит ту же песочницу. sandbox.conf и хуки — это bash, который выполняется на вашем хосте, поэтому загружаются только доверенные проекты: init доверяет созданному проекту, а в клоне прочитайте .opencode-sandbox/ и выполните opencode-sandbox trust (список — ~/.config/opencode-sandbox/projects). .opencode-sandbox/ в worktree или подкаталоге игнорируется.

Файлы проекта

my-project/
├── .opencode-sandbox/
│   ├── sandbox.conf        настройки (bash)                        в git
│   ├── egress.conf         сетевые правила                         в git
│   ├── hooks/
│   │   ├── wt-post-add     после создания worktree                 в git
│   │   └── wt-pre-rm       перед удалением worktree                в git
│   └── local/              личные переопределения, не в git
│       ├── sandbox.conf
│       └── egress.conf
└── .cc-safety-net/policy.json   исключения cc-safety-net           в git

Состояние проекта лежит вне репозитория, в ~/.local/share/opencode-sandbox/<имя>/: база сессий OpenCode, кэши, пароль веб-интерфейса, конфиг Docker-клиента, сгенерированный squid.conf.

Настройки

sandbox.conf — файл bash. Слои, побеждает последний:

  1. умолчания пакета
  2. ~/.config/opencode-sandbox/sandbox.conf — ваши, для всех проектов
  3. .opencode-sandbox/sandbox.conf — проекта
  4. .opencode-sandbox/local/sandbox.conf — ваши, для этого проекта
  5. OCSB_<ИМЯ>=… в окружении — на один запуск (только скаляры)

opencode-sandbox config показывает итоговые значения. Когда файлы читаются, OCSB_PROJECT_DIR уже задан — его можно использовать в значениях. Относительные пути — от проекта; ~/ работает.

Настройка По умолчанию Что задаёт
OCSB_NAME имя каталога проекта имя каталога состояния и контейнера фильтра
OCSB_WEB_PORT 4196 порт веб-интерфейса; сам OpenCode — на следующем
OCSB_EGRESS_PORT 3128 порт фильтра на 127.0.0.1 и шлюзе docker0
OCSB_OPENCODE_BIN opencode из PATH бинарник OpenCode 2
OCSB_ENV_FILE — файл KEY=VALUE с токенами MCP-серверов и провайдера модели
OCSB_KUBECONFIG — kubeconfig агента (~/.kube/config остаётся скрытым)
OCSB_ENV () дополнительные KEY=VALUE для OpenCode
OCSB_RW () дополнительные пути на запись
OCSB_HIDE () дополнительные скрытые пути
OCSB_RW_HOME .cache .npm .asdf .bundle .gem … каталоги инструментов в ~ на запись (если есть)
OCSB_HIDE_HOME .gnupg .aws .kube/config .claude … скрытые пути в ~
OCSB_SKILLS (opencode-sandbox) скилы, которые монтируются агенту
OCSB_SKILL_DIRS () дополнительные каталоги со скилами
OCSB_PLUGINS skill-compact-resurrect файлы плагинов из plugins/ (или абсолютные пути)
OCSB_PANEL 1 панель «Токены» в веб-интерфейсе
OCSB_NOTIFICATIONS 1 уведомления через фильтрующий прокси D-Bus
OCSB_DISPLAY 0 1 — окна агента (например, браузер Playwright) на вашем экране через сокет Wayland
OCSB_TMP private private — свой /tmp агента в каталоге состояния; shared — /tmp хоста
OCSB_SANDBOX 1 0 — только фильтр и netlock, без bubblewrap
OCSB_WORKTREES_DIR ../<проект>-worktrees куда класть worktree
OCSB_WT_PREFIX — каталог дерева = <префикс><slug>
OCSB_WT_BASE текущий HEAD от какой ветки создаются деревья
OCSB_WT_SLUG_REGEX — регулярка с одной группой: slug из имени ветки
OCSB_OPENCODE_CONFIG ~/.config/opencode-sandbox/opencode.jsonc или пакета глобальный конфиг OpenCode
OCSB_STATE_DIR ~/.local/share/opencode-sandbox/<имя> каталог состояния
OCSB_NETLOCK_GROUP opencode-sandbox группа netlock
OCSB_DOCKER_GW адрес docker0 где контейнеры видят фильтр

Пример — проект с внутренними сервисами, файлом токенов и worktree:

OCSB_NAME=shop
OCSB_WEB_PORT=4296            # разные порты — несколько проектов одновременно
OCSB_EGRESS_PORT=3138
OCSB_ENV_FILE=~/.config/shop/agent.env
OCSB_HIDE=(../shop-secrets)
OCSB_ENV=("SHOP_ENV=dev")
OCSB_WT_PREFIX=shop-wt-
OCSB_WT_BASE=main
OCSB_WT_SLUG_REGEX='SHOP-([0-9]+)'

Сетевые правила

Политика — чёрный список: что не запрещено, то разрешено. Всегда запрещены, если не разрешены явно: внутренние сети (10/8, 172.16/12, 192.168/16, 100.64/10, link-local, loopback), имена без точки, порты кроме 80 и 443.

allow        jira.example.com gitlab.example.com     # побеждает block
allow-regex  ^[a-z0-9-]+\.staging\.example\.com$
block        .example.com                            # домен и все поддомены
block-regex  ^prod-
connect      git.example.com:2222 10.0.0.5:6443      # туннели: git по SSH, API Kubernetes
expect-block https://prod.example.com/               # самопроверки для `run` и `egress verify`
expect-pass  https://gitlab.example.com/
expect-pass  https://10.0.0.5:6443/version -k        # опции curl — после URL

Порядок проверки: connect → порты кроме 80/443 запрещены → allow → block → имена без точки и внутренние сети запрещены → остальное разрешено.

Слои (правила складываются): egress/defaults.conf → ~/.config/opencode-sandbox/egress.conf → .opencode-sandbox/egress.conf → .opencode-sandbox/local/egress.conf.

Куда класть хосты компании. В egress.conf проекта: он лежит в репозитории компании, и новый коллега получает рабочие правила вместе с git clone. То, что нельзя коммитить, — в local/egress.conf. В самом пакете хостов компаний нет.

Изменения применяются при следующем run или сразу командой opencode-sandbox egress up (перечитывает правила без перезапуска squid). egress render показывает сгенерированный squid.conf, egress logs — каждый запрос и отказ в реальном времени.

Worktree

opencode-sandbox wt add feature-SHOP-42-cart --seed   # аргументы после ветки — хуку
opencode-sandbox wt rm 42                              # откажет, если есть незакоммиченное
opencode-sandbox wt rm 42 --force                      # выбросит незакоммиченное
opencode-sandbox wt list

wt add создаёт <OCSB_WORKTREES_DIR>/<OCSB_WT_PREFIX><slug> в новой ветке от OCSB_WT_BASE и запускает в нём hooks/wt-post-add. wt rm запускает hooks/wt-pre-rm, проверяет незакоммиченные изменения и удаляет дерево (ветка остаётся). Хуки получают WT_DIR, WT_BRANCH, WT_SLUG, MAIN_ROOT и дополнительные аргументы; ошибка хука прерывает команду. Шаблоны — hooks/*.example после init.

Агенту сказано создавать worktree только для изменений кода в новой ветке — не для вопросов, поиска и ревью.

Скилы и плагины

  • Скилы. OCSB_SKILLS — каталоги скилов (с SKILL.md), которые монтируются агенту только на чтение как глобальные: из skills/ или ваших OCSB_SKILL_DIRS. Скилл opencode-sandbox объясняет агенту среду: что доступно на запись, что значит 403 от прокси, как попросить хост, как работать с worktree.
  • Плагины пакета — git submodules в plugins/: context-panel (панель «Токены») и skill-compact-resurrect (скилы переживают compaction).
  • Глобальный конфиг OpenCode opencode/opencode.jsonc добавляет opencode-notifier и cc-safety-net. Замените его своим ~/.config/opencode-sandbox/opencode.jsonc. Собственный opencode.jsonc проекта (модель, MCP-серверы, плагины проекта) работает как обычно.

Как это устроено

opencode-sandbox run
 ├─ docker: opencode-sandbox-egress-<имя>    squid, сеть хоста, 127.0.0.1 + docker0
 ├─ xdg-dbus-proxy                           сессионная шина — только уведомления
 └─ sg opencode-sandbox                      netlock: группе доступны только loopback и DNS
     └─ bwrap                                / только чтение; проект, worktree, состояние — запись
         └─ opencode serve :4197             прокси в окружении, свои XDG, права = allow
браузер → :4196 (панель внутри OpenCode) → :4197

Внутри песочницы:

  • На запись: проект, каталог worktree, каталог состояния, OCSB_RW, OCSB_RW_HOME и собственный /tmp. Всё остальное — только чтение.
  • Свой /tmp: в /tmp хоста лежат сокеты, через которые можно выйти из песочницы (tmux, X11, сокеты браузеров, других программ), поэтому у агента свой /tmp — в <состояние>/tmp (файлы старше 7 дней удаляются при запуске). Скриншоты и логи агента ищите там. Docker смонтирует путь из /tmp из /tmp хоста — монтируйте пути проекта.
  • Только чтение внутри проекта: то, что хост выполняет вне песочницы, — .opencode-sandbox/, .cc-safety-net/, .git/hooks, .git/config (core.fsmonitor оттуда срабатывает при любом git status, хватит prompt'а оболочки), core.hooksPath, если он внутри проекта, — а также правила egress и домашний каталог safety-net в каталоге состояния. Сам .git — точка монтирования, его нельзя переименовать и подменить. git работает; не проходит только запись git config (например, push -u не запишет upstream). После каждой сессии run сообщает о других .git в проекте и worktree, о worktree, чей .git указывает в другое место, и о переопределениях для отдельных worktree; doctor проверяет то же.
  • Скрыто: OCSB_HIDE_HOME, OCSB_HIDE, приватные ключи в ~/.ssh (config, known_hosts и *.pub видны; подписывает ssh-agent хоста).
  • Сеть: HTTP(S)_PROXY указывают на фильтр. GIT_SSH_COMMAND пускает ssh через него с -F ~/.ssh/config (внутри user namespace системные конфиги ssh, принадлежащие root, выглядят как принадлежащие nobody, и ssh их отвергает). DOCKER_CONFIG передаёт прокси сборкам и контейнерам.
  • OpenCode: свои каталоги XDG_* в каталоге состояния (ваш ~/.config/opencode не используется); права — через OPENCODE_CONFIG_CONTENT: разрешено всё, sudo и systemd-run запрещены.
  • Сессионная шина: заменена на xdg-dbus-proxy, который пропускает только org.freedesktop.Notifications; настоящая шина позволила бы агенту запускать процессы вне песочницы через systemd-run --user.
  • Экран: по умолчанию нет — DISPLAY, WAYLAND_DISPLAY и XAUTHORITY пустые, cookie X скрыт; браузеры — headless. С OCSB_DISPLAY=1 агент получает только сокет Wayland (и /dev/dri для видеокарты): его окна появляются на вашем экране, но, в отличие от X11, клиент Wayland не может читать чужие окна и подделывать в них ввод. X11 не пробрасывается никогда. xdg-open отключён; интерфейс открывает run из вашей обычной сессии.
  • Без тихого отката: при OCSB_SANDBOX=1 и неработающем bubblewrap run не запустится.

Netlock (netlock setup, sudo) создаёт группу opencode-sandbox, добавляет в неё вас и ставит opencode-sandbox-netlock.service: таблица nftables разрешает сокетам этой группы только 127.0.0.0/8, ::1 и DNS-серверы из resolv.conf, остальное отклоняет. OpenCode запускается с этой группой — под правило попадают агент и всё, что он запускает.

Модель безопасности и ограничения

Цель — удержать агента, который ошибается (не тот путь, rm -rf не там, запрос в прод), а не остановить целенаправленного злоумышленника.

  • Docker — выход наружу. Агенту доступен сокет Docker, а Docker может смонтировать любой путь хоста (docker run -v /:/host). Если это важно — не давайте агенту Docker; закрыть это можно фильтрующим прокси для сокета или rootless Podman.
  • Общее ядро. bubblewrap — это namespaces, а не виртуальная машина.
  • Разрешённые хосты — канал утечки. Данные могут уйти на любой хост, который пропускают правила.
  • Проект доступен на запись. Ошибки внутри него настоящие; cc-safety-net блокирует типичные разрушительные команды, git хранит историю — коммитьте чаще.
  • Файлы, которые вы запускаете сами. Хуки, конфиги и метаданные git защищены (см. выше), но код агент правит: изменённый Makefile, скрипт в package.json или файл в bin/ выполнится с вашими правами, когда вы его запустите. Смотрите git diff, прежде чем запускать команды проекта на хосте.
  • Подложенные репозитории обнаруживаются, а не предотвращаются. .git, созданный агентом в новом каталоге или новом worktree, найдёт проверка после сессии; не запускайте там git, пока не посмотрели.
  • С OCSB_TMP=shared доступны /tmp хоста и его сокеты.
  • С OCSB_DISPLAY=1 агент может открывать окна на вашем экране и пользоваться буфером обмена, пока его окно в фокусе.

Решение проблем

Симптом Причина и что делать
Read-only file system путь вне разрешённых на запись. Если агенту он правда нужен — добавьте в OCSB_RW
403 от прокси, CONNECT tunnel failed хост не разрешён: добавьте allow (или connect host:port для порта не 443) в egress.conf, выполните opencode-sandbox egress up
git по SSH: Permission denied (publickey) в ssh-agent нет ключей: ssh-add перед run
git по SSH: 403 добавьте connect <хост>:<порт> в egress.conf
port … is busy другой проект или старый экземпляр: задайте другие OCSB_WEB_PORT / OCSB_EGRESS_PORT
netlock is NOT working opencode-sandbox netlock setup, затем netlock verify
bubblewrap cannot create a sandbox отключены непривилегированные user namespaces (kernel.unprivileged_userns_clone, ограничения AppArmor)
нет панели «Токены» открывайте порт 4196, а не 4197; журнал — <состояние>/data/opencode/log/context-panel.log
нет уведомлений установите xdg-dbus-proxy
браузерные инструменты падают (Missing X server, Failed to connect to Wayland display) в песочнице нет экрана: включите OCSB_DISPLAY=1 (сессия Wayland) или запускайте браузер headless (Playwright MCP: --headless)
не найти файл, который агент положил в /tmp он в ~/.local/share/opencode-sandbox/<имя>/tmp
docker build не скачивает образы реестры доступны через squid: смотрите egress logs

Удаление

opencode-sandbox egress down                 # в каждом проекте
opencode-sandbox netlock teardown            # sudo; группа остаётся: sudo groupdel opencode-sandbox
cd ~/opencode-sandbox && make uninstall
rm -rf ~/.local/share/opencode-sandbox       # состояние: сессии, кэши, пароли
docker rmi opencode-sandbox-egress

Команды

opencode-sandbox init                      добавить .opencode-sandbox/ в текущий репозиторий
opencode-sandbox trust [каталог]           доверять существующему .opencode-sandbox/ (клон)
opencode-sandbox config                    итоговые настройки
opencode-sandbox doctor                    зависимости и настройки хоста
opencode-sandbox run [аргументы opencode]  всё сразу, затем OpenCode
opencode-sandbox prepare                   фильтр + netlock + самопроверки, без OpenCode
opencode-sandbox egress up|down|status|logs|verify|render
opencode-sandbox netlock setup|teardown|status|verify
opencode-sandbox wt add <ветка> [аргументы] | rm <имя|путь> [--force] | list

Лицензия

MIT