はじめに ======== これは最初期の Postfix コンテンツフィルタリングの実装です。 Postfix コンテンツフィルタはフィルタを通していないメールを Postfix から受け取り、次のいずれかをおこないます: - 再び Postfix に投函されますが、内容が書き換えられた後かも しれません。 - 送信者にメールが返されるように (適切な状態コードを Postfix に 送ることで) メールを拒否します。 - メールをどこか別のところに送ります。 この文書はコンテンツフィルタリングの2つのアプローチを記述します: 単純なものと高度なもの。どちらもデフォルトでは全てのメールを フィルタリングします。 最後は、あなたが提供する MX サービスとは異なるドメインに対して 異なるフィルタを使うことについて、そしてメッセージエンベロープや header/body パターンに基づいた選択フィルタリングについての、 ユーザからのメールをフィルタリングする方法の例です。 単純なコンテンツフィルタリングの例 ================================== 最初の例のセットアップは単純です。フィルタを通していないメールを Postfix pipe 配送エージェントから受け取り、フィルタを通したメールを Postfix sendmail コマンドに与えて戻すシェルスクリプトを使います。 SMTP を通して届いたメールのみがコンテンツフィルタに通されます。 .................................. : Postfix : Unfiltered mail----->smtpd \ /local---->Filtered mail : -cleanup->queue- : ---->pickup / \smtp----->Filtered mail ^ : | : | : \pipe-----+ | .................................. | | | | | +-Postfix sendmail<----filter script<--+ メールは /some/where/filter プログラムによってフィルタリングされます。 これは次のような単純なシェルスクリプトです: #!/bin/sh # Localize these. INSPECT_DIR=/var/spool/filter SENDMAIL="/usr/sbin/sendmail -i" # Exit codes from EX_TEMPFAIL=75 EX_UNAVAILABLE=69 # Clean up when done or when aborting. trap "rm -f in.$$" 0 1 2 3 15 # Start processing. cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; } cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; } # filter smtpd \ /local----> : -cleanup->queue- : ---->pickup / ^ | \smtp-----> : | v : : smtpd smtp : : 10026 | : ......................|........... ^ | | v ....|............ : | 10025 : : filter : : : ................. この方法でコンテンツフィルタを有効にするには、main.cf に新しい パラメータを指定します: /etc/postfix/main.cf: content_filter = scan:localhost:10025 こうすると、Postfix はそれぞれの入ってくるメールに scan:localhost:10025 という内容のコンテンツフィルタリングレコードを加えます。 コンテンツフィルタリングのレコードは smtpd と pickup サーバによって 加えられます。 キューファイルがコンテンツフィルタリング情報を持っていると、 キューマネージャは最終配送先にかかわらず、メールを指定されたコンテンツ フィルタに配送します。 この例では、"scan" が少し異なる設定パラメータを持つ Postfix SMTP クライアントのインスタンスです。Postfix master.cf ファイルにサービスを 設定する方法は次の通りです: /etc/postfix/master.cf: scan unix - - n - 10 smtp 10という同時プロセス数制限の代わりに、マシンに合ったプロセス数を いくつでも使うことが出来ます。コンテンツ検査ソフトウェアはたくさんの システムリソースを食い荒らすかもしれないので、同時に多すぎる数の プロセスを持ちたくはないでしょう。 Postfix の inetd に相当する Postfix spawn サービスを使って コンテンツフィルタをセットアップすることもできます。たとえば、 10 までのコンテンツフィルタリングプロセスをオンデマンドに 起動するには: /etc/postfix/master.cf: localhost:10025 inet n n n - 10 spawn user=filter argv=/some/where/filter localhost 10026 "filter" は専用のローカルユーザアカウントです。ユーザはログインせず、 "*" パスワード、存在しないシェルとホームディレクトリを与えます。 このユーザは全ての潜在的に危険なメールの内容を扱います - これが 別のアカウントであるべき理由です。 上の例では、Postfix は localhost:10025 ポートを listen します。 Postfix のかわりにあなたのフィルタで localhost:10025 ポートを listen したいのであれば、あなたのフィルタをスタンドアロンプログラムとして 起動しなければいけません。 注意: localhost の 10025 番ポート SMTP サーバフィルタは自分自身を "220 localhost ..." としてアナウンスすべきです。Postfix と同じ ホスト名を使う SMTP サーバに接続すると、たいていはメール配送ループ 問題があることを意味するため、Postfix は配送を異常終了 します ("host greeted me with my own hostname")。 ここでの例では、/some/where/filter コマンドは PERL スクリプトであると 想定しています。PERL には SMTP を簡単に話せるモジュールがあります。 コマンドラインには localhost の 10026 番ポートで Postfix に メールを送り返すように指定します。 最もシンプルなコンテンツフィルタは、単に入力と出力の間の SMTP コマンドやデータをコピーします。問題があれば、`.' の入力に対して `550 content rejected' 応答を返し、Postfix に戻すメールを入れる 接続を`.' を送らずに切るだけです。 コンテンツフィルタの仕事はメールを適切な診断でバウンスしたり、 localhost の 10026 番ポートで待っている専用のリスナにメールを 送り返すことです: /etc/postfix/master.cf: localhost:10026 inet n - n - 10 smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o myhostname=localhost.domain.tld -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 Postfix バージョン 2 ユーザへの警告: コンテンツフィルタリング後の SMTP サーバでは、main.cf の virtual_alias_maps や virtual_alias_domains 設定を上書きしないでください。"User unknown" でメールを拒否する可能性が あります。 この SMTP サーバには "filter" master.cf エントリと同じプロセス制限が あります。 "-o content_filter=" を指定すると、入ってくるメールに対しての コンテンツフィルタリングを無効にします。 "-o local_recipient_maps=" や "-o relay_recipient_maps=" を 指定すると、不必要なテーブル検索を回避します。 "-o myhostname=localhost.domain.tld" を指定すると、コンテンツフィルタが 単に SMTP コマンドを中継するようなプロキシに基づいている場合の 誤った警告 ("host greeted me with my own hostname") を 回避します。 "-o smtpd_xxx_restrictions" および "-o mynetworks=127.0.0.0/8" を 指定すると、ここでは時間の無駄でしかない UCE 制御を無効にします。 さらなるパフォーマンスの絞り出し ================================ コンテンツフィルタにメールを与えるために特別に設定された smtp 配送 エージェントを走らせたり、コンテンツフィルタリングの前のアドレスの 書き換えを無効にするなど、多くの改良点が考えられます。 以下に示すように master.cf ファイルに多くの main.cf ライクな情報が リストアップされるため、すぐに状況はとても複雑になります。 さらに悪いことに、詳細は Postfix の進化に連れて変化し、異なる プログラムによって異なる設定パラメータが実装されます。 さらにパフォーマンスを絞り出す必要があるのであれば、コンテンツ フィルタの前後に Postfix インスタンスを複数走らせるのがおそらく シンプルでしょう。そうすれば、それぞれのインスタンスはシンプルな main.cf や master.cf を持ち、別々のメールキューを持ち、 システムを把握するのが容易になるでしょう。 前に述べたように、SMTP メールを localhost の 10025 番ポートで受信し、 Postfix に localhost の 10026 番ポートで返すコンテンツフィルタリング プログラムをセットアップします。 ....................................... : Postfix : ----->smtpd \ : : -pre-cleanup-\ /local----> ---->pickup / -queue- : : -cleanup-/ | \smtp-----> : bounces/ ^ v : : and locally | v : : forwarded smtpd scan : : messages 10026 | : ...........................|........... ^ | | v ....|............. : | 10025 : : filter : : : .................. この方法でコンテンツフィルタリングを有効にするには、main.cf に 新しいパラメータを指定します: /etc/postfix/main.cf: content_filter = scan:localhost:10025 /etc/postfix/master.cf: # # これらはすでに master.cf に存在する、通常の入力 "smtpd" と # ローカル "pickup" サーバです。非デフォルトの cleanup サービスを # 選択するためにオプションを追加します(ずっと下にあります)。 # smtp inet n - n - - smtpd -o cleanup_service_name=pre-cleanup pickup fifo n - n 60 1 pickup -o cleanup_service_name=pre-cleanup # # ------------------------------------------------------------------ # # これはコンテンツフィルタの前にメッセージを扱う cleanup デーモンです。 # (もしあれば) header_checks と body_checks をおこないますが、 # バーチャルエイリアスやカノニカルアドレスマッピングはおこないません。 # これはオリジナルの受信者がほとんど損なわれていない状態でメールを # コンテンツフィルタに渡すためです。 # # バーチャルエイリアスやカノニカルアドレスマッピングは、コンテンツ # フィルタ後の2つ目の cleanup フェーズでおこなわれます。 # こうすることでコンテンツフィルタは、最大限の柔軟性を発揮するために、 # ほとんど改変されていないアドレスにアクセスできるようになります。 # # コンテンツフィルタの前にカノニカルまたはバーチャルアドレス # マッピングを特別におこないたいサイトもあるでしょう。そのような # 場合でも、正しくメールを転送したりバウンスするために、フィルタ後の # cleanup インスタンスでアドレスの書き換えを有効にする必要があります。 # pre-cleanup unix n - n - 0 cleanup -o canonical_maps= -o sender_canonical_maps= -o recipient_canonical_maps= -o masquerade_domains= -o virtual_alias_maps= # # ------------------------------------------------------------------ # # これはコンテンツフィルタからメールを取り込む配送エージェントです。 # ほとんどのコンテンツフィルタは CPU や大量のメモリを消費するため、 # 並列数を低く調整しています。10というプロセス制限は # $default_destination_concurrency_limit の効果を補強します。 # 明示的なプロセス制限がなくても、コンテンツフィルタに向いている # 全てのメッセージは同じ配送先であるため、並列数は制限されます。 # scan unix - - n - 10 smtp # # ------------------------------------------------------------------ # # これはコンテンツフィルタからフィルタリングされたメッセージを # 受け取る SMTP リスナです。ループを防ぐために content_filter # パラメータをクリア「しなければいけません」。また、Postfix SMTP # ループ検出コードの起動を回避するために、異なるホスト名を # 使わなければ「いけません」。 # # この "smtpd" は、バウンスや内部で転送されるメールにも使われる、 # 通常の cleanup サービスを使います。 # # mynetworks から誘導されるパラメータは、ホストの IP アドレスの # 一つからの接続であるもの以外の、全てのアクセス制御を無効にします。 # これはたいていの場合はやりすぎですが、デフォルトの制限が多くの # テーブルを使っているのであれば、リソースの使用量を減らせます。 # localhost:10026 inet n - n - - smtpd -o content_filter= -o myhostname=localhost.domain.tld -o local_recipient_maps= -o relay_recipient_maps= -o mynetworks=127.0.0.0/8 -o mynetworks_style=host -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject # # ここでは main.cf の virtual_alias_maps や virtual_mailbox_maps を # 上書きしてはいけません。上書きすると、"User unknown in virtual # (alias|mailbox) recipient table" でメールが拒否されます。 # # ------------------------------------------------------------------ # # これはコンテンツフィルタリング後に使われる、通常の cleanup デーモン # です。ヘッダおよび本体行チェックは、コンテンツフィルタの前の # pre-cleanup サービスですでに扱われているため、おこないません。 # # 通常の cleanup インスタンスは、コンテンツフィルタの前の # pre-cleanup インスタンスで無効にされた、全てのバーチャルエイリアスや # カノニカルアドレスマッピングをおこないます。正確にバウンスや # ローカルで転送されたメールを処理するために、この書き換えは # pre-cleanup インスタンスで無効にしていない場合であっても # おこなわなければいけません。 # cleanup unix n - n - 0 cleanup -o header_checks= -o mime_header_checks= -o nested_header_checks= -o body_checks= # # ------------------------------------------------------------------ # # "scan" と対照的な、通常の "smtp" 配送エージェント。 # smtp unix - - n - - smtp 上の例では、Postfix はそれぞれの入ってくるメールに scan:localhost:10025 という内容のコンテンツフィルタリングレコードを加えます。 Postfix transport テーブルの右側部分と同じ文法が使えます。 コンテンツフィルタリングレコードは smtpd および pickup サーバによって 加えられます。 "scan" transport はメッセージを SMTP コンテンツフィルタに投函する ための専用の "smtp" 配送エージェントのインスタンスです。 専用の "smtp" tranport を使うことで、ローカルのコンテンツフィルタへの メール配送という特定のタスクを調整することができます (低い レイテンシ、低い並列数、予測された低いレイテンシに依存する スループット)。 Postfix spawn サービスを使ったコンテンツフィルタを設定するには、 前の例を参照してください; もちろん Postfix 環境外でスタンドアロンの サーバを使うこともできます。 外部ユーザからのメールだけのフィルタリング ========================================== 最も簡単なのは、master.cf で「1つの」Postfix インスタンスで 「2つの」SMTP サーバアドレスを設定するアプローチです: - コンテンツフィルタを呼ばない、内部ユーザだけのための1つの SMTP サーバアドレス。 - 常にコンテンツフィルタを呼ぶ、外部ユーザのためのもう1つの SMTP サーバアドレス。 /etc/postfix.master.cf: # コンテンツフィルタを使わない、内部ユーザだけのための SMTP サービス。 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 # コンテンツフィルタを使う、外部ユーザ用の SMTP サービス。 1.2.3.5:smtp inet n - n - - smtpd -o content_filter=foo:bar 厄介にする (Getting really nasty) ================================= 上のフィルタリング設定は静的です。与えられた道筋に従うメールは 常にフィルタされるか全くされないかのどちらかです。Postfix 2.0 から オンザフライコンテンツフィルタも使えるようになりました。 Postfix UCE 機能はフィルタリングの動作をオンザフライで指定する ことができます: FILTER foo:bar cleanup サーバの header/body_checks と同様に smtpd アクセスマップで こうすることができます。この機能は特に注意して使わなければいけません: フィルタ後の smtpd および cleanup デーモンで全ての UCE 機能を 無効にしなければいけません。そうしないとコンテンツフィルタリングの ループを引き起こします。 制限: - メッセージ毎に一つのコンテンツフィルタリングのアクションしか 使えません。 - smtpd アクセスマップや header/body_checks の FILTER アクションは main.cf content_filter パラメータで指定されたフィルタに優先します。 - smtpd アクセスマップや header/body_checks の最後の FILTER アクション だけが有効になります。 - 与えられたメッセージの全ての受信者に対して同じコンテンツフィルタが 適用されます。