Postfix のメール受信に制限を掛ける

こんにちは。サイオステクノロジー OSS サポート担当 N です。

RHEL の 標準 MTA として同梱されている Postfix について、メールの受信時の代表的な制限パラメータの動作を確認してみます。
本稿では、下記のメール受信時に動作するアーキテクチャの内、代表的な smtpd(8) で機能する制限をご紹介します。

・メールを受信する仕組み
 https://www.postfix.org/OVERVIEW.html#receiving
・メール受信時の制限
 https://www.postfix.org/TUNING_README.html#server_tips

■環境構成

 RHEL7.6
 postfix-2.10.1-7.el7.x86_64

 メール送信サーバ :sender.example.com
 メール受信サーバ :recipient.example.com
 ※今回は受信サーバ側で制限を設定します。

■動作検証

大量のエラーを発生させる SMTP クライアントの速度を制限する

Postfix の smtpd(8) サーバでは、各セッション毎で発生したエラー数を管理しています。同一セッション内で発生したエラーが一定数を超過すると、その SMTP クライアントへのサーバレスポンスに遅延時間を挿入して、速度を低下させます。それでも尚エラーが発生し続けると、今度は該当のコネクションを切断し、SMTP サーバプロセスが枯渇するのを防ぎます。

 smtpd_soft_error_limit (default: 10)
  SMTP サーバレスポンスに遅延時間を挿入するまでのエラーカウント
 smtpd_error_sleep_time (default: 1s)
  SMTP サーバレスポンスに挿入される遅延時間
 smtpd_hard_error_limit (default: normal: 20, overload: 1)
  SMTP コネクションを切断するまでのエラーカウント

各パラメータの現在の設定値は、postconf コマンドで確認できます。

 # postconf smtpd_soft_error_limit smtpd_error_sleep_time smtpd_hard_error_limit

デフォルトでは以下のような挙動になります。

「同一セッション内で、エラー数が 10回を超えると、サーバレスポンスが 1秒間遅延し、20回を超えるとコネクションを切断」
また、セッション内のエラーをカウントするので、一度でも正常にメールを受信するなどしてセッションが終了すると、エラーのカウントはリセットされます。

送信サーバから telnet コマンドで受信サーバへ接続し、上記の制限を確認してみましょう。

 メール送信元アドレス :root@sender.example.com
 メール送信先アドレス :nobody-user@recipient.example.com
 ※「nobody-user」は Postfix サーバ上には存在しない架空のユーザです。
  したがって、宛先に指定すると 550 応答が返されエラーになります。

 # telnet recipient.example.com 25
 Trying <受信サーバ IP>...
 Connected to recipient.example.com.
 Escape character is '^]'.
 220 recipient.example.com ESMTP Postfix
 HELO sender.example.com
 250 recipient.example.com
 MAIL FROM: root@sender.example.com
 250 2.1.0 Ok
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 RCPT TO: nobody-user@recipient.example.com
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table

### ここまででエラー 10回目 [以降、遅延処理 (+1秒) が入る] ####

 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com
 RCPT TO: nobody-user@recipient.example.com

 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table
 550 5.1.1 <nobody-user@recipient.example.com>: Recipient address rejected: User unknown in local recipient table

### ここまででエラー 20回目 [次でコネクション切断] ####

 RCPT TO: nobody-user@recipient.example.com
 421 4.7.0 recipient.example.com Error: too many errors Connection closed by foreign host.

接続数が多すぎる SMTP クライアントとのコネクション数を制限する

Postfix の smtpd(8) サーバでは、同一の SMTP クライアントからの同時接続数を制限したり、Postfix anvil(8) サービスと連携して、単位時間あたりの接続レートを制限することができます。

 smtpd_client_connection_count_limit (default: 50)
  同一の SMTP クライアントが同時に生成できるコネクションの最大数
 ※今回の動作検証で使用。その他にも、以下のような制限パラメータが存在します。
 anvil_rate_time_unit (default: 60s)
  anvil(8) で機能し、クライアントの接続数などの計算に使用される単位時間
 smtpd_client_connection_rate_limit (default: 0)
  同一の SMTP クライアントが単位時間 (anvil_rate_time_unit) あたりに生成できるコネクションの最大数
 smtpd_client_recipient_rate_limit (default: 0)
  同一の SMTP クライアントが単位時間 (anvil_rate_time_unit) あたりに指定できる受信者アドレスの最大数
  :

デフォルトでは、同時接続数 (smtpd_client_connection_count_limit) は有効ですが、
単位時間あたりの制限 (*_rate_limit) は無効になっています。

 ※これらの制限は、正当なメール受信経路を規制するための使用は避けてください。
  これらは、不正な SMTP クライアントから smtpd(8) サーバを保護する目的 (つまりスパムメールへの対策などの為に) 設計されています。

送信サーバから telnet コマンドを実行して受信サーバへ接続し、上記の制限を確認してみましょう。
デフォルト値では、smtpd を最大100プロセス×コネクション数を最大50個まで許容します。

 # postconf default_process_limit
 default_process_limit = 100
 # postconf smtpd_client_connection_count_limit
 smtpd_client_connection_count_limit = 50

簡単にするため、以下のように、プロセス数 (default_process_limit) を1、プロセス当たりのコネクション数 (smtpd_client_connection_count_limit) を3に設定します。

つまり、同一の SMTP クライアントからの同時接続上限が3に制限されます。

 

[main.cf]
 default_process_limit = 1
 smtpd_client_connection_count_limit = 3

送信元サーバで複数の端末を開いて telnet を 4回実行し、受信サーバ側で netstat を実行してコネクション数を確認します。コネクション3つまでは ESTABLISHED (確立済み) となりますが、4つ目 (ポート:59086) では SYN_RECV (SYN受信済み) で止まり、コネクションを確立 (ESTABLISHED) できません。

 # netstat -tan | grep ':25 ' | grep -v LISTEN
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59086 SYN_RECV
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59082 ESTABLISHED
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59080 ESTABLISHED
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59084 ESTABLISHED

この状態で ESTABLISHED のコネクション (ポート:59080) で “quit” を送信し、コネクションをクローズさせます。

 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59086 SYN_RECV
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59082 ESTABLISHED
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59080 TIME_WAIT
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59084 ESTABLISHED

すると、SYN_RECV だったコネクションが ESTABLISHED に変化し、コネクションが確立されたことが分かります。

 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59082 ESTABLISHED
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59080 TIME_WAIT
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59086 ESTABLISHED
 tcp 0 0 <受信サーバ IP>:25 <送信サーバ IP>:59084 ESTABLISHED

最後に

今回はメール受信時、smtpd(8) で動作する制限パラメータについて動作検証を実施しました。
メール送信時に使用される smtp(8) と混同されがちですが、アーキテクチャそれぞれの役割を意識して、適切に設定したいですね。

次回は smtp(8) で動作するパラメータで、メール送信時の制限について動作検証を実施してみようと思います。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

5人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です