Был у меня такой сервис и работал он только тогда, когда все его файлы и каталоги принадлежали определенному пользователю.
ㅤ
Доступ к сервису имели многие, поэтому люди порой троили и запускали команды в каталоге сервиса от root. Сервису было на это поебать, но до момента его перезапуска.
Обычно это чинилось очень легко, через
chown -R. Все это знали и никого это не смущало. Короче костыль ебаный.Казалось бы, есть масса способов предотвратить такие ошибки: правильные права на файлы, ACL’ы, SELinux.
Но веселья в этом мало! Поэтому я решил заебенить свой собственный мониторинг файловой системы. Скиллов то предостаточно, хули нет.
Спойлер: Я залез в кроличью нору и знатно так хлебнул гавна.
Попытка намбер 1
В Linux есть API под названием fanotify, с его помощью можно получать события о действиях с файлами в юзерспейс.
Всё просто: инициализируем fanotify_init, указываем каталоги через fanotify_mark и читаем события из дескриптора.
Но тут же вылез огромный хуй:
- нельзя отслеживать каталоги рекурсивно (только целый маунт)
-
anotify даёт только PID процесса, который что-то сделал. А чтобы узнать UID/GID — нужно лезть в /proc/<pid>/status. То есть на каждое событие приходится открывать и парсить файлы в /proc.Решение вполне рабочее, но громоздкое. Я такие не люблю, этож думать надо, вайбкодингом не решается.
Попытка намбер 2
Вспоминаем что есть
eBPF. Это штука позволяет запускать программы прямо в ядре Linux. Они компилируются в байткод, проходят проверку, а потом гоняются через JIT почти с нативной скоростью.Что такое eBPF можешь почитать тут и тут.
В
eBPF заебись то, что можно цепляться за разные функции ядра. Например, можно подцепиться к vfs_mkdir или vfs_create — это общий слой для работы с файлами.То есть единая точка входа. Там можно отлавливать события и фильтровать их, не гоняя лишние переключения контекста.
Но и тут вылез хуй:
-
kprobes на функции VFS нестабильны, в новых ядрах сигнатуры могут меняться или функции вообще исчезнуть.- фильтрацию приходится писать прямо в
eBPF, а там свои ограничения. Нет бесконечных циклов, стек всего ~512 байт.Да блядь…
Как я победил рекурсивный обход
Чтобы понять, что именно меняется в каталоге сервиса, пришлось использовать структуру
dentry и подниматься по дереву до родителя.Но так как в
eBPF нельзя сделать «бесконечный» цикл, я ограничил глубину с помощью MAX_DEPTH.На практике этого вполне достаточно. Глубина каталогов мне известна. Ну и конечно, пришлось аккуратно работать с RCU-локами, чтобы дерево не поменялось в момент обхода.
➡️ Кусок кода в первом комментарии, сюда не влез.
Как можно улучшить
В идеале использовать не VFS-хуки, а LSM hooks (Linux Security Module).
Они стабильнее, понятнее и позволяют сразу работать с путями. Там можно красиво получить path и сразу преобразовать его в строку, а потом делать поиск подстроки.
Но в моём ядре этих хуков не было, хуй знает почему, видимо дистрибутив слишком древний. Надо попробовать на новых, чем черт не шутит.
Итоги
Эта поделка как и предполагалась, погрузила меня в печаль, душевные страдания, НО стала отличным тренажером для прокачки:
- Внутренностей Linux ядра
- Работы с eBPF
- И кучу другого с kernel-space
eBPF — мощнейший инструмент, но очень тонкий. Ошибёшься — будешь выебан в жопу.
Информации по нему много, но вся она разбросана. Собрать всё это в кучу было отдельным квестом.
Мораль?
Иногда самое простое решение — это
chown -R. Но куда интереснее — написать свой велосипед и заглянуть в кроличью нору Linux ядра.🛠 #linux #debug #dev
—
✅ @bashdays ✅ @linuxfactory ✅ @blog