Содержание статьи
- Основные понятия и схема работы
- Шпаргалка по хукам
- Селекторы
- Установка и настройка
- Основные локации Tetragon
- Особенности для контейнеров
- Просмотр событий в реальном времени
- Политики трассировки
- Блокировка в политике
- Написание исключений
- Сетевая политика трассировки
- Файловая политика трассировки
- Кулстори на хостинге
- Выводы
Tetragon основан на eBPF — это технология, встроенная в ядро Linux, которая позволяет безопасно и эффективно запускать мини‑программы прямо в ядре. Благодаря этому eBPF-инструменты, как правило, быстрее и потребляют меньше ресурсов, чем классические решения вроде закостенелого auditd.
Тот, кто хотя бы раз настраивал auditd для SOC, наверняка сталкивался с самыми разными, в том числе безумными, конфигурациями — например, очень популярный конфиг от Neo23x0 насчитывает более 800 строк. Попытки преодолеть ограничения и адаптироваться под конкретный дистрибутив и архитектуру — та еще морока.
info
Сразу оговоримся, что Falco и иные инструменты и подходы мы не рассматриваем, — это тема отдельной статьи.
Tetragon универсален и работает на множестве платформ, написан на Go, а еще «из коробки» приспособлен к работе с контейнерами. Но информации по нему (а особенно тому, как использовать его эффективно) крайне мало.
Давай вместе разбираться, как можно играться с Tetragon. Бонусом в конце статьи — реальная история о том, почему Tetragon местами лютый вин.
Основные понятия и схема работы
Начать придется с теории, но мы обойдемся без душных деталей. Итак, внутри у Tetragon технология eBPF. Изначально она была разработана для Linux, но, кстати, и в Microsoft сейчас тоже пилят свою реализацию.
Ядро Linux — это программный слой между приложениями и оборудованием, на котором они работают. Приложения живут в непривилегированном слое, называемом пользовательским пространством, оно не может получить доступ к оборудованию напрямую. Вместо этого приложение делает запросы, используя интерфейс системных вызовов, так называемые syscall, чтобы попросить ядро действовать от его имени.
Этот доступ к оборудованию может включать чтение и запись в файлы, отправку и получение сетевого трафика или даже просто доступ к памяти. Ядро также отвечает за координацию параллельных процессов, позволяя многим приложениям работать одновременно.

eBPF, или Extended Berkeley Packet Filter, — фреймворк, позволяющий пользователям загружать и запускать программы в ядре операционной системы.
А еще BPF — это виртуальная машина внутри Linux, которая выполняет код BPF в режиме ядра. После загрузки программа eBPF прикрепляется к целевой функции ядра, чтобы наш код запускался всякий раз, когда происходит ее вызов. Считай, что это некая прослойка‑надзиратель.
Так как это все работает на привилегированном уровне, нужна серьезная безопасность. Поэтому механизм eBPF включает верификатор, который гарантирует, что программа eBPF загружается только в том случае, если ее запуск безопасен. Детали можно посмотреть в документации.
Есть готовые библиотеки для написания своей утилиты с применением eBPF, но лишь на небольшом числе языков. Среди них — C, Go, Rust и Python. Выбор языка усложняется еще и тем, что не для всех языков есть либы, реализующие поддержку libbpf (популярный вариант для обеспечения переносимости программ eBPF между разными версиями ядра). Но мы с тобой не кодеры и будем использовать готовое решение, разработчики которого уже обо всем позаботились.

Давай разберем основные функциональные компоненты системы.
Политики трассировки (tracing policy) — это для нас основной инструмент работы. Они позволяют настраивать как реакции для мониторинга (логирования), так и активные действия (блокирование доступа, убийство процесса и прочее) на основе заданных критериев. Политики задаются в файлах YAML и складируются в каталог /. В качестве критериев могут выступать названия сисколов, вызовы функций ядра, значения вызываемых аргументов. Можно добавлять кастомный текст, который добавится в алерт.
Чтобы составить собственную политику аудита, надо разбираться в принципах работы ядра Linux. Ключевые сущности, на которые можно навесить генерацию событий (или выполнение иных действий), следующие:
- LSM (Linux Security Modules) BPF, они же хуки безопасности SELinux/AppArmor. Для их использования необходимо выполнение ряда условий — модули должны быть загружены, включены и прочее. Названия функций можно взять из исходников Linux. Из преимуществ — возможность рассчитывать хеши для файлов и обогащать ими генерируемые события. Обычно в компаниях, где используются политики безопасности, с харденингом и так все неплохо, поэтому движемся дальше.
-
Kprobes (Kernel probes), или зонды ядра. По сути, дают возможность создания брейк‑пойнта перед вызовом инструкции ядра с выполнением произвольного кода. Например, можно заменить аргументы — кстати, не только входные. Но kprobes могут меняться между версиями ядра операционки, что сделает наши политики нестабильными. Чтобы вывести список доступных kprobes, выполни в терминале
cat /. В политиках Tetragon для системных вызовов (syscall) рекомендуется использовать обобщенное название вызова, например вместо значенийproc/ kallsyms do_sys_open,__x64_sys_openили__ia32_sys_openв политике можно указывать простоsys_open. Tetragon умный, сам разберется и адаптируется под архитектуру, на которой работает. - Uprobes. В целом аналогичны kprobes, только применяются к программам в пространстве пользователя. Здесь работу с uprobes мы рассматривать не будем, поскольку хуки ядра и так дают нам достаточно возможностей для тонкой настройки политик.
- Tracepoints. Точки трассировки статически прописаны в ядре. Именно они считаются наиболее удачным вариантом для отслеживания и прикрепления своего кода для отладки. Основное преимущество перед зондами — стабильность между релизами.
Чтобы узнать доступные трейспойнты в своей системе, выполни такую команду:
cat /sys/kernel/debug/tracing/available_events
Вывод будет в формате «категория:имя»; последнее — как раз то, за что может уцепиться eBPF.
В некоторых системах ряд точек недоступен, проверить их список можно так:
cat /sys/kernel/debug/krpobes/blacklist
Из минусов трейспойнтов — они есть далеко не для всех функций ядра, и, если нужного нет, придется смотреть в сторону kprobes.
К чему в итоге лучше хукаться и как посмотреть все позиции данного меню? При выборе между названием syscall, функции ядра, kprobe и tracepoint предпочтительнее ориентироваться на трейспойнты, поскольку они имеют более стабильный интерфейс, чем элементы ядра. Функции ядра могут периодически меняться, что в долгосрочной перспективе может привести к проблемам. Если совсем лень разбираться, можно просто грепать в выводе утилиты bpftrace: bpftrace — она покажет просто всё, за что можно зацепиться.
Мы в статье будем использовать микс на основе отдельных сисколов и функций ядра. Это обеспечит плавный переход от auditd (который сравнительно известен) к применению eBPF на основе kprobes и tracepoints.
Шпаргалка по хукам
Чтобы упростить себе и коллегам жизнь, мы составили таблицу по самым популярным или интересным активностям, которые обычно хотят видеть безопасники. В таблице мы свели имена отдельных сисколов, как их привыкли записывать в правилах auditd. Рядом указываем названия функций ядра, сопоставляемых с данными сисколов, — это уже можно пытаться использовать в eBPF-решениях, в том числе в Tetragon.
В качестве бонуса приводим некоторые kprobes и ближайшие tracepoints. Ты можешь приаттачиться почти к любой функции ядра — просто глянь, сколько их (в системе должен быть зарегистрирован kprobe):
cat /sys/kernel/debug/krpobes/list

За деталями о том, за что отвечает каждый сискол, рекомендуем обратиться к таблице. За названиями функций, вызывающими эти сисколы, тебе сюда — на примере архитектуры х64, есть неплохая сводка из проекта Tracee.
Как думаешь, как часто программы вызывают код ядра? Узнать это можно при помощи утилиты strace. Чтобы вывести системные вызовы (трассировку), которые делает приложение, напиши
strace ps
Чтобы просто посмотреть статистику по системным вызовам, можно использовать следующий сниппет:
for f in "cat /etc/passwd" ; do strace -c $f 2>&1 > /dev/null |
grep -v ':' | cut --complement -c 42-50 |
sed '1s/^% /%_/;2d' | head -n -2 ;done | sed -n '1p;/^%/d;p' | datamash -HW -sg5 sum 4 |
xargs printf "%-12s\t%14s\n"Вывод:
GroupBy(syscall) sum(calls)
access 1
arch_prctl 1
brk 3
close 20
execve 1
fadvise64 1
fstat 19
futex 1
getrandom 1
mmap 22
mprotect 3
munmap 2
openat 31
pread64 2
prlimit64 1
read 5
rseq 1
set_robust_list 1
set_tid_address 1
write 1
Важно понимать, что для ускорения работы разработчики Tetragon сделали несколько адаптаций для kprobes, которые можно посмотреть в исходном коде. Например, security_file_permission, который агрегирует в себе сразу несколько системных вызовов, поскольку эта функция ядра проверяет права на файл перед непосредственным обращением к нему.
www
Подробнее об изменениях можно прочитать в разделе документации Use Cases.
Что приятно, Tetragon сам адаптируется под архитектуру, на которой запущен. Для auditd приходилось писать длинные пассажи о том, какие сисколы нужно вызывать при работе на разных архитектурах. Здесь же можно просто задать политики трассировки и указать sys_ptrace, какой вызов будет зарезолвлен в одну из функций ядра (__x64_sys_ptrace/ или __x64_compat_sys_ptrace). За это отвечает параметр syscall в селекторе. Обычно он выставлен false, то есть отслеживаются обращения к функциям ядра. Если поставить true, будут анализироваться все сисколы (даже те, что не дойдут до выполнения функции ядра).
Встроенные в политику трассировки для отслеживания запуска и завершения процессов используют, например, sys_execve, отмеченный в таблице выше, — он соотносится с tracepoint sched_process_exec; со стороны LSM это kprobe security_bprm_committing_creds, завершение детектится через kprobe acct_process, а создание форка процесса — через kprobe wake_up_new_task.
Селекторы
Продолжим про политики. Мы вроде определились, что есть основные системные хуки, от которых можно плясать. А как же нам нафильтровывать мякотку?
В каждой политике можно указать до пяти селекторов, применяемых к каждому ивенту. Селекторы, как очевидно из названия, позволяют фильтровать только интересные нам события. Доступны следующие виды селекторов:
-
matchArgs— по значению аргументов; -
matchReturnArgs— по возвращаемым значениям; -
matchPIDs— по PID процесса; -
matchBinaries— по пути к исполняемому файлу; -
matchNamespaces— по пространствам имен (namespaces); -
matchCapabilities— по Linux capabilities (привилегиям); -
matchNamespaceChanges— по изменениям в пространстве имен; -
matchCapabilityChanges— по изменениям capabilities; -
matchActions— применить действие; -
matchReturnActions— применить действие к выходным событиям из селектора.
Обрати внимание, что, к сожалению, в значениях для работы селекторов нет возможности указывать вайлдкарту (звездочку). В ряде случаев SOC интересно отслеживать изменения в таких файлах, как /, что как раз было бы удобно делать по маске. Тут в качестве альтернативы можно юзать операторы Prefix и Postfix в селекторах.
В целом селекторы довольно несложная штука. Интересно понимать, какие действия (помимо логирования) можно делать в политике (мы приводим здесь не все):
- убить процесс;
- послать сигнал процессу;
- изменить вывод процесса;
- триггернуть URL;
- сделать DNS-резолв.
Последние два действия задумывались в том числе как способ оповещения наподобие Canarytokens, но никто тебе не мешает повесить и проактивную защиту от EDR... Если хочется посмотреть все возможные действия, то загляни в документацию.
В селекторах доступны следующие операторы, помимо привычных «равно», «не равно», «больше» и «меньше». Это указание порта или адреса источника и назначения — со сразу заложенной возможностью определения привилегированных портов. Иронично, но также для этих параметров есть и операторы вида «не порт назначения», «не системный порт» и так далее. Это, вообще говоря, очень полезные функции, только странно, что не завезли просто отдельного оператора «не». Есть оператор проверки битовой маски, сетевого протокола, семейства IP-адреса и состояния TCP-соединения (что может быть полезно при выявлении различных сканирований). Полный список — опять же в документации.
По основам бегло пробежались (мы старались быть лаконичными), приступаем к практическим упражнениям.
Установка и настройка
Ставить будем в Linux. Смотрим номер последнего релиза на GitHub, переходим в браузере. На момент написания статьи актуальная версия — v1.. Скачиваем и устанавливаем ее:
# Скачиваем архивcurl -LO https://github.com/cilium/tetragon/releases/download/v1.3.0/tetragon-v1.3.0-amd64.tar.gz
# Распаковываем в папку tetragon-v1.3.0-amd64tar -xvf tetragon-v1.3.0-amd64.tar.gz
# Переходим в нееcd tetragon-v1.3.0-amd64/
# Запускаем установкуsudo ./install.sh
Получив заветное Tetragon installed successfully!, убедимся, что он запущен и активен: sudo .
Основные локации Tetragon
После установки дальнейшая настройка параметров Tetragon проводится в директории /.
Отдельные параметры можно настраивать, просто создавая файлы: называешь файл так же, как параметр, а внутри сохраняешь значение.
В первую очередь, раз мы собираемся заниматься логированием, укажем путь к файлу, куда писать лог. Папка логов — /:
echo "/var/log/tetragon/tetragon.log" > /etc/tetragon/tetragon.conf.d/export-file
systemctl restart tetragon
В таблице — дефолтные настройки по логам (держим не более пяти файлов размером не более 10 Мбайт):
-
--export-file-max-backups— количество ротируемых JSON-файлов — 5; -
--export-file-max-size-mb— размер файлов в мегабайтах — 10.
Чтобы не забить логами диск, регулируй значения выше в конфиге.
Заглянем в лог, чтобы познакомиться с ним:
less /var/log/tetragon/tetragon.log
Если ты морально не готов отказаться от syscall-событий auditd, где лампово указываются пользовательские и групповые ID (UID, GID, EUID, SUID и прочие), ты всегда можешь их добавить к событиям Tetragon.
Сравни обогащенное событие auditd и Tetragon о запуске ps. Вот версия auditd:
type=SYSCALL msg=audit(1663937111.702:73702361): arch=c000003e syscall=59 success=yes exit=0 a0=7f90824cfbf8 a1=7f907a020608 a2=7f907d9de920 a3=7f90742fcb10 items=2 ppid=1736 pid=3202324 auid=4294967295 uid=996 gid=997 euid=996 suid=996 fsuid=996 egid=997 sgid=997 fsgid=997 tty=(none) ses=4294967295 comm="ps" exe="/usr/bin/ps" key="execve_example" ARCH=x86_64 SYSCALL=execve AUID="unset" UID="git" GID="git" EUID="git" SUID="git" FSUID="git" EGID="git" SGID="git" FSGID="git"
А вот Tetragon с дефолтной политикой трассировки:
{ "process_exec": { "process": { "exec_id": "UEFETUlOOjIwNzk2MzM1MzM0MTA4OjI2NDM4", "pid": 26438, "uid": 0, "cwd": "/root", "binary": "/usr/bin/ps", "flags": "execve clone", "start_time": "2025-03-15T09:16:44.617411202Z", "auid": 4294967295, "parent_exec_id": "UEFETUlOOjE1NzI2NzE4NDk4MzUyOjIxNzA0", "tid": 26438, "in_init_tree": false }, "parent": { "exec_id": "UEFETUlOOjE1NzI2NzE4NDk4MzUyOjIxNzA0", "pid": 21704, "uid": 0, "cwd": "/root", "binary": "/bin/bash", "flags": "execve clone", "start_time": "2025-03-13T18:06:21.898754560Z", "auid": 4294967295, "parent_exec_id": "UEFETUlOOjE1NzI2NzA5NjQ4MzE2OjIxNzAy", "tid": 21704, "in_init_tree": false } }, "node_name": "PADMIN", "time": "2025-03-15T09:16:44.617410306Z"}{ "process_exit": { "process": { "exec_id": "UEFETUlOOjIwNzk2MzM1MzM0MTA4OjI2NDM4", "pid": 26438, "uid": 0, "cwd": "/root", "binary": "/usr/bin/ps", "flags": "execve clone", "start_time": "2025-03-15T09:16:44.617411202Z", "auid": 4294967295, "parent_exec_id": "UEFETUlOOjE1NzI2NzE4NDk4MzUyOjIxNzA0", "tid": 26438, "in_init_tree": false }, "parent": { "exec_id": "UEFETUlOOjE1NzI2NzE4NDk4MzUyOjIxNzA0", "pid": 21704, "uid": 0, "cwd": "/root", "binary": "/bin/bash", "flags": "execve clone", "start_time": "2025-03-13T18:06:21.898754560Z", "auid": 4294967295, "parent_exec_id": "UEFETUlOOjE1NzI2NzA5NjQ4MzE2OjIxNzAy", "tid": 21704, "in_init_tree": false }, "time": "2025-03-15T09:16:44.636431137Z" }, "node_name": "PADMIN", "time": "2025-03-15T09:16:44.636429903Z"}
info
Логи Tetragon удобно смотреть командой tetra .
Теперь добавим обогащение capabilities:
echo "true" > /etc/tetragon/tetragon.conf.d/enable-process-cred
systemctl restart tetragon
ps
tail -f /var/log/tetragon/tetragon.log
Вывод довольно громоздкий, на любителя:
{ "process_exec": { "process": { "exec_id": "UEFETUlOOjIxMDA3MzE3NjA4MjU5OjI2NjU2", "pid": 26656, "uid": 0, "cwd": "/root", "binary": "/usr/bin/ps", "flags": "execve", "start_time": "2025-03-15T09:20:15.599685868Z", "auid": 4294967295, "parent_exec_id": "UEFETUlOOjA6MjE3MDQ=", "cap": { "permitted": [ "CAP_CHOWN", "DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", "CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN", "CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ", "CAP_PERFMON", "CAP_BPF", "CAP_CHECKPOINT_RESTORE" ], "effective": [ "CAP_CHOWN", "DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", "CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN", "CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ", "CAP_PERFMON", "CAP_BPF", "CAP_CHECKPOINT_RESTORE" ] }, "tid": 26656, "process_credentials": { "uid": 0, "gid": 0, "euid": 0, "egid": 0, "suid": 0, "sgid": 0, "fsuid": 0, "fsgid": 0 }, "in_init_tree": false } }, "node_name": "PADMIN", "time": "2025-03-15T09:20:15.599686113Z"}Особенности для контейнеров
В Tetragon не нужны какие‑то особые действия для ловли событий внутри контейнеров, подов и так далее.
Ниже — пример лога после запуска команды whoami внутри контейнера Docker:
{"process_exec":{"process":{"exec_id":"eGFrZXA6MjE0MjM0Nzk1NTg5MTgwNDI6MzYzNTE0NA==", "pid":3635144, "uid":0, "cwd":"/", "binary":"/usr/bin/whoami", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:22:46.921009521Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "tid":3635144, "in_init_tree":true}, "parent":{"exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "pid":3631521, "uid":0, "cwd":"/", "binary":"/bin/bash", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:21:42.234432165Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ1NDk0ODU4NDg6MzYzMTUw", "tid":3631521, "in_init_tree":true}}, "node_name":"fi", "time":"2025-03-10T08:22:46.921008920Z"}{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjE0MjM0Nzk1NTg5MTgwNDI6MzYzNTE0NA==", "pid":3635144, "uid":0, "cwd":"/", "binary":"/usr/bin/whoami", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:22:46.921009521Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "refcnt":1, "tid":3635144, "in_init_tree":true}, "parent":{"exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "pid":3631521, "uid":0, "cwd":"/", "binary":"/bin/bash", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:21:42.234432165Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ1NDk0ODU4NDg6MzYzMTUw", "tid":3631521, "in_init_tree":true}, "function_name":"security_file_permission", "args":[{"file_arg":{"path":"/etc/passwd", "permission":"-rw-r--r--"}}, {"int_arg":4}], "return":{"int_arg":0}, "action":"KPROBE_ACTION_POST", "policy_name":"file-change-read-monitoring-filtered-fullpath", "return_action":"KPROBE_ACTION_POST"}, "node_name":"fi", "time":"2025-03-10T08:22:46.922387741Z"}{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjE0MjM0Nzk1NTg5MTgwNDI6MzYzNTE0NA==", "pid":3635144, "uid":0, "cwd":"/", "binary":"/usr/bin/whoami", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:22:46.921009521Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "refcnt":1, "tid":3635144, "in_init_tree":true}, "parent":{"exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "pid":3631521, "uid":0, "cwd":"/", "binary":"/bin/bash", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:21:42.234432165Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ1NDk0ODU4NDg6MzYzMTUw", "tid":3631521, "in_init_tree":true}, "function_name":"security_file_permission", "args":[{"file_arg":{"path":"/etc/passwd", "permission":"-rw-r--r--"}}, {"int_arg":4}], "return":{"int_arg":0}, "action":"KPROBE_ACTION_POST", "policy_name":"file-change-read-monitoring-filtered-fullpath", "return_action":"KPROBE_ACTION_POST"}, "node_name":"fi", "time":"2025-03-10T08:22:46.922425253Z"}{"process_exit":{"process":{"exec_id":"eGFrZXA6MjE0MjM0Nzk1NTg5MTgwNDI6MzYzNTE0NA==", "pid":3635144, "uid":0, "cwd":"/", "binary":"/usr/bin/whoami", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:22:46.921009521Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "tid":3635144, "in_init_tree":true}, "parent":{"exec_id":"eGFrZXA6MjE0MjM0MTQ4NzIzMzkzNjg6MzYzMTUyMQ==", "pid":3631521, "uid":0, "cwd":"/", "binary":"/bin/bash", "flags":"execve rootcwd clone inInitTree", "start_time":"2025-03-10T08:21:42.234432165Z", "auid":4294967295, "docker":"d7a3f91a19404549b4422f5cdd4975a", "parent_exec_id":"eGFrZXA6MjE0MjM0MTQ1NDk0ODU4NDg6MzYzMTUw", "tid":3631521, "in_init_tree":true}, "time":"2025-03-10T08:22:46.922689961Z"}, "node_name":"fi", "time":"2025-03-10T08:22:46.922688945Z"}Здесь
-
"in_init_tree":— запуск произошел изнутри контейнера;true -
"docker":— идентификатор контейнера."d7a... "
Если внимательней посмотреть на вывод, можно заметить, что при выполнении команды whoami на уровне системы происходит чтение файла / (при наличии политики трассировки с функцией "function_name":), с таким выводом данных можно начать лучше понимать принципы работы ОС.
Просмотр событий в реальном времени
Есть небольшие полезности, которые экономят время. Чем лезть в сиемку и тейлить файлы с логами, куда легче набрать простую команду. Посмотреть активность в реальном времени и в компактном формате на хосте xakep можно так:
tetra getevents -o compact
Увидим следующий вывод (здесь ракета — старт, а взрыв — завершение процессов).
🚀 process xakep /usr/sbin/sshd -D -R
💥 exit xakep /usr/bin/sleep 0.125 0
🚀 process xakep /usr/bin/cat /sys/class/net/veth1752502i0/statistics/rx_packets
💥 exit xakep /usr/bin/cat /sys/class/net/veth1752502i0/statistics/rx_packets 0
🚀 process xakep /usr/bin/expr 28554688 - 28554688
💥 exit xakep /usr/bin/expr 28554688 - 28554688 1
🚀 process xakep /usr/sbin/iptables -L -vn
Политики трассировки
Дальше мы посмотрим, как политики трассировки могут выступить в роли инструмента точечного логирования, и покажем, как использовать действие в качестве гибко настраиваемой превентивной меры.
Папка политик трассировки — /. Сами политики мы подробно рассмотрим далее.
warning
После добавления или изменения политик необходимо перезапускать службу командой systemctl !
Если служба не стартует (или не в состоянии active больше десяти секунд), то с вероятностью, близкой к 100%, косяк — в синтаксисе политики (очень часто YAML портят пустые или служебные символы в конце файла).
Чтобы прочитать журнал и убедиться, что все работает как надо, выполняем следующие команды:
journalctl -xeu tetragon.service
# Или так — иногда помогаетsystemctl status tetragon
Если накосячил в одной из политик, увидишь ошибку.
Failed to start tetragon error="policy handler 'tracing' failed loading policy 'tcp-only-external-addrs': validation failed"
Без дополнительно созданных политик в Tetragon логируются события создания (process_exec) и завершения процесса (process_exit). Глянем логи этих событий.
Пример завершения чтения файла / командой cat:
{ "process_exit": { "process": { "exec_id": "eGFrZXA6MTM4MjkyMzk2NTkyNjM2Mjc6Mjk2NDM5", "pid": 296439, "uid": 0, "cwd": "/root/tetragon/tetragon-v1.2.0-amd64", "binary": "/usr/bin/cat", "arguments": "/etc/passwd", "flags": "execve clone", "start_time": "2024-11-05T11:52:47.775421543Z", "auid": 0, "parent_exec_id": "eGFrZXA6MTM4MTIyMDg0MjAwMDAwMDA6MjcwMTI=", "tid": 296439, "user": { "name": "root" } }, "parent": { "exec_id": "eGFrZXA6MTM4MTIyMDg0MjAwMDAwMDA6MjcwMTI=", "pid": 27012, "uid": 0, "binary": "/usr/bin/bash", "arguments": " " /root/tetragon/tetragon-v1.2.0-amd64"", "flags": "procFS auidnocwd", "start_time": "2024-11-05T07:08:56.536154067Z", "auid": 0, "parent_exec_id": "eGFrZXA6MTM4MTIyMDg0MjAwMDAwMDA6MjcwMTI=", "tid": 27012, "user": { "name": "root" } }, "time": "2024-11-05T11:52:47.778558683Z" }, "node_name": "xakep", "time": "2024-11-05T11:52:47.778555561Z"}Несмотря на определенную избыточность, сразу видно, что юзер root в оболочке bash открывал целевой файл. Приятно, что есть инфа о родительском процессе, — это упрощает расследование инцидентов.
В документации описаны и другие политики с указанием релевантных сценариев безопасности. Например, благодаря мониторингу создания и завершения процессов можно отслеживать выполнение исполняемых файлов в каталоге /, выполнение команд через sudo, использование бита SUID. Настоятельно рекомендуем ознакомиться с этим разделом справки!
Блокировка в политике
Простым мониторингом дело не ограничивается. Можно блокировать что‑то согласно политике (пример — в репозитории Tetragon). Особое внимание обрати на часть после селектора, в конце:
matchActions:- action: Sigkill
На скриншоте выше видно, что были убиты процессы netcat и curl из‑за домена ya. и порта 443 (HTTPS).
Вот что в логах:
{"process_exec":{"process":{"exec_id":"eGFrZXA6MjUzMTMwMDY4NTc3NTUzNTY6MzIzOTE0MA==", "pid":3239140, "uid":0, "cwd":"/root", "binary":"/usr/bin/curl", "arguments":"https://ya.ru", "flags":"execve clone", "start_time":"2025-03-18T09:51:54.520338864Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3239140, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "pid":3195687, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-06T11:34:35.302582913Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3195687, "user":{"name":"root"}, "in_init_tree":false}}, "node_name":"xakep", "time":"2025-03-18T09:51:54.520338132Z"}{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjUzMTMwMDY4NTc3NTUzNTY6MzIzOTE0MA==", "pid":3239140, "uid":0, "cwd":"/root", "binary":"/usr/bin/curl", "arguments":"https://ya.ru", "flags":"execve clone", "start_time":"2025-03-18T09:51:54.520338864Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "refcnt":1, "tid":3239140, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "pid":3195687, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-06T11:34:35.302582913Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3195687, "user":{"name":"root"}, "in_init_tree":false}, "function_name":"tcp_connect", "args":[{"sock_arg":{"family":"AF_INET", "type":"1536", "protocol":"IPPROTO_TCP", "saddr":"10.68.85.126", "daddr":"5.255.255.242", "sport":41422, "dport":443, "cookie":"18446615535296672256", "state":"TCP_SYN_SENT"}}], "action":"KPROBE_ACTION_POST", "policy_name":"tcp-only-external-addrs", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-18T09:51:54.540689266Z"}{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjUzMTMwMDY4NTc3NTUzNTY6MzIzOTE0MA==", "pid":3239140, "uid":0, "cwd":"/root", "binary":"/usr/bin/curl", "arguments":"https://ya.ru", "flags":"execve clone", "start_time":"2025-03-18T09:51:54.520338864Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "refcnt":1, "tid":3239140, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "pid":3195687, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-06T11:34:35.302582913Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3195687, "user":{"name":"root"}, "in_init_tree":false}, "function_name":"tcp_sendmsg", "args":[{"sock_arg":{"family":"AF_INET", "type":"1536", "protocol":"IPPROTO_TCP", "saddr":"10.68.85.126", "daddr":"5.255.255.242", "sport":41422, "dport":443, "cookie":"18446615535296672256", "state":"TCP_ESTABLISHED"}}, {"int_arg":517}], "action":"KPROBE_ACTION_SIGKILL", "policy_name":"tcp-only-external-addrs", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-18T09:51:54.580731247Z"}{"process_exit":{"process":{"exec_id":"eGFrZXA6MjUzMTMwMDY4NTc3NTUzNTY6MzIzOTE0MA==", "pid":3239140, "uid":0, "cwd":"/root", "binary":"/usr/bin/curl", "arguments":"https://ya.ru", "flags":"execve clone", "start_time":"2025-03-18T09:51:54.520338864Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3239140, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "pid":3195687, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-06T11:34:35.302582913Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyODIzNjc2NDAwMDAwMDA6MzE5NTY4Nw==", "tid":3195687, "user":{"name":"root"}, "in_init_tree":false}, "signal":"SIGKILL", "time":"2025-03-18T09:51:54.581941252Z"}, "node_name":"xakep", "time":"2025-03-18T09:51:54.581940387Z"}Обрати внимание на поле "action": и последующее завершение процесса в "process_exit".... По наименованию "policy_name": можно понять, какая политика сработала.
Написание исключений
Иногда отдельные скрипты или процессы, например kumarez., могут генерировать повторяющиеся логи. В таких случаях в Tetragon можно настроить правило исключения, чтобы избежать избыточного логирования.
В папке / создаем файл export-denylist со следующим содержимым:
{ "event_set": [ "PROCESS_EXEC", "PROCESS_EXIT" ], "binary_regex": [ "^/bin/bash$", "^/usr/bin/bash$" ], "arguments_regex": [ "/usr/share/user/scripts/kumarez.sh" ]}После правок не забудь перезапустить службу Tetragon. Подобным образом можно исключить родительские процессы (используя parent_binary или parent_arguments). Это помогает уменьшить шум в журналах.
Еще один полезный сниппет для политик — выявление неудачной попытки обращения к объекту (то есть когда код возврата отличается от 0):
- matchReturnArgs: - index: 0 operator: "NotEqual" values: - 0Сетевая политика трассировки
Мы предпочитаем разбираться с полезными функциями сразу на практике, поэтому разберем структуру простого правила и его работу.
Давай посмотрим, как можно написать правило для выявления DNS-запросов. Вот все, что нам известно про DNS: будет происходить сетевое взаимодействие через 53-й порт по протоколу UDP; соединение — исходящее. Отлично! Для начала нам надо найти, какая функция, сискол или kprobe в Linux отвечает за сетевые соединения.
Общий принцип: лучше всего найти именно конечную функцию, выполняющую действие, которое мы хотим замониторить. Погрепав по сисколам (причем мы просто поискали это слово в исходниках Tetragon), упоминания UDP с ходу не нашли. Окей, обратимся к трейспойнтам:
cat /sys/kernel/debug/tracing/available_events | grep udp
Но вывод тоже жидковат — какие‑то sunrpc. Что ж, остаются пробы:
cat /proc/kallsyms | grep udp | grep send
Вывод небольшой, видим в нем функцию udp_sendmsg — она‑то нам и нужна.
Следующее, с чем надо определиться, — это аргументы функции, которые мы хотим мониторить. Можно грепать репозитории Linux, можно искать в Google. Видим, что первым аргументом идет sock — то есть IP-адрес и порт назначения.
Мы знаем, что порт у нас 53-й, поэтому все необходимое для составления политики у нас есть. Не забываем, что в имени политики недопустимы нижние подчеркивания, и соблюдаем синтаксис YAML:
apiVersion: cilium.io/v1alpha1kind: TracingPolicymetadata: name: "dns-udp-tracking"spec: kprobes: - call: "udp_sendmsg" syscall: false args: - index: 0 type: "sock" selectors: - matchArgs: - index: 0 operator: "DPort" values: - "53"Сохраняем политику вот по такому пути:
/etc/tetragon/tetragon.tp.d/dns_udp.yaml
Перезапускаем службу:
systemctl restart tetragon
И открываем просмотр событий:
tetra getevents
В соседнем терминале делаем DNS-запрос:
nslookup ya.ru
И видим, как сработала наша политика:
{"process_kprobe":{"process":{"exec_id":"UEFETUlOOjQ1NjAwMDAwMDA6MQ==", "pid":1, "uid":0, "cwd":"/", "binary":"/usr/lib/systemd/systemd", "flags":"procFS auid rootcwd", "start_time":"2025-03-18T14:10:14.439326884Z", "auid":4294967295, "parent_exec_id":"UEFETUlOOjE6MA==", "refcnt":36, "tid":598, "in_init_tree":false}, "parent":{"exec_id":"UEFETUlOOjE6MA==", "pid":0, "uid":0, "binary":"<kernel>", "flags":"procFS", "start_time":"2025-03-18T14:10:09.879327579Z", "auid":4294967295, "parent_exec_id":"UEFETUlOOjE6MA==", "tid":0, "in_init_tree":false}, "function_name":"udp_sendmsg", "args":[{"sock_arg":{"family":"AF_INET", "type":"SOCK_DGRAM", "protocol":"IPPROTO_UDP", "saddr":"192.168.135.3", "daddr":"8.8.8.8", "sport":34383, "dport":53, "cookie":"18446637872524733696", "state":"TCP_ESTABLISHED"}}], "action":"KPROBE_ACTION_POST", "policy_name":"dns-udp-tracking", "return_action":"KPROBE_ACTION_POST"}, "node_name":"PADMIN", "time":"2025-03-18T17:06:42.116633601Z"}Из минусов — поскольку это уровень ядра (ниже некуда), то резолва IP-адресов тут не будет. Из плюсов — ты дополнительно видишь, с какими полями события можно работать, например добавить в политику селектор по daddr, чтобы выбирать обращения не к твоему любимому DNS-серверу (просто добавь в конец политики).
- index: 0 operator: "NotDAddr" values: - "8.8.8.8"Если тебе нужно также учесть вариант с TCP, то добавь аналогичное правило, только укажи call: .
Бывает, что тебе сразу известен syscall. В Tetragon можно без труда найти описание аргументов разных сисколов с указанием их типа, например для socket:
"socket": [{ "Name": "family", "Type": "int"},{ "Name": "type", "Type": "int"},{ "Name": "protocol", "Type": "int"}]Однако видим, что открытие сокета не дает нам информации, например, о порте соединения, то есть для использования в чистом виде он не пойдет.
За основу взяты трассировки из примеров tracingpolicy и quickstart.
Можно немного усложнить правило и описать трассировку TCP- и UDP-соединений, при которых происходит обращение на внешние IP-адреса. Готовое правило смотри на нашем GitHub. В правиле используются системные вызовы tcp_connect, tcp_close, tcp_sendmsg и udp_sendmsg.
Вот пример записи из журнала после выполнения команды curl :
{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjQxOTA0NjIyMTQ0MDg3NjA6MzE5MTQyNA==", "pid":3191424, "uid":0, "cwd":"/root", "binary":"/usr/bin/curl", "arguments":"https://ya.ru", "flags":"execve clone", "start_time":"2025-03-05T10:02:49.876992195Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQxODk4MDc0ODAwMDAwMDA6MzE5MDcyOA==", "refcnt":1, "tid":3191424, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQxODk4MDc0ODAwMDAwMDA6MzE5MDcyOA==", "pid":3190728, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-05T09:51:55.142582919Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQxODk4MDc0MzAwMDAwMDA6MzE5MDcyNw==", "tid":3190728, "user":{"name":"root"}, "in_init_tree":false}, "function_name":"tcp_connect", "args":[{"sock_arg":{"family":"AF_INET", "type":"1536", "protocol":"IPPROTO_TCP", "saddr":"10.68.85.126", "daddr":"5.255.255.242", "sport":33396, "dport":443, "cookie":"18446615511771815040", "state":"TCP_SYN_SENT"}}], "action":"KPROBE_ACTION_POST", "policy_name":"net-only-external-addrs", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-05T10:02:49.889349017Z"}Пример блокировки обращения по UDP на DNS-сервер Google с помощью netcat:
{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjUzOTk5NzE5MTc1NjI1NDQ6MzI0MzY5Mw==", "pid":3243693, "uid":0, "cwd":"/root", "binary":"/usr/bin/nc", "arguments":"-zvw3 -u 8.8.8.8 53", "flags":"execve clone", "start_time":"2025-03-19T10:01:19.580149215Z", "auid":0, "parent_exec_id":"eGFrZXA6MjUzOTk5NzE5MTc1NjI1NDQ6MzI0MzY5Mw==", "refcnt":1, "tid":3243693, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjUzOTk5NzE5MTc1NjI1NDQ6MzI0MzY5Mw==", "pid":3195474, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root"", "flags":"procFS auid nocwd", "start_time":"2025-03-06T10:16:07.862583157Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQyNzc2NjAxNDAwMDAwMDA6MzE5NTQ3Mg==", "tid":3195474, "user":{"name":"root"}, "in_init_tree":false}, "function_name":"udp_sendmsg", "args":[{"sock_arg":{"family":"AF_INET", "type":"4352", "protocol":"IPPROTO_UDP", "saddr":"10.68.85.126", "daddr":"8.8.8.8", "sport":55088, "dport":53, "cookie":"18446615534975814144", "state":"TCP_ESTABLISHED"}}, {"int_arg":1}], "action":"KPROBE_ACTION_SIGKILL", "policy_name":"net-only-external-addrs", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-19T10:01:19.598368812Z"}Файловая политика трассировки
Дальше разберем два других правила, которые выявляют доступ к критически важным файлам в Linux, любое чтение или изменение в которых может представлять интерес с точки зрения информационной безопасности. Оба правила используют вызов ядра security_file_permission.
Первое правило — на основе оператора Postfix, то есть окончания пути (некий аналог endswith из Python и JS). Например, у каждого пользователя в Linux может быть свой файл . (/ или /), куда злодей может, например, попытаться вписать свой alias для команды.
Второе правило основано на операторе Equal и содержит полные пути до критических файлов в Linux.
Вот пример записи в журнале об изменении файла / редактором Vi:
{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "pid":3179416, "uid":0, "cwd":"/root/test", "binary":"/usr/bin/vi", "arguments":"/etc/passwd", "flags":"execve clone", "start_time":"2025-03-03T12:22:40.360929717Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "refcnt":1, "tid":3179416, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "pid":3163368, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root/test"", "flags":"procFS auid nocwd", "start_time":"2025-02-28T07:30:40.952582904Z", "auid":0, "parent_exec_id":"eGFrZXA6MjM3NDkzMzMyMzAwMDAwMDA6MzE2MzM2Ng==", "tid":3163368, "user":{"name":"root"}, "in_init_tree":false}, function_name":"security_file_permission", "args":[{"file_arg":{"path":"/etc/passwd", "permission":"-rw-r--r--"}}, {"int_arg":2}], "return":{"int_arg":0}, "action":"KPROBE_ACTION_POST", "policy_name":"file-monitoring-filtered", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-03T12:22:49.454084205Z"}Достойно уважения, что в событии приводятся разрешения, установленные целевому файлу.
{"process_exit":{"process":{"exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "pid":3179416, "uid":0, "cwd":"/root/test", "binary":"/usr/bin/vi", "arguments":"/etc/passwd", "flags":"execve clone", "start_time":"2025-03-03T12:22:40.360929717Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "tid":3179416, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "pid":3163368, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root/test"", "flags":"procFS auid nocwd", "start_time":"2025-02-28T07:30:40.952582904Z", "auid":0, "parent_exec_id":"eGFrZXA6MjM3NDkzMzMyMzAwMDAwMDA6MzE2MzM2Ng==", "tid":3163368, "user":{"name":"root"}, "in_init_tree":false}, "time":"2025-03-03T12:22:49.455738131Z"}, "node_name":"xakep", "time":"2025-03-03T12:22:49.455737021Z"}Если при редактировании файла не сохранить изменение, будет отсутствовать вызов security_file_permission и залогируется просто исполнение команды в process_exec.
Для закрепления — еще пример правила: оно будет срабатывать при изменении конфигов критических служб, при создании файлов (системный вызов fd_install) в папках с задачами, запускаемыми по расписанию, и в других сценариях, которые хакеры используют для закрепления.
Пример лога, который можно наблюдать при создании файла some3. командой touch в папке /, куда потенциально может быть внедрен скрипт автозапуска:
{"process_kprobe":{"process":{"exec_id":"eGFrZXA6MjQwMjM1OTkyNDY1MjgzOTQ6MzE3Nzc0OA==", "pid":3177748, "uid":0, "cwd":"/root/test", "binary":"/usr/bin/touch", "arguments":"/etc/profile.d/some3.txt", "flags":"execve clone", "start_time":"2025-03-03T11:41:46.909112495Z", "auid":0, "parent_exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "refcnt":1, "tid":3177748, "user":{"name":"root"}, "in_init_tree":false}, "parent":{"exec_id":"eGFrZXA6MjQwMjYwNTI2OTgzNDM0ODc6MzE3OTQxNg==", "pid":3163368, "uid":0, "binary":"/usr/bin/bash", "arguments":" " /root/test"", "flags":"procFS auid nocwd", "start_time":"2025-02-28T07:30:40.952584497Z", "auid":0, "parent_exec_id":"eGFrZXA6MjM3NDkzMzMyMzAwMDAwMDA6MzE2MzM2Ng==", "tid":3163368, "user":{"name":"root"}, "in_init_tree":false}, "function_name":"fd_install", "args":[{"int_arg":3}, {"file_arg":{"path":"/etc/profile.d/some3.txt", "permission":"-rw-r--r--"}}], "action":"KPROBE_ACTION_POST", "policy_name":"file-create-open-dirs", "return_action":"KPROBE_ACTION_POST"}, "node_name":"xakep", "time":"2025-03-03T11:41:46.912969231Z"}Кулстори на хостинге
Напоследок расскажем про прикол с eBPF на одном из зарубежных хостингов.
Как‑то раз мы арендовали виртуалку, а именно LXC-контейнер в славной Германии. Запуск auditd на этой машине был запрещен политиками AppArmor. Ниже — переписка с технической поддержкой хостинга.


Из‑за «секурити ризон» не хотят сделать исключение для благородных донов... Что ж, мы ИБшники, нам надо мониторить свою машинку. И тут мы, конечно, вспомнили про eBPF! Поставили Tetragon, направили журналы в SIEM, в нашем случае это была KUMA (Kaspersky Unified Monitoring and Analysis Platform), и заметили, что видим события не только с нашей машины. Например, продление сертификата Let’s Encrypt какого‑то сайта...

Запуски проверок виртуальных машин на хостовой ОС.

Сетевые соединения по разным LXC-контейнерам...

На последнем скриншоте видно, что фиксируются соединения от источников, входящих в черные списки, — в нашем случае это blocklist. (забавно, что немецкий хостер не блокирует трафик, помеченный немецкими же черными списками). Репутация IP-адресов определяется с помощью обогащения данных через опенсорсную платформу индикаторов угроз MISP.
В чем прикол? Если виртуальная машина — LXC-контейнер, то все ядро общее с хостовой машиной и мы видим, что происходит на хостовой ОС!

Выводы
Основной прикол отслеживания системных вызовов в том, что ты можешь читать файл разными утилитами, но системный вызов один. Это как в матрице ATT&CK: если знаешь ТТР, то тебе уже не так важны хеши, IP и другие индикаторы.
Tetragon отлично подойдет для низкоуровневого отслеживания активности в Linux. Но, несмотря на простоту установки, у этого инструмента высокий порог вхождения — все‑таки матчасть Linux знать нужно. Мы верим, что за eBPF-инструментами в продуктах безопасности — будущее, однако победит наиболее дружелюбный и гибкий инструмент, который позволит реализовать все влажные фантазии SOC. Относить ли к ним Tetragon — это вопрос индивидуальный.
Пока что решения на основе eBPF — это обычно части более комплексных решений. Однако с развитием технологии обязательно появятся инструменты для дружелюбного создания политик трассировки. Появятся и эксперты, готовые делиться опытом по части ядра Linux и безопасности, сформируются профильные комьюнити.
Собственно, эта статья и есть первый призыв совершенствовать мониторинг безопасности с применением основанных на eBPF инструментов. На нашем GitHub мы разместили набор правил, в который вошли как коробочные примеры, так и конфиги из этой статьи.
Призываем присоединяться и исследовать увлекательный мир eBPF вместе с нами — добавлять новые критические файлы для мониторинга, политики трассировки, исключения, реализовывать интересные сценарии мониторинга или просто готовить сниппеты. Там же мы разместили коллекцию интересных ссылок на различные материалы по Tetragon и ePBF. Присоединяйся!
www
Другие интересные проекты по eBPF, правила и прочие ссылки:
- Гайд по eBPF (PDF)
- Лаба по Tetragon (нужна учетка на GitHub)
- Falco
- Pulsar
- Tracee
- Kunai
