はじめに
fluentdでログ監視して特定の文字列を検知したらSlackに通知するのを試してみたメモです。
背景として、以下でauditdでファイル改竄検知したらシスログに出すのを検証してました。
auditdでLinuxのファイル改竄検知を行う
これをfluentd経由で特定の文字列をgrepで見つけてSlackに通知しようという算段です。
Slackの通知にはsowawa/fluent-plugin-slackを使いました。
またSlackに通知するのに、大量のエラーメッセージがバーストすると実運用上困るので、
fujiwara/fluent-plugin-suppressを利用して、一定時間あたりの連続投稿に制限をかけてみました。
稼働確認したのは以下の環境です。
- Amazon Linux 2015.09
- td-agent-2.3.1-0.el2015.x86_64
- fluentd: 0.12.20
- fluent-plugin-slack: 0.6.4
- fluent-plugin-suppress: 0.0.6
設定
fluentd自体のインストールなどは割愛します。適宜ググって下さい。
シスログを投げる
シスログ(/var/log/messages)の送信側は本題とあまり関係ありませんが、一応前提となる構成として書いておきます。
実際には他の設定も入ってますが、関連するところを抜粋。各自の環境で適宜読み替えて下さい。
<source>
type config_expander
<config>
type tail
format syslog
path /var/log/messages
pos_file /var/log/td-agent/messages.pos
tag hoge.messages
</config>
</source>
<filter *.**>
@type record_transformer
enable_ruby true
<record>
@hostname ${hostname}
@timestamp ${time.strftime('%Y-%m-%dT%H:%M:%S%z')}
</record>
</filter>
<match *.**>
type forward
retry_limit 5
flush_interval 5s
<server>
host fluentd.local
port 24224
</server>
</match>
以降の説明はすべてログの受信側の設定です。
シスログのタグを取り出す
処理の単位をわかりやすくするために **.messages
にマッチしたものを @messages
ラベルにルーティングしてます。特に要件ではないのでこの辺はご自由にどうぞ。
<source>
type forward
port 24224
</source>
<match **.messages>
type copy
# シスログの監視
<store>
type relabel
@label @messages
</store>
# コピーしてデフォルト処理にも流しておく
#
# (略)
#
</match>
ログを特定の文字列でgrepする
ここから本題。特定の文字列を抽出するにはfluent組み込みのgrepのfilterプラグインを使います。
この時点で入力のデータはこんなかんじになってる想定です。
{
"host": "hoge-i-XXXXXXXX",
"ident": "audispd",
"message": "node=hoge-i-XXXXXXXX type=SYSCALL msg=audit(1479196400.701:2663): arch=c000003e syscall=2 success=no exit=-13 a0=1e18ee0 a1=401 a2=1b6 a3=0 items=1 ppid=14033 pid=14034 auid=513 uid=513 gid=514 euid=513 suid=513 fsuid=513 egid=514 sgid=514 fsgid=514 tty=pts1 ses=403 comm=\"bash\" exe=\"/bin/bash\" key=\"passwd_changes\"",
"@hostname": "hoge-i-XXXXXXXX.example.com",
"@timestamp": "2016-11-15T16:53:20+0900"
}
grepのfilterプラグインはキーごとに指定した正規表現のANDを満たすレコードのみを抽出します。
ident
キーが audispd
で message
に passwd_changes
を含むレコードを抽出するにはこんな感じです。
<label @messages>
<filter **.*>
type grep
regexp1 ident ^audispd$
regexp2 message passwd\_changes
</filter>
...
</label>
また、あとでslackに渡すためにfluent組み込みのrecord_transformerを使って、title
と message
キーを作っておきます。
この場合は、たまたま message
キーが存在しますが、明示するために再設定しています。
titleはident(=この場合デーモン名)とhostnameを@で連結した文字列にしています。この辺はお好みでどうぞ。
<label @messages>
<filter **.*>
type grep
regexp1 ident ^audispd$
regexp2 message passwd\_changes
</filter>
<filter **.*>
type record_transformer
enable_ruby
<record>
title ${ident}@${record["@hostname"]}
message ${message}
</record>
</filter>
<match **.*>
type relabel
@label @slack
</match>
</label>
バースト抑止をかける
大量に同じエラーメッセージがバーストしてSlackを荒らすと実運用上困るので、
fujiwara/fluent-plugin-suppressを使って一定時間内の同じtitleのメッセージ投稿数を制限しておきます。
プラグインをインストールします。fluent-gemのパスは適宜読み替えて下さい。
# /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-suppress
Fetching: fluent-plugin-suppress-0.0.6.gem (100%)
Successfully installed fluent-plugin-suppress-0.0.6
Parsing documentation for fluent-plugin-suppress-0.0.6
Installing ri documentation for fluent-plugin-suppress-0.0.6
Done installing documentation for fluent-plugin-suppress after 0 seconds
1 gem installed
<label @slack>
# バースト抑止のため一定時間内の同じtitleのメッセージ投稿数を制限する
<filter **.*>
type suppress
interval 60
num 5
attr_keys title
</filter>
...
</label>
attr_keysのところが、グループ化するキーです。カンマ区切りで複数指定するとAND条件になるっぽいです。
ここでは同じ title
キーで抑止制限をかけます。
Slackに通知する
やっとSlack通知まできた。
まずSlackのWebフックURLを発行しておきます。
Slackの「Apps&Integrations」から「Incoming WebHooks」を新規に作成してWebフックのURLを発行して下さい。
アイコン用にfluentdの画像素材が欲しい場合は公式で配布されてるこの辺を使うとよいんじゃないかと。
次にsowawa/fluent-plugin-slack プラグインをインストールします。
https://github.com/sowawa/fluent-plugin-slack
fluent-gemのパスは適宜読み替えて下さい。
# /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-slack
Fetching: fluent-plugin-slack-0.6.4.gem (100%)
Successfully installed fluent-plugin-slack-0.6.4
Parsing documentation for fluent-plugin-slack-0.6.4
Installing ri documentation for fluent-plugin-slack-0.6.4
Done installing documentation for fluent-plugin-slack after 0 seconds
1 gem installed
設定は以下のようなかんじにしてみました。他に使える設定項目はREADMEに書いてあるのでご一読下さい。
message
と message_keys
はデフォルト値のままなので省略可能ですが、設定ファイルの読みやすさのために明示してます。
<label @slack>
# バースト抑止のため一定時間内の同じtitleのメッセージ投稿数を制限する
<filter **.*>
type suppress
interval 60
num 5
attr_keys title
</filter>
<match **.*>
type slack
webhook_url https://hooks.slack.com/services/XXXXXXX
channel playground
username fluentd
icon_emoji :fluentd:
color warning
title %s
title_keys title
message %s
message_keys message
flush_interval 10s
</match>
</label>
設定を反映するために一旦td-agentを再起動。
# service td-agent restart
Restarting td-agent: [ OK ]
fluent-catで試してみる
fluent-catのパスは適宜読み替えて下さい。
# echo '{"host": "hoge-i-XXXXXXXX", "ident": "audispd", "message": "node=hoge-i-XXXXXXXX type=SYSCALL
msg=audit(1479196400.701:2663): arch=c000003e syscall=2 success=no exit=-13 a0=1e18ee0 a1=401 a2=1b6 a3=0 items=1 ppid=14033 pid=14034
auid=513 uid=513 gid=514 euid=513 suid=513 fsuid=513 egid=514 sgid=514 fsgid=514 tty=pts1 ses=403 comm=\"bash\" exe=\"/bin/bash\" key=\"
passwd_changes\"", "@hostname": "hoge-i-XXXXXXXX.example.com", "@timestamp": "2016-11-15T16:53:20+0900"}' | /opt/td-agent/embedded/bin/fluent-cat hoge.messages
飛んできた。よさげ。
まとめ
fluentdでログ監視して特定の文字列をgrepで検知したらSlackに通知できるようにしてみました。
ログはなんでもfluentdに投げておけば、あとはよしなにgrepしてSlackに投げるパターンは汎用的でいろいろ使えそうです。