Zabbix サーバでコマンドを実行し、標準では用意されていない項目を監視する

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。

[前回](https://qiita.com/HirohitoHigashi/items/3a70507dfae3aee2d3e4) は、Zabbix クライアントでコマンドを実行することで、標準では用意されていない項目の監視を行いました。
今回はサーバ側でコマンドを実行して監視する例として、SSLサーバ証明書の有効期限の監視を行ってみます。


# 目標

* SSLサーバ証明書が有効かを監視する
* 証明書有効期限30日前になったら通知する

# 手順

Zabbix サーバーには、[外部チェック](https://www.zabbix.com/documentation/6.0/jp/manual/config/items/itemtypes/external) という機能があります。重要な点を抜粋すると、

* Zabbixサーバで任意のスクリプトを実行して監視を行うことができる
* 監視スクリプトは、設定ファイル (zabbix_server.conf) の ExternalScripts オプションによって指定されたディレクトリに置く
* スクリプトには、任意のパラメータが指定できる
* チェック結果（実行結果）は、標準出力に出力する

ということですので、該当ディレクトリに任意のスクリプトをおいて、Zabbixコンソールでそれを利用する設定を追加すれば良さそうです。


## スクリプトの設置

SSLサーバ証明書は、たとえば HTTPS なら443番ポートにアクセスして確認することができます。これには openssl コマンドが利用できますが、openssl コマンドの出力結果は、かなりたくさんの情報が含まれます。Zabbixサーバには phpがインストールされているので、phpを使ってその出力を加工するとともに、パラメータ処理などを少しインテリジェントにしたいと思います。

FreeBSDの標準（ports/pkg）インストールでは、 `/usr/local/etc/zabbix6/zabbix/externalscripts` がデフォルトのスクリプト設置ディレクトリです。

Zabbixサーバーへログインし、該当のディレクトリへ以下のスクリプトを設置します。

```php:/usr/local/etc/zabbix6/zabbix/externalscripts/get_certificate_expiration.php
#!/usr/local/bin/php
<?php
#
# SSL/TLSサーバ証明書の有効期限取得
#
# Usage:
#  get_certificate_expiration.php hostname [port num|https|submission]
# Return:
#  Remaining validity days.
#

if( !isset($argv[1]) ) {
    echo "Usage: " . $argv[0] . " hostname [service|port]\n";
    exit( 1 );
}
$host_name = $argv[1];
$service = isset($argv[2]) ? $argv[2] : "https";
$starttls = "";

if( preg_match("/^[0-9]+$/", $service) ) {
    $port = $service;
} elseif( $service == "https" || $service == "{\$PORT}" ) {
    $port = 443;
} elseif( $service == "submission" ) {
    $starttls = " -starttls smtp";
    $port = 587;
} else {
    echo "Error: service name is not supported.\n";
    exit( 1 );
}

$cmd = ":| openssl s_client -connect " . $host_name . ":" . $port
     . " -servername " . $host_name . $starttls . " 2>/dev/null"
     . " | openssl x509 -noout -enddate";

$enddate = exec( $cmd );
if( $enddate === false ) {
    echo "Error: openssl command.\n";
    exit( 1 );
}

preg_match("/^notAfter=(.+)$/", $enddate, $matches );
if( !isset($matches[1]) ) {
    echo "Error: openssl command gave unexpected response.\n";
    exit( 1 );
}

$d = date_create_from_format("F d H:i:s Y e", $matches[1] );
echo $d->diff( new DateTime())->days, "\n";
exit( 0 );
```

このスクリプトは、
```
get_certificate_expiration.php www.example.com 443
```
として実行すると、有効期限までの日数を標準出力で返してくれます。第2パラメータのポート番号は、`https` もしくは、`submission` を指定することもできます。


## 監視対象サーバの登録

ウェブフロントエンドを使い、監視対象のサーバを追加します。
ここでは、新規にサーバを追加する方法を説明します。既に ZabbixAgentを使った監視登録がしてあるサーバがある場合は、この項をスキップしてください。

* 左ペインのメニューから `設定` > `ホスト` とクリックします
* 右ペイン上部の `[ホストの作成]` をクリックします
* 新しいホストの作成画面が表示されるので、以下の項目を入力します

![202505_143059.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/3a07a809-bc8c-4294-9e43-f57a4f0bd424.png)

　ホスト名： 任意
　テンプレート： HTTPS Service
　グループ： 任意
　インターフェース： エージェント
　DNS名： (FQDN)
　接続方法：DNS

今回、対象のサーバーには ZabbixAgentはインストールしていませんが、DNS名が必要なため、インターフェースにはエージェントを指定し、DNS名欄への入力と接続方法を `DNS` へ変更します。
また、テンプレートは入れなくても構いませんが、一応ここでは、`HTTPS Service` を指定しておきました。


## 証明書有効期限の監視を追加

ホスト一覧に、先ほど追加したサーバが追加されているので、その `アイテム` の箇所をクリックします。

![202505_144639.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/94ddf7ca-e4b5-4830-bfda-bf6d0b5deea8.png)

* 右ペイン上部の `[アイテムの作成]` をクリックします
* アイテム作成画面が表示されるので、以下の項目を入力します

![202505_151622.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/eb37814b-d159-4fa7-8552-4b339dd3bbeb.png)


　名前： SSL expiration
　タイプ： 外部チェック
　キー： get_certificate_expiration.php["{HOST.CONN}","https"]
　監視間隔： 6h

以上で監視設定は完了です。


## トリガーの設定

ホスト一覧から、対象サーバの `トリガー` の箇所をクリックします。

![202505_145755.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/2df462d2-2ae1-43d1-bf6c-f623f649b81e.png)

* 右ペイン上部の `[トリガーの作成]` をクリックします
* トリガー作成画面が表示されるので、以下の項目を入力します

![202505_152429.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/e3103a57-9cad-4c94-89cd-d2ad67517d27.png)

　名前： Server SSL expiration {HOST.CONN}
　深刻度： 軽度の障害
　条件式： 右の `[追加]` ボタンで以下のとおり追加します

![202505_152223.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/ca994dbb-0eab-4035-9b34-35a2c457accd.png)

　アイテム： (ホスト名): SSL expiration
　結果： `<` `30`

以上でトリガーの設定は完了です。
これで、証明書有効期限が30日を切ったら、通知が送られます。


# おわりに

今回は、サーバーでコマンドを実行することによる監視、例題としてサーバ証明書期限監視を行ってみました。例示では、ポート番号443の HTTPSの監視を行いましたが、同様の設定で pop3やsubmissionポートで使われる証明書の監視もできると思います。証明書の更新は、たいてい1年ごとなので忘れやすく、期限切れになると接続ができなくなってしまうため、積極的に監視したい項目です。
また、監視するサーバが多い場合は、監視アイテムの追加と通知トリガーの追加のセットをテンプレートにしておくと便利です。