MySQL5.7.13以降における、Enterprise Audit機能の改善
MySQL5.7.13以降で、全てのユーザー若しくは、特定ユーザーが特定のテーブルに対して行った、read,insert,update,delete処理のみを監査出来るようになりました。
フィルター作成はJSONフォーマットで定義するようです。この機能は、以前から待ち望んでいたので、嬉しい機能の一つです。
【検証バージョン】5.7.13-enterprise-commercial-advanced-log
フィルタリング詳細:
https://dev.mysql.com/doc/refman/5.7/en/audit-log-filtering.html
例) こちらは、confidentialテーブルに対してselectしたSQLだけ監査するようにフィルターしてある場合の監査ログです。
【フィルター】
root@localhost [mysql]> select * from audit_log_filter; +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+ | NAME | FILTER | +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+ | log_all | {"filter": {"log": true}} | | log_confidential | {"filter": {"class": {"name": "table_access", "event": {"log": {"field": {"name": "table_name.str", "value": "confidential"}}, "name": "read"}}}} | +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
1) 以下のように書かれていたので、既にインストール済みバージョンをアンインストールしました。
If the audit_log plugin is already installed from a version of MySQL before 5.7.13,
uninstall it using this statement and then restart the server before installing the current version:
■ オプションファイルもコメントアウト
[mysqld] #plugin-load=audit_log.so #audit-log=FORCE_PLUS_PERMANENT
■ Uninstall作業
root@localhost [(none)]> UNINSTALL PLUGIN audit_log; Query OK, 0 rows affected, 1 warning (0.00 sec) root@localhost [(none)]> show warnings; +---------+------+----------------------------------------------------+ | Level | Code | Message | +---------+------+----------------------------------------------------+ | Warning | 1620 | Plugin is busy and will be uninstalled on shutdown | +---------+------+----------------------------------------------------+ 1 row in set (0.00 sec) root@localhost [(none)]> exit Bye
[root@misc02 admin]# /etc/init.d/mysql.server restart
■ ロードされていない事を確認してインストール準備完了
root@localhost [information_Schema]> select PLUGIN_NAME,PLUGIN_STATUS,PLUGIN_TYPE,LOAD_OPTION from FROM INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME like 'audit%'; Empty set (0.00 sec) root@localhost [information_Schema]>
■ログ
参考) 5.7.13のログを確認したら、UNINSTALL前は以下のようなログが出ていました。
[root@misc01 admin]# cat /usr/local/mysql/data/error.log | grep audit 2016-06-15T21:26:01.568228+09:00 0 [Warning] Plugin audit_log reported: 'Audit Log plugin supports a filtering, which has not been installed yet. Audit Log plugin will run in the legacy mode, which will be disabled in the next release.' 2016-06-15T21:34:11.395386+09:00 4 [Note] Shutting down plugin 'audit_log' [root@misc01 admin]#
2)スクリプトを利用してインストールします。(これまで、Audit Pluginを利用した事が無ければここから設定開始)
スクリプトの内容を確認すると2つのテーブルと5つのファンクション,そして、AUDIT PLUGINのインストールを行っています。テーブルはInnoDBに進もうとしているのに、なぜかMyISAMを利用していますが。。。あとユーザーのサイズ制限がなぜかVARCHAAR(16)
こちらは、後で問い合わせしてみたいと思います。
———————————————————————————-
audit_log_filter_linux_install.sqlの内容
———————————————————————————-
USE mysql; CREATE TABLE IF NOT EXISTS audit_log_filter(NAME VARCHAR(64) BINARY NOT NULL PRIMARY KEY, FILTER JSON NOT NULL) engine= MyISAM; CREATE TABLE IF NOT EXISTS audit_log_user(USER VARCHAR(16) BINARY NOT NULL, HOST VARCHAR(60) BINARY NOT NULL, FILTERNAME VARCHAR(64) BINARY NOT NULL, PRIMARY KEY (USER, HOST), FOREIGN KEY (FILTERNAME) REFERENCES mysql.audit_log_filter(NAME)) engine= MyISAM; INSTALL PLUGIN audit_log SONAME 'audit_log.so'; CREATE FUNCTION audit_log_filter_set_filter RETURNS STRING SONAME 'audit_log.so'; CREATE FUNCTION audit_log_filter_remove_filter RETURNS STRING SONAME 'audit_log.so'; CREATE FUNCTION audit_log_filter_set_user RETURNS STRING SONAME 'audit_log.so'; CREATE FUNCTION audit_log_filter_remove_user RETURNS STRING SONAME 'audit_log.so'; CREATE FUNCTION audit_log_filter_flush RETURNS STRING SONAME 'audit_log.so'; SELECT audit_log_filter_flush() AS 'Result';
Installの実行
インストールスクリプトはSHAREフォルダーにあります。(WindowsとLinuxで別々なので注意して下さい)
[root@misc01 admin]# mysql -u root -p < /usr/local/mysql/share/audit_log_filter_linux_install.sql Enter password: Result OK [root@misc01 admin]# [root@misc01 admin]# mysql -u root -p -e "select PLUGIN_NAME,PLUGIN_STATUS,PLUGIN_TYPE,LOAD_OPTION FROM INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME like 'audit%'" Enter password: +-------------+---------------+-------------+-------------+ | PLUGIN_NAME | PLUGIN_STATUS | PLUGIN_TYPE | LOAD_OPTION | +-------------+---------------+-------------+-------------+ | audit_log | ACTIVE | AUDIT | ON | +-------------+---------------+-------------+-------------+ [root@misc01 admin]# root@localhost [mysql]> show tables from mysql like 'audit%'; +--------------------------+ | Tables_in_mysql (audit%) | +--------------------------+ | audit_log_filter | | audit_log_user | +--------------------------+ 2 rows in set (0.00 sec) root@localhost [mysql]>
ここで、監査の初期設定は完了です。
これから、フィルター設定を幾つか入れて確認してみます。
3) 監査用のユーザー作成、ルール作成、ルール適用して検証してみます
root@localhost [mysql]> CREATE USER 'audit_target'; Query OK, 0 rows affected (0.00 sec) root@localhost [mysql]> GRANT ALL ON *.* TO 'audit_target'; Query OK, 0 rows affected (0.00 sec) root@localhost [mysql]> SELECT user, host FROM mysql.user WHERE user = 'audit_target'; +----------------+------+ | user | host | +----------------+------+ | audit_target | % | +----------------+------+ 1 row in set (0.00 sec) root@localhost [mysql]> select * from audit_log_user; Empty set (0.00 sec) root@localhost [mysql]> select * from audit_log_filter; Empty set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }') AS 'Result'; +--------+ | Result | +--------+ | OK | +--------+ 1 row in set (0.00 sec) root@localhost [mysql]> select * from audit_log_filter; +-------------+---------------------------+ | NAME | FILTER | +-------------+---------------------------+ | log_all | {"filter": {"log": true}} | +-------------+---------------------------+ 1 row in set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_user('audit_target@%', 'log_all') AS 'Result'; +--------+ | Result | +--------+ | OK | +--------+ 1 row in set (0.00 sec) root@localhost [mysql]> select * from audit_log_user; +----------------+------+-------------+ | USER | HOST | FILTERNAME | +----------------+------+-------------+ | audit_target | % | log_all | +----------------+------+-------------+ 1 row in set (0.00 sec) root@localhost [mysql]>
4) 別ホストから対象アカウントを利用してアクセスしてみます。
[root@misc02 admin]# mysql -h 192.168.56.113 -u audit_target Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 5.7.13-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. audit_target@192.168.56.113 [(none)]> CREATE DATABASE audit_log_test_db; Query OK, 1 row affected (0.05 sec) audit_target@192.168.56.113 [(none)]> USE audit_log_test_db; Database changed audit_target@192.168.56.113 [audit_log_test_db]> CREATE TABLE confidential (memo varchar(100)); Query OK, 0 rows affected (0.02 sec) audit_target@192.168.56.113 [audit_log_test_db]> INSERT INTO audit_log_test_table VALUES(1); Query OK, 1 row affected (0.02 sec) audit_target@192.168.56.113 [audit_log_test_db]> exit Bye [root@misc02 admin]#
■上記のログがAuditログに記録されませんでした。
検証したところ、USER()を見ているようでした。
注意
current_user()を認識していないのは、Bugとの回答を頂きました。
既に修正済みで、次のメンテナンスリリースでFIXされるとの事でした。
なので、MySQL5.7.14を待ちたいと思います。
FIX後は以下の様に’%’でホストを指定している場合、user()毎に作成する必要は有りません。また、JSONで定義を作成し有効にした場合に、mysqlクライアントからの接続出来ますが、
workbenchから接続出来なくなる不具合があるようです。Bug Reportを上げたので、5.7.13で利用される場合は制限がある事をご理解下さい。
http://bugs.mysql.com/bug.php?id=81897
audit_target@192.168.56.113 [(none)]> select user(),current_user(); +-----------------------+------------------+ | user() | current_user() | +-----------------------+------------------+ | audit_target@misc02 | audit_target@% | +-----------------------+------------------+ 1 row in set (0.00 sec)
以下のように対象HOSTを追加しました。
追加したところ、audit_targetのAuditログが記録されている事が確認出来ました。
(例)
SELECT audit_log_filter_set_user(‘audit_target@misc02’, ‘log_all’) AS ‘Result’;
root@localhost [mysql]> select * from audit_log_user;select * from audit_log_filter; +--------------+----------------+------------+ | USER | HOST | FILTERNAME | +--------------+----------------+------------+ | audit_target | localhost | log_all | | audit_target | % | log_all | | audit_target | 192.168.56.109 | log_all | | audit_target | misc02 | log_all | +--------------+----------------+------------+ 4 rows in set (0.00 sec) root@localhost [mysql]>
5) 基本的な機能は確認出来たので、ユーザー、テーブル、DMLの種類によるフィルタリングを確認してみます。
※ 全ての処理はもちろん取得し記録出来るので、特定のオブジェクトに対しての特定の処理のみを確認しています。
ここではユーザーを作成して、confidentialテーブルへのREAD(select)のみをログに記録する設定をしています。
READのみなので、INSERTはここでは取得していません。
root@localhost [mysql]> select * from audit_log_user;select * from audit_log_filter; +--------------+----------------+------------+ | USER | HOST | FILTERNAME | +--------------+----------------+------------+ | audit_target | localhost | log_all | | audit_target | % | log_all | | audit_target | 192.168.56.109 | log_all | | audit_target | misc02 | log_all | +--------------+----------------+------------+ 4 rows in set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_filter('log_confidential', '{ "filter": { "class": { "name": "table_access","event": { "name": "read","log": { "field": { "name": "table_name.str","value": "confidential" } } } } } }'); +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | audit_log_filter_set_filter('log_confidential', '{ "filter": { "class": { "name": "table_access","event": { "name": "read","log": { "field": { "name": "table_name.str","value": "confidential" } } } } } }') | +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | OK | +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec) audit_target@localhost [mysql]> root@localhost [mysql]> CREATE USER confidential@'%'; Query OK, 0 rows affected (0.00 sec) root@localhost [mysql]> alter user confidential@'%' identified by 'password'; Query OK, 0 rows affected (0.00 sec) root@localhost [mysql]> GRANT ALL ON *.* TO 'confidential'@'%'; Query OK, 0 rows affected (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_user('confidential@%','log_confidential'); +----------------------------------------------------------------+ | audit_log_filter_set_user('confidential@%','log_confidential') | +----------------------------------------------------------------+ | OK | +----------------------------------------------------------------+ 1 row in set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_user('confidential@localhost','log_confidential'); +------------------------------------------------------------------------+ | audit_log_filter_set_user('confidential@localhost','log_confidential') | +------------------------------------------------------------------------+ | OK | +------------------------------------------------------------------------+ 1 row in set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_user('confidential@192.168.56.109','log_confidential'); +-----------------------------------------------------------------------------+ | audit_log_filter_set_user('confidential@192.168.56.109','log_confidential') | +-----------------------------------------------------------------------------+ | OK | +-----------------------------------------------------------------------------+ 1 row in set (0.00 sec) root@localhost [mysql]> SELECT audit_log_filter_set_user('confidential@misc02','log_confidential'); +---------------------------------------------------------------------+ | audit_log_filter_set_user('confidential@misc02','log_confidential') | +---------------------------------------------------------------------+ | OK | +---------------------------------------------------------------------+ 1 row in set (0.00 sec) root@localhost [mysql]> select * from audit_log_filter; +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+ | NAME | FILTER | +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+ | log_all | {"filter": {"log": true}} | | log_confidential | {"filter": {"class": {"name": "table_access", "event": {"log": {"field": {"name": "table_name.str", "value": "confidential"}}, "name": "read"}}}} | +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec) root@localhost [mysql]> select * from audit_log_user; +--------------+----------------+------------------+ | USER | HOST | FILTERNAME | +--------------+----------------+------------------+ | audit_target | localhost | log_all | | audit_target | % | log_all | | audit_target | 192.168.56.109 | log_all | | audit_target | misc02 | log_all | | confidential | % | log_confidential | | confidential | localhost | log_confidential | | confidential | 192.168.56.109 | log_confidential | | confidential | misc02 | log_confidential | +--------------+----------------+------------------+ 8 rows in set (0.00 sec) root@localhost [mysql]>
6) 上記コマンドを実行して、別ホストからアクセスしてログを確認してみました。
Select -> Insert -> Selectの順番でログを確認しています。
confidential@192.168.56.113 [Audit]> select * from confidential; Empty set (0.00 sec) confidential@192.168.56.113 [Audit]> insert into confidential(memo) values('秘密の情報'); Query OK, 1 row affected (0.01 sec) confidential@192.168.56.113 [Audit]> select * from confidential; +-----------------+ | memo | +-----------------+ | 秘密の情報 | +-----------------+ 1 row in set (0.01 sec) confidential@192.168.56.113 [Audit]>
audit.logの中身を確認してみると。
きちんと設定したテーブルに対しての参照処理のみ記録されている事が確認出来ました。
<AUDIT_RECORD>> <TIMESTAMP>2016-06-16T03:09:53 UTC</TIMESTAMP> <RECORD_ID>87435_2016-06-16T02:35:04</RECORD_ID> <NAME>TableRead</NAME> <CONNECTION_ID>6</CONNECTION_ID> <USER>confidential[confidential] @ misc02 [192.168.56.109]</USER> <OS_LOGIN/> <HOST>misc02</HOST> <IP>192.168.56.109</IP> <COMMAND_CLASS>select</COMMAND_CLASS> <SQLTEXT>select * from confidential</SQLTEXT> <DB>Audit</DB> <TABLE>confidential</TABLE> </AUDIT_RECORD> <AUDIT_RECORD> <TIMESTAMP>2016-06-16T03:10:12 UTC</TIMESTAMP> <RECORD_ID>87436_2016-06-16T02:35:04</RECORD_ID> <NAME>TableRead</NAME> <CONNECTION_ID>6</CONNECTION_ID> <USER>confidential[confidential] @ misc02 [192.168.56.109]</USER> <OS_LOGIN/> <HOST>misc02</HOST> <IP>192.168.56.109</IP> <COMMAND_CLASS>select</COMMAND_CLASS> <SQLTEXT>select * from confidential</SQLTEXT> <DB>Audit</DB> <TABLE>confidential</TABLE> </AUDIT_RECORD>
これまでより、断然監査がし易くなっているので是非検証してみて下さい。
トライアル(試用版)ダウンロード: https://www-jp.mysql.com/trials/
Enterprise Monitorも合わせて検証するとAudit Logの状況がリアルタイムで可視化出来ます。(閾値を超えたら、メールかSNMPで管理者に連絡)
参考)
https://dev.mysql.com/doc/refman/5.7/en/audit-log-installation.html
http://mysqlserverteam.com/mysql-5-7-new-audit-log-filtering-feature-part-1/
PlanetMySQL Voting: Vote UP / Vote DOWN