Статьи

Аудит MySQL с помощью McAfee и MongoDB

Автор Мэтью Бём

Приветствую всех! Давайте обсудим стороннее решение аудита для MySQL и то, как мы можем использовать MongoDB, чтобы разобраться во всех этих данных.

Плагин McAfee MySQL Audit отлично справляется с низкоуровневыми операциями на сервере MySQL. Это делается через некоторые нестандартные API, поэтому установка и настройка плагина могут быть немного сложными. Информация об аудите по умолчанию хранится в формате JSON, в текстовом файле.

Существует 1 объект JSON для каждого действия, которое происходит в MySQL. Если пользователь входит в систему, есть объект. Если этот пользователь запрашивает таблицу, есть объект. Представьте себе 1000 активных соединений из приложения, каждое из которых выполняет 2 запроса в секунду. Это 2000 объектов JSON в секунду, записываемых в журнал аудита. Через 24 часа это будет почти 173 000 000 записей аудита!

Как можно понять, сколько объектов JSON? Один из вариантов — написать собственный синтаксический анализатор в $ YOUR_FAVORITE_LANGUAGE, преобразовать JSON в операторы INSERT и записать данные обратно в MySQL (Примечание. Если вы сделаете это, вы можете внести в белый список эту таблицу в плагине, чтобы эти INSERT не повторялись аудит вошел). Или мы можем использовать систему, предназначенную для импорта, хранения и запроса объектов JSON, такую ​​как MongoDB.

Установите плагин McAfee Audit

Сначала нам нужно скачать исходный код для плагина и скачать исходный код для конкретной версии MySQL, которую вы используете. Это не полное пошаговое руководство по установке этого плагина; просто несколько очков высокого уровня.

Мой клиент для этого упражнения все еще находится на Percona Server 5.1.73, поэтому нам нужен исходный текст для этой ТОЧНОЙ версии от percona.com .

Мы можем клонировать mcafee / mysql-Audit,  используя git.

Разархивируйте исходный код MySQL и скомпилируйте его; просто не делайте «make install», только «./configure» и «make» необходимы.

Теперь скомпилируйте плагин. Вы можете прочитать подробные инструкции .

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

Как только это будет сделано, вы можете:

INSTALL PLUGIN AUDIT SONAME 'libaudit_plugin.so';

Если плагин не загружается, проверьте журнал ошибок MySQL на предмет причины и обратитесь к документации плагина о том, как решить.

Теперь нам нужно включить ведение журнала аудита, потому что по умолчанию ничего не включено.

SET GLOBAL audit_record_cmds = "select,insert,update,delete";
SET GLOBAL audit_json_file = ON;
SET GLOBAL audit_record_objs = "*.*,{}";
SET GLOBAL audit_force_record_logins = ON;

Загляните внутрь @@ datadir, и вы увидите файл с именем mysql-audit.json. Вы можете указать этот файл, если хотите просмотреть его, чтобы убедиться, что данные записываются.

Если вы хотите больше узнать о плагине аудита, ознакомьтесь с публикацией Фернандо « Опыт работы с плагином аудита  McAfee» .

Настройка MongoDB

Позвольте мне начать с того, что я впервые имею дело с MongoDB в каком-то смысле. Я запустил экземпляр EC2 в AWS (m3.large, CentOS 6) и установил MongoDB, используя yum и репозитории Mongo.

Поскольку эфемерное хранилище для моего экземпляра было смонтировано в / opt,  я изменил только этот параметр в прилагаемом  /etc/mongod.conf  и перезапустил mongo (перезапуск службы mongod) .

dbpath=/opt/mongo

Затем я скопировал  mysql-audit.json с хоста MySQL, используя SSH:

[percona@mysql-host ~]$ scp -i .ssh/amazon.pem /data/mysql/mysql-audit.json root@54.177.22.22:/tmp/

Затем я импортировал этот файл JSON непосредственно в MongoDB:

[root@ip-10-255-8-15 ~]# mongoimport --db test --collection audit --drop --file /tmp/mysql-audit.json

Приведенная выше команда mongoimport указывает базу данных, в которую нужно импортировать ( тестировать), и в какую коллекцию ( аудит) . Я также указываю —  удалить базу данных перед импортом. Это удаление необходимо, потому что плагин аудита добавляет в файл JSON, и если мы повторим эти шаги импорта без  –drop , мы будем дублировать данные.

Если будет достаточно интереса, в комментариях ниже я буду исследовать возможность использования функциональности сокетов Плагина Audit для потоковой передачи событий непосредственно в Монго.

Пока что это цикл стирка-полоскание-повторение; хотя есть возможность вращать журнал аудита JSON через определенное время и ежедневно импортировать каждый файл.

Осмысление данных

Вот пример «документа» (т. Е. События аудита), который создается плагином аудита.

{
"_id" : ObjectId("5571ea51b1e714b8d6d804c8"),
"msg-type" : "activity",
"date" : "1433438419388",
"thread-id" : "10214180",
"query-id" : "295711011",
"user" : "activebatchSVC",
"priv_user" : "activebatchSVC",
"host" : "ecn.corp",
"ip" : "10.2.8.9",
"cmd" : "select",
"objects" : [
{
"db" : "",
"name" : "*",
"obj_type" : "TABLE"
},
{
"db" : "risque",
"name" : "markets_source_tfutvol_eab",
"obj_type" : "VIEW"
},
{
"db" : "historical",
"name" : "futureopt",
"obj_type" : "TABLE"
},
{
"db" : "risque",
"name" : "securities_futures_optdef",
"obj_type" : "TABLE"
},
{
"db" : "risque",
"name" : "markets_source_tfutvol_eab",
"obj_type" : "VIEW"
},
{
"db" : "historical",
"name" : "futureopt",
"obj_type" : "TABLE"
},
{
"db" : "risque",
"name" : "securities_futures_optdef",
"obj_type" : "TABLE"
}
],
"query" : "SELECT far, bar, baz FROM mytable"
}

!! MongoDB БАГ !!

Notice that last field in the document is named “query.” When I attempted some basic aggregate() functions on this field, I received errors on bad syntax. After much frustration, lots Googling and repeated testing, I came to the only conclusion that “query” is a reserved word in MongoDB. There is little to no documentation on this, aside from an almost 3 year old bug report that simply helped confirm my suspicion.

To work around the above bug issue, let’s rename all of the “query” fields to “qry”:

db.audit.update({}, { $rename: { "query": "qry"} }, false, true);

Now we can begin.

Basic Command Counters

Using any of the “top level” fields in each document, we can run reports (called aggregates in Mongo). So an easy one is to get a list of all unique “commands” and how many times they occurred.

> db.audit.aggregate([ { $group: { "_id": "$cmd", "count": { $sum: 1 } } } ]);
{ "_id" : "Failed Login", "count" : 2 }
{ "_id" : "select", "count" : 458366 }
{ "_id" : "Connect", "count" : 455090 }
{ "_id" : "insert", "count" : 2 }
{ "_id" : "Quit", "count" : 445025 }
{ "_id" : null, "count" : 1 }

Breaking down the command above, we are grouping all values in the “cmd” field and counting them up. The SQL equivalent would be:

SELECT cmd, count(cmd) FROM audit GROUP BY cmd;

User Counts

Let’s get a list and count of all user activities. This will include any of the commands listed in the previous aggregate.

> db.audit.aggregate([ { $group: { "_id": "$user", "count": { $sum: 1 } } } ]);
{ "_id" : "baw", "count" : 1883 }
{ "_id" : "eq_shrd", "count" : 1 }
{ "_id" : "reski", "count" : 3452 }
{ "_id" : "alin", "count" : 1 }
{ "_id" : "oey", "count" : 62 }
{ "_id" : "dule", "count" : 380062 }
{ "_id" : "ashi", "count" : 802 }
{ "_id" : "tech_shrd", "count" : 392464 }

A couple interesting things come out here. Firstly, the tech_shrd user does the most ‘activities’ over all other users. Is this expected? Is this normal? Your environment will determine that.

Specific User Activities

Let’s pick a specific user and get their activity counts to make sure they aren’t doing anything weird.

> db.audit.aggregate([
... { $match: { "user": "tech_shrd" } },
... { $group: { "_id": "$cmd", "count": { $sum: 1 } } }
... ]);
{ "_id" : "select", "count" : 132970 }
{ "_id" : "Connect", "count" : 133919 }
{ "_id" : "Quit", "count" : 125575 }

The SQL equivalent:

SELECT cmd, count(cmd) FROM audit WHERE user = 'tech_shrd';

Activities By User

We saw above that there were 2 insert commands. Who ran those?

> db.audit.aggregate([
... { $match: { "cmd": "insert" } },
... { $group: { "_id": "$user", "count": { $sum: 1 } } }
... ]);
{ "_id" : "graz", "count" : 2 }

More simply, we could have just done this to see the entire document/record which would include the SQL that the user executed, timestamp, hostname, etc.

> db.audit.find({ "cmd": "insert" });

The SQL equivalents:

SELECT user, count(user) FROM audit WHERE cmd = 'insert';
SELECT * FROM audit WHERE cmd = 'insert';

Table Activity

The most complex example I could come up with was trying to find out how many times each table was referenced. In theory, with weeks or even months of audit data, we could decide which tables aren’t needed any longer by the application.

> db.audit.aggregate(
... { $unwind: "$objects" },
... { $group: { _id : "$objects.name", count: { $sum: 1 } } },
... { $sort: { "count": -1 } }
... );
{ "_id" : "*", "count" : 17359 }
{ "_id" : "swaps", "count" : 4392 }
{ "_id" : "futureopt", "count" : 3666 }
...(more)

You’ll notice in the sample document above that “objects” is an array of objects with 1 element for each table/view referenced in the ‘qry’ field. We need to “unwind” this array into single elements before we can count them. If someone knows a better way, please let me know. The Audit Plugin uses “*” to represent a derived table from a sub-SELECT, which has no proper name. We can remove all of these using:

> db.audit.update({ }, { $pull: { "objects": { "name": "*" } } }, false, true);

Audit Plugin Caveat: The ‘objects’ array is not a distinct list of the tables involved. For example, a SELECT statement that self-joins twice would produce 3 identical elements in the ‘objects’ array for that audit record. This may skew results. If anyone knows a cool Mongo trick to remove duplicates, please share in the comments.

Conclusion

For a quick wrap-up, we installed the McAfee Audit Plugin, exported some audit data, set up a MongoDB instance in AWS and imported the audit data. As you can see, the possibilities are plentiful on what kind of information you can gather. Feel free to comment on an aggregation you’d like to see if we were running this type of audit on your system.

Cheers,
Matthew