Splunkで大量な条件のアラートを効率的に管理する

# はじめに

Splunkで他監視ツールのイベントやSyslog、SNMP Trapなどを取り込んでいる場合、任意のキーワードにマッチするイベントが発生したときにメールなどでアラートしたいときがあります。

もしアラート条件が100件以上あるような場合はどうすべきでしょうか？

一つ一つアラートルールを作る方法がすぐ思い浮かびますが、実は危険です。
毎分サーチさせるとすると、例えば毎分100個のサーチが走ることになりますのでリソース負荷が高くサーバースペックによっては遅延、もしくは最悪スキップが発生することになります。
管理も面倒ですし。

それではどうすれば良いでしょうか？

# ルックアップによる管理

一つの方法として、ルックアップファイルにアラート条件を一覧化し、それを使い一度のサーチでチェックする方法が使われます。
本記事では簡単な例での実装例を紹介します。

# 実装例

## データサンプル

適当なSyslogデータを用意しました。CiscoのASAです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/48faea23-9c89-a993-f625-a567404987a5.png)

## ルックアップによるアラート条件一覧管理

まずルックアップファイルを用意します。
どのような項目を用意するかは要件次第ですが、今回はシンプルにアラート対象としたいメッセージ、送信先メールアドレスを管理することにしました。
不要になったときに簡単に非活性にするようにDisabledという項目も用意しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/62fa2c49-440b-beb0-fd6e-4944ef91a23c.png)

ちなみにルックアップファイル管理は[Splunk App for Lookup File Editing](https://splunkbase.splunk.com/app/1724/)が超絶便利です。


アラート条件のメッセージでワイルドカード（*）を使えるようにルックアップ定義の詳細オプション > 一致タイプに定義を入れています。WILDCARD(<フィールド名>)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/a1392fb5-a584-cde0-eff3-2ca357cd7003.png)


## SPLによるアラート対象の抽出

ルックアップファイルの準備ができたので、単純にSPLでlookupしてアラート対象イベントを抽出するだけです。

```bash: SPL例
...サーチ条件
| table _time, Host, Message
| lookup <ルックアップファイル名> Message OUTPUT alert_id Email Disabled
| where isnull(Disabled) AND isnotnull(Email)
```

アラート対象が絞れました。通知先のメールアドレスも付与されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/afba1909-d33b-e6cd-05d3-57f3b187089d.png)


## アラート作成

アラートとして保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/2fc2aa49-e090-b1ea-19f9-81572a385769.png)

生成条件を「各結果に対して」にすると各イベントごとにメール送信されます。
宛先を`$result.Email$`とするとサーチ結果の値を使うことができます。
パラメーター設定方法についてはこちらを参照ください。

https://docs.splunk.com/Documentation/Splunk/latest/Alert/EmailNotificationTokens?ref=hk


もし同一宛先への通知をまとめたい場合は`|stats values(Message) as Message by Email`などにしてしまいましょう。
その他、通知したい情報をSPLでいい感じにまとめればいずれも`$result.<フィールド名>$`で渡せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/51e22549-6ca9-e6b0-bd98-9aa5175062d5.png)


## アラート条件を正規表現で

場合によっては通知対象に正規表現を使いたい場合があります。
残念ながらルックアップでは標準では正規表現には対応していません。
その代わりにSPLでなんとかします。

まずはルックアップファイルを用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/c8de1fd7-64de-4af6-4bfc-eae8bb340429.png)

そしてこのような感じでSPLを書きます。

```bash:SPL例
...サーチ文
| table _time, Host, Message 
| eval 
    [| inputlookup <ルックアップファイル名> 
    | eval search="Email=if(match(Message,\"".Message_regex."\"),\"".Email."\",Email)," 
    | fields search 
    | mvcombine search
    | eval search=trim(mvjoin(search," "),", ")]
| where isnotnull(Email)
```

無事抽出、メールアドレス付与できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/25b69070-f9a4-65df-a60b-9c8d7ce449ef.png)


このSPLはサブサーチ内で正規表現を扱える`Match`を使ったif文が生成されて戻されるという仕組みです。
こちらの記事を参考にしています。

https://community.splunk.com/t5/Splunk-Search/regex-in-lookup-file/m-p/479859

ただし条件が増えると処理も重くなるので、正規表現は貪欲にしない、チェック対象イベントをできる限り前処理して減らすなどの対応が必要です。


## アラート抑止

これも同様の考えでいけます。

まずはルックアップファイルを用意します。これも抑止の要件次第で用意する項目は考えます。
絶対に必要なのは、いつからいつまで抑止するかという項目です。
今回は抑止対象のアラートID、ホストを項目として用意しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/e4b29dd0-1155-495e-3a9b-57c57e7a5bd4.png)

SPLを書きます。

```bash:SPL例
...サーチ文
| table _time, Host, Message
| lookup <アラート対象ルックアップファイル名> Message OUTPUT alert_id Email Disabled
| where isnull(Disabled) AND isnotnull(Email)
| lookup <抑止用ルックアップファイル名> ignore_alert_id as alert_id OUTPUT ignore_from ignore_to ignore_disabled
| lookup <抑止用ルックアップファイル名> ignore_host as Host OUTPUTNEW ignore_from ignore_to ignore_disabled
| eval chk_time=_time, chk_ignore_from=strptime(ignore_from, "%Y/%m/%d %H:%M:%S"), chk_ignore_to=strptime(ignore_to, "%Y/%m/%d %H:%M:%S")
| eval ignore=if(chk_ignore_to>chk_time AND chk_time>=chk_ignore_from, 1, 0)
| where ignore!=1 AND isnull(ignore_disabled)
```

少しずつ見ていきます。

```bash:SPL例
| lookup <抑止用ルックアップファイル名> ignore_alert_id as alert_id OUTPUT ignore_from ignore_to ignore_disabled
| lookup <抑止用ルックアップファイル名> ignore_host as Host OUTPUTNEW ignore_from ignore_to ignore_disabled
```

抑止条件で指定したアラートIDまたはホストに一致するイベントにルックアップで抑止期間を付与します。
アラートIDとホストの二種類があるので二行作っています。
二行目は`OUTPUTNEW`にしていることにご注意ください。`OUTPUT`では一行目で値が得られても空白で上書きされてしまいます。

```bash:SPL例
| eval chk_time=_time, chk_ignore_from=strptime(ignore_from, "%Y/%m/%d %H:%M:%S"), chk_ignore_to=strptime(ignore_to, "%Y/%m/%d %H:%M:%S")
| eval ignore=if(chk_ignore_to>chk_time AND chk_time>=chk_ignore_from, 1, 0)
```

各時刻データ（イベント発生日時、抑止from、抑止to）をunixtimeに変換しチェックしています。
`抑止to > イベント発生日時 >= 抑止from`
抑止対象であればignoreに1を立てます。


```bash:SPL例
| where ignore!=1 AND isnull(ignore_disabled)
```

最後にignoreフラグが立ったイベントをアラート対象から除外しています。
ignore_disabledも簡単に抑止条件を解除したい場合に用意したフィールドなので、なくても動きます。


これによりアラート対象の全イベントをまず抽出し、その後に抑止条件に合致するイベントを除外する動きが実現しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/50a13917-ef84-2bfe-3b41-4d8e297d1334.png)

後はアラート定義をするだけです。


## ルックアップファイルの管理は？

いくつか考えられます。
・手動で管理（Lookup File Editor使えばWebUIからインポート処理も行えます）
・ServiceNowなんかで管理しておき、[ServiceNow Add-on](https://splunkbase.splunk.com/app/1928/)で取り込み
・ルックアップファイルをKVストア形式にしてRESTで操作

https://qiita.com/symmr/items/be395ea60e1bb936b8a7




# 終わりに

大量にアラート条件がある場合にルックアップファイルで管理する方法を説明しました。
他にも良い方法があれば是非教えてください。