Zabbixの監視設定の変更を検知して、何か処理させる -MySQLのUDFを使ったトリガー処理実行-

Zabbixの運用をしている中で、Zabbixの監視設定が変わったこととを検知したいなと思うことがありませんか？
例えば、複数人で運用をしていて、誰かが想定外の監視設定を行ったりとか、変更された設定箇所の情報を検知してエクセル等の別資料に書き起こしたいとか。

Zabbixには監査の機能があり、管理画面に「ログインした」とか「ログアウトした」とか、「ホストを追加した」とか「無効化した」とかそのような情報は記録されます。この監査情報、Zabbix API経由で確認できなかったりと、自動的に更新情報を検知する用途では使えません。(このアイテムを追加したとか、細かいことも記録されなかったり。。)
しかもDBのカラムを見ても更新時間の情報とか持っていなかったりします。

そこで、今回はZabbixのアプリケーションレベルより低いレイヤーであるDB部分で設定関連のテーブルに変更(INSERT,UPDATE,DELETE)があったときにトリガー処理させる方法を試してみます。

# MySQL UDFの設定

MySQLのUDF(User Defined Function)は、Cで実装された共有ライブラリ(.so)をMySQLに組み込んでMySQL内で独自の関数を実行できるようにする機能です。
一からUDFを実装しても良いですが、時間かかりそうなので、すでに実装されたUDFを活用してみます。

今回は以下で公開されているlib_mysqludf_sysというOS上でコマンド実行できるようにするUDF関数を設定してみます。

* https://github.com/mysqludf/lib_mysqludf_sys

コードを取得し、環境にあわせてbuildし、共有ライブラリを作成。そのライブラリをMySQLに読み込めばOKです。
buildして読み込むまではinstall.shという形でスクリプト化されているので簡単に導入できます。

```
$ git clone https://github.com/mysqludf/lib_mysqludf_sys.git
$ yum install mariadb-devel # ←lib_mysqludf_sys.cのコンパイルに必要
$ cd lib_mysqludf_sys
$ vim Makefile
```

※以下のような感じで環境にあわせて変更します。64bit版で動かしたのでLIBDIRを変更。gccのオプションとして-fPICを付与。LIBDIRを環境内のMySQLpluginの場所に応じて変更します。

```shell:Makefile

#LIBDIR=/usr/lib
LIBDIR=/usr/lib64/mysql/plugin

install:
        gcc -fPIC -Wall -I/usr/include/mysql -I. -shared lib_mysqludf_sys.c -o $(LIBDIR)/lib_mysqludf_sys.so
```

インストール実行

```shell

$ ./install.sh
Compiling the MySQL UDF
gcc -fPIC -Wall -I/usr/include/mysql -I. -shared lib_mysqludf_sys.c -o /usr/lib64/mysql/plugin/lib_mysqludf_sys.so
MySQL UDF compiled successfully

Please provide your MySQL root password
Enter password:
MySQL UDF installed successfully
```

組み込まれているかは以下で確認。sys_execとか使えるようになってますね。

```
$ mysql -uroot
> SELECT * FROM mysql.func;
+-----------------------+-----+---------------------+----------+
| name                  | ret | dl                  | type     |
+-----------------------+-----+---------------------+----------+
| lib_mysqludf_sys_info |   0 | lib_mysqludf_sys.so | function |
| sys_get               |   0 | lib_mysqludf_sys.so | function |
| sys_set               |   2 | lib_mysqludf_sys.so | function |
| sys_exec              |   2 | lib_mysqludf_sys.so | function |
| sys_eval              |   0 | lib_mysqludf_sys.so | function |
+-----------------------+-----+---------------------+----------+
5 rows in set (0.01 sec)
```

# MySQLのトリガーを作成

MySQLには指定のテーブルに対する操作の前後に処理を行うトリガーの機能があります。これを利用してZabbixのitemsテーブルの操作後にshellスクリプトを実行する設定を入れてみます。

```
$ mysql -uroot zabbix
以下のクエリを実行。
DELIMITER //
CREATE TRIGGER notification_change AFTER INSERT ON items
FOR EACH ROW
BEGIN
  DECLARE command CHAR(255);
  DECLARE result CHAR(255);
  SET command = CONCAT('/tmp/example.sh ', 'INSERT ', NEW.itemid);
  SET result = sys_exec(command);
END //
DELIMITER ;
```

ZabbixデータベースのitemsテーブルにINSERTが発生した後に、以下のコマンドをsys_exec関数を用いて実行する設定です。

```
/tmp/example.sh INSERT アイテムID
```

FOR EACH ROWの中では、検知の対象の行ごとに処理ループが走り、1件ずつスクリプトが実行処理されます。このとき、対象の行のカラム情報を取り出したければ「NEW.カラム名」で取り出し、スクリプトに引き渡すなどできます。

/tmp/example.shは以下のような内容で実行権限をつけて仕込んでおきます。

```shell:/tmp/example.sh
#!/bin/bash

OPERATION_TYPE=$1
ITEM_ID=$2

echo "Execute $OPERATION_TYPE : itemid -> $ITEM_ID" >> /tmp/zabbix_operation.log
```

# Zabbixにアイテムを登録してみる

Zabbixの管理画面からアイテムを登録してみます。すると、example.shが実行されて、以下のようにログに記録されます。

```/tmp/zabbix_operation.log
Execute INSERT : itemid -> 28732
```

テンプレートをホストに割り当ててみます。

```/tmp/zabbix_operation.log
Execute INSERT : itemid -> 28732
Execute INSERT : itemid -> 28733 ←テンプレート割当により追加されたアイテム
Execute INSERT : itemid -> 28734 ←テンプレート割当により追加されたアイテム
Execute INSERT : itemid -> 28735 ←テンプレート割当により追加されたアイテム
```

INSERTだけではなく、UPDATE、DELETEも登録すれば自由に設定の変更を検知して処理をさせることができそうです。

# まとめ

Zabbixの機能だけでは少し検知しづらいようなZabbix自体の変更検知についてもDB側でうまく処理してあげると、設定変更を検知して自由に処理させるということもできるかと思います。ただし、MySQL側にライブラリを組み込んで利用したりしているので実際に使う際には十分にテストしてから使ってください。