Postfix before-queue Milterサポート


はじめに

Postfix バージョン 2.3 では Sendmail バージョン 8 の Milter (mail filter) プロトコルのサポートを導入します。このプロトコルはMTAの外側のアプリケーションでメールの内容だけでなくSMTPのイベント (接続、切断)、SMTP コマンド (HELO、MAIL FROM など) を検査するのに使われます。これはすべてメールがキューに入る前におこなわれます。

Postfix に Milter サポートを追加した理由は、望まないメールをブロックするだけでなく、信憑性を検証したり (例: SenderID+SPFDomain keys)、メールに電子署名したり (例: Domain keys) といった既存アプリケーションがたくさんあるためです。そういったもろもろのソフトウェアごとにもうひとつ Postfix 固有のバージョンを持つのは人的およびシステムのリソースの無駄遣いです。

Postfix 2.3 は Sendmail バージョン 8 の Milter プロトコルのバージョン 4 までのすべての要求のうち、以下のひとつを除くすべてを実装しています: メッセージ本文の置換。いずれにせよ、本ドキュメントの最後にある回避方法制限の節を参照してください。

このドキュメントは以下の話題に関する情報を提供します:

Milter アプリケーションをPostfixと接続する方法

Postfixの Milter 実装は異なるふたつのメールフィルタのリストを使います: リストのひとつめはSMTPメールでだけ使われるフィルタで、もうひとつのリストは非SMTPメールで使われるものです。ふたつのリストの適用先は異なっていて都合がよくありません。これを避けるには Postfix に重大な再構築が必要になるでしょう。

Postfix の構造に精通している人のために、以下に Milter アプリケーションがどのように Postfix とつながるかの図を示します。数字が続く名前は Postfixコマンドまたはサーバプログラムであり、影つきの箱の中の名前に数字のないものは Postfix のキューを表します。煩雑にならないよう、ローカルから投稿する経路は簡潔にしてあります (OVERVIEW ドキュメントにもっと完全な記述があります)。

SMTP 専用
フィルタ
非 SMTP
フィルタ
^
|
|
v
^
|
|
|
|
|
|
v
ネットワーク -> smtpd(8)
\
ネットワーク -> qmqpd(8) -> cleanup(8) -> incoming
/
pickup(8)
:
ローカル -> sendmail(1)

Milter アプリケーションのビルド

Milter アプリケーションは C や JAVA、Perl で書かれているかもしれませんが、この文書では C アプリケーションだけを扱います。Milter アプリのために Sendmail 8 Milter プロトコルを実装したオブジェクトライブラリが必要になります。Postfix は現在そのようなライブラリを提供していませんが、Sendmail にはあります。

いくつかの Linux や *BSD ディストリビューションには Sendmail の libmilter ライブラリがデフォルトでインストールされています。これを使えば、ヘタにいじりまわさなくても dk-miltersid-milter のようなアプリケーションができあがります:

$ gzcat dk-milter-x.y.z.tar.gz | tar xf -
$ cd dk-milter-x.y.z
$ make
[...出力は略...]

ほかのプラットホームではふたつの選択肢があります:

Milter アプリケーションを動作させる

Milter アプリケーションを動かすには、オプションのためフィルタのドキュメントを参照してください。典型的なコマンドはこのようになります:

# /some/where/dk-filter -u userid -p inet:portnumber@localhost ...other options...

userid に他のアプリケーションで使われていない値 ("postfix" や "www" などでない値) を指定してください。

Postfix の設定

Sendmail と同じく、Postfix にはどのように Milter アプリケーションとやりとりするかを制御する設定オプションが多数あります。初期の Postfix の Milter プロトコル実装では、たくさんのオプションがグローバル、すなわちすべての Milter アプリケーションに適用されます。将来の Postfix バージョンでは Milter ごとのタイムアウトや Milter ごとのエラーハンドリングなどをサポートするでしょう。

この節の情報:

SMTP 専用 Milter アプリケーション

SMTP 専用 Milter アプリケーションは Postfix smtpd(8) サーバをとおって届いたメールを扱います。これは典型的にはいらないメールをフィルタしたり、承認された SMTP クライアントからのメールに署名したりするのに使われます。Postfix smtpd(8) サーバをとおって届いたメールは次に述べる非 SMTP フィルタは適用されません。

注意: Postfix が自分で付加した Received: メッセージヘッダを除去するために header_checks(5) の IGNORE アクションを使ってはいけません。これはメール署名フィルタで問題を起こします。かわりに、Postfix の Received: メッセージヘッダは残し、情報を無害化するために header_checks(5) の REPLACE アクションを使ってください。

SMTP 専用 Milter アプリケーション (ひとつ以上) は smtpd_milters パラメータに指定する。それぞれの Milter アプリケーションは listen しているソケットの名前で識別されます; それ以外の Milter 設定オプションは後の節で議論します。Milter アプリケーションは指定された順に適用され、コマンドを拒否した最初の Milter アプリケーションが他の Milter アプリケーションからの応答に優先します。

/etc/postfix/main.cf:
    # Milters for mail that arrives via the smtpd(8) server.
    # See below for socket address syntax.
    smtpd_milters = inet:localhost:portnumber ...other filters...

listen するソケットの一般的な文法は以下のようになります:

unix:pathname

指定されたパス名でバインドされているローカルの UNIX ドメインサーバへの接続。もし smtpd(8) ないしは cleanup(8) プロセスが chroot して動作していれば、絶対パスは Postfix のキューディレクトリからの相対パスとして解釈されます。

inet:host:port

指定したローカルないしはリモートホストの指定した TCP ポートへの接続。ホストとポートは数字か名前で指定します。

注意: Postfix の文法は inet:port@host という形式の Milter の文法とは異なります。

非 SMTP Milter アプリケーション

非 SMTP フィルタは Postfix sendmail(1) のコマンドラインや Postfix qmqpd(8) サーバをとおって届いメールを扱います。典型的にはメールの電子署名に使われます。非SMTPフィルタを不要なメールのフィルタに使うこともできますが、この節で後述するような制限があります。Postfix smtpd(8) サーバをとおって届いたメールは非 SMTP フィルタは適用されません。

注意: Postfix が自分で付加した Received: メッセージヘッダを除去するために header_checks(5) の IGNORE アクションを使ってはいけません。これはメール署名フィルタで問題を起こします。かわりに、Postfix の Received: メッセージヘッダは残し、情報を無害化するために header_checks(5) の REPLACE アクションを使ってください。

非 SMTP Milter アプリケーションは non_smtpd_milters パラメータに指定します。このパラメータは前節の smtpd_milters と同じ文法を使います。SMTP 専用フィルタと同じように、ひとつ以上の Milter アプリケーションを指定でき、指定した順に適用され、コマンドを拒否した最初の Milter アプリケーションが他のアプリケーションからの応答に優先します。

/etc/postfix/main.cf:
    # Milters for non-SMTP mail.
    # See below for socket address syntax.
    non_smtpd_milters = inet:localhost:portnumber ...other filters...

非 SMTP メールで Milter アプリケーションを使うときにひとつ小さなめんどうごとがあります: SMTP セッションがありません。Milter アプリケーションをうまく動かすために、Postfix cleanup(8) サーバは実はなんと SMTP クライアントの接続と切断のイベントおよび、SMTP クライアントの EHLO、MAIL FROM、RCPT TO、DATA コマンドをシミュレートしています。

これは一般的には期待どおりに動きますが、ひとつだけ例外があります: 非 SMTP フィルタはシミュレートされた RCPT TO コマンドを拒否したり、一時的失敗にしたりしてはいけません。 non_smtpd_milters アプリケーションが受信者を拒否したり一時的失敗したりすると、Postfix は設定エラーを報告し、メールはキューに留まります。

メールに電子署名するメールフィルタではこの問題は起きません。

Milter のエラーの扱い

Milter アプリケーションのエラーを Postfix がどのように扱うかは milter_default_action パラメータで指定します。デフォルトでは、クライアントが後で再試行できるように一時エラーのステータスを応答するという動作になります。フィルタがなかったかのようにメールを受けとりたければ "accept" を指定し、恒久エラーのステータスでメールを拒否するには "reject" とします。

    # What to do in case of errors?Specify accept, reject, or tempfail.
    milter_default_action = tempfail

Milter プロトコルバージョン

Postfix は Sendmail の libmilter ライブラリを使ってビルドされるわけではないので、Postfix が使うべき Milter プロトコルのバージョンを設定する必要があるかもしれません。デフォルトのバージョンは2です。

milter_protocol = 2

もしPostfix milter_protocol で設定したバージョンが小さすぎると、libmilter ライブラリはこのようなエラーメッセージをログに記録します:

application name: st_optionneg[xxxxx]: 0xyy does not fulfill action requirements 0xzz

直すには Postfix の milter_protocol のバージョン番号を増やします。しかしながら、Postfix でサポートされていない機能もあるので、下にある制限の節も参照してください。

もし Postfix の milter_protocol で設定したバージョンが大きすぎると、libmilter ライブラリは警告をログに出力せず単純にハングアップし、Postfix は以下のどちらかの警告メッセージを出力します:

postfix/smtpd[21045]: warning: milter inet:host:port: can't read packet header: Unknown error : 0
postfix/cleanup[15190]: warning: milter inet:host:port: can't read packet header: Success

直すには Postfix の milter_protocol のバージョン番号をもっと小さくします。

Milter プロトコルのタイムアウト

Postfix は Milter のプロトコルのそれぞれの段階で異なる制限時間があります。この表はどのタイムアウトがありいつ使われるかを示したものです (EOH = ヘッダの終わり、EOM = メッセージの終わり)。

パラメータ 制限時間 プロトコルの段階
milter_connect_timeout 30s CONNECT
milter_command_timeout 30s HELO, MAIL, RCPT, DATA, UNKNOWN
milter_content_timeout 300s HEADER, EOH, BODY, EOM

注意: DNS 検索を多くおこなうアプリケーションでは30秒は大きくありません。しかしながら、上のタイムアウトを大きく増やしすぎると、リモート SMTP クライアントがあきらめてメールが何回も配送されるかもしれません。これはキュー投入前フィルタではもともとある問題である。

Sendmail マクロのエミュレーション

Postfix は限られた数の Sendmail マクロを表に示したようにエミュレートします。SMTP プロトコルのの段階ごとに異なるのマクロが利用できます (EOM = メッセージの終わり)。これらが利用できるかは Sendmail と常に同じというわけではありません。解決するには下の回避方法の節を参照してください。

利用可能
i DATA, EOM キューID
j いつでも myhostname の値
_ いつでも 検証されたクライアント名とアドレス
{auth_authen} MAIL, DATA, EOM SASL ログイン名
{auth_author} MAIL, DATA, EOM SASL 送信者
{auth_type} MAIL, DATA, EOM SASL ログインメソッド
{client_addr} いつでも クライアント IP アドレス
{client_connections} CONNECT このクライアントの同時接続
{client_name} いつでも クライアントホスト名 (検索、照合に失敗したときは "unknown")
{client_ptr} CONNECT, HELO, MAIL, DATA クライアントの逆引き名 (検索に失敗したときは "unknown")
{cert_issuer} HELO, MAIL, DATA, EOM TLS クライアント証明書の issuer (発行者)
{cert_subject} HELO, MAIL, DATA, EOM TLS クライアント証明書の subject
{cipher_bits} HELO, MAIL, DATA, EOM TLS セッション鍵のサイズ
{cipher} HELO, MAIL, DATA, EOM TLS 暗号化方式
{daemon_name} いつでも milter_macro_daemon_name の値
{mail_addr} MAIL 送信者アドレス
{rcpt_addr} RCPT 受信者アドレス
{tls_version} HELO, MAIL, DATA, EOM TLS プロトコルバージョン
v いつでも milter_macro_v の値

Postfix は SMTP プロトコルの段階ごとに特定のマクロ一式を送ります。このマクロは表に説明したようなパラメータで設定されます (EOM = メッセージの終わり)。

パラメータ名 プロトコルバージョン プロトコルの段階
milter_connect_macros 2 以上 CONNECT
milter_helo_macros 2 以上 HELO/EHLO
milter_mail_macros 2 以上 MAIL FROM
milter_rcpt_macros 2 以上 RCPT TO
milter_data_macros 4 以上 DATA
milter_end_of_data_macros 2 以上 EOM
milter_unknown_command_macros 3 以上 不明なコマンド

回避方法

コンテンツフィルタが DomainKey その他の署名を壊すかもしれません。SMTPベースのコンテンツフィルタを使っているなら、advanced content filterの例に書かれているように master.cf に "-o disable_mime_output_conversion=yes" (注意: "=" の前後に空白を置かない) を加えるべきです。

Sendmail の Milter アプリケーションはもともと Sendmail バージョン 8 用に開発されたものであり、Postfix とは構造が異なります。結果として、いくつかの Milter アプリケーションは Postfix では成り立たない環境を前提にしているものがあります。

制限

この節では Postfix の Milter 実装における制限を列挙します。いくつかの制限は時間をかけて実装が進むうちに取り除かれるでしょう。もちろん、キュー投入前フィルタリングの通常の制限は常に適用されます。解説は CONTENT_INSPECTION_README を参照してください。