Одним из недостатков Open Source версии AlienVault OSSIM является отсутствие логгера. Он есть только в версии Enterprise, которая не всем по карману. Оказывается этот недостаток можно устранить очень легко и, главное, очень эффективно. В самом OSSIM для этого уже почти всё есть, надо только научиться использовать и чуть-чуть добавить. А поможет нам в этом MongoDB и десяток строк кода (ну, может два десятка, лень пересчитывать).
Немного теории
OSSIM состоит из двух основных компонент: агент и сервер. Агент занимается обработкой логов, поступающих от разных источников, и приведением их к единому формату. Из записей сырых логов он создает объект класса Event, который отдает серверу. Объект Event в одном из своих атрибутов содержит и строку сырого лога от источника. Передачей события серверу занимается класс OutputPlugins, кот которого лежит в /usr/share/alienvault/ossim-agert/Output.py
. Так вот, если мы почитаем этот код, мы увидим, что вывод данных предусмотрен не только на сервер, есть и другие варианты. OSSIM «из коробки» может выводить данные в текстовый файл, в файл в формате csv, в SQL базу данных (в любую, которую поддерживает модуль питона adoDB). Эти опции можно просто включить в файле конфигурации агента в /etc/ossim/agent/config.cfg
. Различные «выводы» можно включать одновременно. Таким образом вы можете включить параллельную передачу событий внешнему логгеру.
А вот здесь уже возникает другой вопрос: что выбрать для хранения логов? Я подумал, что лучше использовать базу данных. Так будет удобнее работать с записями. Теперь переходим к экспериментальной части.
Немного экспериментов
Первым делом я решил отправить логи во внешнюю базу MySQL. После двух дней тестирования под нагрузкой и малоуспешных попыток справиться с ошибкой Lost connection during query, я понял что мой кун-фу не достаточно хорош для того, чтобы заставить сервер MySQL принимать данные из системы, выдающей немногим более двух миллионов событий в сутки. Я отступил с позором. Если не перевелись богатыри на земле Русской, пусть они сразятся с этим змием.
Позор поражения вверг меня в задумчивость. Озарение пришло минут через десять. Я решил, что реляционная СУБД в принципе может быть не лучшим решением для хранения логов. Надо посмотреть на что-нибудь для big data. Mongo — вот что должно сработать. И сработало. Сервер MongoDB, запущенный в среде Debian 8 на VirtualBox на скромном офисном десктопе, работает как часы. Там старенькая Mongo 2.4 из стандартного репозитария Debian. Для рабочей системы, наверно, возьму Mongo 3.2.
События пишутся. Поиск работает великолепно, в том числе по русскому тексту в логах. Ресурсов эта штука практически не ест. И вы, конечно, знаете, но не лишне напомнить, что Mongo поддерживает текстовый поиск с учетом морфологии и для русского языка тоже.
Изменения в коде OSSIM
Кое-что я дописал. Совсем чуть-чуть. Прежде всего я добавил в Output.py код своего класса для создания объекта «вывод». Сначала в секции GLOBAL IMPORTS
.
# GLOBAL IMPORTS # import os import re import string import sys import uuid from pymongo import MongoClient from bson import BSON
Здесь добавлены две последние строки. Понятно зачем. И сам класс выглядит вот так:
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
Здесь следует пояснить код метода event. Именно он занимается передачей события на сервер Mongo. Я поправил класс Event (его код лежит в /usr/share/alienvault/ossim-agert/Event.py
). Добавил туда метод to_bson_esguard()
. На самом деле это копия имеющегося там метода to_bson с одним отличием. В стандартном методе атрибут log (именно в нем содержится сырой лог источника) объявляется как бинарный и записывается в Base64. А мне этого совсем не надо. Мне там нужен текст в utf8. Именно это я и поменял.
Но есть одна маленькая засада. Дело в том, что не все плагины пишут в payload текст. Есть по крайней мере одна негодяйка Suricata, которая может поместить туда действительно бинарные данные. Она это делает не всегда. Обычно тоже текст, но иногда чисто поток байтов. При попытке сунуть серверу бинарник в поле, описанном как текст, он возвращает ошибку парсера utf8. Не мудрствуя лукаво, я включил передачу в блок try и, при возникновении ошибки, перекодирую event, используя стандартный метод to_bson()
, и повторно отправляю на сервер. Это, быть может, не оптимально, но просто.
Еще в Outup.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
И наконец я приписал в Agent.py, в секцию чтения и обработки файла конфигурации, проверку наличия в нем раздела [output-esguard]
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)
В самом файле конфига (/etc/ossim/agent/config.cfg
) новая секция выглядит вот так:
[output-server] enable=True ip=172.17.2.10 port=40001 send_events=True [output-esguard] enable=False host=your_mongoDB_host_ip port=27017 base=ossim user=your_mongoDB_username pass=your_mongoDB_userpassword
Вот и всё.
Весь код в совокупности лежит в моём репозитарии на bitbucket только пока не в главной ветке, надо заглянуть в ветку v.-2.1.1.
На досуге сделаю какой-нибудь веб-интерфейс к логгеру. Пока я пользуюсь халявным вьювером для MongoDB их в Инете много. Разумеется, вы можете своим способом решить проблему с логгером, я всего лишь хотел сказать, что она может быть решена очень просто.
Уведомление: OSSIM. Обзор. | ESGUARDIAN