Как устроен перехват#
astra-shim решает две задачи: переписывает пути, зашитые в программу, и подменяет команды, которых в песочнице нет. Обе задачи описываются одной таблицей правил, а применяются двумя независимыми слоями перехвата.
Таблица правил#
Правила лежат в одном файле формата ключ = значение. Путь к нему задаёт
переменная окружения ASTRA_SHIM_CONFIG; при сборке через aft-app это
/app/etc/astra-shim.conf.
debug = 1
/usr/lib/myapp = /app/lib/myapp
/etc/myapp = /app/etc/myapp
apt = flatpak-spawn --host apt
systemctl = flatpak-spawn --host systemctl
Ремап пути срабатывает по префиксу и границе компонента. Правило
/usr/lib/myapp поймает /usr/lib/myapp/data.bin, но не
/usr/lib/myapp-other. Так правило не расползается на соседние каталоги.
Подмена команды сопоставляется по имени исполняемого файла без каталога.
Исходные аргументы дописываются после команды-замены, поэтому запись
apt = flatpak-spawn --host apt превращает вызов apt install foo в
flatpak-spawn --host apt install foo.
Строка debug = 1 включает журнал перехвата в поток ошибок. Она нужна при
подборе правил и в готовой сборке не оставляется.
Слой LD_PRELOAD#
Основной слой, работает всегда. Библиотека подключается к процессу через
LD_PRELOAD и перехватывает функции libc. Уже подключённые программой
библиотеки в LD_PRELOAD сохраняются.
Перехватываются обращения к файловой системе: open, openat, creat,
fopen, freopen, stat, lstat, fstatat, statx, access,
faccessat, opendir, readlink, readlinkat, realpath,
canonicalize_file_name, mkdir, mkdirat, rmdir, unlink,
unlinkat, rename, link, symlink, chmod, chown. Для
старых сборок дополнительно перехватывается семейство __xstat, через которое
до glibc 2.33 проходили вызовы stat.
Перехватывается запуск процессов: execve, execv, execvp,
execvpe, posix_spawn, posix_spawnp, system, popen.
Этого хватает обычным программам, собранным против libc, включая приложения на Electron.
Слой seccomp#
Дополнительный слой, включается переменной ASTRA_SHIM_SECCOMP=1. Он ловит
то, что LD_PRELOAD физически не видит:
загрузку разделяемых библиотек динамическим загрузчиком
ld.so, который зовётopenatнапрямую, минуя перехватываемые обёртки;статически слинкованные программы и программы на Go, которые обращаются к ядру, не проходя через libc.
Слой построен на механизме ядра SECCOMP_RET_USER_NOTIF. Лаунчер ставит
фильтр на openat, openat2, newfstatat, statx, faccessat,
faccessat2 и readlinkat, а супервизор по совпавшему правилу сам
открывает файл по новому пути и передаёт дескриптор в целевой процесс либо
отвечает на запрос о свойствах файла. Пути, не совпавшие ни с одним правилом,
ядро выполняет как есть, поэтому накладные расходы ложатся только на системные
вызовы с путями.
Композиция слоёв#
Слои используют одну таблицу правил и не мешают друг другу. Для обычного вызова
через libc путь переписывает LD_PRELOAD, и до ядра доходит уже /app/...,
который ни с одним правилом не совпадает и проходит без вмешательства. Для
ld.so и статических программ до ядра доходит исходный путь, и его
переписывает seccomp. Двойного применения правила не происходит.