[Перевод] Мониторинг системных вызовов Linux

be53026fb4d54b87994d6aa6be91f9c6.png



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


  1. Сколько уникальных исходящих TCP-соединений установили ваши серверы за последний час?
  2. Какие процессы и пользователи инициировали установку этих соединений?

Если вы в состоянии ответить на оба вопроса, отлично — дальше можете не читать. А если ответа нет, то получить эту информацию поможет go-audit.



Системные вызовы (syscalls) — средство общения между пользовательскими программами и ядром Linux. Они используются для таких вещей, как соединение сетевых сокетов, чтение файлов, загрузка модулей ядра, создание новых процессов и т. д. и т. п. Если вы когда-либо использовали strace, dtrace, ptrace или что-то подобное со словом trace в названии, то системные вызовы вам уже не в новинку.


Большинство людей используют упомянутые trace-инструменты для отладки стоящих особняком единичных случаев, мы же в Slack собираем системные вызовы в качестве источника данных для постоянного мониторинга, что и вам советуем.



Linux Audit был добавлен в ядро начиная с версии 2.6.(14?). Система аудита состоит из двух основных компонентов: код ядра для мониторинга системных вызовов и демон, журналирующий связанные с этими вызовами события.


7e27cb5b3e234c86bd67308c562c1f96.png Архитектура kauditd и go-audit


Давайте посмотрим на пример использования auditd. Допустим, мы хотим записывать в журнал события чтения файла /data/topsecret.data. (Пожалуйста, не храните секретную информацию в файле под названием topsecret.data). При использовании auditd нужно сначала сообщить ядру о нашем желании получать информацию об интересующих событиях. Это делается с помощью команды auditctl, которая запускается от имени суперпользователя:


auditctl -w /data/topsecret.data -p rwxa

Теперь ядро будет генерировать событие каждый раз, когда кто-то получит доступ к /data/topsecret.data (в том числе и через symlink). Это событие посылается процессу в пространстве пользователя (обычно auditd) через нечто под названием netlink-сокет. (Другими словами, netlink — это когда вы говорите ядру посылать сообщения процессу с помощью его PID на соответствующий сокет.)


В большинстве Linux-дистрибутивов процесс пространства пользователя auditd пишет данные в /var/log/audit/audit.log. Если подключенный к netlink-сокету юзерспейсный процесс отсутствует, сообщения будут появляться в консоли и могут быть просмотрены с помощью dmesg.


Все это замечательно, но наблюдение за единственным файлом — это слишком простой пример. Давайте возьмем что-нибудь поинтереснее, что-нибудь связанное с сетью.


Демоны (и еще бедовая netcat) обычно используют системный вызов listen для организации приема входящих сетевых соединений. Например, если Apache желает слушать 80-й порт, он посылает соответствующий запрос ядру. Для журналирования этих событий мы снова сообщаем ядру о наших намерениях с помощью auditctl:


auditctl -a exit,always -S listen

Теперь каждый раз, когда процесс начинает слушать на каком-либо сокете, мы получаем соответствующее сообщение. Здорово! Такое журналирование может быть настроено на любой системный вызов. Для решения обозначенных в начале статьи вопросов нам нужен connect. Чтобы организовать наблюдение за каждым новым процессом и командой, можно использовать execve.


Суперзамечание. Мы не ограничены лишь действиями пользователей. Подумайте о таких интересных событиях, как запуск Апачем bash или создание исходящего соединения к какому-то непонятному IP-адресу, и о том, что эти действия могут означать.



Итак, теперь у нас есть некоторое количество событий в /var/log/audit/audit.log, но файлы журнала — это еще не система мониторинга. Что нам делать со всеми собранными данными? К сожалению, у формата журнала auditd есть особенности, которые затрудняют его обработку.


  1. Формат данных в основном представлен в виде «ключ=значение».
  2. Событие может занимать одну или несколько строк.
  3. События могут переплетаться и появляться не по порядку.

2e8ca8691d994f0b9fc18d823455b7ae.png Пример вывода auditd


Есть несколько инструментов для обработки событий журнала (например, aureport и ausearch), но они, похоже, нацелены на расследование уже случившегося события, а не на постоянный мониторинг.



Мы видели много вариантов полезного использования получаемых от auditd данных. Но нужно было придумать способ масштабирования такой системы мониторинга. В итоге в качестве замены части auditd, работающей в пространстве пользователя, мы решили создать go-audit. Вот цели, на которые мы ориентировались:


  1. Конвертирование многострочных событий auditd в один JSON-blob.
  2. Общение с ядром напрямую с помощью netlink.
  3. Высокая производительность.
  4. Минимизация (или полное исключение) фильтрации событий на наблюдаемых узлах.

Первые три условия, скорее всего, очевидны и понятны, а последнее требует пояснения.



Вот два главных вопроса, на которые мы должны ответить при обсуждении четвертого пункта:


  • Почему вы не хотите фильтровать события на каждом сервере?
  • Не будете ли вы пересылать уйму лишней информации?

Представьте, что на ваших серверах установлена утилита curl (вероятно, что так оно и есть). Во время очередных учений товарищ из «красной команды» (red team) использует curl для загрузки руткита и затем для выгрузки данных. Получив этот урок, вы начинаете журналировать каждую команду и отфильтровывать все, кроме curl, при запуске которой каждый раз поднимается тревога.


У такого подхода есть несколько проблем.


  1. Существует примерно 92 481 124,5 способа выполнить загрузку руткита и выгрузку данных без использования curl. Мы даже перечислить их не сможем.
  2. Взломщик может посмотреть ваши правила для auditd и заметить, что вы следите за curl.
  3. Есть вполне легитимные случаи использования curl.

Нам нужно кое-что получше…


А не посылать ли все данные централизованной системе журналирования и оповещения, вместо того чтобы отфильтровывать конкретные команды на местах? У этого подхода есть несколько удивительно полезных свойств.


  1. Взломщик не знает, какие системные вызовы вас интересуют. (А как метко подметил Rob Fuller, невидимые ловушки вызывают у хакеров ночные кошмары.)
  2. Мы можем анализировать взаимосвязь различных событий, чтобы понимать, когда выполнение curl вполне законно, а когда нет.
  3. Новые правила можно оценивать и тестировать на архивных данных.
  4. Теперь у нас есть внешнее независимое хранилище криминалистической информации.


Итак, друзья, встречайте — go-audit. Мы выпускаем этот инструмент как ПО с открытым исходным кодом. Им можно пользоваться свободно и совершенно бесплатно. Ребята из нашей команды Secops создали первую версию go-audit больше года назад, и мы используем его в промышленной эксплуатации почти столько же. Это небольшой, но очень важный элемент нашей инфраструктуры мониторинга. Рекомендую посмотреть мою предыдущую статью о принятом у нас подходе к работе с сигналами тревоги (alerting).


В репозитории go-audit размещено множество примеров по конфигурированию и сбору данных. Здесь в Slack мы используем rsyslog + relp, поскольку хотим как можно быстрее убирать данные с сервера, но при этом иметь возможность записать события на диск, если syslog временно не может передать их по сети. Мы бы могли без труда перейти на другой механизм доставки логов и будем рады услышать ваши идеи.


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



Возможно, вы обратили внимание, что в этой статье еще ни разу не встретилось слово «безопасность». Давайте это обсудим. Я считаю, что многие инструменты общего назначения часто могут быть использованы и для обеспечения безопасности, но обратное, как правило, неверно. Auditd выдает данные мониторинга безопасности в таком виде, что их сложно использовать каким-либо другим способом, а go-audit был разработан как инструмент общего назначения. Его полезность сразу очевидна для специалистов служб эксплуатации и разработчиков, которые могут использовать go-audit для решения проблем на всем многообразии современных и в том числе очень крупных систем.


Давайте вернемся к вопросам, поставленным в начале статьи. Любая компания с IDS/IPS/Netflow/PCAP и т. д. на сетевом шлюзе может многое сказать о своих сетевых соединениях и, возможно, ответить на первый вопрос. Но ни одно из перечисленных решений не предоставит контекст в виде user/pid/command, необходимый для ответа на второй вопрос. А в нем как раз и заключается различие между «кто-то запустил что-то где-то в нашей сети, и оно соединилось с таким-то IP» и «Mallory запустила curl от имени root на bigserver01 и подключилась к 1.2.3.4, порт 1337».


В Slack мы часто говорим: «Не дай лучшему стать врагом хорошего». Go-audit не идеален, но мы считаем, что этот инструмент действительно очень неплох, и рады им с вами поделиться.



FAQ:


Почему auditd, а не sysdig или osquery?

Osquery — отличный инструмент. На самом деле в Slack мы его тоже используем. Но для боевых серверов предпочитаем go-audit. Причина заключается в том, что эти системы работают в режиме 24/7, и нам нужно постоянно передавать данные. С osquery мы получаем снимок текущего состояния машины. Если что-то завершило выполнение в интервале между опросами, есть все шансы это пропустить. Думаю, такая модель хороша для ноутбуков и других пользовательских устройств, но для высокодоступных систем предпочитаю передавать данные непрерывным потоком.


Sysdig также является замечательным инструментом отладки, и я использовал его достаточно активно. Основная проблема в том, что ему требуется модуль ядра на каждой машине. Sysdig Falco кажется полезным, но они предпочитают фильтровать события на каждом объекте мониторинга. А, как было сказано выше, нам интереснее хранить правила централизованно, вне зоны досягаемости взломщика, получившего доступ к системе.


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


Что нам делать со всеми этими сигналами тревоги?

Они отправляются в кластер Elasticsearch. Там для генерации сигналов тревоги на основе анализа постоянно поступающих данных и для общего мониторинга мы используем ElastAlert. Также можно посмотреть на другие популярные системы журналирования, способные работать с большими объемами данных, но (мое личное мнение не обязательно должно совпадать с мнением моего работодателя) у меня фундаментальные проблемы с денежной оценкой структур, которые побуждают записывать в журналы меньше данных, чтобы сэкономить финансы.


О журналах какого размера идет речь?

Короткий ответ: значение может меняться в очень широких пределах.


Длинный ответ: зависит от того, какие системные вызовы журналировать и как много серверов будут это делать. Мы пишем сотни гигабайт в день. Кажется, это много, но в данный момент у нас постоянно журналируются события с 5500 машин. Также следует учитывать выделение кластеру дополнительных ресурсов для отражения атак типа DoS.


Почему rsyslog?

Мы обладаем значительным опытом работы с rsyslog, у которого есть несколько полезных свойств. Настойчиво рекомендуем использовать версию 8.20+, где мы исправили несколько ошибок. У нас была возможность доверить выполнение доставки сообщений и самому go-audit, но выгоды такого подхода не перевесили преимуществ использования инструмента, который исправно служит нам уже несколько лет.


Благодарности
  • Моему коллеге Nate Brown, сделавшему go-audit намного лучше.


  • Парням из Mozilla, занимающимся audit-go, за то, что вдохновили нас на этот проект.


  • Большому количеству людей, прочитавших эту статью и написавших полезные отзывы.


  • Chicago Cubs за победу в чемпионате.

Комментарии (1)

  • 27 декабря 2016 в 11:27

    0

    Кто знает, почему на Ubuntu 16.04.1 LTS не признаёт имена системных вызовов?


    ~$ uname -a
    Linux ... 4.4.0-57-generic #78-Ubuntu SMP Fri Dec 9 23:46:51 UTC 2016 i686 i686 i686 GNU/Linux
    ~$ sudo auditctl -a exit,always -S connect
    Syscall name unknown: connect
    

© Habrahabr.ru