Перейти к содержанию
zoryn/ maintainer-assistant

Песочница

zoryn запускает скрипты из .gear/up.d/ и .gear/merge-up.d/ в песочнице для защиты чувствительных файлов (~/.ssh, ~/.gnupg, ~/.config) от ненадёжных скриптов из репозитория.

Установка

Bubblewrap для лёгкой изоляции:

apt-get install bubblewrap

Для гибридного режима (bwrap + chroot hasher с зависимостями сборки):

apt-get install bubblewrap hasher

Режимы запуска

РежимОписание
hybridbwrap + chroot hasher — использует зависимости сборки пакета (по умолчанию, если установлены оба инструмента)
bwrapТолько bubblewrap — лёгкая изоляция через namespaces, использует системные пути хоста
directБез изоляции — полный доступ к файловой системе (не рекомендуется)

Режим определяется автоматически: hybrid, если доступны bwrap и hsh; bwrap, если только bwrap; иначе direct.

Если настроенный режим недоступен, zoryn запросит подтверждение перед запуском хуков без изоляции. В неинтерактивном режиме выполнение отклоняется. Используйте --no-sandbox для пропуска подтверждения и запуска без песочницы.

Что общее у всех режимов с изоляцией

Для hybrid и bwrap (то есть всего, кроме direct):

АспектПоведение
Каталог проектаКорень git примонтирован на чтение и запись по тому же пути. Хук видит репозиторий и может его менять.
Рабочий каталогПеред exec делается chdir в корень git.
Домашний каталог ($HOME)Подменяется на пустой tmpfs. ~/.ssh, ~/.gnupg, ~/.config, файлы истории, сокеты агентов — ничего из этого не видно.
~/.gitconfigЕсли существует, монтируется только на чтение, чтобы git внутри хука знал вашу личность.
Подпись коммитовОтключена внутри песочницы (commit.gpgsign/tag.gpgsign принудительно выключены через GIT_CONFIG_*). Keyring GnuPG не виден, ключи внутрь не пробрасываются — подписанный коммит лишь упал бы. Подписывайте на хосте, если нужна подпись.
ОкружениеПолностью очищается через --clearenv. Заново подкидываются только PATH, HOME, GIT_CONFIG_* (подпись выключена) и TAG/COMMIT/SHORTCOMMIT (для merge-хуков).
PATHПо умолчанию /usr/bin:/bin (настраивается через [sandbox] path = "…").
/procМонтируется свежий procfs.
/devBind-mount с хоста (--dev-bind /dev /dev) с полным доступом к устройствам.
Пространства процесса--unshare-all — новые PID-, IPC-, UTS-, cgroup-namespaces.
СетьРазрешена (--share-net). Хуки могут вызывать curl, go mod download, cargo fetch и т. п. Ключ [sandbox] outbound_interface направляет весь трафик хуков через один интерфейс хоста (см. ниже).
Время жизни--die-with-parent — песочница умирает вместе с zoryn.
Терминал--new-session — у песочницы собственная сессия, поэтому код внутри неё не может подбросить нажатия клавиш вашему шеллу через TIOCSTI.

Различие режимов — откуда берутся /usr, /lib, /bin, /sbin, /etc:

bwrap

Системные каталоги (/usr, /lib, /lib64, /etc, /bin, /sbin) bind-mount'ятся только на чтение с хоста. Хук видит ровно те инструменты, которые установлены в вашей системе.

  • Плюсы: нулевые накладные расходы — bwrap стартует мгновенно (chroot готовить не нужно).
  • Минусы: BuildRequires из spec не гарантированно присутствует; версии библиотек — те, что стоят на хосте.

Подходит для хуков, которым ничего сверх системного не нужно (типичные склейки на sed/awk).

hybrid

Сначала hsh готовит hasher-chroot со сборочными зависимостями пакета (из BuildRequires:), затем bwrap запускается с /usr, /lib, /lib64, /etc, /bin, /sbin, /var из этого chroot, а не с хоста (поверх /var монтируется writable tmpfs /var/tmp для временных файлов). Каталог проекта, /proc, /dev, сеть и изоляция домашнего каталога — те же, что в bwrap.

/etc/resolv.conf берётся с хоста, чтобы DNS работал и внутри chroot'а.

  • Плюсы: хук видит ровно тот toolchain, которым будет собираться пакет — воспроизводимо между машинами.
  • Минусы: первый запуск платит за подготовку chroot (секунды — минута; дальше кэшируется в ~/hasher).

Подходит для хуков, которым нужны сборочные инструменты (go mod vendor, cargo vendor, meson subprojects download и т. п.).

Дополнительные пакеты сверх BuildRequires: можно перечислить в .gear/version-up:

[sandbox]
packages = ["go", "make", "curl"]

Они ставятся через hsh-install после подготовки chroot.

Чтобы полностью пропустить BuildRequires: пакета, задайте specbr = false. Тогда chroot инициализируется «голым» (hsh --initroot-only, без сборки src.rpm) — ставятся только явно заданные пакеты (git, а также [sandbox.chroot] packages из ~/.zoryn и перечисленные выше packages), но не сборочные зависимости из spec. Удобно, когда эти зависимости хукам не нужны, а сборка src.rpm лишь тратит время:

[sandbox]
specbr = false
packages = ["go", "make", "curl"]

По умолчанию specbr = true (собрать src.rpm и использовать его BuildRequires:). При specbr = false пользовательская команда [sandbox.chroot] prepare (из ~/.zoryn) игнорируется в пользу «голой» инициализации — zoryn выводит предупреждение.

direct

Никакой изоляции. Скрипт-хук выполняется в окружении текущего процесса с полным доступом к файловой системе, ~/.ssh, сети, переменным окружения и т. д. Эквивалент запуска хука руками.

Использовать только если вы проверили хуки и доверяете репозиторию.

Конфигурация

В ~/.zoryn:

[sandbox]
runner = "hybrid"
path = "/usr/bin:/bin"
# outbound_interface = "wg0"

[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"

[sandbox.hasher]
workdir = "~/hasher"

Для отдельного проекта (~/.config/zoryn/projects.d/<project>.toml):

[sandbox]
runner = "hybrid"

[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"

Дополнительные пакеты

В .gear/version-up:

[sandbox]
packages = ["go", "make", "curl"]

Пакет git ставится всегда. Пакеты из этой секции ставятся после подготовки chroot через hsh-install, поверх BuildRequires: из spec. Добавьте specbr = false, чтобы пропустить эти BuildRequires: и сборку src.rpm (см. hybrid выше).

Трафик хуков через заданный интерфейс

По умолчанию хуки работают в сети хоста. Чтобы их исходящий трафик уходил только через один интерфейс — например, VPN-туннель — задайте outbound_interface в секции [sandbox] (глобально в ~/.zoryn или для проекта в projects.d/<project>.toml):

[sandbox]
outbound_interface = "wg0"

Тогда bwrap-песочница хука оборачивается в rootless-namespace pasta, исходящие сокеты которого привязаны к этому интерфейсу (SO_BINDTODEVICE), — соединения хуков покидают хост только через него. Механизм тот же, что у ключа outbound_interface в devenv. Работает для раннеров bwrap и hybrid; direct и устаревший hasher игнорируют ключ с предупреждением.

Требования и ограничения:

  • Должна быть установлена pasta (пакет passt). Если ключ задан, а pasta нет, хуки завершаются с ошибкой — тихого отката на полную сеть хоста не происходит.
  • На интерфейсе должен быть IP-адрес, а у хоста — маршрут по умолчанию (его интерфейс служит шаблоном для pasta).
  • /etc/resolv.conf остаётся хостовым: если DNS-серверы недоступны через выбранный интерфейс, разрешение имён в хуках не заработает.
  • Сервисы на localhost хоста из хука недоступны (у namespace свой loopback).

Флаги CLI

zoryn up --sandbox=hybrid    # использовать hybrid (по умолчанию)
zoryn up --sandbox=bwrap     # bubblewrap с путями хоста
zoryn up --no-sandbox        # полностью отключить песочницу

Как работает hybrid

В режиме hybrid hasher готовит chroot со сборочными зависимостями пакета (из spec-файла), а bwrap запускает скрипты, используя пути из этого chroot. Это даёт и изоляцию файловой системы, и доступ к нужным инструментам без setuid-привилегий во время выполнения.