Привет! Меня зовут Кермен, я — аналитик на второй линии SOC. Наша команда исследует данные от инфраструктуры и сервисов Ozon для выявления нелегитимной активности: от нарушения политик информационной безопасности до целенаправленных атак.
Каждую минуту к нам поступают миллионы событий — просматривать их вручную и в режиме реального времени невозможно. Вот почему мы автоматизируем часть своей работы с помощью правил корреляции — запускаемых по расписанию запросов, которые оповещают нас при выполнении условия: был найден индикатор компрометации, произошла последовательность действий или достигнут порог в количестве событий.
Иногда информации в событиях нам недостаточно, поэтому мы обогащаем их дополнительными признаками с помощью вспомогательных объектов: таблиц, макросов, моделей машинного обучения.
Они и правила корреляции — это уникальный контент нашего SOC. И сегодня количество всех объектов исчисляется сотнями, а уследить за каждым довольно сложно: нужно понять, когда правило было создано, изменялось, кто был автором, тестировалось ли и жизнеспособно ли ещё.
У нас не было процесса и инструментов, которые позволили бы ответить на наши вопросы. Как итог, мы достигли хаоса, а господствовать над ним не смогли.
Нам захотелось навести порядок, поэтому позаимствовали опыт разработчиков: сделали новый формат хранения, добавили больше метаинформации, настроили CI/CD, и теперь у наших объектов есть свой жизненный цикл, включающий в себя этапы разработки, тестирования, перенос в продакшн, пересмотр и отключение.
Для этого на свет появился фреймворк для разработки и управления контентом — catzone или, как его чаще называют, котозон. В статье расскажу, как мы к нему пришли, из чего он состоит и как сейчас упрощает нашу жизнь.
Кстати, о традиции называть сервисы SOC с использованием котов рассказал наш руководитель в People Tech Ask.
Жить как раньше нельзя
Помните про миллионы событий? Отлично! Они хранятся в нашей SIEM — системе, которая помогает нам собирать данные, хранить и обрабатывать их. В большинстве случаев SIEM — проприетарное решение, где пользователи играют по правилам вендора: добиться изменений системы трудно или невозможно.
Когда я пришла на стажировку, то конфигурация нашей SIEM уже хранилась в системе контроля версий, и любые изменения должны были пройти код-ревью.
Разработчику контента, кроме самого создания запроса, необходимо было делать следующее:
-
добавлять лапами в запрос обязательные поля — метаданные самого правила: уникальный идентификатор, статус, тип, критичность и др.;
-
соблюдать ограничения конфигурационных файлов: экранировать перевод на новую строку, копировать параметры, которые никогда не меняются, но нужны;
-
решать конфликты слияния, поскольку правила хранились в одном большом файле;
-
создавать страницу с плейбуком на Confluence;
-
быть предельно внимательным: никто, кроме автора и ревьюера, ошибку не заметит.
При этом нам не хватало метаданных о наших объектах (например, кто, когда, зачем создал, когда проводилось ревью, когда признали ненужным и по какому тикету), которые бы были в одном месте, а не были разбросаны по тикетам, тредам и merge request'ам. А ещё хотелось хранить некоторые метрики о сработках, про которые недавно рассказывала моя коллега, в самом правиле, чтобы следить за его качеством.
Да, наши сердца потребовали перемен! И в теории для наших хотелок мы могли бы доработать SIEM, но есть два но:
-
неудобства работы с конфигурационными файлами остаются;
-
лезть лапами в проприетарное решение затратно и страшно.
Казалось бы, на этом всё, но за свою мечту надо бороться! Костыль? Костыль!
Как жить по-новому
Мы посмотрели в несколько open source-проектов и вдохновились идеей: правила оформляем в YAML'ике, а конфигурационные файлы создаём через Jinja-шаблоны. Такой принцип работы стал основой для нашего собственного решения.
Всего-то нужно было определиться с содержимым YAML'ов, структурой их хранения, научиться создавать конфиги, настроить пайплайн и прикрутить ещё пару автоматизаций. План звучал легко и надёжно, оставалось только сделать ^^ Делов-то!
YAML — формат дружелюбный для пользователя, потому сомнений его использовать не было. Сложнее было определиться со структурой: что должно быть сейчас, и что пригодится в будущем?
На размышления ушла неделя, после чего мы выделили две группы полей:
-
обязательные для всех объектов;
-
уникальные для каждого типа объекта.
Обязательными стали следующие поля:
-
имя объекта;
-
версия;
-
автор;
-
дата создания, последнего пересмотра (при необходимости — закрытия);
-
описание;
-
теги: история, связанный тикет, статус.
Некоторые из них используются для последующего анализа:
-
подсчитываем, сколько создали и сколько закрыли;
-
уведомляем, если пора пересматривать объект;
-
инициируем перевод в продакшн после подсчёта качества сработок и срока тестирования.
У нас много объектов, и про все особенности в одной статье рассказать не выйдет, поэтому часть уникальных полей предлагаю рассмотреть на примере detections.
Вообще, мы явно это не указывали, но заметили, что у нас есть три группы различных сохранённых запросов:
-
detections — то, что превратится в алерты;
-
baselines — то, что подготавливает данные для алертов, отчётов и дашбордов;
-
reports — то, что является выгрузкой или отчётом.
У detections есть mode — режимы их работы:
-
observer — режим наблюдения, реагировать на которые первой линии не нужно. Часто это правила с низкой критичностью и большим количеством легитимных действий;
-
responder — режим, при котором нам нужно реагирование первой линии. Да, алерты нужно разобрать!
Несколько observer-правил могут входить в одно responder-правило, так мы ищем цепочку событий и уменьшаем шум. Такие многоступенчатые правила имеют свой тип, который мы называем meta. Еще есть TTP, аномалия и митигация.
Конечно, мы добавляем Kill Chain-фазу, технику, тактику и процедуру по MITRE Matrix, критичность.
Ещё у нас есть confidence — показатель того, насколько корректны логика и реализация запроса в правиле. Это целочисленное значение, которое проставляется путём анализа сработок алертов и тегов, которые проставила первая линия. Мы стремимся к тому, чтобы пересматривать правила, если они шумят.
Но возможно, что этот показатель будет пересмотрен, потому что переходим к нашей новой идее — динамическим исключениям.
Каждый объект хранится в отдельном YAML и в собственном каталоге (иногда, и в подкаталоге). И это удобно, поскольку позволяет нам гибче управлять codeowner'ами: одна из команд департамента теперь самостоятельно управляет своими исключениями для правил.
Кстати, о правах: контентом могут пользоваться не только сотрудники департамента ИБ, но и другие наши коллеги. Однако мы не готовы раскрывать им всё, поэтому в самих YAML-объектах указываем тех, кому предоставляем доступ. Стало удобнее, ведь раньше при создании, например, таблиц нужно было делать настройки в разных конфигах, а сейчас все в одном файле.
Pipeline — это что-то про pen, pineapple, apple, pen?
Репозиторий создан, примеры YAML'иков есть, а значит, время собирать всё воедино с помощью трёх этапов.
До создания MR
Есть два пути создания нового YAML'ика:
-
если правило уже существует, то для более лёгкого переноса в новый формат можно воспользоваться парсером, который создаёт готовый YAML'ик, где нужно заполнить пустые поля;
-
если нужно создать правило с нуля, то можно взять готовый шаблон.
Как только файл готов, то его нужно закоммитить. И тут на помощь приходит pre-commit hook, который валидирует поля и значения. Например, проверяет, что правило с типом report хранится в каталоге report, дата соответствует формату, а в поле status записано только одно из двух значений — prod или test.
Для каждого объекта валидация своя, и она не раз спасала меня от ситуаций, после которых стыдно смотреть ревьюеру в глаза. И да, проверка на этом этапе — это способ помочь исполнителю не наделать ошибок по невнимательности.
После создания MR
Если автор сделал всё, что мог, и готов представить своё творение, то он открывает merge request. На самом деле, ревью кода — это больная для нас тема, потому что занимает львиную долю рабочего времени аналитика. Все ещё находимся в поисках решения! Но пока стараемся снизить нагрузку путём дополнительных автоматических проверок.
Стандартизация
Некоторые поля лучше доверить автоматике, поэтому на этом этапе изменённые или созданные YAML'ики приводятся к единому виду: добавляется идентификатор объекта, а также идентификаторы используемых в запросе макросов и таблиц, обновляется дата изменений, сам же запрос приводится к нашему стандарту оформления кода.
Ранее мы также пытались просчитывать критичность нашего правила на основе имеющихся полей, но такой подход рождал больше споров, чем метод экспертной оценки. От чего-то в жизни приходится отказываться, поэтому субъективность — наше все.
Проверка корректности создания конфигов
Пусть даже YAML'ик будет коряв, запрос неправильный, а ревьюер в хорошем настроении, то поломать конфиги все равно не получится. Здесь у нас настроена проверка, что итоговый файл в порядке, и со слиянием изменений ничего не сломается. При неуспешном прохождении проверки добавить изменения не получится.
После слияния MR
Создание конфигов
Помните про нашу боль в самом начале? Да, я про обязательные поля в запросе! Сейчас они являются тегами в YAML'ике, а потому в запрос их добавлять больше не нужно! Однако нам они все ещё нужны для отправки в IRP, но теперь поля подставляются в запрос бесшумно и бесшовно при создании конфигурационных файлов.
Здесь мы также подставляем параметры, которые никогда не менялись. Думаю, что половина команды уже не знает об их существовании. Хорошо это или плохо?
После того как конфиги создадутся, а таблицы и модели переместятся в нужный каталог, снова запускается проверка корректности. Только при успешном прохождении проверки мы двигаемся дальше!
Релиз
На всякий случай создаём релизы и храним их, мало ли что-то произойдёт! Ну а ещё отслеживаем наш путь и считаем, сколько обновлений создаём.
Деплой
Созданное приложение мы сохраняем в артефакты, а потом деплоим в главный репозиторий. Уже оттуда содержимое котозона долетает до SIEM.
Наши боли и ошибки
Нам с трудом давалась дисциплина: поскольку на начальном этапе всё было в руках одного человека, то изменения вносились быстро и беспорядочно, а документация менялась с опозданием.
При этом тестирование было минимальным, что тоже приводили к last last fix'ам.
Сейчас мы научились релизить новые версии фреймворка так, чтобы они были протестированы, изменения задокументированы, а сотрудники с ними ознакомлены. Но первые тестировщики котозона настрадались!
Так что нельзя забывать три важных принципа:
-
тестировать, тестировать и ещё раз тестировать;
-
документация спасает от большого числа вопросов;
-
не доверяй пользователю, даже если он — твой коллега. Кто-то обязательно не прочтёт документацию.
Смело смотрим в будущее
С котозоном мы уже год, он прижился и стал одним из питомцев SOC. Неустанно растёт и совершенствуется, а о чём можно ещё мечтать?
Ладно, лукавлю, мечтаем мы о многом:
-
перенести в новый формат источники данных, процессы и исключения для правил;
-
добавить валидацию и тестирование как полноценную стадию пайплайна;
-
создать линтер и ещё много-много всего.
Сейчас фреймворк поддерживают инженеры, а потому можно верить в наше светлое будущее! Осталось только пинговать и строить глазки как у кота из Шрека.
Вот такая вышла история, надеюсь, наш опыт был в чем-то полезным, и вы почерпнули для себя какие-то идеи. Не забывайте подтягивать изменения с main-ветки, будьте модными и молодёжными, следите за трендами котозона! Увидимся!
Автор: kotik0bat