白水啓章 作成 2019/08/05 更新 2023/11/17
手元ではいくつか新機能を追加して使っていますが、特に利用報告が無いため、こちらのリポジトリ内容はそのままになっています。
最新版が見たい等ありましたら、issue 等でご連絡ください。
「Postfix キュー投入前コンテンツフィルタ」の枠組みを利用したSPAM対策
https://shirouzu.jp/tech/content_filter
仕組みの説明はこちらをご覧ください。 http://www.postfix-jp.info/trans-2.2/jhtml/SMTPD_PROXY_README.html
Postfix内部に入り込んて、通信を中継します。 そして「必要があれば」それを変更します。(man in the middle のようなもの)
それを SPAMフィルタとして使うメリットは下記の通り。
- header_checks/body_checks(=1行毎の判断)よりも柔軟(=メール全体での判断が可能)。
- SPAMと見做した時点ではまだSMTP通信中のため、直接エラーコードを返せる。
(「キュー投入後コンテンツフィルタ」と違い、「受信のSMTP通信で成功」を返さない&エラーメールも発生せず)
- base64 や quoted-printable をデコードした後の判定が可能
- (メールヘッダだけでなく)SMTPレベルの MAIL FROM: / RCPT TO:、さらに XFORWARD(逆引き名、IPアドレス、ポート、HELO内容等)を含む判断が可能
- SPAM判定となり、エラーを返して受信拒否した場合も、全体の受信内容をファイルとして保存可能。
(つまり、SPAM条件が適正だったか正確な事後調査が可能。spam_dat.DBG=1以上で有効) - ホワイトリストによる除外指定が可能。
- syslog に SPAM判定されたメールの message-id 及び、マッチした正規表現リストを出力。
(SMTPにさほど詳しく無いので、フィードバック歓迎します)
1.spam_dat.py.sampleを参考に、設定ファイル(spam_dat.py)を適宜、作成/変更します。
2.content_filter.py を起動します。
3.master.cf の smtp行を下記に書き換えます。
smtp inet n - n - 20 smtpd
-o smtpd_proxy_filter=127.0.0.1:60025
-o smtpd_client_connection_count_limit=20
4.master.cf に下記を追記します。
127.0.0.1:60026 inet n - n - - smtpd
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=permit_mynetworks
-o mynetworks=127.0.0.0/8
5.Postfix をリスタートします。
6.syslog(mail.log) の content_filter出力を確認します。
下記の書式で、ホワイトリスト定義(WHITE_HEAD/WHITE_DATA)とSPAM定義(CHECK_HEAD/CHECK_DATA)を指定。 (HEADはヘッダのみ検査、DATAはヘッダ&ボディを検査)
CHECK_DATA = [
[ b'正規表現1_1', b'正規表現1_2,... ], # ルール1
[ b'正規表現2_1', b'正規表現2_2,... ], # ルール2
:
[ b'正規表現n_1', b'正規表現2_2,... ], # ルールn
]
- 指定は正規表現で行う
- 1ルールにつき、1つ以上の正規表現文字列(バイト列)を列挙
- 1ルール内の全要素がマッチ(AND条件)= そのルールにマッチ
- どれか1つのルールにマッチすると、判定終了
それ以外の設定項目の説明は spam_dat.py を参照してください。
運用中にSPAMメールが通過した時、spam_dat.py にマッチパターンを追加して対応しますが、検証に -f オプションが使えます。 DBGレベルを2以上に設定していると、全てのメールに関して、下記のような SMTP通信記録が残ります。
content_filter[18302]: smtp_log for msg_id= to /tmp/content_filter/smtp_20190811_134429_0.txt
これを使って、
content_filter -f /tmp/content_filter/smtp_20190811_134429_0.txt
などとすると、下記のように同じメールが届いた際に SPAM判定されるかどうかを事前判定できます。
SPAM判定される場合: SPAM is detected. msg_id=<xxxx> CHECK_HEAD(1) = [ regex_pattern1, regex_pattern2... ]
SPAM判定されない場合: pass msg_id=<xxxx>