Light mode

Сбор событий Linux: есть цель — ищем путь

  • #Linux
  • #MaxPatrol SIEM

О чем материал

Изучаем внутреннее устройство компонентов Linux, участвующих в маршрутизации системных журналов

Я периодически сталкиваюсь с вопросами клиентов и коллег из смежных отделов о том, как можно оптимизировать работу разных компонентов журналирования внутри Linux. В основном они касаются предлагаемых нами настроек доставки журналов в MaxPatrol SIEM и того, как их можно модифицировать с оглядкой на особенности конкретной инфраструктуры. Поэтому я составил схему различных вариантов взаимодействия компонентов журналирования Linux между собой для быстрого поиска ответов. 

В этой статье я не затрону всех тонкостей, известных лишь узкому кругу специалистов, а кратко опишу каждый блок схемы и тем самым постараюсь ответить на большинство возникающих вопросов. Каждый раздел будет посвящен отдельной группе компонентов, принимающих участие в транспортировке журналов от источника к точке назначения.

Общая схема

На рис. 1 приведена полная схема взаимодействия компонентов журналирования в Linux. 

Рисунок 1. Схема взаимодействия компонентов журналирования в Linux

Некоторые участки схемы представляют из себя маршрут доставки событий в MaxPatrol SIEM, настройку которого мы описываем в нашей документации по настройке источников. 

Что ж, приступим к разбору схемы. Каждый следующий раздел будет описывать отдельный «регион» этой большой «карты мира». 

Компоненты аудита Linux

Скажу прямо, мы солидарны со всеми коллегами из нашей отрасли: встроенный аудит Linux далек от идеала. Но пока мы только ищем грамотную альтернативу, которая была бы сбалансирована по нашим требованиям к функциональности и выглядела надежной, — работаем с тем, что имеем. Все потому, что системные события важны для понимания происходящего на узле. А найти решение, идеально подходящее и нам и всем клиентам, пока что видится довольно внушительной задачей.

Подробнее о содержимом событий аудита, архитектуре и взаимодействии компонентов подсистемы читайте здесь.

Рисунок 2. Компоненты аудита Linux

Подсистема аудита 

Это полноценный компонент ядра Linux, способный регистрировать все выполняющиеся системные вызовы. Со стороны пользовательского пространства общение с ним происходит через специально выделенный netlink-сокет. Используя официальные утилиты в пакете auditd, администратор узла может включать и отключать подсистему, устанавливать для нее правила мониторинга, анализировать статистику и т. п. Различные сторонние утилиты могут использовать API из библиотеки libaudit для формирования событий в формате auditd, которые затем попадают в общий журнал.

Служба аудита 

Это компонент на стороне пользовательского пространства, который получает события со стороны ядра. Рассмотрим несколько файлов его конфигурации:

  1. /etc/audit/auditd.conf — содержит системные параметры службы.

Версии auditd ниже 3.0:

local_events = yes

write_logs = yes

log_file = /var/log/audit/audit.log


log_format = ENRICHED


dispatcher = /sbin/audispd


# ...

Версии auditd, начиная с 3.0:

local_events = yes

write_logs = yes

log_file = /var/log/audit/audit.log

log_format = ENRICHED

plugin_dir = /etc/audit/plugins.d

# ...

Полный список доступных директив можно посмотреть на man-странице auditd.conf(5). В контексте транспортировки журналов нас интересуют только некоторые из них:

  • local_events. Необходимо убедиться, что выставлено в yes, чтобы события для текущего узла в принципе генерировались.
  • write_logs. Можно установить в yes, чтобы события сохранялись в локальный файл журнала. Мы же рекомендуем выставлять ее в no, так как с предлагаемыми нами параметрами журналы будут сохраняться сразу в общий системный журнал.
  • log_file. Полный путь к локальному файлу журнала.
  • dispatcher/plugin_dir. Определяют дальнейшее движение событий в сторону диспетчера аудита. Директива plugin_dir в новых версиях auditd пришла из файла конфигурации /etc/audisp/audispd.conf, о котором я расскажу ниже.

Стоит также отметить, что служба auditd имеет встроенный механизм ротации локальных журналов (в наших рекомендациях по настройке мы его не касаемся). Но если вам все-таки понадобится эта функциональность, то вот директивы, которые за нее отвечают:

  • max_log_file_action. Необходимо установить в ROTATE для включения ротации по достижении файлом журнала определенного размера. Можно также указать KEEP_LOGS, но тогда директива num_logs будет игнорироваться, что может привести к переполнению хранилища.
  • max_log_file. Размер файла журнала в мегабайтах, при достижении которого происходит ротация.
  • num_logs. Максимальное количество единовременно хранимых файлов журнала.

Если вам потребуется выполнить ротацию не на основе размера файла, а через заданные промежутки времени, придется проявить фантазию. Например, можно создать задание для службы cron и воспользоваться в нем командой:

service auditd rotate

Под капотом она посылает процессу auditd сигнал USR1, который выполняет ротацию файлов:

rotate)


    log_daemon_msg "Rotating $DESC logs" "$NAME"


    start-stop-daemon --stop --signal USR1 --quiet --pidfile "$PIDFILE"

--name "$NAME"


    log_end_msg $? 

При этом для max_log_file_action необходимо задать значение IGNORE. Значение из директивы num_logs будет продолжать ограничивать количество хранимых файлов журнала.

  1. /etc/audit/rules.d/*.rules — набор файлов, содержащих правила мониторинга системных событий. Про синтаксис правил можно почитать здесь, а более подробно — здесь. Я же расскажу о том, чего не найти в официальной документации ;)

Вместо перечисления конкретных системных вызовов для отслеживания применительно к какому-либо файлу можно оперировать параметрами для -F perm или -p. За каждым типом доступа, который можно указать в этих флагах, кроется своя группа системных вызовов. 

rwxa

Для каталогов и файлов:

open, openat, openat2 с флагом 'r'
readlink, readlinkat
listxattr, llistxattr, flistxattr
getxattr, lgetxattr, fgetxattr
quotactl

Для каталогов и файлов:

open, openat, openat2 с флагом 'w'
creat
rename, renameat, renameat2
mkdir, mkdirat, rmdir
link, linkat, symlink, symlinkat
unlink, unlinkat
mknod, mknodat

Для файлов:

acct
swapon
quotactl
truncate, truncate64, ftruncate, ftruncate64
bind
fallocate 

Для файлов:

execve, execveat

Для каталогов и файлов:

chmod, fchmod, fchmodat
chown, lchown, fchown, fchownat, chown32, fchown32, lchown32
setxattr, lsetxattr, fsetxattr
removexattr, lremovexattr, fremovexattr
link, linkat

Таблица 1. Флаги

Например, из таблицы 1 можно узнать, что удаление файлов кроется за флагом w (запись), что поначалу может быть неочевидно. 

После того как все правила мониторинга будут добавлены в файл, необходимо отправить их в подсистему аудита. Для применения правил из отдельного файла можно воспользоваться командой: 

auditctl -R <путь_к_файлу>

При обнаружении ошибки в одной из строк файла дальнейшие правила не будут добавляться в итоговый список. Чтобы игнорировать такие ошибки, нужно в начале файла прописать строку "-i".

Если же нужно применить правила из всех файлов каталога сразу, можно воспользоваться утилитой augenrules. Она представляет собой bash-скрипт, который объединяет содержимое всех файлов *.rules в файл /etc/audit/audit.rules и применяет к нему команду auditctl -R. Этот же скрипт выполняется при перезапуске службы auditd, поэтому вручную вносить какие-либо изменения в /etc/audit/audit.rules не рекомендую. Для игнорирования ошибок, про которые я писал чуть выше, строку "-i" нужно прописать в начале первого файла (файлы идут в алфавитном порядке).

Диспетчер аудита 

Отдельный компонент, способный напрямую принимать события от службы auditd и перенаправлять их в разные точки назначения, используя собственные плагины. Я буду рассматривать только плагин Syslog, включенный в стандартный набор. Посмотрим на его конфигурационные файлы:

  1. /etc/audisp/audispd.conf и /etc/audisp/plugins.d/syslog.conf — комбинация файлов, характерная для версий auditd ниже 3.0. Задает параметры непосредственно диспетчера и плагина Syslog.

audispd.conf

q_depth = 250

overflow_action = SYSLOG

priority_boost = 4

max_restarts = 10

name_format = HOSTNAME

#name = mydomain

plugin_dir = /etc/audisp/plugins.d/

syslog.conf

active = yes

direction = out

path = builtin_syslog

type = builtin

args = LOG_LOCAL6

format = string

Для транспортировки событий нужно активировать плагин, выставив active в yes. По умолчанию они отправляются в сокет Syslog с facility и priority, равными LOG_USER и LOG_INFO соответственно. Через параметр args можно поменять что-то одно или сразу оба. Возможные значения:

  • для facility: LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_AUTH, LOG_AUTHPRIV, LOG_DAEMON, LOG_SYSLOG, LOG_USER;
  • для priority: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT, LOG_ALERT, LOG_EMERG.

Пример одновременной смены facility и priority

args = LOG_LOCAL6 LOG_INFO

  1. /etc/audit/auditd.conf и /etc/audit/plugins.d/syslog.conf — файлы из новых версий auditd. Про auditd.conf я уже рассказывал выше. Как и про syslog.conf, который сменил местоположение и значения директив path и type, но все так же определяет параметры для плагина Syslog.

syslog.conf

active = yes

direction = out

path = /sbin/audisp-syslog

type = always

args = LOG_INFO

format = string

Для директивы args в новых версиях появляется возможность задать значение interpret. Его функциональность схожа с той, которая становится доступна, если в файле auditd.conf для директивы log_format задать значение ENRICHED. С чем удобнее работать — решать вам. Мы рекомендуем использовать ENRICHED, чтобы сохранить совместимость нормализаций, а также из-за большего объема предоставляемых в событиях данных.

log_format = ENRICHED

type=SERVICE_START msg=audit(1748533040.162:1531): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=unconfined msg='unit=auditd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' UID="root" AUID="unset"

args = interpret

type=SERVICE_START msg=audit(05/29/25 15:36:01) : pid=1 uid=root auid=unset ses=unset subj=unconfined unit=auditd comm=systemd exe=/usr/lib/systemd/systemd hostname=? addr=? terminal=? res=success

Стоит заметить, что в args можно указать максимум два аргумента. Так что задать одновременно facility, priority и interpret не получится. Кроме того, если с помощью args не менять facility с LOG_USER на что-то другое, то события будут передаваться дальше с заголовком audisp-syslog[...]. В остальных случаях будет выставлен audispd[...].

Подсистема журналирования systemd

Компонент подсистемы инициализации systemd, который представляет собой службу systemd-journald с набором различных модулей-сокетов для приема и отправки событий. Журналы хранятся в виде бинарных файлов, для просмотра которых используется утилита journalctl. 

Рисунок 3. Подсистема журналирования в первом приближении

Модули сокетов

systemd-journald-audit.socket

Модуль привязывается к системному netlink-сокету, который, в свою очередь, общается с подсистемой аудита.

[Socket]

ListenNetlink=audit 1

Мы рекомендуем клиентам отключать его: когда он работает одновременно с диспетчером аудита, возникает дублирование событий auditd в журнале. А оно нам совсем ни к чему, ведь ресурсы узла и MaxPatrol SIEM для их обработки не безграничны.

Пример дублирования внутри журнала system:

May 25 18:27:19 host.name audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=unconfined msg='unit=auditd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'

May 25 18:27:19 host.name audisp-syslog[1411]: node=host.name type=SERVICE_START msg=audit(1748197639.934:1280): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=unconfined msg='unit=auditd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' UID="root" AUID="unset"

systemd-journald-dev-log.socket

Модуль привязывается к сокету /run/systemd/journal/dev-log. При этом файл /dev/log — это символическая ссылка на /run/systemd/journal/dev-log. Для служб, которые привыкли отправлять свои события в /dev/log, к примеру с помощью функции syslog(3), точка для отправки остается прежней.

[Socket]

ListenDatagram=/run/systemd/journal/dev-log

Symlinks=/dev/log

В качестве эксперимента можно попробовать отправить событие, используя Python:

import syslog

syslog.openlog(ident="syslog", logoption=syslog.LOG_PID, facility=syslog.LOG_USER)
syslog.syslog(syslog.LOG_INFO, "Test syslog")
syslog.closelog()

Получим событие в журнале:

May 25 21:07:36 host.name syslog[4095110]: Test syslog

systemd-journald.socket

Модуль привязывается к сокетам /run/systemd/journal/stdout и /run/systemd/journal/socket, которые принимают по нативному протоколу журнала systemd события, отправляемые извне с помощью функции sd_journal_print(3).

[Socket]

ListenStream=/run/systemd/journal/stdout

ListenDatagram=/run/systemd/journal/socket

В качестве эксперимента, как и в случае с syslog, попробуем отправить событие с помощью Python:

from systemd import journal

journal.send("Test native journald")

Получим событие: 

May 25 21:12:27 host.name python3[4095335]: Test native journald

Служба журналирования

/etc/systemd/journald.conf — очень простой для анализа файл конфигурации. Закомментированные строки содержат директивы со значениями по умолчанию. Полный список директив можно изучить здесь.

[Journal]

#Storage=auto

#ForwardToSyslog=yes

#ForwardToKMsg=no

#ForwardToConsole=no

#ForwardToWall=yes

#ReadKMsg=yes

# ...

Нам интересны те, которые отвечают за маршрутизацию событий:

  • Storage. При значении persistent журналы сохраняются на диске в каталоге /var/log/journal. Если он существует, а значение директивы выставлено в auto, это будет равнозначно persistent. При значении volatile журналы сохраняются в каталоге /run/log/journal, который очищается при каждой перезагрузке узла. При значении none журналы не сохраняются локально, но продолжают пересылаться в другие точки назначения, задаваемые через директивы ForwardTo*.
  • ForwardToSyslog. При установке значения в yes события будут отправляться в сокет /run/systemd/journal/syslog. Если планируется использовать RSyslog с модулем imuxsock, необходимо включить эту директиву.
  • ReadKMsg. Установка значения в yes позволит забирать события, которые шлются ядром в файл /dev/kmsg.

Служба systemd-journald также имеет встроенный механизм ротации собственных журналов, которая может выполняться как на основе размера файлов, так и по времени. Мы не будем подробно разбирать эту тему, но для общего ознакомления приведу краткую выжимку.

За ротацию по размеру отвечают директивы System* (для /var/log/journal) и Runtime* (для /run/log/journal):

  • *MaxFileSize. Максимальный размер одного файла журнала, по достижении которого происходит ротация.
  • *MaxFiles. Максимальное количество файлов (при его превышении более старые файлы удаляются).
  • *MaxUse. Максимальный размер всех файлов (при его превышении более старые файлы удаляются).
  • *KeepFree. Размер свободного места в файловой системе, при достижении которого более старые файлы будут удаляться.

За ротацию по времени отвечают директивы:

  • MaxFileSec. Продолжительность ведения одного файла журнала.
  • MaxRetentionSec. Время жизни файла, по прошествии которого он удаляется.

Комбинация этих директив может порождать разные неожиданные ситуации, поэтому следует внимательно подбирать для них значения.

Система RSyslog

Самая распространенная система для обработки и маршрутизации событий. Представляет собой службу rsyslogd с набором различных модулей для взаимодействия с источниками и точками назначения.

Рисунок 4. На самом деле модулей у RSyslog намного больше

Модули

Каждый модуль RSyslog заточен под тот или иной источник (модули с именами im*) или точку назначения (имена om*). Они загружаются в конфигурацию с помощью директивы module(load="<имя_модуля>"). Дополнительные параметры можно изучить в разделе Module Parameters документации RSyslog для каждого конкретного модуля (пример для imfile).

Системные события могут быть получены через модули imuxsock или imjournal. Однако разработчики RSyslog предупреждают, что использование imjournal довольно затратно по потреблению ресурсов. Если вам не нужно получать особым образом структурированные данные, лучше использовать imuxsock.

Для отправки данных на удаленный сервер, как правило, используется модуль omfwd. Он встроенный, поэтому его можно не загружать с помощью module(). Если же вы захотите поменять его параметры, используйте синтаксис module(load="builtin:omfwd" <ваши_параметры>).

Настройка службы

/etc/rsyslog.conf

Настройка приема событий выполняется с помощью директивы input(type="<имя_im_модуля>"). Дополнительные параметры можно посмотреть в разделах Input Parameters официальной документации для каждого конкретного модуля. Отправка событий настраивается внутри директивы action(type="<имя_om_модуля>"). Дополнительные поля для нее можно изучить в разделах Action Parameters для каждого конкретного модуля.

Замечу, что для некоторых im-модулей внутри input() доступна директива Ruleset. Она позволяет привязать выбранный источник к конкретному набору правил отправки событий вовне. Каждый такой набор задается с помощью директивы ruleset() и содержит в себе один или несколько action(). Все action(), заданные вне явно объявленных ruleset(), попадают в стандартный набор с именем RSYSLOG_DefaultRuleset.

Рисунок 5. Путь событий от входа к выходу 

Проверить, какие модули запущены в данный момент, можно с помощью команды:

top -H -p pgrep rsyslog

Рисунок 6. Потоки с префиксом in: содержат имена модулей для приема событий, а с префиксом rs: — имена action()

Для более тонкой настройки маршрутизации событий используются различные фильтры. После их применения события могут быть перенаправлены на один или несколько action() (см. пример 1) либо на конкретный ruleset() (см. пример 2).

Пример 1

module(load="imfile")

input(

type="imfile"

File="/var/log/messages"

Severity="info"

Facility="local7"

# Ruleset="RSYSLOG_DefaultRuleset"

)

if $syslogfacility-text == "local7" and $syslogpriority-text == "info" then {

action(

type="omfwd"

target="10.10.10.10"

protocol="udp"

port="514"

)

action(

type="omfwd"

target="10.10.10.11"

protocol="udp"

port="514"

)

}

Пример 2

module(load="imfile")

input(

type="imfile"

File="/var/log/messages"

Severity="info"

Facility="local7"

Ruleset="forward_to_siem"

)

ruleset(

name="forward_to_siem"

action(

type="omfwd"

target="10.10.10.10"

protocol="udp"

port="514"

)

action(

type="omfwd"

target="10.10.10.11"

protocol="udp"

port="514"

)

)

if $syslogfacility-text == "local7" and $syslogpriority-text == "info" then call forward_to_siem

При должной сноровке можно выстраивать цепочки для перемещения событий из одной точки в другую. Здесь все зависит от вашей фантазии.

Система Syslog-NG

Менее популярная, чем RSyslog, но легко доступная для установки система обработки событий. Стоит оговориться, что в Linux в один момент времени может быть запущена только одна из этих систем (если вы, конечно, не энтузиаст и не придумали возможность это обойти).

Рисунок 7. У Syslog-NG модули не всегда разбиваются на «прием» и «отправку», поэтому внимательно читайте документацию

Модули

Для сбора системных событий используется модуль system(). Под капотом он скрывает набор других фильтров и параметров — в зависимости от операционной системы и ее конфигурации. В Linux это либо модуль systemd-journal(), собирающий события из журнала systemd, либо комбинация из модулей unix-dgram() (собирает события из /dev/log) и file() (собирает события из /dev/kmsg или /proc/kmsg). Чтобы узнать содержимое system() в вашей системе, можно воспользоваться специальным скриптом из официального GitHub-репозитория Syslog-NG.

События auditd, в свою очередь, могут собираться из файла /var/log/audit/audit.log с помощью модуля linux-audit(). Но если вы настроили диспетчер аудита для отправки событий в сокет Syslog, использовать отдельный модуль необязательно.

Для передачи данных на удаленный сервер рекомендуем использовать модуль network() вместо устаревших udp() и tcp(). Протокол передачи при этом указывается во вложенной директиве transport(). Информацию о других модулях для приема и отправки событий можно почитать в официальной документации.

Настройка службы

/etc/syslog-ng/syslog-ng.conf

Рисунок 8. В процессе настройки маршрутизации событий задаются именованные сущности

source: перечень источников событий

source src {

unix-dgram("/dev/log");

internal();

file("/proc/kmsg" log_prefix("kernel: "))

};

filter: фильтр для событий

filter auth {

facility(auth, authpriv)

  and not filter(debug);

};

destination: перечень точек назначения для отправки событий

destination d_auth {

file("/var/log/auth.log");

};

Затем эти три сущности объединяются в блок log, который описывает одну из цепочек маршрутизации (назовем их так):

log {

source(src);

filter(auth);

destination(d_auth);

};

Внутри одной цепочки можно использовать несколько source()filter() и destination(). При этом несколько filter() будут объединяться через логическое «И». С помощью таких цепочек, по аналогии с RSyslog, можно реализовывать различные сценарии пересылки событий из одного места в другое — в зависимости от ваших потребностей.

Дальше все события попадают в MaxPatrol SIEM. И здесь я завершаю свой краткий экскурс в мир настроек журналирования Linux. Надеюсь, вы вынесете для себя что-то полезное и станете чуть лучше понимать, как все работает на самом деле. Еще увидимся!

Мы дěлаем Positive Research → для ИБ-экспертов, бизнеса и всех, кто интересуется ✽ {кибербезопасностью}