CentOS 7環境におけるqmailのポート枯渇問題 ― 経緯と対処の記録

CentOS 7環境におけるqmailのポート枯渇問題 ― 経緯と対処の記録 技術メモ
Fernando Hernandez on Unsplash

本記事は、CentOS 7環境で運用していたqmailメールサーバーにおいて発生した「接続が切断されずポートが埋まってしまう」問題について、その発見から原因究明、そして最終的な解決に至るまでの経緯を包括的にまとめたものです。

この問題は、SSL/TLS通信を行うSMTPS(ポート465)やPOP3S(ポート995)などのサービスで顕著に発生し、サーバーの同時接続数上限を食い潰すことでメールサービス全体の可用性に深刻な影響を与えました。

第1章:問題の発見と症状

1.1 最初の異変

問題が最初に認識されたのは、ユーザーからの「メールが送受信できない」という報告からでした。サーバー管理者として状況を確認したところ、netstatコマンドで大量のESTABLISHED状態の接続が残存していることが判明しました。

# 接続状況の確認
netstat -antp | grep :465 | grep ESTABLISHED

この確認により、SMTPS(ポート465)に対して、通常では考えられない数の接続がESTABLISHED状態で維持されていることが明らかになりました。特に衝撃的だったのは、一部の接続が3日以上もESTABLISHED状態のまま残存していたことです。

1.2 症状の詳細

問題の接続には以下の特徴がありました:

  1. 接続状態の異常な持続: netstatで確認すると「ESTABLISHED off」と表示され、TCPキープアライブが無効な状態で接続が維持されていた
  2. 同一IPからの多数接続: 特定のIPアドレスから20以上の同時接続が発生していた
  3. サービスログの不在: qmail-smtpdのログには該当する接続に関する記録がなく、SMTP層まで到達していないことが推測された
  4. ポートの枯渇: tcpserver-sslの同時接続数上限(-c 50や-c 100)に達し、正規ユーザーの接続が拒否される事態に発展

1.3 影響範囲

この問題は以下のサービスに影響を及ぼしました:

  • SMTPS(ポート465):暗号化メール送信
  • POP3S(ポート995):暗号化メール受信
  • IMAPS(ポート993):暗号化IMAP接続

特にSMTPSへの影響が大きく、メール送信が完全に停止する事態が複数回発生しました。

第2章:原因の調査と究明

2.1 初期仮説と検証

当初、問題の原因として以下を疑いました:

  1. qmail-smtpdのタイムアウト設定不備
  2. ネットワークの問題による接続の異常終了
  3. クライアント側の異常動作

しかし、これらの仮説はいずれも検証の結果、棄却されました。

2.2 通信フローの理解

問題解決の鍵となったのは、qmailにおけるSSL/TLS通信の流れを正確に理解することでした:

クライアント
    ↓
TCP接続確立(3-way handshake)
    ↓ ← ここでnetstatにESTABLISHEDと表示される
tcpserver-ssl(TLSハンドシェイク開始)
    ↓ ← ★問題の接続はここで停止していた★
qmail-smtpd(SMTPセッション開始)
    ↓
メール送受信処理

重要な発見は、問題の接続がtcpserver-sslの段階、具体的にはTLSハンドシェイクの途中で停止していたことです。これが、qmail-smtpdのログに記録が残らなかった理由でした。

2.3 攻撃パターンの特定

Dovecotのログを詳細に分析したところ、以下のようなエラーパターンが発見されました:

エラーメッセージ 意味
wrong version number 古いSSLバージョン(SSLv2/v3)での接続試行
no shared cipher 弱い暗号スイートでの接続試行
sslv3 alert handshake failure SSLv3での接続(POODLE脆弱性狙い)
unknown protocol 非TLSプロトコルでの接続試行

これらのエラーは、自動化ツール(sslyze、testssl.sh、nmapのssl-enum-ciphersなど)によるSSL/TLS脆弱性スキャンの典型的なパターンでした。

2.4 根本原因の特定

最終的に特定された根本原因は以下の2点です:

  1. tcpserver-sslにタイムアウト設定がなかった: 起動オプションに-t(タイムアウト)オプションが含まれておらず、TLSハンドシェイクが完了しない接続が永続的に残存していた
  2. スローロリス型の攻撃手法: 攻撃者はTCP接続を確立した後、TLSハンドシェイクを意図的に完了させず、接続を保持し続けることでサーバーリソースを枯渇させていた

2.5 TCPハンドシェイクとTLSハンドシェイクの違い

TCP 3-way ハンドシェイク(TCP接続確立)

  1. クライアント → サーバー:SYN
  2. サーバー → クライアント:SYN-ACK
  3. クライアント → サーバー:ACK

この段階で、netstatにESTABLISHEDと表示されます。

SSL/TLSハンドシェイク(暗号化通信確立)

  1. Client Hello(クライアントがサポートするTLSバージョン・暗号スイートを通知)
  2. Server Hello(サーバーが選択したTLSバージョン・暗号スイートを通知)
  3. Certificate(サーバー証明書の送信)
  4. Server Hello Done
  5. Client Key Exchange(共通鍵の素の送信)
  6. Change Cipher Spec / Finished(暗号化開始の通知)

攻撃者はTCP接続を確立した後、TLSハンドシェイクを開始するが完了させない、あるいは全く開始しないことで、tcpserver-sslのプロセスを占有し続けていました。

第3章:解決策の検討と実装

3.1 tcpserver-sslのタイムアウト設定

最も効果的な対策は、tcpserver-sslの起動オプションに-t(タイムアウト)オプションを追加することでした。

変更前の起動コマンド:

tcpserver-ssl -qvs -l0 -c 100 -HR -u 89 -g 89 -n /var/qmail/cert.pem ...

変更後の起動コマンド:

tcpserver-ssl -qvs -l0 -c 100 -t 30 -HR -u 89 -g 89 -n /var/qmail/cert.pem ...

この設定により、TLSハンドシェイクが30秒以内に完了しない接続は自動的に切断されるようになりました。

3.2 iptablesによる同時接続数制限

ネットワークレベルでの防御として、iptablesのconnlimitモジュールを使用した同時接続数制限を実装しました。

# 同一IPからSMTPS(465)への同時接続を8に制限
iptables -A INPUT -p tcp --dport 465 -m connlimit --connlimit-above 8 --connlimit-mask 32 -j DROP

# 同一IPからPOP3S(995)への同時接続を15に制限
iptables -A INPUT -p tcp --dport 995 -m connlimit --connlimit-above 15 --connlimit-mask 32 -j DROP

# 同一IPからIMAPS(993)への同時接続を15に制限
iptables -A INPUT -p tcp --dport 993 -m connlimit --connlimit-above 15 --connlimit-mask 32 -j DROP

3.3 レート制限の実装

recentモジュールによる制限:

# 同一IPから1分間に20回以上の新規接続を制限
iptables -A INPUT -p tcp --dport 465 -m state --state NEW -m recent --set --name SMTPS
iptables -A INPUT -p tcp --dport 465 -m state --state NEW -m recent --update --seconds 60 --hitcount 20 --name SMTPS -j DROP

hashlimitモジュールによるIPごとの制限:

# IPごとに1分間5接続まで(バースト10まで許容)
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 25 -m hashlimit --hashlimit-upto 5/min --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-name t_smtp -j ACCEPT
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 25 -j DROP

3.4 カーネルパラメータの調整

# /etc/sysctl.conf に追加
net.ipv4.tcp_keepalive_time = 300    # 5分で最初のkeepalive probe送信
net.ipv4.tcp_keepalive_intvl = 30    # 30秒間隔で再試行
net.ipv4.tcp_keepalive_probes = 3    # 3回失敗で切断

# conntrackのタイムアウト短縮
net.netfilter.nf_conntrack_tcp_timeout_established = 3600  # 1時間(デフォルト5日)

第4章:タイムアウト設定の体系的理解

4.1 各レイヤーのタイムアウト

種類 制御対象 設定方法 効果
tcpserver-ssl -t TLSハンドシェイクの完了待ち -t 30 ◎ 最も効果的
tcp_keepalive 無通信TCPセッションの生存確認 sysctl.conf △ 補助的
timeoutコマンド 子プロセスの実行時間制限 timeout 60 cmd × 効かない
conntrack timeout ファイアウォールの接続追跡 /proc/sys/net/netfilter/ △ 補助的

4.2 timeoutコマンドが効かない理由

当初、以下のような設定を試みましたが効果がありませんでした:

timeout 60 /var/qmail/bin/qmail-smtpd

これが効かない理由は、問題の接続がqmail-smtpdに到達する前の段階(tcpserver-ssl)で停止していたためです。到達しないプロセスにtimeoutをかけても意味がありません。

第5章:具体的なトラブルシューティング事例

5.1 事例1:3日間切断されなかった接続

発見時の状況:

[root@server ~]# netstat -ano | grep :465
tcp  0  0 xxx.xxx.xxx.1:465  xxx.xxx.xxx.100:61173  ESTABLISHED off (0.00/0/0)

「ESTABLISHED off」の表示は、TCPキープアライブが無効な状態で接続が維持されていることを示します。括弧内の「0.00/0/0」はキープアライブタイマーが動作していないことを意味します。

根本原因:

この接続は、クライアント(または攻撃者)がTCP接続を確立した後、TLSハンドシェイクのClient Helloを送信せずに放置したものでした。tcpserver-sslはTLSハンドシェイクの開始を待ち続けており、タイムアウト設定がなかったため、永遠に待機し続ける状態となっていました。

5.2 事例2:SSL/TLS脆弱性スキャンによる大量接続

別の事例では、某クラウドサービスのVPSから約4分間で50回以上のIMAPS接続試行が発生しました。Dovecotのログには以下のようなエラーが記録されていました:

imap-login: Disconnected: TLS handshaking: SSL_accept() failed: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher
imap-login: Disconnected: TLS handshaking: SSL_accept() failed: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
imap-login: Disconnected: TLS handshaking: SSL_accept() failed: error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol

これらのエラーパターンから、攻撃者がsslyzeやtestssl.shなどのTLS脆弱性スキャンツールを使用していることが判明しました。

5.3 事例3:同一IPからの大量同時接続

発見時の状況:

[root@server ~]# netstat -antp | grep xxx.xxx.xxx.201 | grep 465 | wc -l
23

対処の経過:

  1. iptablesのconnlimitルールを適用
  2. 既存の接続は即座には切断されないが、新規接続は拒否されるように
  3. 既存接続が自然に終了するにつれ、接続数が減少
  4. 最終的に同一IPからの接続が4つにまで減少

この経験から、connlimitルールは新規接続にのみ適用され、既存のESTABLISHED接続には影響しないことを学びました。

第6章:学んだ教訓

6.1 技術的な教訓

  1. レイヤーの理解が重要: 問題解決には、TCP層、TLS層、アプリケーション層それぞれの役割と相互関係を正確に理解することが不可欠
  2. デフォルト設定の危険性: tcpserver-sslのデフォルト設定にはタイムアウトがなく、これが問題の根本原因となった
  3. 多層防御の重要性: アプリケーションレベル、ネットワークレベル、カーネルレベルの各層で対策を講じることで、より堅牢な防御が可能

6.2 運用上の教訓

  1. 監視の重要性: 問題の兆候(ESTABLISHED接続の異常な増加)を早期に検知できる監視体制が必要
  2. ログの活用: 複数のログソースを組み合わせて分析することで、問題の全体像を把握できる
  3. ドキュメント化: 問題の経緯と解決策を詳細に記録することで、将来の類似問題への対応が容易になる

第7章:最終的な設定まとめ

7.1 tcpserver-sslの起動設定

tcpserver-ssl -qvs -l0 -c 100 -t 30 -HR -u 89 -g 89 \
  -n /var/qmail/cert.pem \
  -x /var/vpopmail/etc/tcp.smtp.cdb \
  0 smtps /var/qmail/bin/qmail-smtpd \
  /var/vpopmail/bin/vchkpw /bin/true 2>&1 | splogger smtps 3 &

主要オプション:

  • -t 30: 30秒でTLSハンドシェイクタイムアウト
  • -c 100: 最大同時接続数100
  • -HR: ホスト名逆引きを無効化(パフォーマンス向上)

7.2 iptables設定

# 同時接続数制限
iptables -A INPUT -p tcp --dport 465 -m connlimit --connlimit-above 8 --connlimit-mask 32 -j LOG --log-prefix "[SMTPS-LIMIT] "
iptables -A INPUT -p tcp --dport 465 -m connlimit --connlimit-above 8 --connlimit-mask 32 -j DROP

# レート制限
iptables -A INPUT -p tcp --dport 465 -m state --state NEW -m recent --set --name SMTPS
iptables -A INPUT -p tcp --dport 465 -m state --state NEW -m recent --update --seconds 60 --hitcount 20 --name SMTPS -j DROP

7.3 カーネルパラメータ

# /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.netfilter.nf_conntrack_tcp_timeout_established = 3600

おわりに

本記事で記録した問題は、qmailとtcpserver-sslという古い技術スタックにおける設計上の課題と、現代のセキュリティ脅威との間のギャップを浮き彫りにしました。

SSL/TLS脆弱性スキャンやスローロリス型攻撃は、今日では一般的な攻撃手法となっており、これらに対する適切な防御策は必須です。

今回の経験を通じて、メールサーバーの運用においては以下の点が特に重要であることを再認識しました:

  1. 各コンポーネントの役割と通信フローの正確な理解
  2. 明示的なタイムアウト設定の実装
  3. 多層防御アプローチの採用
  4. 継続的な監視とログ分析

これらの対策を適切に実装することで、類似の問題の再発を防止し、安定したメールサービスの提供が可能となります。

本記事は、2024年8月から2025年12月にかけて実施した問題調査と対策実装の記録をまとめたものです。

コメント

タイトルとURLをコピーしました