Postfix SMTP アクセスポリシー委譲


Postfix SMTP アクセスポリシー委譲の目的

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]

注意:

以下は SMTPD ポリシー委譲要求に特有です:

ポリシーサーバは 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

注意:

 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

クライアント側のポリシー委譲プロトコルを制御する他の設定パラメータ:

例: greylist ポリシーサーバ

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

注意:

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 する

頻繁に騙られる特定のドメインに対して 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 ...

注意:

メールすべてを greylisting する

全てのメールに対して 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     ...

注意:

日常の greylist 管理

greylist サーバはデータベースのエントリを削除しないため、greylist データベースは時間がたつと大きくなっていきます。放っておくと、greylist データベースは最終的にファイルシステムの空きを使い果たしてしまいます。

ステータスファイルサイズがある閾値を超えた際に単にそのファイルを リネームしたり削除しても悪影響はありません; Postfix は自動的に新しい ファイルを作成します。最悪の場合、新しいメールは1時間程度遅れます。 影響を知るには、週末の真夜中にそのファイルをリネームもしくは削除して ください。

Perl greylist サーバの例

これは 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";
    }
}