[Zabbix] 自分好みのLLD（ローレベルディスカバリ）を作る (ディスカバリルールでSSL証明書の有効期限を取得）

# 概要
Zabbixのディスカバリルールって便利だから色んなことに応用できそうだけど、
自作したいなー、と思って作ったのでやり方メモ

## 知識的な話

イメージだけの話だと、
ディスカバリルール→LLD（ローレベルディスカバリ）→プロトタイプ
の順番で作用しています。

### ディスカバリルール

ディスカバリルールは、Zabbixが監視対象のホストに対して **「どのオブジェクトを探すか」** を定義します。設定上はキーを設定したり、などで通常のアイテムと大差はありませんが、仕組み的には後述のローレベルディスカバリの実行結果が返ってくることを前提に作られている特殊なものです。

### ローレベルディスカバリ

ローレベルディスカバリ（LLD）は、ディスカバリルールが実行された結果として、システムが返してくる具体的なオブジェクトを自動で検出し、その結果を元に監視設定を動的に生成する仕組みです。以下のように決まったJSONのフォーマットで返却される必要があります。（dataの中身は例です）

```json
{
  "data": [
    {
      "{#ID}": "1",
      "{#DOMAIN}": "www.example.foo"
    },
    {
      "{#ID}": "2",
      "{#DOMAIN}": "www.example.bar"
    }
  ]
}
```

<font color=Salmon>**ディスカバリルールを自作する際は、規定のJSONフォーマットで取得できるように、LLDを実行するスクリプトを作成することが重要です。**<font>

### プロトタイプ

LLDで取得した値を元に、動的にアイテム・トリガー・グラフを作成することができます。どのようなプロトタイプを作成するかをイメージしてからLLDを作ると良いですね。

## 手順

### LLD用のスクリプトの作成

今回はApacheに設定されたVirtualhostのServerNameを収集するLLDを作成します。
スクリプトを以下のように作成しました。

```shell
#!/bin/bash
# JSONデータを保持する変数
json_output='{"data":['

# httpd -S の結果から 443 namevhost を grep し、ドメイン名を抽出
output=$(httpd -S 2>/dev/null | grep "443 namevhost" | awk '{print $4}')

# カウンタ変数の初期化
counter=1

# 変数 output を改行ごとに処理
while read -r domain; do
    # ドメイン情報をJSONフォーマットで追加（連番付き）
    json_output+='{"{#ID}":"'"$counter"'","{#DOMAIN}":"'"$domain"'"},'

    # カウンタを増加
    ((counter++))
done <<< "$output"

# 最後のカンマを削除してJSONを閉じる
json_output=${json_output%,}
json_output+=']}'

# 結果を表示
echo "$json_output"
```

:::note
LLDに対応したJSONで値を返却できるなら、シェルスクリプトでもpythonではなんでもいいです。
:::

### zabbix_agentd.confにUserParameterとして設定

独自のLLDののため、UserParameterとして設定します。

```
UserParameter=apache.virtualhost.discovery,/path/to/script/check_virtualhost_details.sh
```
※ キーの名称は既存のものと被ってなければなんでも構いません

zabbix-agentを再起動して、反映させてください。

### Zabbix Serverから取得できるかを確認

可能であれば、一度Zabbixサーバー側からzabbix_getコマンドでLLDの実行結果が取得できるか確認しておきます。

```
# zabbix_get -s [宛先IPアドレス] -k apache.virtualhost.discovery
>> 結果が帰ってくることを確認
```

:::note warn
結果が帰って来ない場合は通信や接続許可設定を見直してください。また、取得処理がタイムアウトしている可能性もあるので、必要に応じてZabbix側のタイムアウト値を調整します。（デフォルトで3秒...だったはず）
:::

### ディスカバリルールの設定

取得が問題なければZabbix側にディスカバリルールを設定していきます。

ディスカバリルールは任意の名称で作成しますが、キーに関してはエージェント側のUserParameterに設定したキーの値を記入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/019d22a9-7cf5-5cc4-1eeb-4d31a9d94713.png)

### プロトタイプの作成

ディスカバリルールが作成できたらプロトタイプを作成します。作成するプロトタイプはどのような処理を作りたいかにも依りますが、今回は対象サイトで使われているSSL証明書の有効期限を取得するアイテムを作成します。

これも単純にはできないため、エージェント側にUserParameterで専用のスクリプトを作成しています。

```shell:check_ssl_expired.sh
#!/bin/sh -
date +"%s" --date="`openssl s_client -connect ${1}:443 -servername ${1} </dev/null 2>/dev/null | openssl x509 -enddate -noout | cut -d'=' -f2`" | awk '{printf("%d\n",($0-systime())/86400-1/86400+1)}'  
```

スクリプトは以下の記事を参考に作成

https://qiita.com/santasan1224/items/9929aaa740b14800c3f9

```shell:zabbix_agent.conf
UserParameter=ssl.expired[*],/path/to/script/check_ssl_expired.sh "$1"
```

Zabbixサーバー側に戻って、以下のようにアイテムを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/5a89952e-5c49-a1f8-c87a-779c6c564842.png)

アイテム側で指定している ```{#DOMAIN}```が、LLDで取得したJSONに含まれる値と対応しています。LLDの取得結果の数だけ、このアイテムは動的に複製され、値が取得可能です。

# まとめ

決まってJSONの形式で出力する必要はありますが、複数のサーバーで動的に値が変わるけど、同じルールで値を取得したい、みたいな場合に独自にLLDを作れると便利ですね。一方で、LLDで取得するべきなのか、マクロとかでうまいことやるべきなのかの設計は結構葛藤する...