Postfix SMTP サーバは、特定の SMTP プロトコルの段階で、メールをブロッ クまたは受け入れるための多くの組み込みの仕組みを持つ。バージョン 2.1 から、Postfix は Postfix の外側で実行している外部サーバにポリシー判定 を委任できるようになった。
このポリシー判定の仕組みは、単純な greylist ポリシーなら、1ダース行程度の Perl で実装できる。他のポリシー委任の例 は、http://spf.pobox.com/ の Meng Wong による SPF ポリシーサーバである。両方のポリシーの例は、Postfix の ソースコード中のディレクトリ examples/smtpd-policy に見つけられる。
ポリシー委任は現在、Postfix にポリシーを追加する好ましい方法である。 数行の Perl で新しい機能を、C コードで同じことを試みるよりもとても簡単 に開発できる。性能の違いは、もっとも厳しい環境を除けば、無視される。
このドキュメントは次の話題をカバーする:
Postfix ポリシー委任プロトコルは本当に簡単である。クライアント要求 は改行で区切られた name=value 属性のリストで、空行で終了される。サーバ の応答は1つの name=value 属性で、空行で終了される。
これは、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= size=12345 [empty line]
Notes:
"request" 属性は必要である。この例では request type は "smtpd_access_policy" である。
属性の順番は重要でない。ポリシーサーバは関心のない属性は無視すべきである。
同じ属性名が一度以上送られた時、サーバは最初の値か最後の属性値を保持するだろう。
属性値が有効でない時、クライアントは属性を送信しないか、空の値("name=")で属性を送信する。
属性名は "=", null, 改行を含んではいけない。属性値は null または改行を含んではいけない。
"instance" 属性の値は、異なる要求を同じメッセージ配送とみなすために使用することができる。
次は SMTPD 委任ポリシー要求の使用である:
プロトコル名は ESMTP または SMTP である。
プロトコル状態は CONNECT, EHLO, HELO, MAIL, RCPT, DATA, VRFY, ETRN である。これらは、Postfix SMTP サーバが OK/REJECT/HOLD/etc. を決定した SMTP プロトコル状態である。
SASL属性は SASL サポートが Postfix に組み込まれている時だけに送られる。
ポリシーサーバは Postfix SMTPD access(5) テーブルに許される任意のアクション で応答する。例:
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
最初の例は、ポリシーサーバが TCP ソケット 127.0.0.1 ポート 9998 で listen することを示す。2番目の例は、UNIX ドメインソケットの絶対パスを 示す。3番目の例は、Postfix キューディレクトリからの相対パスを示す; Postfix master デーモンから起動されるポリシーサーバにはこれを使用する。
UNIX ドメインソケットで listen する "policy" という名前のポリシーサー ビスを生成し、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
NOTES:
2,11行目: Postfix spawn(8) デーモン はデフォルトで、1000 秒後に子プロセスを殺す。これは、SMTP クライアント が SMTP サーバプロセスに接続されるのと同じくらい長い間実行されるポリシー デーモンには短すぎる。デフォルトの時間制限は main.cf の明示的な "policy_time_limit" 設定で抑えられる。パラメータ名は master.cf エント リ ("policy") に "_time_limit" を繋げたものである。
8,9行目: 常に "reject_unauth_destination" の後に "check_policy_service" を指定せよ。そうしないと、あなたのシステムがオープンリレーになる
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 (default: 300s): Postfix SMTP サーバが未使用のポリシークライアント接続をクローズするま での時間。
smtpd_policy_service_max_ttl (default: 1000s): Postfix SMTP サーバがアクティブなポリシークライアント接続をクローズす るまでの時間。
smtpd_policy_service_timeout (default: 100s): ポリシーサーバに対する接続,送信,受信の時間制限。
グレイリストは、http://www.greylisting.org/ で述べられているゴミメールの防衛策である。アイデアは postfix-users メー リングリストで、 それが普及する一年前に議論された。
Postfix ソースツリー内のファイル examples/smtpd-policy/greylist.pl は簡単なグレイリストポリシーサーバを実装している。このサーバはすべての (client, sender, recipient) triple について、時刻を格納する。デフォル トでは、時刻が 60秒以上古くなるまで受け付けられない。これは、ランダム に選択された送信者アドレスのゴミメールと、ランダムに選択されたオープン プロキシーを通ったメールを停止する。これは IP アドレスを頻繁に変更する スパマーからのゴミメールも停止する。
examples/smtpd-policy/greylist.pl を /usr/libexec/postfix またはあ なたのシステムに適切な場所にコピーする。
greylist.pl Perl スクリプト中に、グレイリストデータベースファイルの 場所と、受け付けられるまでの遅延時間を指定する必要がある。デフォルト設 定は:
$database_name="/var/mta/greylist.db"; $greylist_delay=60;
/var/mta ディレクトリ(またはあなたの選んだ場所)は "nobody" またはポ リシーサービス用に master.cf に設定したユーザ名で書き込めるようにすべ きである。
例:
# mkdir /var/mta # chown nobody /var/mta
Note: グレイリストデータベースは、/tmp や /var/tmp のような全員に書 き込み可能なディレクトリに生成してはいけない。そして、空き領域が無くな りそうなファイルシステムにグレイリストデータベースを生成してはいけない。 Postfix はメールキューとメールボックス格納では、"領域不足" 状態で生き られるが、壊れたグレイリストデータベースは生きられない。ファイが壊れる と、あなたが手でファイルを削除するまでメールが受信できなくなる。
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
Notes:
3行目: 各要求と応答の冗長ログには "greylist.pl -v" を指定する。
2,6行目: Postfix spawn(8) デーモンは デフォルトで、1000秒後に子プロセスを殺す。これは、SMTP クライアントが SMTP サーバプロセスに接続されるのと同じくらい長い間実行されるポリシー デーモンには短すぎる。デフォルトの時間制限は main.cf の明示的な "policy_time_limit" 設定で抑えられる。パラメータ名は master.cf エント リ ("policy") に "_time_limit" を繋げたものである。
上述の "Policy client/server configuration" 節で説明したように、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" を指定する。
頻繁に偽造メールが現れる特定のドメインに対してグレイリストを有効に するのは、比較的安全である。頻繁に偽造される 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 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 ...
NOTES:
9行目: 上述の "例: グレイリストポリシーサー バ" 節で説明したように、Solaris では unix: スタイルの代わりに inet: スタイルのソケットを使用する必要がある。
6行目: 確実に "reject_unauth_destination" の後に "check_sender_access" を記述せよ。そうしないと、あなたのシステムがオープンメールリレーになる。
3行目: Postfix 2.0 snapshot リリースでは、 "reject_unlisted_recipient" は "check_recipient_maps" と呼ばれている。 Postfix 2.1 は両方の形式を理解する。
3行目: グレイリストデータベースは偽のアドレスですぐに汚染される。 グレイリストルックアップを防御する場合、未知の送信者/受信者を拒否する 他の制限が助けになる。
すべてのメールについてグレイリストを有効にする場合、ワンタイム送信 者アドレスを使用するメーリングリストを例外にしたいだろう。そのようなメー リングリストは比較的すぐにあなたのグレイリストデータベースを汚染するか らである。
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 ...
NOTES:
7行目: 上述の "例: グレイリストポリシーサー バ" 節で説明したように、Solaris では unix: スタイルの代わりに inet: スタイルのソケットを使用する必要がある。
6-7行目: 確実に reject_unauth_destination の後に check_sender_access と check_policy_service を記述せよ。そうしないと、あなたのシステムがオープンメールリレーになる。
3行目: グレイリストデータベースは偽のアドレスですぐに汚染される。 グレイリストルックアップを防御する場合、未知の送信者/受信者を拒否する 他の制限が助けになる。
グレイリストデータベースは時間が経つと大きくなる。グレイリストサー バはデータベースエントリを削除しないからである。注意しないと、グレイリ ストデータベースは最終的にあなたのファイルシステムの空き領域を使いきる。
状態ファイルサイズがある閾値を超えた時、悪影響なしに単純にファイル をリネームまたは削除できる; Postfix は自動的に新しいファイルを生成する。 最悪の場合、新しいメールが1時間以上遅延する。影響を小さくするには、週 末の始まりの夜中にファイルをリネームまたは削除する。
これは、グレイリストポリシーの実装例 Perl サブルーチンである。 examples/smtpd-policy/greylist.pl として Postfix ソースとともに配布さ れる一般的な目的のサンプルポリシーサーバの一部である。
# # greylist status database and greylist time interval. DO NOT create the # greylist status database in a world-writable directory such as /tmp # or /var/tmp. DO NOT create the greylist database in a file system # that can run out of space. # $database_name="/var/mta/greylist.db"; $greylist_delay=60; # # Demo SMTPD access policy routine. The result is an action just like # it would be specified on the right-hand side of a Postfix access # table. Request attributes are available via the %attr hash. # sub smtpd_access_policy { my($key, $time_stamp, $now); # Open the database on the fly. open_database() unless $database_obj; # Lookup the time stamp for this client/sender/recipient. $key = lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; $time_stamp = read_database($key); $now = time(); # If new request, add this client/sender/recipient to the database. if ($time_stamp == 0) { $time_stamp = $now; update_database($key, $time_stamp); } # The result can be any action that is allowed in a Postfix access(5) map. # # To label the mail, return ``PREPEND headername: headertext'' # # In case of success, return ``DUNNO'' instead of ``OK'', so that the # check_policy_service restriction can be followed by other restrictions. # # In case of failure, return ``DEFER_IF_PERMIT optional text...'', # so that mail can still be blocked by other access restrictions. # 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"; } }