MongoDB в качестве логгера для OSSIM

Доброго дня, уважаемые читатели! Эта статья задумывалась мной как дополнение к замечательной статье Евгения Соколова об использовании MongoDB в качестве логгера для OSSIM (т.е. долгосрочного хранилища журналов событий). Я дополнил оригинальную статью Евгения комментариями и разъяснениями, где, на мой взгляд, это было необходимо для лучшего понимания.

Некоторые части оригинальной статьи Евгения я позволил себе скопировать с позволения самого автора.

1. Для чего это нужно?

Те, кто хочет использовать SIEM систему Alienvault OSSIM, или уже используют ее, наверняка обратили внимание на одно из главных ее отличий от платного аналога (Alienvault USM) – отсутствие компонента “Logger”. Таким образом, единственным вариантом хранения собираемых журналов событий является база данных MySQL, работающая в основе OSSIM. Но чем больше становится ее объем, тем медленней начинает работать поиск и вся система в целом. Оптимальным видится вариант хранения небольшого «оперативного» объема журналов в базе OSSIM. Для долгосрочного хранения лучше использовать что-то другое, например MongoDB.

2. Формулировка задачи и исходные данные

Задача: настройка OSSIM для отправки собираемых журналов событий на хранение во внешнюю БД, которой является MongoDB.

Исходные данные:

  • сервер с установленной OSSIM (использована версия 5.2.2, IP: 192.168.0.111);
  • сервер с установленной MongoDB (сервер под управлением Debian 8.5, база данных MongoDB версии 3.2, IP: 192.168.0.77).

На процессе установки MongoDB останавливаться не буду, т.к. он хорошо описан на официальном сайте системы.
Схема взаимодействия компонентов приведена на рисунке ниже.


Рисунок 1 — Схема взаимодействия компонентов OSSIM и mongoDB

OSSIM состоит из двух основных компонент: агент (ossim-agent) и сервер (ossim-server). Агент занимается обработкой журналов событий, поступающих от разных источников, и их нормализацией, т.е. приведением к единому формату. После нормализации агентом, события передаются компоненту ossim-sever для обработки (корреляция, построение отчетов и т.п.). После обработки события помещаются в БД MySQL. В рамках решения поставленной задачи будет настроена отправка агентом нормализованных событий на сервер mongoDB, как показано на рисунке 1.

Немного углубившись в код агента (написанный на python), можно увидеть, что агент создает объект класса Event, который отдает серверу. Объект Event в одном из своих атрибутов содержит и строку сырого лога от источника. Передачей события серверу занимается класс OutputPlugins, код которого лежит в /usr/share/alienvault/ossim-agent/Output.py. Если мы почитаем этот код, то увидим, что вывод данных предусмотрен не только на сервер, но также и в текстовый файл, в файл в формате csv, в SQL базу данных (в любую, которую поддерживает модуль python adoDB). Эти опции можно просто включить в файле конфигурации агента в /etc/ossim/agent/config.cfg. Различные «выводы» можно включать одновременно. Таким образом можно включить параллельную передачу событий внешнему логгеру.

3. Решение

Здесь необходимо пояснить, что для реализации требуемого функционала нужно будет вносить правки в код самого ossim, а точнее трех файлов, отвечающих за обработку агентом журналов событий: Output.py, Agent.py, Event.py.
Для решения задачи будет выполнено:

  • добавление кода в файлы ossim (Output.py, Agent.py, Event.py);
  • настройка mongoDB для приема журналов событий;
  • включение отсылки журналов событий на сервер mongoDB.

 

3.1. Добавление кода в файлы ossim (Output.py, Agent.py, Event.py)

Файлы Output.py, Agent.py, Event.py располагаются на сервере OSSIM в папке /usr/share/alienvault/ossim-agent.
Модифицированные для работы с mongoDB образцы файлов Output.py, Agent.py, Event.py, которые можно скопировать в свою инсталляцию OSSIM, заменив исходные (на всякий случай, сделав их резервную копию) можно взять по ссылке в папке «OSSIM/usr/share/alienvault/ossim-agent/» ветки 2.1.1. Ниже будут описаны изменения, внесенные в оригинальные файлы Output.py, Agent.py, Event.py в OSSIM.

Модификация Output.py
Прежде всего, необходимо добавить в Output.py код класса для создания объекта «вывод». В секции GLOBAL IMPORTS:

# GLOBAL IMPORTS
#
import os
import re
import string
import sys
import uuid
from pymongo import MongoClient
from bson import BSON

Добавлены две последние строки. «from pymongo import MongoClient» — подключает модуль клиента mongoDB (очевидно, для работы с mongo). «from bson import BSON» — подключает кодек BSON (binary JSON).

Далее добавляется класс «OutputESGuard», который выполняет передачу событий на сервер mongoDB. Выглядит он следующим образом:

class OutputESGuard(OutputPlugins):
    def __init__(self, conf):
 
        logger.info("Added ESGuard output")
        logger.debug("OutputDB options: %s" % (conf.hitems("output-esguard")))
 
        self.conf = conf
 
        self.dbhost = self.conf.get('output-esguard', 'host')
        self.dbport = self.conf.get('output-esguard', 'port')
        self.dbschema = self.conf.get('output-esguard', 'base')
        self.dbuser = self.conf.get('output-esguard', 'user')
        self.dbpass = self.conf.get('output-esguard', 'pass')
  
        mongodbURI = "mongodb://" + self.dbuser + ":" + self.dbpass + "@" + self.dbhost + ":" + self.dbport + "/" + self.dbschema
        try :
            self.conn = MongoClient(mongodbURI)
            self.log_db = self.conn[self.dbschema]
            self.event_coll = self.log_db['logger']
            self.activated = True
        except Exception, e:
            logger.error(": Error connecting to Mongodb %s" % (e))
  
    def event(self, e):          
 
        if e["event_type"] == "event"  and self.activated:            
 
            try :
                self.event_coll.insert_one(BSON.decode(e.to_bson_esguard()))
            except :
                logger.error(": Error insert data to mongodb log_coll. Plugin_id is %s. Retry as binary" % (e['plugin_id']))  
                self.event_coll.insert_one(BSON.decode(e.to_bson()))
  
    def shutdown(self):
        logger.info("Closing ESGuard output ..")
        self.conn.close()
        self.activated = False

Также в Output.py в код класса Output был добавлен метод add_esguard_output:

@staticmethod
def add_esguard_output(conf):
    if Output.esguard_output is False:
        Output._outputs.append(OutputESGuard(conf))
        Output.esguard_output = True

Модификация Event.py
В код класса Event (/usr/share/alienvault/ossim-agent/Event.py) был добавлен метод to_bson_esguard() (который используется методом event в output.py). Этот метод (to_bson_esguard()) представляет собой копию имеющегося метода to_bson с одним отличием. В стандартном методе атрибут log (именно в нем содержится событие в сыром виде) объявляется как бинарный и записывается в Base64. Нам же там нужен текст в utf8. Именно это и было изменено.

Однако не все плагины пишут в payload текст. Есть, например, Suricata, которая может поместить туда бинарные данные. Это происходит не всегда. Обычно тоже текст, но иногда — поток байтов. При попытке сунуть серверу бинарник в поле, описанном как текст, он возвращает ошибку парсера utf8. Для предупреждения такой ошибки включена передача в блок try. При возникновении ошибки, event перекодируется, используя стандартный метод to_bson(), и повторно отправляется на сервер.

Модификация Agent.py
В Agent.py необходимо добавить секцию чтения и обработки файла конфигурации(/etc/ossim/agent/config.cfg), проверку наличия в нем раздела [output-esguard] и чтения параметров, в нем указанных. В данном разделе как раз будет указан IP адрес и учетные данные сервера mongoDB, в который отправляются журналы событий.

def init_output(self):
    '''
        Initialize Outputs
    '''
 
    printEvents = True
 
    if self.conf.has_section("output-properties"):
        printEvents = self.conf.getboolean("output-properties", "printEvents")
    Output.print_ouput_events(printEvents)
 
    if self.conf.has_section("output-plain"):
        if self.conf.getboolean("output-plain", "enable"):
            Output.add_plain_output(self.conf)
 
    # output-server is enabled in connect_server()
    # if the connection becomes availble
 
    if self.conf.has_section("output-csv"):
        if self.conf.getboolean("output-csv", "enable"):
            Output.add_csv_output(self.conf)
 
    if self.conf.has_section("output-db"):
        if self.conf.getboolean("output-db", "enable"):
            Output.add_db_output(self.conf)
 
    if self.conf.has_section("output-esguard"):
        if self.conf.getboolean("output-esguard", "enable"):
            Output.add_esguard_output(self.conf)

 

3.2. Настройка mongoDB для приема журналов событий

Минимальная настройка mongoDB подразумевает под собой:

  • создание пользователя для подключения к БД;
  • настройку листнера для подключения к БД по сети;
  • включение аутентификации;

Для создания пользователя БД нужно подключиться к БД из консоли сервера, на котором она установлена:

# mongo
MongoDB shell version: 3.2.8
connecting to: admin

Создание пользователя выполняется командой:

db.createUser( { user: "ossim", pwd: "1q2w3e4r", roles: [ {role: "readWrite", db: "test"} ] } )

приведенной выше командой мы создали пользователя с именем «ossim», паролем «1q2w3e4r», имеющего права на чтение и запись в БД «test». Согласно идеологии mongoDB вручную создавать БД (как это принято в реляционных БД, том же MySQL) не нужно. Она создается автоматически, в тот момент, когда в нее поступает первая запись.
Также, для удобства, можно создать пользователя с привилегиями создания и управления учетными записями других пользователей (для того, чтобы мы могли управлять пользователями после того, как включим аутентификацию и авторизацию при доступе к БД):

db.createUser( { user: "uadm", pwd: "1q2w3e4r", roles: [ "userAdminAnyDatabase" ] } )

Настройки инсталляция mongoDB «из коробки» не позволяют подключаться к БД по сети, поэтому, необходимо закомментировать в секции «net» файла конфигурации «/etc/mongod.conf» строку «bindIp: 127.0.0.1» (или вписать туда адреса интерфейсов, на которых будет отвечать БД):

net:
  port: 27017
#  bindIp: 127.0.0.1

Кроме того, эти же самые настройки разрешают подключение к БД анонимных пользователей (о_О). Для включения механизмов аутентификации и авторизации необходимо добавить в секцию «security» строки «authorization: enabled» (в mongoDB версии 2.х делается иначе. Если используете версию 2.х, см. официальную документацию на сайте):

security:
  authorization: enabled

И выполнить перезапуск сервера:

/etc/init.d/mongod restart

Проверить, то, что УЗ пользователя работает можно командой из консоли:

# mongo localhost:27017/test -u ossim -p
MongoDB shell version: 3.2.8
Enter password:
connecting to: localhost:27017/test

 

3.3. Включение отсылки журналов событий на сервер mongoDB

Итак, на данный момент, мы имеем компонент ossim-agent сервера ossim с настроенным дополнительным выводом событий в mongoDB и саму БД, готовую к приему данных по сети. Осталось только сообщить агенту ossim, куда передавать журналы событий. Для этого, в кофигурационный файл агента (/etc/ossim/agent/config.cfg) нужно добавить соответствующую секцию. Пример ее содержимого приведен ниже:

[output-esguard]
enable=True
host=192.168.0.77
port=27017
base=test
user=ossim
pass=1q2w3e4r

Конфигурация говорит сама за себя: тумблер включения «enable», адрес сервера «host», порт «port» (по-умолчанию 27017), имя базы данных «base», имя пользователя «user» и пароль «pass».

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

/etc/init.d/ossim-agent restart

 

4. Заключение

Если все сделано правильно, то события уже начали передаваться в mongoDB. Посмотреть, появились ли они там можно с помощью графических клиентов, коих множество(гугл в помощь) или подключившись к mongoDB через консоль и выполнив команду «show collections»:

# mongo localhost:27017/test -u ossim -p
MongoDB shell version: 3.2.8
Enter password:
connecting to: localhost:27017/test
> show collections
logger.20160810
logger.20160811
logger.20160812
logger.20160813
logger.20160814
logger.20160815