PostfixとSendmail
Spam ScannerをPostfixに接続する方法は2つあります。
- milterとして(推奨):PostfixはSMTPセッション中、メッセージを受け取る前に、各メッセージについて問い合わせます。スパムは4xxまたは5xxの応答で拒否できるため、その処理は自分のサーバーではなく送信側のサーバーが担います。Sendmailも同じプロトコルを使います。
- コンテンツフィルターとして:Postfixがメッセージを受け取り、
spamscanner filterに渡します。フィルターはヘッダーを追加し、sendmailでメッセージを戻します。SMTPセッション中に拒否することは一切ありません。
どちらの方法でも、すべてのメッセージに次のヘッダーを追加します。
X-Spam-Flag: YES
X-Spam-Score: 8.0
X-Spam-Level: ********
X-Spam-Status: Yes, score=8.0 required=5.0 tests=BAYES_99,DECEPTIVE_LINK version=7.0.0
X-Spam-Action: tagメッセージにすでにあるX-Spam-*ヘッダーは先に削除されるため、送信者が自分のメールを問題なしと偽ることはできません。
milter#
1. milterを起動する#
npm install --global spamscanner
spamscanner milter --port 7831 --auth --subject-tag "[SPAM]" --verbose--rejectを指定すると、拒否のしきい値(15点)に達したメッセージは451 4.7.1 Message rejected as spamで拒否されます。451は一時的なエラーです。送信者は後で再送し、誤りがあっても設定を変えれば修正できます。結果が正しいと確認できたら、--reject-code 550で恒久的に拒否できます。--quarantineを指定すると、スパムは代わりにPostfixの保留キューに入ります。
systemdのサービスにする場合は、/etc/systemd/system/spamscanner-milter.serviceに次のように記述します。
[Unit]
Description=Spam Scanner milter
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/spamscanner milter --port 7831 --auth --subject-tag [SPAM]
User=spamscanner
Group=spamscanner
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.targetsudo useradd --system --no-create-home --shell /usr/sbin/nologin spamscanner
sudo systemctl daemon-reload
sudo systemctl enable --now spamscanner-milter2. Postfixから使うように設定する#
/etc/postfix/main.cfに次のように記述します。
smtpd_milters = inet:127.0.0.1:7831
milter_protocol = 6
# If the milter is down: accept mail unfiltered (accept) or ask senders to retry (tempfail).
milter_default_action = accept
# Long enough for a language model, if one is configured.
milter_content_timeout = 120ssudo postfix reloadsmtpd_miltersはSMTP経由で届くメールが対象です。sendmailコマンドで投入したメールもスキャンする必要がない限り、non_smtpd_miltersは空のままにしてください。
3. テストする#
swaksでテストメッセージを送れます。GTUBEは、どのスパムフィルターもスパムとして扱うテスト用の文字列です。
swaks --to you@example.com --server 127.0.0.1 \
--body 'XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X'--rejectを指定しなければ、メッセージはX-Spam-Flag: YESとタグ付きの件名で配送されます。--rejectを指定すると、swaksに451または550の応答が表示されます。
コンテンツフィルター#
SMTPセッション中にメールを決して拒否してはならない場合や、milterを使えないサーバーで使います。
/etc/postfix/master.cfにフィルターのサービスを追加し、SMTPのリスナーで使います。
smtp inet n - n - - smtpd
-o content_filter=spamscanner:dummy
spamscanner unix - n n - 10 pipe
flags=Rq user=spamscanner null_sender=
argv=/usr/bin/node /usr/local/lib/node_modules/spamscanner/dist/esm/cli.js filter --subject-tag [SPAM] -f ${sender} -- ${recipient}Postfixはほとんど空の環境でフィルターを実行するため、argvではNode.jsとスクリプトをフルパスで指定します(command -v nodeとnpm root --globalで確認できます)。その後、次を実行します。
sudo postfix reloadフィルターはsendmail -G -iでメッセージを戻します。この方法で投入されたメールは再びsmtpリスナーを通らないため、二重にフィルタリングされることはありません。
終了コードでPostfixに結果を伝えます。0は配送済み、69は拒否(--reject指定時。Postfixが送信者にバウンスする)、75は一時的な失敗(Postfixがメッセージを保持して再試行する)です。スキャンや配送の失敗はすべて75になるため、設定が壊れていてもメールが失われたりバウンスしたりすることはありません。
Sendmail#
sendmail.mcに次のように記述します。
INPUT_MAIL_FILTER(`spamscanner', `S=inet:7831@127.0.0.1, F=T, T=S:30s;R:30s;E:5m')F=Tを指定すると、milterを利用できない間、Sendmailは一時的な失敗で応答します。代わりにメールをフィルタリングせずに受け入れるには、これを外してください。sendmail.cfを再生成し、Sendmailを再起動します。
スパムを迷惑メールフォルダーに振り分ける#
タグ付けだけでは、スパムは受信トレイに配送されます。Dovecotでは、Sieveのルールでスパムを移動します。
require ["fileinto"];
if header :is "X-Spam-Flag" "YES" {
fileinto "Junk";
stop;
}その他のメールサーバーではDovecot、Exim、Haraka、procmailを扱っています。ユーザーが迷惑メールフォルダーに出し入れしたメールから学習する方法は学習を参照してください。
テスト#
リポジトリのエンドツーエンドテストでは、実際のPostfixを動かします。ハムはヘッダー付きで配送され、偽造されたX-Spam-Flagは削除され、スパムはタグ付けされ、GTUBEはSMTPセッション中に550で拒否され、コンテンツフィルターは2つ目のポートでメールにタグを付けます。scripts/e2e-postfix.shがそのPostfixを準備し、test/e2e/postfix.test.jsがメールを送ります。