メールサーバに問題があった際に迅速に気づけるよう、メールサーバの送受信状況を常に把握できるよう、zabbixの監視対象にpostfixの送受信状況を含めます。
※zabbixの設計上、通常はzabbix-proxyを使うべきところを、監視対象が1台しかいないセグメントだったので、proxy導入せずに強引に対応しています。zabbixの監視スクリプトの勉強には丁度良いかもしれませんが、2台以上存在するセグメントの監視であればzabbix-proxyを導入したうえで、zabbix-postfixを、オフィシャルサイトの手順通りに導入するのが良いと思います。
Postfixのメール送受信状況取得コマンド
今回、postfixのzabbixテンプレートをインストールして改造している過程で、主に以下のコマンドを使ってログ分析しているのがわかりました。更に、zabbixの場合、60秒間隔等、ユーザの指定した感覚で状況を聞きに来るので、60秒おきにログ全量を読み込んだら、重複してログを見ることになってしまいます。その解決策として、pygtail.pyで前回どこまで読み込んだかをファイルに記録しておき、60秒後に前回読み込んだ後の未読み込み分を処理していました。
今回はzabbix-postfixを利用させてもらったのですが、同パッケージ中で、メールの状況を取得するのに使われていたコマンドは以下でした。
(今回利用させてもらったzabbix-postfix以外でも、ほぼ同じコマンドを叩いていることも調査の過程でわかりました)
mailq
一つはpostfixを運用している方なら誰でも使ったことがあるであろうmailqコマンドです。きれいにメールを転送しきっていると以下のように今はキューに何もないことを示すメッセージが表示されます。
mailq
Mail queue is empty
バウンスメールが大量にあったりすると、メッセージIDと概要が件数分出力されます。迷惑メールの不正中継等に利用されると、バウンスメール件数が溜まっていくので、mailq実行結果の件数が増える事があります。
pflogsumm
もう一つはpflogsummコマンドです。実行オプションは多々あるのですが、以下のように結果出力されます。
/usr/sbin/pflogsumm -h 0 -u 0 --bounce_detail=0 --deferral_detail=0 --reject_detail=0 --no_no_msg_size --smtpd_warning_detail=0 /var/log/mail.log
Grand Totals
------------
messages
497 received
468 delivered
444 forwarded
10 deferred (118 deferrals)
63 bounced
292 rejected (38%)
0 reject warnings
0 held
0 discarded (0%)
20069k bytes received
19909k bytes delivered
383 senders
107 sending hosts/domains
12 recipients
11 recipient hosts/domains
Per-Day Traffic Summary
-----------------------
date received delivered deferred bounced rejected
--------------------------------------------------------------------
Mar 13 20xx 67 62 54 13 5
Mar 14 20xx 77 72 4 9 2
Mar 15 20xx 98 94 17 11 24
Mar 16 20xx 81 77 18 8 142
Mar 17 20xx 88 83 9 9 116
Mar 18 20xx 86 80 16 13 3
Per-Hour Traffic Daily Average
------------------------------
time received delivered deferred bounced rejected
--------------------------------------------------------------------
0000-0100 2 1 1 1 2
0100-0200 2 1 2 1 2
0200-0300 1 0 1 1 2
0300-0400 2 2 2 1 1
0400-0500 1 1 2 1 1
0500-0600 0 0 1 0 2
0600-0700 4 4 1 0 1
0700-0800 1 1 1 0 1
0800-0900 5 5 2 0 1
0900-1000 6 6 1 0 2
1000-1100 6 6 1 0 2
1100-1200 6 6 1 0 2
1200-1300 7 7 0 0 2
1300-1400 7 6 0 1 1
1400-1500 4 3 0 1 2
1500-1600 6 6 0 1 3
1600-1700 4 4 0 0 2
1700-1800 4 4 1 0 2
1800-1900 9 8 1 1 3
1900-2000 4 3 0 1 2
2000-2100 2 2 0 0 3
2100-2200 2 2 0 0 3
2200-2300 1 1 0 0 3
2300-2400 1 1 1 0 3
smtp delivery failures: none
Fatal Errors: none
Panics: none
Master daemon messages
----------------------
3 daemon started -- version 3.4.13, configuration /etc/postfix
1 terminating on signal 15
Zabbixでの監視(zabbix-postfixを利用)
postfixのサイトでzabbixテンプレートの紹介があったので、まずはそちらを見て、zabbix-postfixを活用させていただくことにしました。選択した理由は単純にコードがわかりやすく、英語記載の為わかりやすかっただけです。
ただし、上記はzsendコマンドを用いてデータをzabbix-serverに送るようになっていたので、リモートから単純にAgentのデータを取得できるように適当に改造しました。
zabbix-postfixのセットアップ
殆どサイトに記載されている通りにセットアップしましたが、zabbix-senderは不要なので入れていません。(zabbix-serverへ通信可能なセグメントに属しているのであれば、一緒にインストールすれば良いと思います。)
sudo apt install pflogsumm bc zabbix-agent
cd /tmp
git clone https://github.com/rafael747/zabbix-postfix.git
cd /tmp/zabbix-postfix
sudo cp zabbix_postfix.sh /usr/local/sbin/
sudo cp pygtail.py /usr/local/sbin/
sudo chmod +x /usr/local/sbin/pygtail.py
sudo chmod +x /usr/local/sbin/zabbix_postfix.sh
sudo cp zabbix_postfix /etc/sudoers.d/
sudo chmod 440 /etc/sudoers.d/zabbix_postfix
sudo cp zabbix_postfix.conf /etc/zabbix/zabbix_agentd.d/
ここから下の作業は、senderを利用可能なNW構成で運用している場合、対応する必要がありません。senderを利用不可の環境下では、対応することで、Agent経由でデータ取得できるようになります。
Agentでリモートからデータ取得できるように改造する
/usr/local/sbin/zabbix_postfix.shの中身を以下のように修正しました。見て分かるようにオリジナルのzsendをしている方は、pflogsummコマンド一回の実行で、全部のパラメータのデータをzabbix-serverに送るような効率的な処理をしていましたが、今回の修正ではパラメータ毎にpflogsummを実行する非効率な処理になっています。
当方のサーバの場合、pflogsummコマンドを実行してもほぼ一瞬でコマンドが終わる事と、リソース使用状況も大して変化がなかったことから、今回はこれで良しとしました。中間ファイルを書こうかとも思ったのですが、あまり手を入れるとオリジナルの変更に追随するのが大変になってしまうので。。。
コード全量を乗せずに、変更対象箇所をコメントアウトして、その下に修正結果を記載するようにしています。viで開いて編集してください。
# 引数でreceived等、取得したいパラメータを指定できるようにする
TARGET=$1
# POSで示しているdatファイルはログを何処まで読んだかの制御ファイルの為、取得項目ごとに
# POS="/tmp/zabbix-postfix-offset.dat"
POS="/tmp/zabbix-postfix-offset.${TARGET}.dat"
# zsendしている箇所はTARGET引数を見て、該当するケースを出力するように変更
#zsend pf.received $(echo -e "${DATA}" | grep -m 1 received | cut -f1 -d"r")
#zsend pf.delivered $(echo -e "${DATA}" | grep -m 1 delivered | cut -f1 -d"d")
#zsend pf.forwarded $(echo -e "${DATA}" | grep -m 1 forwarded | cut -f1 -d"f")
#zsend pf.deferred $(echo -e "${DATA}" | grep -m 1 deferred | cut -f1 -d"d")
#zsend pf.bounced $(echo -e "${DATA}" | grep -m 1 bounced | cut -f1 -d"b")
#zsend pf.rejected $(echo -e "${DATA}" | grep -m 1 rejected | cut -f1 -d"r")
#zsend pf.rejectwarnings $(echo -e "${DATA}" | grep -m 1 "reject warnings" | cut -f1 -d"r")
#zsend pf.held $(echo -e "${DATA}" | grep -m 1 held | cut -f1 -d"h")
#zsend pf.discarded $(echo -e "${DATA}" | grep -m 1 discarded | cut -f1 -d"d")
#zsend pf.bytesreceived $(echo -e "${DATA}" | grep -m 1 "bytes received" | cut -f1 -d"b"|sed -e 's/k/\*1024/g' -e 's/m/\*1048576/g' -e 's/g/\*1073741824/g' |bc)
#zsend pf.bytesdelivered $(echo -e "${DATA}" | grep -m 1 "bytes delivered" | cut -f1 -d"b"|sed -e 's/k/\*1024/g' -e 's/m/\*1048576/g' -e 's/g/\*1073741824/g' |bc)
[ $TARGET = 'received' ] && echo $(echo -e "${DATA}" | grep -m 1 received | cut -f1 -d"r")
[ $TARGET = 'delivered' ] && echo $(echo -e "${DATA}" | grep -m 1 delivered | cut -f1 -d"d")
[ $TARGET = 'forwarded' ] && echo $(echo -e "${DATA}" | grep -m 1 forwarded | cut -f1 -d"f")
[ $TARGET = 'deferred' ] && echo $(echo -e "${DATA}" | grep -m 1 deferred | cut -f1 -d"d")
[ $TARGET = 'bounced' ] && echo $(echo -e "${DATA}" | grep -m 1 bounced | cut -f1 -d"b")
[ $TARGET = 'rejected' ] && echo $(echo -e "${DATA}" | grep -m 1 rejected | cut -f1 -d"r")
[ $TARGET = 'rejectwarnings' ] && echo $(echo -e "${DATA}" | grep -m 1 "reject warnings" | cut -f1 -d"r")
[ $TARGET = 'held' ] && echo $(echo -e "${DATA}" | grep -m 1 held | cut -f1 -d"h")
[ $TARGET = 'discarded' ] && echo $(echo -e "${DATA}" | grep -m 1 discarded | cut -f1 -d"d")
[ $TARGET = 'bytesreceived' ] && echo $(echo -e "${DATA}" | grep -m 1 "bytes received" | cut -f1 -d"b"|sed -e 's/k/\*1024/g' -e 's/m/\*1048576/g' -e 's/g/\*1073741824/g' |bc)
[ $TARGET = 'bytesdelivered' ] && echo $(echo -e "${DATA}" | grep -m 1 "bytes delivered" | cut -f1 -d"b"|sed -e 's/k/\*1024/g' -e 's/m/\*1048576/g' -e 's/g/\*1073741824/g' |bc)
次に、「/etc/sudoers.d/zabbix_postfix」に以下を追加しました。
zabbix ALL=NOPASSWD:/usr/local/sbin/zabbix_postfix.sh
最後に「/etc/zabbix/zabbix_agentd.d/zabbix_postfix.conf」に以下を追加しました。1行目が元々の行で、2行目以降が追加分です。
UserParameter=pf.mailq,sudo /usr/bin/mailq | grep -v "Mail queue is empty" | grep -c '^[0-9A-Z]'
UserParameter=pf.received,sudo /usr/local/sbin/zabbix_postfix.sh received
UserParameter=pf.delivered,sudo /usr/local/sbin/zabbix_postfix.sh delivered
UserParameter=pf.forwarded,sudo /usr/local/sbin/zabbix_postfix.sh forwarded
UserParameter=pf.deferred,sudo /usr/local/sbin/zabbix_postfix.sh deferred
UserParameter=pf.bounced,sudo /usr/local/sbin/zabbix_postfix.sh bounced
UserParameter=pf.rejected,sudo /usr/local/sbin/zabbix_postfix.sh rejected
UserParameter=pf.rejectwarnings,sudo /usr/local/sbin/zabbix_postfix.sh rejectwarnings
UserParameter=pf.held,sudo /usr/local/sbin/zabbix_postfix.sh held
UserParameter=pf.discarded,sudo /usr/local/sbin/zabbix_postfix.sh discarded
UserParameter=pf.bytesreceived,sudo /usr/local/sbin/zabbix_postfix.sh bytesreceived
UserParameter=pf.bytesdelivered,sudo /usr/local/sbin/zabbix_postfix.sh bytesdelivered
Zabbix Agentの再起動
新しいスクリプトを追加したので、zabbix-agentを再起動します。
systemctl restart zabbix-agent.service
Zabbix Serverへのテンプレート登録と設定変更
template_postfix.xmlを作業用のPCにダウンロードし、Zabbix管理画面からテンプレート登録します。

登録したテンプレートの設定を変更します。インポートしたテンプレートのアイテムを選択すると、タイプがzabbixトラッパーになっているものがあります。これらを全てZabbix エージェントに変更します。
下図のような感じで、更新間隔はもともとあったZabbixエージェントと同じように60秒間隔にします。

zabbixトラッパーとなっていたものは、上記画面のように全てZabbixエージェントに変更します。
上記までで登録したテンプレートを、該当ホストに括り付けて設定完了です。データは以下のように取れました。

初回取り込み時に、それまで蓄積したログ全部を取り込んでしまうので、グラフがはねてしまうのですが、以降は60秒おきにデータ取得するようになるので、運用していくうちに普段の数字がつかめると思います。
最後に
今回は他のサーバソフトウェアに応用しやすそうなzabbix-postfixを使わせて頂きました。作者の方、どうもありがとうございました。
zabbix-postfixを改造することで、他のログ監視に役立てられるノウハウが蓄積されました。LinuxのFWログ等も普段は適当に流し読みしているだけなのですが、分析コマンド有無を確認したうえで、同様に処理を作りこむことで容易に監視が出来るのかと思います。