ログトラップの仕込み

あるログファイルに特定のパターンや特定の文言が出たら それをトラップして対応するアクションを行わせたい… というような仕組みを、 一般にそう言うかどうか知らないが、ログトラップと呼ぶことにする。

出来合いのツールとしては swatch が定番である。 余裕があれば|面倒クサければ、自作してもよい。

どのようなツールを使うとしても、決めておくことは以下の通り。

  • 監視対象ファイル
  • 引っかけたい文字列
  • 引っかけたあとの挙動
  • どのユーザで処理するか

swatch の設定

導入は依存モジュールの判断が面倒なので、ports から入れると楽だ。

# cd /usr/ports/security/swatch
# make install clean

あとは上述の4項目を仕込んでいく。 「監視対象ファイル」と「どのユーザで処理するか」は /etc/rc.conf にて設定する。 ユーザは指定しなければ root となる。 指定する場合は、$HOME に .swatch_script.nnnnn というファイルができるので、 実在する HOME を持っているユーザを指定する必要がある。 つまり、nobody みたいな HOME が /nonexistent になっているユーザでは ちゃんと動かすことができない。 また、いちいち書かなくてもわかるだろうが、 対象ログファイルは該当ユーザが読み込める必要がある。 ログファイルの位置やユーザ (プロセス) によっては chroot してもよいだろう。 その他 rc の仕組みで動かすためのオプションを追加して、 /etc/rc.conf への仕込みは最終的に以下のようになる。

swatch_enable="YES"  
swatch_rules="1"    
swatch_1_flags="--config-file=/usr/local/etc/swatch1.conf --tail-file=/var/log/
messages --daemon --pid-file=/var/run/swatch1.pid"
swatch_1_pidfile="/var/run/swatch1.pid"
#swatch_1_user="nobody"
#swatch_1_chdir="/var/tmp"

複数の監視を行う場合は、swatch_rules を増やしてあげて、 対応する flags や pidfile を定義してあげればよい。

残った二つの項目「引っかけたい文字列」と「引っかけたあとの挙動」は それぞれ別のファイルで指定する。 それは --config-file にて指定する。 上記例の場合は /usr/local/etc/swatch1.conf である。 このファイルは、以下のようにパターンと挙動を定義する。

watchfor /PATTERN TO PICK/
exec     "/usr/local/bin/counterAction.sh $_"

watchfor でパターンを、exec でアクションを指定するわけだ。 アクションの引数となっている「$_」には引っかかった行がそのまま渡されるので 「(」やその他の シェル構文にとって特殊な文字はまともに渡せないので注意するべし。

ここまで設定できれば あとは /usr/local/etc/rc.d/swatch.sh で起動してやればよい。 だが、困ったことに swatch は停止時にちゃんと停止できないケースがある。 これを回避するには (乱暴だが) sig_stop を SIGKILL で上書きしてしまえばよい。 どう乱暴かと言うと、該当ユーザの HOME にできる .swatch_script.nnnnn が 停止時に消されずに残ってしまうのだ。これは溜ったら日付を見て手動で消すしかない。

:
load_rc_config ${name}

sig_stop=SIGKILL

if [ -n "${swatch_rules}" ]; then
:

起動されるアクションは実行できれば何でもよい。 メイルを送るなり設定ファイルを書き換えてサービスを再起動するなり 何でも好きなように書けばよい。

まとめると、以下のように指定することになる。

  • 監視対象ファイル → /etc/rc.conf の swatch_n_flags の --tail-file
  • 引っかけたい文字列 → config ファイルの watchfor 行
  • 引っかけたあとの挙動 → config ファイルの exec 行
  • どのユーザで処理するか → /etc/rc.conf の swatch_n_user

スクリプトで自作する場合

swatch のような出来合いのツールは確かに便利だが、 仕掛けが大きいので、ちょっと躊躇するかもしれない。そういう場合は、 /usr/bin/tail や GNU tail などを用いてスクリプトを自作してしまえばよい。

FreeBSD の /usr/bin/tail は -F filename を指定することで ファイルの名前変更やローテートを追跡することができる。

スクリプトの例は以下の通り。

#!/bin/sh
#
# tail を用いた簡易ログトラップスクリプト。
#
# http://moko.cry.jp:3232/~keiji/linux/doc/easylogview.rhtml
# を元に改変したもの
#

PATH="/bin:/usr/bin:/usr/local/bin"       # その他必要な PATH を指定するべし
export PATH

LOGFILE="/var/log/messages"               # 監視対象ログファイル
PATTERN="pattern"                         # grep 抽出のパターン
ACTION="/usr/local/bin/counterAction.sh"  # ヒット時のアクション

# tail -n 1 --follow=name $FILE | while read INPUT; do # GNU tail の場合
tail -n 0 -F $LOGFILE | while read line; do
  if echo "$line" | egrep -e "$PATTERN" > /dev/null 2>&1; then
    $ACTION "$line"
  fi
done

見て判る通り、 単純に tail の出力を while で受けて一行づつ grep しているだけである。 起動時にはこのスクリプトをバックグラウンドで実行させればよいし、 停止させるには tail を kill すれば止まる。

「どのユーザで処理するか」は実行時のユーザに依存するよう逃げてしまっているので 設定は「監視対象ファイル」「パターン」「アクション」の三つだけである。 このサンプルスクリプトではそこまで踏み込んでいないが、 この三つの設定を別ファイルにして引数で指定するようにすれば、 複数のログファイル監視も簡単に実現できる。

挙動の確認

swatch を用いるにしろ自作スクリプトを用いるにしろ、 ログトラップのような運用にかかる仕込みはテストが大事である。 少なくとも以下の点を注意してテストする。

  • 正しく起動・停止できるか。人間系が絡む場合はちゃんと文書化されているか。
  • パターンに正規表現を用いる場合、マッチに過不足がないか。
  • ログファイルがローテートされる場合、ちゃんと追跡されるか。
  • ログファイルが cat /dev/null などでクリアされる場合、ちゃんと追跡されるか。

参考サイト:

[$Revision: 1.3 $ $Date: 2010.05.02 03:57:40 $]
[EOF]