やるきなし

2021/08/14 11:34 / SpamAssassin をユーザ権限でインストールする

Web サーバ兼 MTA なサーバを(ある組織で)共有で使っているのだけど,以前は SPAM 対策がされていた(おそらくプロプライエタリなもので,SPAM を隔離した旨メールが届いて,Web から確認するタイプのもの)のだけど,サーバリプレースに伴いその機能がなくなり,SPAM が多くなってきた(特に ML で悲惨)ので対策.

もともと procmail + fml でなんとなくシステムを作ってあるので(MTA は postfix と qmail の併用),procmail から簡単に使える SpamAssassin を利用する.タイトルどおりユーザ権限で,必要なものをどんどん入れていく.のメモ.

方針としては以下.

まず,SpamAssassin の機嫌を尋ねる.

ユーザ権限で CPAN でモジュールを入れると ~/perl5/ にファイルがインストールされていく.加えて ~/.cpan/ に管理用(や build)のファイルが溜まっていく.手元環境でインストールしたもの,以下.たぶん.順番とか怪しい.

Mail::SPF は /usr/sbin にファイル(spfd)を copy しようとして失敗する(権限がない; see https://rt.cpan.org/Public/Bug/Display.html?id=34768).以下のようにして $HOME/perl5/bin にインストールするようにする.spfd は今のところ使わないつもり.

cpan Mail::SPF (→ install でコケる)
cd ~/.cpan/build/Mail-SPF-v2.9.0-0 (build directory に移動)
sed 's,/usr/sbin,/home/myn/perl5/bin,' -i Build.PL
cpan .

Mail::DKIM はまず依存する Crypt::OpenSSL::RSA や Crypt::OpenSSL::Random のインストールでこける.これは,手元では独自に OpenSSL をインストールしていて,その OpenSSL のパスを探せず(あるいは設定できず)に嵌った.

Crypt::OpenSSL::RSA は Crypt::OpenSSL::Guess に依存して,これがパスを探してくれるが,カスタムなパスをどのように渡すが謎だったので,直接推定(?)させることにした.~/perl5/lib/perl5/Crypt/OpenSSL/Guess.pm@guesses という連想配列の先頭に '/home/myn/local/bin/openssl' => '/home/myn/local', を追加して,確実にこれが推定されるようにする.ほか rpath 関連もなにか問題があったような気がするが(~/.cpan/CPAN/MyConfig.pmmakepl_argLIBSINC を自前で指定していると Crypt::OpenSSL::Guess の推定が使われないとかだったように思う; makepl_arg の設定をなくし,Crypt::OpenSSL::Guess で正しく推定できれば LD_RUN_PATH 付きで build されていたような気がする).

Mail::DKIM が依存するものがインストールできたとして,次は Mail::DKIM の test でコケる.t/policy.tt/public_key.t のテストでコケていて,

cd ~/.cpan/build/Mail-DKIM-1.20200907-0
% perl -Ilib t/policy.t
1..19
ok 1 - new() works
ok 2 - parse() works
DNS query timeout for _domainkey.policy.test.authmilter.org at lib/Mail/DKIM/DNS.pm line 100.

これ,8.8.8.8 (Google の Public DNS) および 1.1.1.1 (Cloudflare の Public DNS) を DNS サーバ(リゾルバ)としてテストするのが原因(ゲートウェイをパケットが通らない環境なので...).それぞれのテストファイルのリゾルバ(のリスト)の箇所を手元の /etc/resolv.conf に合わせて修正して解決.

Net::LibIDN2 は,そもそも libidn2 (IDN (internationalized domain names) の encode/decode ライブラリ)を install するところから(システム側にヘッダファイルが用意されていない...).

wget https://ftp.gnu.org/gnu/libidn/libidn2-2.3.2.tar.gz
tar zxfv libidn2-2.3.2.tar.gz
cd libidn2-2.3.2
./configure --prefix=/home/myn/local
make
make install

そのうえで cpan Net::LibIDN2 が上記 install した libidn2 を見つけられずに当然ハマる.上記 libidn2.so の symbol が見つけられず perl module LibIDN2.so が load できない,とか.以下でなんとかする.cpan する前に ./Build したかも.いずれにせよ --extra_linker_flags でしていた option 付きで link された LibIDN2.so~/perl5/lib/perl5/x86_64-linux-thread-multi/auto/Net/LibIDN2/LibIDN2.so にインストールされれば OK.ldd LibIDN2.so で自前インストールの libidn2.so に link されているか確認できる.

cd .cpan/build/Net-LibIDN2-1.01-0
perl Build.PL --extra_compiler_flags=-I/home/myn/local/include --extra_linker_flags='-L/home/myn/local/lib -lidn2 -Wl,--rpath,/home/myn/local/lib'
cpan .

色々整った段階でようやく Mail-SpamAssassin をインストール.

tar jxfv Mail-SpamAssassin-3.4.6.tar.bz2
cd Mail-SpamAssassin-3.4.6
perl Makefile.PL CONTACT_ADDRESS=admin@example.com
make
make install

~/perl5/bin/spamassassin に install される.CONTACT_ADDRESS を指定しないとインタラクティブに聞かれる.

SpamAssassin 設定

もう疲れたので,手短に.

% spamassassin -D < mail_file

とかでテストする.はじめは rule が存在しないので sa-update で取得する(かつ cron を仕込んでおく).なお ~/perl5/var/spamassassin/3.004006/ に rule ファイルが置かれる.

~/.spamassassin/user_prefs にユーザ設定を置く.

required_score 10
score SUBJECT_ENCODED_TWICE 0
score DNS_FROM_AHBL_RHSBL 0
report_safe 0
internal_networks 1XX.XX.XX.XX....

whitelist_from ...@....

SPAM 判定のしきい値は高め(10; default は 5)に設定した.スコア 0 にしているものは,以前そのような設定にしたもので,今でもこのルールがあるかどうかは確認していない.internal_networks にはこのメールサーバに到達するために通常通過するサーバをリストする(それらの IP address の良し悪しを評価しなくなる; 評価すると SPAM Score が通常は下がる).

whitelist_fromwelcomelist_from に将来的に(Target Milestone: 4.0.0)変更になる様子だが,現時点(3.4.6)では welcomelist_from と書くと parse error になる.なお,blacklist_fromblocklist_from になる.ただし,spamassassin の report では USER_IN_BLACKLIST DEPRECATED: See USER_IN_BLOCKLIST とか表示されるので感じ悪い.

あとは procmail から spamassassin を filter として呼び出して,その結果に応じて処理をする.procmail をローカル配送に使っていないので,無理やり procmail を呼び出すようにしていて,

[~/.qmail-hoge]
|/usr/bin/procmail /home/myn/spool/procmail/hoge

そのレシピファイルで spamassassin を通して,その結果に応じて処理を変える.以下では spam@example.com に転送しているが,これはローカル配送を想定していて,結果同じメールサーバ(example.com)の Maildir に保存されていく,みないな設定にした.なお spamassassin.lock のロックファイルは $HOME/spamassassin.lock に作成される.

[/home/myn/spool/procmail/hoge]
LOGFILE=$HOME/spool/procmail/log/hoge.`date +%Y-%m`
SENDMAIL=/var/qmail/bin/sendmail
DEFAULT=$HOME/Maildir/.myn/

:0fw: spamassassin.lock
* < 10000000
| /home/myn/perl5/bin/spamassassin

:0
* ^X-Spam-Flag: YES
!spam@example.com

:0c
!myn@example.com

あと,はじめはあまり賢くないので,sa-learn --ham および sa-learn --spam で HAM (!SPAM)と SPAM の違いを覚えさせる.ある程度覚えるとあとは autolearn してくれる(はず).

追記 (2021/8/15)

高速化のために re2c をインストールして,rule ファイルを native code にコンパイルできるようにしていたのだけど,手動で sa-compile する必要があるのを忘れていた(sa-update とともに cron に仕込む).

ただし,手元の CPAN 環境では,手動で実行した感じ以下のようにエラーで落ちる.

Aug 15 14:15:06.345 [4115915] info: generic: base extraction starting. this can take a while...
Aug 15 14:15:06.345 [4115915] info: generic: extracting from rules of type body_0
100% [==========================================================================================] 5277.67 rules/sec 00m00s DONE
100% [==========================================================================================] 4722.65 bases/sec 00m00s DONE
Aug 15 14:15:07.069 [4115915] info: body_0: 1328 base strings extracted in 1 seconds
cd /tmp/.spamassassin4115915hkQrfJtmp
reading bases_body_0.in
cd Mail-SpamAssassin-CompiledRegexps-body_0
re2c -i -b -o scanner1.c scanner1.re
re2c -i -b -o scanner2.c scanner2.re
re2c -i -b -o scanner3.c scanner3.re
re2c -i -b -o scanner4.c scanner4.re
re2c -i -b -o scanner5.c scanner5.re
re2c -i -b -o scanner6.c scanner6.re
re2c -i -b -o scanner7.c scanner7.re
/usr/bin/perl Makefile.PL PREFIX=/tmp/.spamassassin4115915hkQrfJtmp/ignored INSTALLSITEARCH=/home/myn/perl5/var/spamassassin/compiled/5.026/3.004006 
Only one of PREFIX or INSTALL_BASE can be given.  Not both.
command failed: exit 25

CPAN の初期設定段階で ~/.zshrc に以下が書かれていたのが原因の様子.ということで PERL_MM_OPT= sa-compile でうまく compile できる.

PERL_MM_OPT="INSTALL_BASE=/home/myn/perl5"; export PERL_MM_OPT;

踏まえて仕込んだ cron (から呼ばれる script)はだいたい以下のような感じ.case 文は Debian の /etc/cron.daily/spamassassin を参考にした.

/home/myn/perl5/bin/sa-update
case $? in
    0)
        # got updates!
        PERL_MM_OPT= sa-compile
        ;;
    1)
        # no updates
        ;;
    2)
        # lint failed!
        ;;
    *)
        echo "sa-update failed for unknown reasons" 1>&2
        ;;
esac

コンパイルされたものは ~/perl5/var/spamassassin/compiled/ に置かれることになる.

追記 (2021/8/16)

書き忘れていた.コンパイルされた rule を利用するには ~/perl5/etc/mail/spamassassin/v320.pre の以下の行のコメントアウトを外す.

loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody

Related articles