English

トップ レポート 講演資料(PDF) 紹介記事 ホワイトリスト ブラックリスト 監視ツール 導入事例 Q&A ブログ リンク 更新履歴 連絡先

拒絶ログソーティングスクリプト

(旧バージョン)

 S25Rスパム対策方式によって正当なメールサーバが誤って拒絶されているのを発見するのに有用なシェルスクリプトを紹介します。メールサーバがウェブサーバを兼ねているなら、このスクリプトをcgi-binディレクトリ配下のディレクトリにパスワード付きで置くことにより、ウェブブラウザで拒絶記録を容易に監視できます。コマンドとして実行することもできます。

機能
 このスクリプトは、クライアント制限によって応答コード「450」(「後で再試行せよ」の意味)で拒絶されたアクセスの記録をPostfixのメールログから抽出し(ほかの理由による拒絶は抽出されません)、再試行アクセスが連続して並ぶようにソーティングして表示します。すなわち、クライアントIPアドレス、送信者アドレス、および受信者アドレスとも同じであるアクセスは、連続した行で表示されます。それらのいずれかでも異なるアクセスは、空白行で分断して表示されます。
 また、最後に次のデータを表示します。
活用法
 正当なメールサーバは、応答コード「450」による拒絶に対して、必ず適度な時間間隔を置きながら送信を再試行します。その拒絶の記録は、このスクリプトによって連続して表示されます。したがって、ホワイトリストに登録すべき正当なメールサーバからのアクセスを見つけるのに助けになります。
 連続して表示されたアクセスが次のすべての条件を満たすならば、クライアントはおそらく、ホワイトリストに登録すべき正当なメールサーバです。  一方、連続して表示されたアクセスが次のいずれかの条件に該当するならば、クライアントはおそらく、あるいは間違いなく不正です。  クライアントが正当か不正か、いずれとも判断しにくい場合は、ひとまずホワイトリストに登録して、もし受信者からスパムの苦情が来たら登録を取り消すのがよいでしょう。

必要な設定
 HTTPデーモンの権限でメールログファイルが読めるようにアクセス権を設定してください。多くのシステムでは、以下のコマンドで設定できます。
chgrp nobody /var/log/maillog*
chmod g+r /var/log/maillog*
改造
シェルスクリプトコード
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo "Mail rejection log (450 Client host rejected) - sorted"
echo
#
# (1) Input mail log.
#
cat /var/log/maillog.1 /var/log/maillog | \
#
# (2) Extract records indicating "450 Client host rejected".
#
egrep 'reject:.+ 450 .*Client host rejected:' | \
#
# (3) Extract essential items.
#
gawk '
{
  client=substr($0, match($0, /from [^]]+\]/)+5, RLENGTH-5)
  sub(/\[/, " [", client)
  sender=substr($0, match($0, /from=<[^>]*>/), RLENGTH)
  rcpt=substr($0, match($0, /to=<[^>]*>/), RLENGTH)
  helo=substr($0, match($0, /helo=<[^>]*>/), RLENGTH)
  printf "%s %2d %s %s %s %s %s\n", $1, $2, $3, client, sender, rcpt, helo
}
' | \
#
# (4) Convert month names into month numbers.
#
gawk '
BEGIN {
  month_num["Jan"]=1
  month_num["Feb"]=2
  month_num["Mar"]=3
  month_num["Apr"]=4
  month_num["May"]=5
  month_num["Jun"]=6
  month_num["Jul"]=7
  month_num["Aug"]=8
  month_num["Sep"]=9
  month_num["Oct"]=10
  month_num["Nov"]=11
  month_num["Dec"]=12
  max_month_num=0
}
{
  $1=month_num[$1]
  if ($1>max_month_num)
    max_month_num=$1
  else if ($1<max_month_num)
    $1+=12
  printf "%3d %2d %s %s %s %s %s %s\n", $1, $2, $3, $4, $5, $6, $7, $8
}
' | \
#
# (5) Sort according to IP address, sender address and recipient address.
#
sort -k 5,7 | \
#
# (6) Insert a blank line between records with a different triplet.
#
gawk '
BEGIN {
  prev_triplet=""
}
{
  if (prev_triplet!="") {
    if (prev_triplet!=$5 $6 $7)
      print ""
  }
  print
  prev_triplet=$5 $6 $7
}
' | \
#
# (7) Convert retry records in a sequence into one line.
#
gawk '
BEGIN {
  RS=""
}
{
  gsub(/\n/, "\036")
  print
}
' | \
#
# (8) Sort according to date and time.
#
sort -k 1,3 | \
#
# (9) Reconvert retry records in a sequence into multiple lines.
#
gawk '
{
  gsub(/\036/, "\n")
  print
  print ""
}
' | \
#
# (10) Reconvert month numbers into month names.
#
gawk '
BEGIN {
  month_name[1]="Jan"
  month_name[2]="Feb"
  month_name[3]="Mar"
  month_name[4]="Apr"
  month_name[5]="May"
  month_name[6]="Jun"
  month_name[7]="Jul"
  month_name[8]="Aug"
  month_name[9]="Sep"
  month_name[10]="Oct"
  month_name[11]="Nov"
  month_name[12]="Dec"
}
{
  if ($0!="") {
    $1=month_name[($1-1)%12+1]
    printf "%s %2d %s %s %s %s %s %s\n", $1, $2, $3, $4, $5, $6, $7, $8
  }
  else
    print ""
}
' | \
#
# (11) Output sorted records with counting.
#
gawk '
BEGIN {
  Suppress_single_access_records=0
  RS=""
  acc_count=0
  host_and_rcpt=""
  msg_count=0
  seq_count=0
}
{
  retry_count=gsub(/\n/, "\n")
  acc_count+=1+retry_count
  if (index(host_and_rcpt, $5 $7)==0) {
    ++msg_count
    host_and_rcpt=$5 $7 host_and_rcpt
  }
  if (retry_count>0)
    ++seq_count
  if (!(retry_count==0 && Suppress_single_access_records)) {
    print
    print ""
  }
}
END {
  print "access count =", acc_count, \
      ", estimated message count =", msg_count, \
      ", retry sequence count =", seq_count
}
'
トップ レポート 講演資料(PDF) 紹介記事 ホワイトリスト ブラックリスト 監視ツール 導入事例 Q&A ブログ リンク 更新履歴 連絡先