Linux/postfixのメール受信強化(SpamAssasinなど)

SpamAssassin ・・・って、名前が怖いよ(2016-12-05、追記:2019-10-12)


(※ここにある情報は古いです。CentOS Stream8での手順 を別途メモしています)


postfix を使ったメールの送受信について
SPF や DKIM を使って送信することは、DNS設定とopendkimでできたのですが、
受信する際の不安が残るので、以下の方法で対応していきます。

SPF で受信する(policyd-spf)

postfix でメール受信する際に、SPFヘッダを付けてもらうようにします。
(なお、DKIMヘッダは opendkim が付けてくれます)

今回は policyd-spf というパッケージを入れます。 perl版とpython版があるようです。(pythonの方が多機能?)

後で気が付いたのですが・・・

# yum install pypolicyd-spf
(中略)
Installing:
 pypolicyd-spf              noarch              1.3.2-1.el7                 epel               44 k
Installing for dependencies:
 postfix                    x86_64              2:2.10.1-6.el7              base              2.4 M

おや!? postfix が依存に入っている!
私の場合、ソースからビルドしているので、yum でも入れられたら困ります!
(後で気が付いたくらいなので実害は無かったとは思うのですが、気持ち悪いので)
という訳で、ソースから(せっかくだから)perl 版も入れてみることにしました。後述。

pypolicyd-spfのインストール

こちらは yum から入れてみました。
もちろん ソースからも可能かと思います。

# yum install pypolicyd-spf
(依存するパッケージも含めて、入れてくれます)
# find / -name "policyd*"
(・・・インストール後、どこに入ったのか分からないので探してます)
 /etc/python-policyd-spf/policyd-spf.conf
 /usr/libexec/postfix/policyd-spf
 など

「man policyd-spf.conf」で、設定方法がわかります。
この時点では SPF を要求するけど、
SPF 判定できない場合のメール受信拒否まではしないので、
所々の設定を False に変えています。後日、チューニングしましょう。

# vi /etc/python-policyd-spf/policyd-spf.conf
HELO_reject = False         # Fail から False へ変更
Mail_From_reject = False    # Fail から False へ変更

postfix との連携

次は postfix の設定です。 まずは master.cf へ追記します。

# vi /etc/postfix/master.cf
(以下の内容を追記)
policy-spf unix  -       n       n       -       -       spawn
  user=nobody argv=/bin/python /usr/libexec/postfix/policyd-spf
                 /etc/python-policyd-spf/policyd-spf.conf

python のPATHは「which python」等で探します。
policyd-spf と conf は、さっき find したPATHを参照。

余談ですが、master.cf に複数行追加するときは、
行頭にスペースが有る/無いで意味が変わるので注意すること。
(暫くそのミスに気が付かなかったもので・・・よく忘れます)

次に、main.cf への追記です。

# vi /etc/postfix/main.cf
smtpd_recipient_restrictions =
        permit_mynetworks
        (中略)
        check_policy_service unix:private/policy-spf # この行を追記

policy-spf_time_limit = 1800

smtpd_recipient_restrictions の内容は環境によって違うと思いますが、
最後に、さきほどの policy-spf を追加しています。
また、policyd-spf_time_limit は デフォルト値でも良い気がしますが、
(~time_limit 行が無いと、command_time_limit=1000s が使われる)
このパラメータの存在を忘れないように [wink] 、念のため暫定値を書きました。

さぁ、postfix の設定を再読み込みします。

# service postfix reload

policyd-spf-perl のインストール

前述の通り、自分は yum から入れられないことに(随分経ってから)気が付いたので
ソースから・・・なら perl で、入れます。
yum を使わないのは、単に私の環境だと yum で見つからなかったからです。

www.openspf.org から、
2019.10現在ではwww.openspf.org は無くなってしまっていましたが、
launchpad.net/postfix-policyd-spf-perl は有りました。
ソースコードはこちらからダウンロードできます。

# tar zxvf postfix-policyd-spf-perl-2.010.tar.gz
# cd postfix-policyd-spf-perl-2.010/
# cp postfix-policyd-spf-perl /usr/local/lib/policyd-spf-perl
# ls -l /usr/local/lib/policyd-spf-perl
(パーミッションが、postfixから実行できるものであることを確認しておく)

展開からインストールまでやってしまいましたが、
方法は全て同梱の INSTALL テキストに書いてあります。
そして、そのテキストの頭にあるように、必要なperlモジュールを追加します。

モジュール追加その1 cpan

# cpan NetAddr::IP
# cpan Mail::SPF
# cpan Sys::Hostname::Long

モジュール追加その2 yum

# yum install perl-Mail-SPF perl-NetAddr-IP perl-Sys-Hostname-Long

cpanの初回起動時は初期設定が必要です。後で変更できるのでデフォルトでも良いかと。
ただ、私はまだ全く使いこなせていないので、
全体では無くローカル(~/perl5) にモジュール追加されて、-cしたり-fしたり、
その後で yum で入れれば良かったなぁ、と思いました・・

いずれにせよ、インストールユーザ以外で

$ /usr/local/lib/policyd-spf-perl # 試しに実行してみる
$ perl -e 'use NetAddr::IP'       # モジュールを呼んでみる

等やってみて「Can't locate ~.pm in @INC」のような見つかりませんエラーが出なければOKです。

postfix との連携

戻って、あとはINSTALLテキスト通りにpolicyd-spf-perlの設定です。

まずは master.cf へ追記します。

# vi /etc/postfix/master.cf
(以下の内容を追記)
policy-spf unix  -       n       n       -       -       spawn
   user=nobody argv=/usr/local/lib/policyd-spf-perl

次に、main.cf への追記です。これは python版と同じ内容です。

# vi /etc/postfix/main.cf
smtpd_recipient_restrictions =
        permit_mynetworks
        (中略)
        check_policy_service unix:private/policy-spf # この行を追記

policy-spf_time_limit = 1800

python版と違って、perl版には configとかはありません。

受信してみよう

これで、他のSPFに対応しているメールから、
このメールサーバ目がけて送信して、結果をメールのプロパティ等で確認しましょう。

Received-SPF: Pass (sender SPF authorized)

等の記載が増えていると思います。

Let's SpamAssasin!

SPF/DKIM のチェック結果次第で「受け取らない」とはっきり決めるなら、
postfix の header_checks を使って REJECT か FILTER するという手もありそうですが、
今回は今後のことも踏まえて(・・・というより興味から)
より総合的にスパム判定してくれる「SpamAssassin」にチャレンジしたいと思います。

インストール

yum で入れました。
結構な数の依存ファイル(perlライブラリ群)を入れる必要があるようなので、
yumで自動で入れてもらうと楽ですね。

# yum install spamassassin

設定

インストールはできたので。さっそく設定を、
と、思ったけど、現時点では設定イメージがわかない。
(ググったり、「perldoc Mail::SpamAssassin::Conf」に目を通したりしましたが)
スタートはほぼデフォルト値に落ち着きました。今後更新&チューニングします。

/etc/mail/spamassassin/local.cf の概要

required_hits 5
report_safe 1
rewrite_header Subject [SPAM]
add_header ham HAM-Report _REPORT_

postfix との連携方法は、以下に書いてありました。
https://wiki.apache.org/spamassassin/IntegratedSpamdInPostfix
この例に倣って、PATH は直して、/usr/bin/spamfilter.sh を作りました。

/usr/bin/spamfilter.sh を作成。少し内容を変えています。

(前半のコメント部分は省略)
 SENDMAIL=/sbin/sendmail
 #SPAMASSASSIN=/bin/spamc   # 負荷が増えたら、spamc & spamd 構成へ移行する予定。
 SPAMASSASSIN=/bin/spamassassin
 
 logger <<<"Spam filter piping to SpamAssassin, then to: $SENDMAIL $@"
 ${SPAMASSASSIN} | ${SENDMAIL} "$@"
 
 exit $?

動作確認用に、メール受信の度に loggerで syslog に書いてますが、後日削除。
上のコメントに書いている通り、
spamc(クライアント)から常駐させた spamd へ処理を依頼する形の方が
メールを処理する際の効率は良いのですが、
まずはシンプルに受信の都度 spamassassin コマンドを起動する形にしました。
詳しくは「man spamc」などを参照。

あとは実行権限を変えます。

# chmod 755 /usr/bin/spamfilter.sh

postfix との連携

そして、これらをSPFの時と同様に postfix へ取り込みます。

# vi master.cf
(まず、smtp に -o オプションを追加)
smtp      inet  n       -       n       -       -       smtpd -o content_filter=spamfilter
(そして最終行に以下を追加)
spamfilter unix  -       n       n       -       -       pipe
  flags=Rq user=daemon argv=/usr/bin/spamfilter.sh -oi -f ${sender} ${recipient}

そして、postfix を再読み込みして、受信準備は完了です。

# service postfix reload

受信してみよう

このメールサーバへメールを送信してみると、
メールヘッダに色々追記されることが確認できました。
メールソフトのプロパティから内容を見てみましょう。

X-Spam-Checker-Version: SpamAssassin x.x.x (20xx-xx-xx) on
	xxxx.ne.jp
X-Spam-Level: xx
X-Spam-Status: No, score=2.3 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
   DKIM_VALID_AU,DNS_FROM_AHBL_RHSBL,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,
   RCVD_IN_MSPIKE_H2,SPF_PASS autolearn=no autolearn_force=no version=3.4.0
X-Spam-HAM-Report: 
   *  2.4 DNS_FROM_AHBL_RHSBL RBL: Envelope sender listed in dnsbl.ahbl.org
   *      [listed in xxx.ne.jp.rhsbl.ahbl.org.	IN]
   [A]
   * -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no
   *      trust
   *      [xx.xx.xx.xx listed in list.dnswl.org]
   * -0.0 SPF_PASS SPF: sender matches SPF record
   * -0.0 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2)
   *      [xx.xx.xx.xx listed in wl.mailspike.net]
   *  0.0 HTML_MESSAGE BODY: HTML included in message
   * -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's
   *       domain
   *  0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily
   *      valid
   * -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature

やたら詳細なのは、local.cf の HAM-Report _REPORT_ の出力ですかね。後日変えます。
でも、無事検証できていることが確認できました!SPFも、DKIMも!

上記の内容から、
HAM-Report や tcpdump を見ると、受け取ったメールのドメインが大丈夫なのか、
dnswl.org やら、mailspike.net やらに随時問い合わせに行っているんですね。

そして、万が一 スパムメールを受信すると、
先ほど「/etc/mail/spamassassin/local.cf」で設定したように、
「SPAM」という件名でメールが来ます。
スパムメール本体は、その添付ファイルとして受け取れます。
例えばこんな内容のメールです。

Content preview:  スパムメール本文のプレビュー

Content analysis details:   (5.6 points, 5.0 required)

pts rule name              description
---- ---------------------- --------------------------------------------------
0.4 NO_DNS_FOR_FROM        DNS: Envelope sender has no MX or A DNS records
-1.0 ALL_TRUSTED            Passed through trusted hosts only via SMTP
0.8 DKIM_ADSP_NXDOMAIN     No valid author signature and domain not in DNS
1.2 MISSING_HEADERS        Missing To: header
2.4 DNS_FROM_AHBL_RHSBL    RBL: Envelope sender listed in dnsbl.ahbl.org
                           [listed in localhost.xxxxxt.rhsbl.ahbl.org.]
                           [IN	A]
1.8 MISSING_SUBJECT        Missing Subject: header	

なお、この例は自分宛てに怪しいメール作って送ったものです。
総合得点が基準値を超えたので(5.0 required を超えて 5.6 points 獲得)
おめでとう、みごとスパム認定です!


あとは、怪しいメールが来る度に、sa-learn コマンドにメールを食べさせて、
学習させれば spamassasin が更に強い子に成長するでしょう。
たくさん食べて大きくおなりなさい・・・いや、やめて、スパム一通も来ないで。 [worried]

ルールの更新(sa-update)

私のCentOS 7 環境だと、yum install 時に
/etc/cron.d/sa-update で sa-update.cron を呼ぶようになっていました。
が、
中身を見ると、うちでは前述の通り daemon起動していないこともあり、
(spamfilter.shの設定で都度呼ぶので、spamd を起動していないので)
特に何もせずに exit していた模様。うへぇ。
・・・まぁ、その間も spamassasin はきっちり仕事してくれていましたが。

# vi /etc/sysconfig/sa-update
SAUPDATE=yes に設定する

これで定期的に sa-update が動いて、
メールチェックルールを最新版にしてくれます。*1


*1 例えばデフォルトだと /var/lib/spamassassin/ 配下のルールを更新してます。ざっと眺めると、何をもってSpam認定しているかの具体例がみれて面白いです。