
Приветствую всех, кто заглянул на огонек! Меня зовут Роман, и я занимаюсь исследованием безопасности Linux (и всякого другого, связанного с ним) в PT Expert Security Center.
Мне периодически приходится сталкиваться с вопросами клиентов или коллег из смежных отделов по поводу оптимизации работы различных компонентов журналирования внутри Linux. Особенно это касается тонкостей их настройки для доставки журналов в MaxPatrol SIEM. Поэтому в какой-то момент возникла идея обрисовать как минимум для самого себя целостную картину их взаимодействия и влияния друг на друга. Результатом работы стала довольно компактная схема, которая помогает оперативнее и эффективнее отвечать на возникающие вопросы для решения проблем. В статье я постараюсь вкратце описать каждый ее блок. Не ручаюсь за то, что будут затронуты все тонкости, известные лишь узкому кругу специалистов. Но на большую часть вопросов я отвечу.
Каждый раздел будет посвящен отдельной группе компонентов, принимающих участие в транспортировке журналов от источника к точке назначения.
Для быстрой навигации
Общая схема
Ниже приведена цельная схема взаимодействия компонентов журналирования в Linux, которую я буду рассматривать.

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

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

Подсистема аудита
Подсистема аудита — это полноценный компонент ядра Linux, способный регистрировать все выполняющиеся системные вызовы. Со стороны пользовательского пространства общение с ним происходит через специально выделенный netlink-сокет. Используя официально поставляемые в пакете auditd утилиты, администратор узла может включать и отключать подсистему, устанавливать для нее правила мониторинга, анализировать статистику и т. п. Различные сторонние утилиты могут использовать API из библиотеки libaudit для формирования событий в формате auditd, которые затем попадают в общий журнал.
Служба аудита
Компонент на стороне пользовательского пространства, получающий события со стороны ядра. Рассмотрим несколько его файлов конфигурации.
/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 будет продолжать ограничивать количество хранимых файлов журнала.
/etc/audit/rules.d/*.rules
Набор файлов, содержащих правила мониторинга системных событий.
Вкратце про синтаксис правил можно почитать на man-странице audit.rules(7), а более подробно — на странице auditctl(8). Здесь же расскажу немного о том, чего не найти в официальной документации.
Вместо перечисления конкретных системных вызовов для отслеживания применительно к какому-либо файлу можно оперировать параметрами для -F perm или -p. За каждым типом доступа, который можно указать в этих флагах, кроется своя группа системных вызовов.
r |
w |
x |
a |
Для каталогов и файлов: 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 |
Из таблицы можно, например, узнать, что удаление файлов кроется за флагом w (запись), что поначалу может быть не особо очевидно.
После того как все правила мониторинга будут добавлены в файл, необходимо отправить их в подсистему аудита. Для применения правил из отдельного файла можно воспользоваться командой:
auditctl -R <путь_к_файлу>
При обнаружении ошибки в какой-нибудь из строк файла дальнейшие правила не будут добавляться в итоговый список. Чтобы игнорировать такие ошибки, нужно в начале файла прописать строку "-i".
Если же нужно применить правила из всех файлов из каталога сразу, можно воспользоваться утилитой augenrules. Она представляет из себя bash-скрипт, который объединяет содержимое всех файлов *.rules в файл /etc/audit/audit.rules и применяет к нему команду auditctl -R. Этот же скрипт выполняется при перезапуске службы auditd, поэтому вручную вносить какие-либо изменения в /etc/audit/audit.rules не стоит. Для игнорирования ошибок, про которые я писал чуть выше, строку "-i" нужно прописать в начале первого файла (файлы идут в алфавитном порядке).
Диспетчер аудита
Отдельный компонент, способный напрямую принимать события от службы auditd и перенаправлять их в разные точки назначения, используя собственные плагины. Я буду рассматривать только плагин Syslog, включенный в стандартный набор. Посмотрим на его конфигурационные файлы.
/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
/etc/audit/auditd.conf и /etc/audit/plugins.d/syslog.conf
Файлы из новых версий auditd. Про auditd.conf я уже рассказывал выше. Как и про syslog.conf, который просто сменил местоположение, но все так же определяет параметры для плагина Syslog.
Для директивы 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.

Модули сокетов
systemd-journald-audit.socket
Модуль привязывается к системному netlink-сокету, который, в свою очередь, общается с подсистемой аудита.
[Socket]
ListenNetlink=audit 1
Своим клиентам мы рекомендуем отключать его, так как при одновременной его работе с диспетчером аудита возникает дублирование событий auditd в журнале. А оно нам совсем ни к чему, ведь ресурсы узла и MaxPatrol SIEM для их обработки не безграничны.
Пример дублирования внутри журнала systemd
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
Очень простой для анализа файл конфигурации. Закомментированные строки содержат директивы со значениями по умолчанию. Полный список директив можно изучить на man-странице journald.conf(5).
[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 также имеет встроенный механизм ротации собственных журналов, которая может выполняться как на основе размера файлов, так и по времени. Как и в случае с auditd, в своих рекомендациях мы не разбираем подробно эту тему. Но для общего ознакомления приведу здесь краткую выжимку.
За ротацию по размеру отвечают директивы System* (для /var/log/journal) и Runtime* (для /run/log/journal):
*MaxFileSize. Максимальный размер одного файла журнала, по достижении которого происходит ротация.
*MaxFiles. Максимальное количество файлов. При его превышении более старые файлы удаляются.
*MaxUse. Максимальный размер всех файлов. При его превышении более старые файлы удаляются.
*KeepFree. Размер свободного места в файловой системе, при достижении которого более старые файлы будут удаляться.
За ротацию по времени отвечают директивы:
MaxFileSec. Продолжительность ведения одного файла журнала.
MaxRetentionSec. Время жизни файла, по прошествии которого он удаляется.
Комбинация этих директив может порождать разные неожиданные ситуации, поэтому следует внимательно подбирать значения для них.
Система RSyslog
Самая распространенная система для обработки и маршрутизации событий. Представляет из себя службу rsyslogd с набором различных модулей для взаимодействия с источниками и точками назначения.

Модули
Каждый модуль 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.

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

Для более тонкой настройки маршрутизации событий используются различные фильтры. После их применения события могут быть перенаправлены либо на один или несколько 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 в один момент времени может быть запущена только одна из этих систем (если вы, конечно, не энтузиаст и не придумали причины и способ это обойти).

Модули
Для сбора системных событий используется модуль 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

В процессе настройки маршрутизации событий задаются именованные сущности.
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. Надеюсь, вы вынесете для себя что-то полезное из прочитанного и станете чуть лучше понимать, как это все работает на самом деле. Еще увидимся.
greenlittlefrog
Каждый раз удивительно, насколько костыльно в Linux сделаны простые вещи.
Lx6g1ZG1
В стать рассматривается доставка журналов в SIEM и компоненты в нем участвующие - это конструктор. Вы вправе сделать так как Вам нужно - чего не в Linux скорее всего не получится. Так что это не про "костыльно"