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 コードでやろうとするよりもずっと簡単です。パフォーマンスの違いは非常に要求の厳しい環境以外では目立たないでしょう。アクティブなシステムでは、ポリシーデーモンプロセスは入ってくるSMTP接続数が $max_use に達するまで複数回使われます。

このドキュメントは以下の話題をカバーしています:

プロトコルの記述

Postfix ポリシー委譲プロトコルは本当にシンプルです。クライアントの要求は name=value 属性が改行で分割された並びであり、空行で終わります。サーバの応答は name=value 属性1つであり、これも空行で終わります。

Postfix SMTP サーバが SMTPD アクセスポリシー委譲の要求で送る全ての属性の例を示します:

Postfixバージョン2.1以降:
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
recipient_count=0
client_address=1.2.3.4
client_name=another.domain.tld
reverse_client_name=another.domain.tld
instance=123.456.7
Postfixバージョン2.2以降:
sasl_method=plain
sasl_username=you
sasl_sender=
size=12345
ccert_subject=solaris9.porcupine.org
ccert_issuer=Wietse+20Venema
ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
Postfixバージョン2.3以降:
encryption_protocol=TLSv1/SSLv3
encryption_cipher=DHE-RSA-AES256-SHA
encryption_keysize=256
etrn_domain=
[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       -       0       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       n       -       0       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       -       0       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       n       -       0       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";
    }
}