このドキュメントは Postfix バージョン 2.1 以降を要求します。
通常、Postfix はメールを受け取り、それをメールキューに入れてから配送します。ここで述べる外部コンテンツフィルタを使うと、メールはキューに入った「後で」フィルタリングされます。このアプローチはメールフィルタリングプロセスからメール受信プロセスを切り離し、並列に走らせるフィルタリングプロセスの数を最大限にします。
キューに入った後のコンテンツフィルタは次のように使われることを意図しています:
ネットワークまたは
ローカルユーザ-> Postfix
キュー-> コンテンツ
フィルタ-> Postfix
キュー-> ネットワークまたは
ローカルメールボックス
このドキュメントは一つの Postfix インスタンスを以下の全てに使っている実装を記述しています: メールの受信やフィルタリング、配送。2つの別々の Postfix インスタンスを使う応用はこのドキュメントの後のバージョンでカバーされる予定です。
キューに入った後のコンテンツフィルタと、入ってくる SMTP メールが Postfix キューに入れられる「前に」フィルタリングされる SMTPD_PROXY_README ドキュメントに記述されたアプローチを混同しないでください。
このドキュメントは全てのEメールをフィルタリングする2つのアプローチと、選択的にメールをフィルタリングするいくつかのオプションを記述しています:
外部コンテンツフィルタは Postfix からフィルタリングされていないメールを受け取り (ずっと下に記述されています)、以下のいずれかをおこないます:
Postfix にメールを差し戻します。これはコンテンツや配送先を変更した後かもしれません。
(Postfix に適切な状態コードを送り返すことで) メールを拒否します。Postfix はメールを送信者に返します。
注意: メールワームや詐称された spam が多い中、送信者アドレスがほとんどの場合は元々のものではないため、送信者アドレスにウィルスを送り返すのは「とても悪い考え」です。既知のウィルスは破棄し、疑わしいものは人が処理を決められるように検疫するほうがよいでしょう。
最初の例は構築が単純です。Postfix はネットワークから smtpd(8) サーバでフィルタリングされていないメールを受け取り、フィルタを Postfix pipe(8) 配送エージェントでコンテンツフィルタに配送します。コンテンツフィルタはフィルタリングされたメールを Postfix に Postfix sendmail(1) コマンドで差し挟み、Postfix が最終的な配送先に配送できるようにします。
これは Postfix sendmail(1) コマンドを使って投函されたメールはコンテンツフィルタリングできないことを意味します。
以下の図で、数字が続く名前は Postfix コマンドまたはデーモンプログラムを表しています。Postfix アーキテクチャの概要は OVERVIEW ドキュメントを参照してください。
フィルタリング前
->
smtpd(8)
pickup(8)>- cleanup(8) -> qmgr(8)
Postfix
キュー-< local(8)
smtp(8)
pipe(8)->
->
フィルタ後
フィルタ後
^
||
vmaildrop
キュー<- Postfix
postdrop(1)<- Postfix
sendmail(1)<- コンテンツ
フィルタ
コンテンツフィルタは以下のような簡単なシェルスクリプトでも構いません:
1 #!/bin/sh 2 3 # Simple shell-based filter. It is meant to be invoked as follows: 4 # /path/to/script -f sender recipients... 5 6 # Localize these. The -G option does nothing before Postfix 2.3. 7 INSPECT_DIR=/var/spool/filter 8 SENDMAIL="/usr/sbin/sendmail -G -i" # NEVER NEVER NEVER use "-t" here. 9 10 # Exit codes from <sysexits.h> 11 EX_TEMPFAIL=75 12 EX_UNAVAILABLE=69 13 14 # Clean up when done or when aborting. 15 trap "rm -f in.$$" 0 1 2 3 15 16 17 # Start processing. 18 cd $INSPECT_DIR || { 19 echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; } 20 21 cat >in.$$ || { 22 echo Cannot save mail to file; exit $EX_TEMPFAIL; } 23 24 # Specify your content filter here. 25 # filter <in.$$ || { 26 # echo Message content rejected; exit $EX_UNAVAILABLE; } 27 28 $SENDMAIL "$@" <in.$$ 29 30 exit $?
注意:
8行目: 2.3より前のPostfixでは -G オプションは何も起こりません。それ以降ではメッセージヘッダのアドレス書き換えを無効にします。
8行目: -i オプションは "." のみが含まれる場合でも読み込みを止めないようにします。
8行目: 「絶対に絶対に絶対に」ここでは "-t" コマンドラインオプションを使わないでください。メーリングリストのメールがメーリングリストに戻されるようにメールを間違って配送してしまいます。
21行目: まずメッセージをファイルにキャプチャし、コンテンツをサードパーティコンテンツフィルタプログラムに通すという考え方です。
22行目: メールをファイルにキャプチャできない場合は、終了ステータス 75 (EX_TEMPFAIL) で終了してメール配送を遅延させます。Postfix はメッセージを deferred メールキューに置き、後で再び試行します。
25行目: ここに実際に標準入力でコンテンツを受け取るコンテンツフィルタプログラムプログラムを指定する必要があります。
26行目: コンテンツフィルタプログラムが問題を見つけた場合、終了ステータス 69 (EX_UNAVAILABLE) で終了することでメールをバウンスさせます。はメッセージを配送できないとして送信者に返します。
注意: メールワームや spam が多い中、送信者アドレスが詐称されているかもしれないため、送信者アドレスに既知のウィルスや spam を送り返すのは「とても悪い考え」です。悪いことが既知のコンテンツは破棄し、疑わしいコンテンツは人が検査できるように検疫するのが安全です。
28行目: コンテンツに問題がない場合には Postfix sendmail コマンドへの入力として与えられ、フィルタコマンドの終了ステータスは Postfix sendmail コマンドが生成する終了ステータスとなります。Postfixは通常通りメールを配送します。
30行目: Postfix は Postfix sendmail コマンドの終了ステータスを返します。
結果に満足するまで、まずはしばらくこのスクリプトを手で動かすことを推奨します。実際のメッセージ (ヘッダ+本体) を入力として走らせます:
% /path/to/script -f sender recipient... <message-file
コンテンツフィルタスクリプトに満足したら:
"filter" という専用のローカルユーザアカウントを作ります。このユーザは潜在的に危険な全てのメールのコンテンツを扱います - これが別のアカウントにすべき理由です。"nobody"、ましてや "root" や "postfix" を使ってはいけません。
"filter" ユーザだけがアクセスできる /var/spool/filter ディレクトリを作成します。これはコンテンツフィルタリングスクリプトがテンポラリファイルを置くことが想定された場所です。
Postfix が pipe(8) 配送エージェントでコンテンツフィルタにメールを配送するように設定します。
/etc/postfix/master.cf: # ============================================================= # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ============================================================= filter unix - n n - 10 pipe flags=Rq user=filter argv=/path/to/script -f ${sender} -- ${recipient}
これは同時に最大 10 までのコンテンツフィルタを動かします。10 という並列プロセス数制限の代わりに、あなたのマシンに適したプロセス制限数を使ってください。コンテンツ検査ソフトウェアはシステムリソースをむさぼり食うかもしれないため、同時に膨大な数は走らせない方がよいでしょう。
SMTP で到達したメールのみにコンテンツフィルタを使うには、Postfix SMTP サーバを定義している master.cf エントリに "-o content_filter=filter:dummy" を加えます:
/etc/postfix/master.cf: # ============================================================= # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ============================================================= smtp inet ...other stuff here, do not change... smtpd -o content_filter=filter:dummy
"content_filter" 行は入ってくるメールメッセージそれぞれに "filter:dummy" というコンテンツフィルタを要求するレコードを Postfix に加えさせます。このレコードは通常のメールルーティングを上書きし、メールをコンテンツフィルタに与えるようにします。
content_filter 設定パラメータは Postfix transport テーブルの右側部分と同様な文法を受け付けます。
変更を完了するために "postfix reload" を実行してください。
上に示したようなシェルスクリプトで、SMTP で到着してから出て行くまでの通過にかかる Postfix のパフォーマンスが4倍ほど落ちます。コンテンツフィルタリングのプロセスでさらにテンポラリファイルを作成したり削除したりするたびごとに、さらに通過のパフォーマンスが悪くなるでしょう。ローカルで投函されたり、ローカルに配送されるメールはすでに SMTP で通過するメールよりも遅いため、パフォーマンスの影響は少ないです。
上のようなコンテンツフィルタの問題は、あまり堅牢ではないということです。それは、ソフトウェアがしっかり定義されたプロトコルで Postfix と話をしないためです。シェルがなんらかのメモリアロケーション問題でフィルタシェルスクリプトが止まった場合、スクリプトは /usr/include/sysexits.h にあるような正しい終了ステータスを生成しません。メールは deferred キューに行くのではなく、バウンスされます。同様にコンテンツフィルタ自身がリソース問題に当たった場合も堅牢性がなくなる可能性があります。
単純なコンテンツフィルタの方式は header_checks や body_checks パターンで呼び出されるコンテンツフィルタアクションに対しては適切ではありません。これらのパターンは Postfix sendmail コマンドでメールが差し挟まれる際に再び適用され、その結果メールフィルタリングループに入ってしまいます。高度なコンテンツフィルタリングの手法 (以下参照) では、フィルタリングされたメールに対して header_checks や body_checks パターンを無効にすることが可能となります。
"単純な" コンテンツフィルタリングを無効にするには:
master.cf ファイルを編集し、"-o content_filter=filter:dummy" テキストを Postfix SMTPサーバを定義したエントリから取り除きます。
"postsuper -r ALL" を実行して、すでにあるキューファイルからコンテンツフィルタの情報を取り除きます。
もう一度 "postfix reload" を実行します。
2つ目の例はかなり複雑ですが、よいパフォーマンスを出し、マシンがリソース問題に当たったときもメールをバウンスする可能性が低くなります。このコンテンツフィルタはフィルタリングされていないメールを localhost ポート 10025 で SMTP を使って受け取り、フィルタリングされたメールを localhost ポート 10026 で SMTP を使って Postfix に差し戻します。
SMTP が使えないコンテンツフィルタソフトウェアに対しては、Bennett Todd の SMTP プロキシがよい PERL/SMTP コンテンツフィルタリングフレームワークを実装しています。参照: http://bent.latency.net/smtpprox/。
以下の図で、数字が続く名前は Postfix コマンドまたはデーモンプログラムを表しています。Postfix アーキテクチャの概要は OVERVIEW ドキュメントを参照してください。
フィルタリング前
フィルタリング前->
->smtpd(8)
pickup(8)>- cleanup(8) -> qmgr(8)
Postfix
キュー-< smtp(8)
local(8)->
->フィルタ後
フィルタ後^
||
vsmtpd(8)
10026smtp(8)
^
||
vコンテンツフィルタ 10025
ここで上げる例では、SMTP で到達したメールや Postfix sendmail コマンドを使ってローカルで投かんされたメールを含めて、全てのメールをフィルタリングします。ローカルユーザをフィルタリングから除外する方法や、配送先に依存するコンテンツフィルタの設定方法は、このドキュメントの最後の方にある例を参照してください。
テンポラリファイルを作らないのであれば、SMTP で到着してから出て行くまでの通過にかかる Postfix のパフォーマンスが2倍ほど落ちることが想定されます。テンポラリファイルを作るごとに数倍パフォーマンスが失われます。
全てのメールに対して高度なコンテンツフィルタ方式を有効にするには、次のように main.cf に指定します:
/etc/postfix/main.cf: content_filter = scan:localhost:10025 receive_override_options = no_address_mappings
"content_filter" 行は入ってくるメールメッセージそれぞれに "scan:localhost:10025" というコンテンツフィルタを要求するレコードを Postfix に加えさせます。コンテンツフィルタ要求レコードは smtpd(8) および pickup(8) サーバ (と、サービスを有効にしている場合は qmqpd(8)) によって加えられます。
コンテンツフィルタリング要求はキューファイルに保管されます; このようにして Postfix はフィルタリングを必要とするメールを管理します。キューファイルがコンテンツフィルタリング要求を含んでいると、キューマネージャは最終配送先に関わらず、メールを指定されたコンテンツフィルタに配送します。
"receive_override_options" 行はコンテンツフィルタリングの前にアドレスを操作できないようにし、コンテンツフィルタがバーチャルエイリアス展開やカノニカルマッピング、自動 bcc、アドレスマスカレードなどの結果ではなく、元のメールアドレスを見られるようにします。
この例で、"scan" は少し異なる設定パラメータを持つ Postfix SMTP クライアントのインスタンスです。このように Postfix master.cf ファイルでサービスを設定します:
/etc/postfix/master.cf: # ============================================================= # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ============================================================= scan unix - - n - 10 smtp -o smtp_send_xforward_command=yes -o disable_mime_output_conversion=yes -o smtp_generic_maps=
これは同時に最大 10 までのコンテンツフィルタを動かします。10 という並列プロセス数制限の代わりに、あなたのマシンに適したプロセス制限数を使ってください。コンテンツ検査ソフトウェアはシステムリソースをむさぼり食うかもしれないため、同時に膨大な数は走らせない方がよいでしょう。
"-o smtp_send_xforward_command=yes" を付けると、scan transport はコンテンツフィルタを通してフィルタ後の smtpd プロセスに元のクライアント名やIPアドレスを転送して、フィルタリングされたメールが実際のクライアント名やIPアドレス付きでログに記録されるようにしようとします。それ以上の情報は smtp(8) や XFORWARD_README を参照してください。
"-o disable_mime_output_conversion=yes" は domainkeys やその他のデジタル署名の損壊を防ぐ回避策です。SMTPベースのコンテンツフィルタには 8BITMIME を問題なく扱えるのにサポートを通知しないものがあるため、これが必要です。
"-o smtp_generic_maps=" は generic(5) マップでのローカルアドレス書き換えを防ぐ回避策です。そのような書き換えはメールが外部のインターネットに送られるときのみになされるべきです。
コンテンツフィルタは Postfix の inetd と同等な Postfix spawn サービスを使って、設定することができます。例えば、localhost ポート 10025 で待つ、最大 10 のコンテンツフィルタリングプロセスは次のようになります:
/etc/postfix/master.cf: # =================================================================== # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # =================================================================== localhost:10025 inet n n n - 10 spawn user=filter argv=/path/to/filter localhost 10026
"filter" は専用のローカルユーザアカウントです。ユーザはログインせず、 "*" パスワード、存在しないシェルとホームディレクトリが与えられます。このユーザは潜在的に危険な全てのメールのコンテンツを扱います - これが別のアカウントにすべき理由です。
Postfix のかわりにあなたのフィルタで localhost:10025 ポートを listen したいのであれば、あなたのフィルタをスタンドアロンプログラムとして起動しなければならず、また Postfix spawn サービスを使ってはいけません。
適切な診断でメールをバウンスするか、ローカルホストの 10026 ポートで待っている専用のリスナを通してメールを Postfix に返すことがコンテンツフィルタの仕事です。
最も単純なコンテンツフィルタは、単に入力と出力の間で SMTP コマンドやデータをコピーするものです。問題があった場合にやらねばならないのは、 Postfix からの `.' の入力に対して `550 content rejected' と応答し、 Postfix にメールを差し戻す接続で `.' を送らずに切断することだけです。
/etc/postfix/master.cf: # =================================================================== # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # =================================================================== localhost:10026 inet n - n - 10 smtpd -o content_filter= -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o smtpd_authorized_xforward_hosts=127.0.0.0/8
注意: "=" や "," 文字の前後に空白を使わないでください。
注意: SMTP サーバは "filter" master.cf エントリのプロセス制限よりも小さくしてはいけません。
"-o content_filter=" は main.cf の設定を上書きし、コンテンツフィルタからのメールにはコンテンツフィルタリングを要求しません。これは必須で、そうしないとコンテンツフィルタリングのループにメールがとどまってしまいます。
"-o receive_override_options" はコンテンツフィルタの前にすでにおこなわれたことを二重に実行するのを避けるために、main.cf の設定を上書きします。これらのオプションは main.cf で指定されるオプションと相互補完します:
受信者を知らないかどうか調べようとするのをやめるために "no_unknown_recipient_checks" を指定します。
ヘッダ/本体チェックを無効にするために "no_header_body_checks" を指定します。
Milter アプリケーションを無効にするために "no_milters"を指定します (このオプションはPostfix 2.3以降でのみ使えます)。
ここでは "no_address_mapping" を指定しません。これはコンテンツ フィルタの後でバーチャルエイリアス展開、カノニカルマッピング、 アドレスマスカレードやその他のマッピングを有効にします。main.cf での "receive_override_options" 設定はコンテンツフィルタの前でこれらをマッピングしないようにします。
これらの receive override options は SMTP サーバ自身によって実装され、 cleanup サーバに渡されます。
"-o smtpd_xxx_restrictions" および "-o mynetworks=127.0.0.0/8" は main.cf の設定を上書きします。これらはここでの時間を無駄にするだけのジャンクメール制御を無効にします。
"-o smtpd_authorized_xforward_hosts=127.0.0.0/8" で、フィルタリングされたメールに実際のクライアント名や IP アドレスを付けてログに記録できるように、scan transport は元のクライアント名や IP アドレスをフィルタリング後の smtpd プロセスに転送しようとします。XFORWARD_README や smtpd(8) を参照してください。
ここで述べたコンテンツフィルタリングの "サンドイッチ" アプローチでは、利用可能な CPU やメモリ、I/O リソースに対してフィルタの並列度がマッチしていることが重要です。コンテンツフィルタリングプロセスが少なすぎると、流量が少なくても active キュー にメールがたまってしまいます; 並列数が大きすぎると、リソースが不十分でプロセスが落ちてしまい、コンテンツフィルタ宛のメールが遅延することになってしまいます。
今のところ、コンテンツフィルタのパフォーマンスチューニングは試行錯誤です; フィルタリングされたメッセージとフィルタリングされていないメッセージで同じキューを共有しているため、分析するには不都合です。このドキュメントの概要で触れたように、複数の Postfix インスタンスを使ったコンテンツフィルタリングが将来のバージョンでカバーされます。
"高度な" コンテンツフィルタリングを無効にするには:
main.cf の以下の2行を削除またはコメントアウトします。高度なコンテンツフィルタリングに対するその他全ての変更は、コンテンツフィルタリングが無効になっている時には効果がありません。
/etc/postfix/main.cf: content_filter = scan:localhost:10025 receive_override_options = no_address_mappings
"postsuper -r ALL" を実行して、すでにあるキューファイルからコンテンツフィルタの情報を取り除きます。
もう一度 "postfix reload" を実行します。
最も簡単な方法は、master.cf で複数の SMTP サーバ IP アドレスを使う「1つの」Postfix インスタンスを設定することです:
内部ユーザからのみのメール用に2つの SMTP サーバ IP アドレスを用意し、コンテンツフィルタリングを無効にします。
/etc/postfix.master.cf: # ================================================================== # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ================================================================== 1.2.3.4:smtp inet n - n - - smtpd -o smtpd_client_restrictions=permit_mynetworks,reject 127.0.0.1:smtp inet n - n - - smtpd -o smtpd_client_restrictions=permit_mynetworks,reject
外部ユーザからのメール用に1つの SMTP サーバアドレスを用意し、コンテンツフィルタリングを有効にします。
/etc/postfix.master.cf: # ================================================================= # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ================================================================= 1.2.3.5:smtp inet n - n - - smtpd -o content_filter=filter-service:filter-destination -o receive_override_options=no_address_mappings
この後は、main.cfファイルで "content_filter" や "receive_override_options"を指定してはいけないことを除いて、上に概要が示された "高度な" または "単純な" コンテンツフィルタリングの例と同じ手順に従うことができます。
あなたが MX サービスを提供していて、ドメインごとに異なるコンテンツフィルタを適用したいのであれば、master.cf で複数の SMTP サーバ IP アドレスを持つ「1つの」Postfix インスタンスを設定することができます。それぞれのアドレスは異なるコンテンツフィルタサービスを提供します。
/etc/postfix.master.cf: # ================================================================= # service type private unpriv chroot wakeup maxproc command # (yes) (yes) (yes) (never) (100) # ================================================================= # SMTP service for domains that are filtered with service1:dest1 1.2.3.4:smtp inet n - n - - smtpd -o content_filter=service1:dest1 -o receive_override_options=no_address_mappings # SMTP service for domains that are filtered with service2:dest2 1.2.3.5:smtp inet n - n - - smtpd -o content_filter=service2:dest2 -o receive_override_options=no_address_mappings
この後は、main.cfファイルで "content_filter" や "receive_override_options"を指定してはいけないことを除いて、上に概要が示された "高度な" または "単純な" コンテンツフィルタリングの例と同じ手順に従うことができます。
それぞれのドメインを適切な SMTP サーバインスタンスに向けるように、DNS の MX レコードを設定します。
上のフィルタリング設定は静的なものです。決められた道筋に従うと、メールは常にフィルタリングされるか全くされないかのどちらかです。Postfix 2.0 では動的にもコンテンツフィルタリングを有効にできるようになりました。
access(5) テーブルのルールでコンテンツフィルタリングを有効にするには:
/etc/postfix/access: whatever FILTER foo:bar
header_checks(5) または body_checks(5) テーブルのパターンでコンテンツフィルタリングを有効にするには:
/etc/postfix/header_checks: /whatever/ FILTER foo:bar
cleanup サーバのヘッダ/本体チェックと同様に、smtpd access マップでもこれをおこなうことができます。この機能は細心の注意を払って使わなければいけません: フィルタリング後の smtpd や cleanup デーモンで全ての UCE 機能を無効にしなければいけません。そうしないとコンテンツフィルタリングループを起こしてしまいます。
制限:
smtpd access マップや header/body_checks の FILTER アクションは main.cf の content_filter パラメータで指定されるフィルタに優先します。
メッセージが2つ以上のフィルタアクションを引き起こす場合は、最後のものが有効になります。
同じコンテンツフィルタが与えられたメッセージの全ての受信者に適用されます。