Postfix SMTP サーバには SMTP プロトコルの特定の場面でメールを拒否したり 受け付けるためのメカニズムがいくつか組み込まれています。バージョン 2.1 の 時点で、Postfix はポリシーの決定を Postfix の外で動く外部サーバに委譲する ことができるようになりました。
このポリシー委譲メカニズムを使うと、このドキュメントの最後に示すような、 たった十数行程度の Perl で単純なgreylist ポリシーを 実装できます。ポリシー委譲のほかの例としては、 http://spf.pobox.com/ にある Meng Wong による SPF ポリシーサーバがあります。どちらのポリシーの例も Postfix ソースコードの examples/smtpd-policy ディレクトリに含まれています。
ポリシー委譲は Postfix にポリシーを追加するのに現在好まれている 方法です。数行の Perl で新しい機能を開発するのは、同じことを C コードで やろうとするよりもずっと簡単です。パフォーマンスの違いは非常に要求の 厳しい環境以外では目立たないでしょう。
このドキュメントは以下の話題をカバーします:
Postfix ポリシー委譲プロトコルは本当にシンプルです。クライアントの 要求は name=value 属性が改行で分割された並びであり、空行で終わります。 サーバの応答は name=value 属性1つであり、これも空行で終わります。
Postfix SMTP サーバが SMTPD アクセスポリシー委譲の要求で送る全ての 属性の例を示します:
request=smtpd_access_policy protocol_state=RCPT protocol_name=SMTP helo_name=some.domain.tld queue_id=8045F2AB23 sender=foo@bar.tld recipient=bar@foo.tld client_address=1.2.3.4 client_name=another.domain.tld instance=123.456.7 sasl_method=plain sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ccert_issuer=Wietse Venema ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04 size=12345 [empty line]
注意:
request" 属性は必須です。この例では、要求タイプは "smtpd_access_policy" です。
属性は順不同です。ポリシーサーバは気にしない属性を無視すべきです。
同じ属性名が2度以上送られると、サーバは最初の値を保持するかも しれませんし、最後の属性値を保持するかもしれません。
属性値が得られないと、クライアントはその属性を送らないか、 値を空にして ("name=") その属性を送ります。
クライアントアドレスは 1.2.3.4 という形のドットで区切られたIPv4の 4つの数字または 1:2:3::4:5:6 のような形のIPv6アドレスです。
属性名に "=" や null、改行を含んだり、属性値に null や改行を 含んではいけません。
"instance" 属性値は同じメッセージ配送に関する別の要求を関連 づけるのに使われます。
"size" 属性値にはクライアントが MAIL FROM コマンドで指定した メッセージのサイズ (指定されていなければゼロ) が入ります。Postfix 2.2 以降では、クライアントが END-OF-DATA コマンドを送った時に実際のメッセージ サイズになります。
"sasl_*" 属性 (Postfix 2.2以降) はクライアントがSASLで認証された 方法に関する情報を示します。
"ccsert_*" 属性 (Postfix 2.2以降) はクライアントがTLSで認証された 方法に関する情報を示します。
以下は SMTPD ポリシー委譲要求に特有です:
プロトコル名は ESMTP または SMTP です。
プロトコル状態は CONNECT, EHLO, HELO, MAIL, RCPT, DATA, END-OF-MESSAGE, VRFY または ETRN です; これらはPostfix SMTPサーバが OK/REJECT/HOLD/他を決定するSMTPプロトコルの場面です。
ポリシーサーバは Postfix SMTPD access(5) テーブルで許されるいずれかの action で応答します。例:
action=defer_if_permit Service temporarily unavailable [empty line]
これにより、Postfix SMTP サーバが要求を恒久的に拒否する理由が 見つからない場合は、 Postfix SMTP サーバは要求を 450 一時的エラーコードに "Service temporarily unavailable" という文を付けて拒否するようになります。
問題が起こった場合、ポリシーサーバは応答を返してはいけません。 代わりにサーバは警告をログに記録して接続を切らなければいけません。 Postfix はしばらくしてから要求を再試行します。
Postfix でポリシーを委譲するクライアントは TCP ソケットもしくは UNIX ドメインソケットに接続できます。例:
inet:127.0.0.1:9998 unix:/some/where/policy unix:private/policy
最初の例では、ポリシーサーバが 127.0.0.1 のポート 9998 で TCP ソケットを listen するように指定しています。2つ目の例では UNIX ドメイン ソケットの絶対パス名を指定しています。3番目の例では Postfix キュー ディレクトリからの相対パス名を指定しています; Postfix master デーモンに よって呼ばれるポリシーサーバに対してはこれを使ってください。
"policy" という名前の UNIX ドメインソケットで listen し、Postfix spawn(8) デーモンの制御かで動くポリシー サーバを作るには、このようなものを使います:
1 /etc/postfix/master.cf: 2 policy unix - n n - - spawn 3 user=nobody argv=/some/where/policy-server 4 5 /etc/postfix/main.cf: 6 smtpd_recipient_restrictions = 7 ... 8 reject_unauth_destination 9 check_policy_service unix:private/policy 10 ... 11 policy_time_limit = 3600
注意:
2, 11行目: Postfix spawn(8) デーモンは デフォルトで1000秒後に子プロセスを kill します。これは SMTP クライアントが SMTP サーバプロセスに接続している間動き続けるポリシーデーモンには 短すぎます。デフォルトの時間制限は明示的に "policy_time_limit" を設定する ことで上書きされます。パラメータの名前は master.cf エントリの名前 ("policy") と "_time_limit" サフィックスをつなげたものです。
8, 9行目: "check_policy_service" は必ず "reject_unauth_destination" の 「後に」指定してください。そうしないとシステムがオープンリレーになって しまいます。
Solaris の UNIX ドメインソケットは信頼して使うことができません。 代わりに TCP ソケットを使ってください:
1 /etc/postfix/master.cf: 2 127.0.0.1:9998 inet - n n - - spawn 3 user=nobody argv=/some/where/policy-server 4 5 /etc/postfix/main.cf: 6 smtpd_recipient_restrictions = 7 ... 8 reject_unauth_destination 9 check_policy_service inet:127.0.0.1:9998 10 ... 11 127.0.0.1:9998_time_limit = 3600
クライアント側のポリシー委譲プロトコルを制御する他の設定パラメータ:
smtpd_policy_service_max_idle (デフォルト: 300s): Postfix SMTP サーバが使われていないポリシークライアント接続を閉じるまでの時間。
smtpd_policy_service_max_ttl (デフォルト: 1000s): Postfix SMTP サーバがアクティブなポリシークライアント接続を閉じるまでの時間。
smtpd_policy_service_timeout (デフォルト: 100s): ポリシー サーバへの接続、ポリシーサーバへの送信、またはポリシーサーバからの受信に おける時間制限。
Greylisting は http://www.greylisting.org/ に かかれているような、ジャンクメールに対する防衛方法です。このアイディアは 有名になる1年以上前に postfix-users メーリングリストで議論されました。
Postfix ソースツリーにあるファイル examples/smtpd-policy/greylist.pl は 単純化された greylist ポリシーサーバを実装します。このサーバは全ての (クライアント、送信者、受信者) の組み合わせに対するタイムスタンプを 保存します。デフォルトでは、タイムスタンプが60秒以上経過するまでメールは 受け付けられません。これはランダムに選択された送信者アドレスを持つ ジャンクメールや、ランダムに選択されたオープンプロキシを経由して送られる メールを止めます。また、頻繁に IP アドレスを変えるスパマーカらのジャンク メールも止めます。
examples/smtpd-policy/greylist.pl を /usr/libexec/postfix または システムの適した場所にコピーしてください。
greylist.pl Perl スクリプトには、treylist データベースファイルの 場所と、メールが受け入れられるまでの遅延時間の長さを指定する必要が あります。デフォルトの設定:
$database_name="/var/mta/greylist.db"; $greylist_delay=60;
/var/mta ディレクトリ (もしくはあなたが選択した場所) は "nobody" もしくはポリシーサービスに対して master.cf で設定したユーザ名が書き込める 必要があります。
例:
# mkdir /var/mta # chown nobody /var/mta
注意: /tmp や /var/tmp のように誰でも書き込めるディレクトリに greylist データベースを作ったり、空きを使い果たしてしまう可能性のある ファイルシステムにデータベースを「作らないでください」。Postfix は メールキューやメールボックス保管庫用の "空きスペースがない" 状態でも 生き残ることができますが、greylist データベースが壊れると生き残ることは できません。ファイルが壊れたら手でファイルを削除するまでメールを全く 受信できなくなってしまいます。
greylist.pl Perl スクリプトは Postfix master デーモンの制御下で 動かすことができます。例えば、"nobody" ユーザとしてスクリプトを 走らせるには、Postfix プロセスのみがアクセス可能な UNIX ドメイン ソケットを使います:
1 /etc/postfix/master.cf: 2 policy unix - n n - - spawn 3 user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl 4 5 /etc/postfix/main.cf: 6 policy_time_limit = 3600
注意:
3行目: それぞれの要求や応答の冗長なログを取るには、"greylist.pl -v" を指定します。
2, 6行目: Postfix spawn(8) デーモンは デフォルトで1000秒後に子プロセスを kill します。これは SMTP クライアントが SMTP サーバプロセスに接続している間動き続けるポリシーデーモンには 短すぎます。デフォルトの時間制限は明示的に "policy_time_limit" を設定 することで上書きされます。パラメータの名前は master.cf エントリの名前 ("policy") と "_time_limit" サフィックスをつなげたものです。
Solaris では、上の "ポリシークライアント/サーバ設定" セクションで詳細が 述べられているように、unix: 形式のソケットではなく inet: 形式のソケットを 使わなければいけません。
1 /etc/postfix/master.cf: 2 127.0.0.1:9998 inet - n n - - spawn 3 user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl 4 5 /etc/postfix/main.cf: 6 127.0.0.1:9998_time_limit = 3600
このサービスを呼び出すためには、"check_policy_service inet:127.0.0.1:9998" と指定します。
頻繁に騙られる特定のドメインに対して greylisting を有効にするのは 比較的安全です。よく騙られる MAIL FROM ドメインのリストは http://www.monkeys.com/anti-spam/filtering/sender-domain-validate.in に あります。
1 /etc/postfix/main.cf: 2 smtpd_recipient_restrictions = 3 reject_unlisted_recipient 4 ... 5 reject_unauth_destination 6 check_sender_access hash:/etc/postfix/sender_access 7 ... 8 smtpd_restriction_classes = greylist 9 greylist = check_policy_service unix:private/policy 10 11 /etc/postfix/sender_access: 12 aol.com greylist 13 hotmail.com greylist 14 bigfoot.com greylist 15 ... etcetera ...
注意:
9行目: Solaris では、上の "例: greylist ポリシーサーバ" セクションで詳細が述べられているように、unix: 形式のソケットではなく inet: 形式のソケットを使わなければいけません。
6行目: "check_sender_access" は 確実に "reject_unauth_destination" の 「後に」指定してください。そうしないとシステムがオープンメールリレーに なってしまいます。
3行目: Postfix 2.0 スナップショットリリースでは、 "reject_unlisted_recipient" は "check_recipient_maps" と呼ばれていました。Postfix 2.1 は両方の形式を 理解します。
3行目: greylist データベースは偽のアドレスですぐに汚染されて しまいます。知らない送信者や受信者を拒否するような他の制限で greylist 検索を保護することが役立ちます。
全てのメールに対して greylisting を有効にすると、一時的な送信者 アドレスを使うメーリングリストは比較的急速に greylist データベースを 汚染してしまうので、そのようなメーリングリストをまず間違いなく除外したく なるはずです。
1 /etc/postfix/main.cf: 2 smtpd_recipient_restrictions = 3 reject_unlisted_recipient 4 ... 5 reject_unauth_destination 6 check_sender_access hash:/etc/postfix/sender_access 7 check_policy_service unix:private/policy 8 ... 9 10 /etc/postfix/sender_access: 11 securityfocus.com OK 12 ...
注意:
7行目: Solaris では、上の "例: greylist ポリシーサーバ" セクションで詳細が述べられているように、unix: 形式の ソケットではなく inet: 形式のソケットを使わなければいけません。
6-7行目: check_sender_access および check_policy_service は 確実に reject_unauth_destination の 「後に」指定してください。そうしないとシステムがオープンメールリレーに なってしまいます。
3行目: greylist データベースは偽のアドレスですぐに汚染されて しまいます。greylist 検索に先だって知らない送信者や受信者を拒否するように 制限しておくことが役立ちます。
greylist サーバはデータベースのエントリを削除しないため、greylist データベースは時間がたつと大きくなっていきます。放っておくと、greylist データベースは最終的にファイルシステムの空きを使い果たしてしまいます。
ステータスファイルサイズがある閾値を超えた際に単にそのファイルを リネームしたり削除しても悪影響はありません; Postfix は自動的に新しい ファイルを作成します。最悪の場合、新しいメールは1時間程度遅れます。 影響を知るには、週末の真夜中にそのファイルをリネームもしくは削除して ください。
これは greylist ポリシーの例を実装した Perl サブルーチンです。 Postfix ソースの examples/smtpd-policy/greylist.pl として配布されている 汎用のサンプルポリシーサーバの一部です。
# # greylist 状態データベースおよび greylist 時間間隔。/tmp や /var/tmp のように # 誰でも書き込めるディレクトリに greylist 状態データベースを「作っては # いけません」。greylist データベースを空きを使い果たしてしまうファイル # システムに「作ってはいけません」。 # $database_name="/var/mta/greylist.db"; $greylist_delay=60; # # デモ SMTPD access ポリシールーチン。結果は Postfix access テーブルの右側部分で # 指定されるのと全く同じ action です。要求属性は %attr ハッシュを通じて # 得られます。 # sub smtpd_access_policy { my($key, $time_stamp, $now); # 動的にデータベースを開きます。 open_database() unless $database_obj; # この client/sender/recipient に対応するタイムスタンプを検索します。 $key = lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; $time_stamp = read_database($key); $now = time(); # 新しい要求の場合、データベースにこの client/sender/recipient を加えます。 if ($time_stamp == 0) { $time_stamp = $now; update_database($key, $time_stamp); } # result は Postfix access(5) マップで許されるいずれかの action です。 # # メールにラベルを付けるには ``PREPEND headername: headertext'' を返します。 # # 成功の場合は ``OK'' ではなく ``DUNNO'' を返し、check_policy_service 制限に # 他の制限が続くようにします。 # # 失敗の場合は ``DEFER_IF_PERMIT optional text...'' を返し、他の access # 制限でもブロックできるようにします。 # syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose; if ($now - $time_stamp > $greylist_delay) { return "dunno"; } else { return "defer_if_permit Service temporarily unavailable"; } }