up_l dmy up_r
dmy home dmy dmy dmy

●postfixのスパムメール対策(排除率は99.975%だった)●
(2008/01/26更新)


※(2008/01/26追加)
動的IPの検査項目を追加。
動的IP及び逆引きできないホストでエンベロープのFromが特定(主にフリーメール)のFQDNだった場合は要求を拒否する設定を追加。
cidr_tableの自動更新時に「133.0.0.0/8」を追加するよう修正。

※(2007/11/12追加)
最近、本記事に関してS25R方式を導入しているということで、リンクして下さるサイト様をぼちぼち見掛けるのですが、
本記事ではあくまでS25R方式を参考にしたスパム対策でありS25R方式ではありませんのでご注意下さい。
S25R方式を導入されたい方はスパム対策技術さんをご一読下さい。

※(2007/7/18追加)
本スパムメール対策は基本的に海外ホストからの非スパムメールを受取ることがほとんど無く、
国内ホストからのスパムメールが少ない環境の方
ならば幸せになれるかもしれません、
それ以外の環境で使用すると思わぬ弊害(reject_unknown_clientによるREJECT等)や逆効果といった自体を招く可能性がありますので、
導入される場合は以下の説明を一通り目を通して頂き、貴殿の運用ポリシーに合っているか等十分検討して下さい。


従来のスパムメール対策Postfixのスパムメール対策Ver3で紹介したcidr_table等細かい修正を加えて、
どのくらいREJECTの効果が上がるか?、また誤判定を減らすことができるか?ということを調査したかったので1ヶ月間統計を取りました。

今回のスパムメール対策の趣旨は基本的には従来と同じ運用ポリシーですが、ウチに来るスパムメールのほとんどが海外ホストから来ているということに着目して、
国内のIPを掻き集めてそれをcidr_tableに追加することで、国内ホストからの接続だった場合はsmtpd_client_restrictionsの評価をスキップして、
海外ホストからの接続だった場合のみsmtpd_client_restrictionsを評価させるというのが今回の目的だった訳です。

国内ホストのIPの収集、cidr_tableの作成についてはCIDRを算出するスクリプトを作ってみた。をご覧下さい。

http://ftp.apnic.net/stats/apnic/delegated-apnic-latestは日々更新される為、毎回手動でcidr_tableを更新するのも大変なので、
APNICで管理している国内ホストのIPを自動で収集してcidir_tableの更新も自動化するようにしました。

運用ポリシーは下記のとおりです。

» Postfix設定パラメータによる制限の誤判定を限りなく0にする
» 配送されたメールがスパムと判定された場合はそのメールを隔離する
» 隔離されたスパムメールの中に非スパムメールが混在していないか確認できるようにする
» 隔離されたスパムメールを容易に削除できるようにする
» 国内ホストからのメールは制限を甘くする
» 海外ホストからのメールは制限を厳しくする

■Postfix設定パラメータによる制限の設定

「S25R方式」を参考にした制限
ルール1:逆引きFQDNの最下位のホスト名にハイフン(-)で2つ以上に区切られた1〜3桁の数字が含まれる場合は要求を拒否
ルール2:逆引きFQDNの最下位のホスト名にドット(.)で2つ以上に区切られた1〜3桁の数字が含まれる場合は要求を拒否
ルール3:逆引きFQDNの最下位のホスト名にエックス(x)で2つ以上に区切られた1〜3桁の数字が含まれる場合は要求を拒否
ルール4:逆引きFQDNの最下位のホスト名に連続した4つ以上の数字が含まれる場合は要求を拒否
ルール5:逆引きFQDNの最下位のホスト名に連続した3桁以上の数字と連続した3桁以上の英字の並びが含まれる場合は要求を拒否
ルール6:逆引きFQDNの最下位のホスト名に連続した3桁以上の英字と連続した3桁以上の数字の並びが含まれる場合は要求を拒否
ルール7:逆引きFQDNの最下位のホスト名に連続した3桁以上の数字と連続した3桁以上の英字がハイフン(-)で2つ以上に区切られている場合は要求を拒否
ルール8:逆引きFQDNの最下位のホスト名に連続した3桁以上の英字と連続した3桁以上の数字がハイフン(-)で2つ以上に区切られている場合は要求を拒否
ルール9:逆引きFQDNの最下位のホスト名に「dhcp、dial、ppp、dsl、flets、ftth、cable」が含まれる場合は要求を拒否

●/etc/postfix/client_restrictionsの作成
> vi /etc/postfix/client_restrictions

------------------------------------------------------------------------------
# *** WHITE LIST ***
#/^192\.168\.1\.2$/                                            OK

# *** BLACK LIST ***
#
#/^219\.117\.232\.107$/                                        REJECT
#/^59\.106\.20\.223$/                                          REJECT
#/^124\.37\.15\.8$/                                            REJECT
#/^ns1\.mdhfc\.com$/                                           REJECT

# *** GENERIC PROTECTION ***
# [rule 1]
/^[^.]*.*[0-9]{1,3}-[0-9]{1,3}.*\..+\.[a-z]/                   450 may not be mail exchanger
# [rule 2]
/^[^.]*.*[0-9]{1,3}\.[0-9]{1,3}.*\..+\.[a-z]/                  450 may not be mail exchanger
# [rule 3]
/^[^.]*.*[0-9]{1,3}x[0-9]{1,3}.*\..+\.[a-z]/                   450 may not be mail exchanger
# [rule 4]
/^[^.]*.*[0-9]{4}.*\..+\.[a-z]/                                450 may not be mail exchanger
# [rule 5]
/^[^.]*.*[0-9]{3,}[a-z]{3,}.*\..+\.[a-z]/                      450 may not be mail exchanger
# [rule 6]
/^[^.]*.*[a-z]{3,}[0-9]{3,}.*\..+\.[a-z]/                      450 may not be mail exchanger
# [rule 7]
/^[^.]*.*[0-9]{3,}-[a-z]{3,}.*\..+\.[a-z]/                     450 may not be mail exchanger
# [rule 8]
/^[^.]*.*[a-z]{3,}-[0-9]{3,}.*\..+\.[a-z]/                     450 may not be mail exchanger
# [rule 9]
/^[^.]*.*(dhcp|dial|ppp|dsl|flets|ftth|cable).*\..+\.[a-z]/    450 may not be mail exchanger
------------------------------------------------------------------------------

●/etc/postfix/client_cidrの作成
getApnicCidr.txtをDLして下記を適宜修正して下さい。

> wget http://cmf.ohtanz.com/download/getApnicCidr.txt
> vi getApnicCidr.txt

------------------------------------------------------------------------------
1: #!/usr/bin/perl                              #PerlのPATH
8: my $CIDR_TABLE = '/etc/postfix/client_cidr'; #postfix用cidr_tableのPATH
------------------------------------------------------------------------------
修正が終わったらgetApnicCidr.plにリネームしてパーミッションを変更します。

> mv getApnicCidr.txt /usr/local/bin/getApnicCidr.pl
> chmod 700 /usr/local/bin/getApnicCidr.pl

※getApnicCidr.plを実行して/etc/postfix/client_cidr.dbが生成されていたら成功です。後は1日1回自動更新されるようにgetApnicCidr.plをcronに追加しておきます。

●/etc/postfix/client_matching、/etc/postfix/sender_matchingの作成
動的IP及び逆引きできないホストでエンベロープのFromが特定のFQDNだった場合は要求を拒否する設定

> vi /etc/postfix/client_matching
------------------------------------------------------------------------------
# [rule 1]
/^[^.]*.*[0-9]{1,3}-[0-9]{1,3}.*\..+\.[a-z]/                    clientsender_matching
# [rule 2]
/^[^.]*.*[0-9]{1,3}\.[0-9]{1,3}.*\..+\.[a-z]/                   clientsender_matching
# [rule 3]
/^[^.]*.*[0-9]{1,3}x[0-9]{1,3}.*\..+\.[a-z]/                    clientsender_matching
# [rule 4]
/^[^.]*.*[0-9]{4}.*\..+\.[a-z]/                                 clientsender_matching
# [rule 5]
/^[^.]*.*[0-9]{3,}[a-z]{3,}.*\..+\.[a-z]/                       clientsender_matching
# [rule 6]
/^[^.]*.*[a-z]{3,}[0-9]{3,}.*\..+\.[a-z]/                       clientsender_matching
# [rule 7]
/^[^.]*.*[0-9]{3,}-[a-z]{3,}.*\..+\.[a-z]/                      clientsender_matching
# [rule 8]
/^[^.]*.*[a-z]{3,}-[0-9]{3,}.*\..+\.[a-z]/                      clientsender_matching
# [rule 9]
/^[^.]*.*(dhcp|dial|ppp|dsl|flets|ftth|cable).*\..+\.[a-z]/     clientsender_matching
# [rule 10]
/^unknown$/                                                     clientsender_matching
------------------------------------------------------------------------------
※内容はclient_restrictionsのルール1〜9と逆引きできないホストを追加したものです。
※clientsender_matchingはsmtpd_restriction_classesで作成したPostfix制限クラスです。

> vi /etc/postfix/sender_matching

------------------------------------------------------------------------------
/.*@yahoo\.co\.jp$/             450 may not be mail exchanger
/.*@hotmail\.co\.jp$/           450 may not be mail exchanger
/.*@hotmail\.com$/              450 may not be mail exchanger
/.*@gmail\.com$/                450 may not be mail exchanger
/.*@nifmail\.jp$/               450 may not be mail exchanger
------------------------------------------------------------------------------
※sender_matchingの内容はお好みで適宜変更して下さい。

●/etc/postfix/main.cfの編集

> vi /etc/postfix/main.cf
------------------------------------------------------------------------------
disable_vrfy_command = yes                                      #SMTP VRFYコマンドを無効にする。

smtpd_client_restrictions =                                     #SMTPコネクション時の制限
   permit_mynetworks                                            #自ネットワークからの要求は許可
   check_client_access btree:/etc/mail/dracd                    #POP before SMTP認証済ホストからの要求は許可
   check_client_access cidr:/etc/postfix/client_cidr            #国内ホストからの要求は許可
   reject_unknown_client                                        #逆引きできないホストからの要求は拒否
   check_client_access regexp:/etc/postfix/client_restrictions  #動的IPからの接続だった場合は要求を拒否
   reject_unlisted_recipient                                    #宛先不明なアドレスへの要求は拒否
   reject_unauth_destination                                    #自ネットワーク以外への要求は拒否

smtpd_helo_required = yes                                       #HELO又はEHLOコマンドを送信してこない要求は拒否

smtpd_restriction_classes =                                     #Postfix制限クラスの定義
   clientsender_matching

clientsender_matching =
   check_sender_access regexp:/etc/postfix/sender_matching      #client_matching及びsender_matchingでマッチした要求を拒否

smtpd_sender_restrictions =                                     #MAIL FROMコマンド時の制限
   permit_mynetworks                                            #自ネットワークからの要求は許可
   check_client_access btree:/etc/mail/dracd                    #POP before SMTP認証済ホストからの要求は許可
   check_sender_access regexp:/etc/postfix/sender_restrictions  #メールアドレスのaccessテーブルによる制限
   check_client_access regexp:/etc/postfix/client_matching      #動的IPからの接続だった場合はsender_matchingの検査を実施
   reject_unknown_sender_domain                                 #メールアドレスのホスト名がDNS A又はMXレコードを持っていない場合は要求を拒否
   reject_non_fqdn_sender                                       #メールアドレスのホスト名がFQDN形式ではない場合は要求を拒否
   reject_unverified_sender                                     #メールアドレスがバウンスしたり配送先に到達できない場合は要求を拒否

smtpd_recipient_restrictions =                                  #RCPT TOコマンド時の制限
   permit_mynetworks                                            #自ネットワークからの要求は許可
   check_client_access btree:/etc/mail/dracd                    #POP before SMTP認証済ホストからの要求は許可
   reject_unauth_destination                                    #自ネットワーク以外への要求は拒否
------------------------------------------------------------------------------
※/etc/postfix/sender_restrictionsは個別に許可又は拒否したいアドレス(エンベロープのFROM)を指定します。
※編集が終わったらpostfix reloadを実行します。
※参考までに当方の現在のmain.cfです。

※ここまでの制限の流れは以下のようになります。

1.SMTP VRFYコマンドを無効にする
2.HELO又はEHLOコマンドを送信してこない要求は拒否
3.SMTPコネクション時の制限
   3-1.自ネットワークからの要求は許可
   3-2.POP before SMTP認証済ホストからの要求は許可
   3-3.国内ホストからの要求は許可
   3-4.逆引きできないホストからの要求は拒否
   3-5.動的IPからの接続だった場合は要求を拒否
   3-6.宛先不明なアドレスへの要求は拒否
   3-7.自ネットワーク以外への要求は拒否
4.MAIL FROMコマンド時の制限
   4-1.自ネットワークからの要求は許可
   4-2.POP before SMTP認証済ホストからの要求は許可
   4-3.accessテーブル(sender_restrictions)に指定したアドレス(又はFQDN等)は要求を拒否又は許可
   4-4.動的IPからの接続だった場合はアドレス(又はFQDN等)の検査を実施
      4-5.accessテーブル(sender_matching)にマッチしたFQDNは要求を拒否
   4-6.メールアドレスのホスト名がDNS A又はMXレコードを持っていない場合は要求を拒否
   4-7.メールアドレスのホスト名がFQDN形式ではない場合は要求を拒否
   4-8.メールアドレスがバウンスしたり配送先に到達できない場合は要求を拒否
5.RCPT TOコマンド時の制限
   5-1.自ネットワークからの要求は許可
   5-2.POP before SMTP認証済ホストからの要求は許可
   5-3.自ネットワーク以外への要求は拒否

■ベイジアンスパムフィルタ「bsfilter」の導入

bsfilterのインストール
> apt-get install bsfilter

●作業ディレクトリの作成
> mkdir /var/lib/bsfilter
> chmod 755 /var/lib/bsfilter

●bsfilter.confの作成
> vi /etc/bsfilter.conf

------------------------------------------------------------------------------
homedir /var/lib/bsfilter         #作業ディレクトリ
insert-revision                   #メールのヘッダに「X-Spam-Revision」を追加
insert-flag                       #メールのヘッダに「X-Spam-Flag」を追加
insert-probability                #メールのヘッダに「X-Spam-Probability」を追加
auto-update                       #cleanかspamか判定した後、その結果をtokenデータベースに追加し確率データベースも更新
pipe                              #標準出力する(procmail用)
------------------------------------------------------------------------------

http://www.db.is.kyushu-u.ac.jp/computer/solarisserver/bs.spamからスパムなトークンを頂いてくる
> wget http://www.db.is.kyushu-u.ac.jp/computer/solarisserver/bs.spam

●頂いてきたスパムなトークンを学習させる
> bsfilter --config-file /etc/bsfilter.conf --import-spam ./bs.spam

●クリーンなメールを学習させる
> bsfilter --config-file /etc/bsfilter.conf --mbox --add-clean ./***.mbx
※普段使っているメーラーがmbox形式だったのでそれを全部入れてみました。

●sdbmを更新
> bsfilter --update --config-file /etc/bsfilter.conf

●オーナーの関係でデータベースが読み込みできなかったのでパーミッションを変更
> chmod 666 /var/lib/bsfilter/*

●スパムと認識されたクリーンなメールを学習させるスクリプトを作成
> vi /usr/local/bin/bsfilter-clean.sh

------------------------------------------------------------------------------
#!/bin/sh

FILE=$1

if [ -n "$1" ]; then
    bsfilter --homedir /var/lib/bsfilter --sub-spam --add-clean --update ${FILE}
    chmod 666 /var/lib/bsfilter/*
fi
------------------------------------------------------------------------------
> chmod 700 /usr/local/bin/bsfilter-clean.sh

※使い方:/usr/local/bin/bsfilter-clean.sh クリーンなメール

●クリーンと認識されたスパムなメールを学習させるスクリプトを作成 > vi /usr/local/bin/bsfilter-spam.sh

------------------------------------------------------------------------------
#!/bin/sh

FILE=$1

if [ -n "$1" ]; then
    bsfilter --homedir /var/lib/bsfilter --sub-clean --add-spam --update ${FILE}
    chmod 666 /var/lib/bsfilter/*
fi
------------------------------------------------------------------------------
> chmod 700 /usr/local/bin/bsfilter-spam.sh

※使い方:/usr/local/bin/bsfilter-clean.sh スパムなメール

■procmailから使用するDNSBL、URLBLチェックスクリプトの導入
※自作のPerlスクリプト「procdnsbl」を使用します。(base64encode、quoted-printable対応)
procdnsblはDNSBL、URLBLに該当した場合にメールヘッダーにX-DnsBL、X-UrlBLを付与します。

> wget http://cmf.ohtanz.com/download/procdnsbl.txt
> mv procdnsbl.txt /usr/local/bin/procdnsbl
> chmod 755 /usr/local/bin/procdnsbl
※利用するDNSBL、URLBLを変更したい場合はprocdnsbl内の15行目の@DNS_BL、20行目の@URL_BLを変更して下さい。

■procmailの準備

●procmail用ログファイルの作成
> touch /var/log/procmail
> chmod 666 /var/log/procmail

●procmail用ログファイルをlogrotateに追加 > vi /etc/logrotate.d/procmail

------------------------------------------------------------------------------
/var/log/procmail {
    create 666 root root
}
------------------------------------------------------------------------------

●スパムと判定されたメールを格納するspamディレクトリの作成
> mkdir ~hoge/Maildir/spam
> chown hoge:hoge ~hoge/Maildir/spam
> chmod 700 ~hoge/Maildir/spam

●spamディレクトリに入ったメールを1つのメールにまとめるスクリプトの作成
※~hoge/Maildir/newに擬似的なメールを生成します。
> vi /usr/local/bin/spamsend.sh

------------------------------------------------------------------------------
#!/bin/sh

export LANG=en
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
CONF=/usr/local/etc/spamsend.txt
HOST=`hostname`
MAILDATE=`date "+%a, %d %b %Y %H:%M:%S +0900"`

LIST=`cat $CONF`

for u in $LIST
do
    COUNT=`ls /home/$u/Maildir/spam/ | wc -l`

    if [ $COUNT -ne 0 ]; then
        FILEDATE=`date '+%s'`
        MAIL=/home/$u/Maildir/new/$FILEDATE.$$.$HOST

        echo "Date: $MAILDATE" >> $MAIL
        echo "From: postmaster@example.com" >> $MAIL
        echo "To: $u@example.com" >> $MAIL
        echo "Subject: SPAM MAIL LIST" >> $MAIL
        echo "MIME-Version: 1.0" >> $MAIL
        echo "Content-Type: text/plain; charset=iso-2022-jp" >> $MAIL
        echo "Content-Transfer-Encoding: 7bit" >> $MAIL
        echo "" >> $MAIL
        echo "[[[[[SPAM MAIL LIST]]]]]" >> $MAIL
        echo "" >> $MAIL

        nkf -mj /home/$u/Maildir/spam/* >> $MAIL
        chown $u:$u $MAIL
        chmod 600 $MAIL
        rm -f /home/$u/Maildir/spam/*
    fi
done
------------------------------------------------------------------------------

※From、Toのメールアドレスは適宜修正して下さい。

> chmod 700 /usr/local/bin/spamsend.sh

●spamsend.sh対象のアカウントを管理するファイルの作成
> vi /usr/local/etc/spamsend.txt

------------------------------------------------------------------------------
hoge
hogehoge
------------------------------------------------------------------------------

●spamsend.shをcronに追加
> vi crontab -e

------------------------------------------------------------------------------
0 0-23/4 * * * /usr/local/bin/spamsend.sh
------------------------------------------------------------------------------
※4時間おきに実行

●~hoge/.forwardの作成
> vi ~hoge/.forward

------------------------------------------------------------------------------
"|IFS=' ' && exec /usr/bin/procmail -f- || exit 75 #~/Maildir/"
------------------------------------------------------------------------------

●~hoge/.procmailrcの作成
> vi ~hoge/.procmailrc

------------------------------------------------------------------------------
PATH=/bin:/usr/bin:/usr/local/bin
LOGFILE=/var/log/procmail
MAILDIR=${HOME}/Maildir
DEFAULT=${MAILDIR}

:0 fw
| /usr/local/bin/procdnsbl

:0
* ^X-(Dns|Url)BL: *
${MAILDIR}/spam/.

:0 fw
| /usr/bin/bsfilter --config-file /etc/bsfilter.conf

:0
* ^X-Spam-Probability: *(1|0\.[89])
${MAILDIR}/spam/.

:0
${MAILDIR}/
------------------------------------------------------------------------------

■上記スパムメール対策の統計(2007/4/26-2007/5/26)

内容 割合(%) 合計
メール総数 ----- 30,298
非スパムメールで正常に配送されたメール ----- 453
非スパムメールで誤判定によりREJECTされたメール ----- 1
スパムメール総数 ----- 29,844
reject_unknown_client(smtpd_client_restrictions)によるREJECT 44.317% 13,226
client_restrictions(smtpd_client_restrictions)によるREJECT 27.043% 8,071
reject_unauth_destination(smtpd_recipient_restrictions)によるREJECT 16.666% 4,974
reject_unverified_sender(smtpd_sender_restrictions)によるREJECT 9.415% 2,810
reject_unknown_sender_domain(smtpd_sender_restrictions)によるREJECT 1.722% 514
procmail(bsfilter、procdnsbl)によるREJECT 0.646% 193
その他のREJECT(Relay access denied等) 0.164% 49
配送されたスパムメール 0.023% 7
Postfix設定パラメータトータルのREJECT 99.329% 29,644
Postfix設定パラメータ、procmail(bsfilter、procdnsbl)トータルのREJECT 99.975% 29,837

※上記統計を取るために、ブラウザ上からPostfixでREJECTされたログ、procmailで配送されたログを確認するためのスクリプトを作成しました。

RejectMail

RejectMail.zipをダウンロードして同封されているGrepMaillog.pl、index.cgiを適宜修正して下さい。

GrepMaillog.pl----------------------------------------------------------------
 1:#!/usr/bin/perl
 ・
 ・
 ・
 8:my $VIEWDIR  = '/hoge/RejectMail';                  #WWWアクセス可能な作業ディレクトリ
 9:my $MAILLOG  = '/var/log/maillog';                  #PostfixログのPATH
10:my $PROCMAIL = '/var/log/procmail';                 #procmailログのPATH
11:my $WWWUSER  = 'www';                               #WWW上からCGI実行可能なユーザー
------------------------------------------------------------------------------
index.cgi---------------------------------------------------------------------
 1:#!/usr/bin/perl
 ・
 ・
 ・
 5:my $VIEW_DIR = '/hoge/RejectMail';                  #WWWアクセス可能な作業ディレクトリ
 6:my $VIEW_URI = 'http://www.example.com/RejectMail'; #作業ディレクトリのURI
 7:my $MY_SCR   = 'index.cgi';                         #このスクリプトのファイル名
------------------------------------------------------------------------------

※これら2つのスクリプトを作業ディレクトリに設置してパーミッションを変更後、
GrepMaillog.plを手動で実行して作業ディレクトリにyyyymmdd.prc、yyyymmdd.txtが作成されているか確認してみて下さい、
yyyymmdd.prc、yyyymmdd.txtが作成されていたら、ブラウザからindex.cgiにアクセスしてみて下さい、
何れも問題なければGrepMaillog.plをcronに追加して設置は完了です。

■あとがき

統計を取ってみた感想として、1通だけ誤判定してしまったメールがあったものの、予想以上の好結果に非常に満足しております。
まさか1ヶ月で約3万通(リトライ要求もあるのでもう少し少ないとは思いますが)ものスパムが来ているとは思ってもいませんでしたけど・・・(゜д゜)

また当方は以前からメールチェッカー「Liam」を使用しているのでLiamでspamsend.shでまとめられたスパムメール中に非スパムメールが混在していないかをチェックし、
もし混在してた場合はそのまま非スパムメールをコピペして救済するといった方法をとっています。

これだけのフィルタリングを行うとサーバーへの負荷が気になるところですが、
昨年末に自宅サーバーを余っていたCPU:Pen4 3.06Ghz、Memory:512Mx2、MB:P4P800-E Deluxeに
Vine4.0を入れて運用してますが、load averageが1.00に達することはありません。

結論として1つのフィルタリングのみに頼らず複数のフィルタリングを組み合わせることが1番効果的ではないかと思いました。

Postfix2.3からアクセステーブルでsleepが使用できる(2.2でもコンパイル時にオプションを付ければ可)そうなので、
「S25R方式」で該当した場合にsleepで遅延を起こすことで、向こうが諦めてコネクションを切ってくる
(正常なMTAであれば5分まで平気なはず?)なんてこともできるみたいです。VineでPostfix2.3のrpm早くリリースされないかな〜・・・

▲TOP

dmy
low_l dmy low_r