SMTPメールを出力するサーバからメールを受け取り、「本文中にAaaとBbbという文字があればrelay、でなければreject」を実装する必要が出てきた
ただし、以下の条件がある
- SMTPメールを出力するサーバへのログインはできない
結論:
ProxSMTP(+スクリプト)を搭載したサーバを別途用意、そこにSMTPメールを中継させて判定を行うようにした
$ sudo apt-get -y install proxsmtp
$ sudo service postfix stop
$ sudo update-rc.d -f postfix remove
※Postfixが入るのはご愛嬌
Listen: 0.0.0.0:25
OutAddress: upper-mx.example.local:25
FilterCommand: /opt/bin/filter.rb
# !/usr/bin/env ruby
require "mail"
module Postfix
module Filter
def self.run(input)
mail = Mail.new(input)
return true if (mail.body =~ /Aaa/ and mail.body =~ /Bbb/)
false
end
end
end
if __FILE__ == $0
if Postfix::Filter.run(msg = STDIN.read)
STDOUT.puts msg
exit 0
else
STDERR.puts "550 Content Rejected: could not relay."
exit 255
end
end
今回得られた知見:
一般的なSMTPサーバというものは「原則relay」(= default allow)に則って設計/実装されている。
よって「特定条件のみrelay」 (= default reject)を実現するのが、極めて大変。
postfix / exim4 / sendmail / Courier-MTA について、default rejectが可能なのか調査した。
Postfix
ビルトインヘッダ・本体検査(header_checks / body_checks)による検査
# passという文字がbodyに見つかったらDUNNO(=OK)、そうでない場合はREJECTとしたい
/pass/ DUNNO
/.*/ REJECT
ルータ等のfilterを書いている人なら「これでOKじゃね?」と思うが、残念。
header_checks/body_checksでは、DUNNO(OK)で判定が止まらないため、REJECT行まで行ってしまう。(バグではなく仕様)
単純なcontent_filterによる検査
シェルスクリプト等へSTDIN/STDOUTを使ったフィルタ実装は、content_filter
に来た時点で既にqueueに入っているため、スクリプト内ではdeferもしくはbounceのみ選択可能。rejectはできない。
高度なcontent_filterによる検査
inetもしくはunix domainソケットを使った、他プロセスと通信してcontent_filterする場合は、単純な場合と違い、他プロセスの結果に応じてrejectを発生させる事は可能。
※ただし、今回の用途で考えると、このためだけに2つもプロセスを起動するのはシステムがデカすぎるため、不採用としました
exim4
system_filter
で実装可能...と思われましたが、設定ファイルがデカすぎ&わかりづらく、system_filterを適用できませんでした。
たぶんこれで行けると思うんですけど。
# Exim filter
if $message_body contains "Aaa" and $message_body contains "Bbb"
then
seen finish
else
fail "cannot relay."
endif
※一行目に# Exim filter
って書かなきゃいけないところが、もうダメ。むり。
sendmail / Courier-MTA
もう、力つきて、調べてない。
あとがき
もう、こんな仕事、やりたくない。。。
Qiitaの中でProxSMTPネタって、Qiitaの中では初じゃないでしょうか?