こんにちは。サイオステクノロジー 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) で動作するパラメータで、メール送信時の制限について動作検証を実施してみようと思います。