知られざるトラフィックコントローラー
サーバーに64GBのRAMを搭載し、CPU使用率がわずか2%であっても、新しい接続がすべてドロップされることがあります。このパラドックスの背後には、通常conntrackという原因が隠れています。Netfilterのステートフルエンジンであるconntrackは、システムを通過するすべてのネットワークフローを記憶します。これは、現代のNATやステートフルファイアウォールを機能させるための隠れた台帳のようなものです。
ロードバランサーが中程度の負荷で失敗した際、多くのシニアエンジニアが頭を抱えるのを見てきました。多くの場合、カーネルが新しい「会話」を追跡するためのスペースを使い果たしただけなのです。高トラフィックなKubernetesノードや多忙なNginxプロキシを管理する人にとって、このサブシステムの習得は必須です。
ステートレス vs ステートフル:なぜ接続を追跡するのか
conntrackを理解するには、かつてのトラフィックフィルタリング手法と現在の手法を比較してみるのが近道です。
ステートレスフィルタリング(旧来の手法)
ステートレスファイアウォールは、すべてのパケットを完全な「初対面」として扱います。単純なウェブリクエストを許可するには、送信ポートと特定の戻りパスを手動で開く必要があります。高速ですが、柔軟性に欠けます。ファイアウォールは、入ってくるパケットが実際にサーバーから要求されたものかどうかを判断できないため、基本的なスプーフィング攻撃に対して脆弱になります。
ステートフルフィルタリング(Conntrackの手法)
ConntrackはTCPの3ウェイハンドシェイクを監視します。SYN、SYN-ACK、ACKのシーケンスを識別すると、そのフローをESTABLISHEDとしてマークします。最初の接続を許可するルールが1つあれば、Netfilterがその後のやり取りを自動的に処理します。さらに、既存の接続によってトリガーされたICMPエラーメッセージなどの「関連する(related)」トラフィックも認識します。
ステートトラッキングのトレードオフ
メリット
- 強固なセキュリティ:検証済みの進行中のセッションの一部ではないパケットを自動的にドロップできます。
- シームレスなNAT:Conntrackは内部プライベートIPとパブリックIPをマッピングするデータベースとして機能し、戻りのトラフィックが正しく宛先に届くようにします。
- プロトコルの認識:セカンダリポートを動的に開くFTPやSIPのような複雑なプロトコロナをインテリジェントに管理します。
リスク
- メモリ消費:追跡される各接続は約300バイトのカーネルメモリを消費します。少量に思えますが、100万件のアクティブな接続は約300MBのRAMを占有します。
- テーブルの溢れ:conntrackテーブルにはハードリミットがあります。トラフィックの急増時やDDoS攻撃中にこの上限に達すると、カーネルはすべての新しい接続を拒否します。
- CPUコスト:巨大なステートテーブルに対してすべてのパケットを照合するのは、単純なルーティングよりも負荷がかかります。
高パフォーマンス環境向けのチューニング
標準的なLinuxディストリビューションの設定は保守的であることが多いです。大規模なオフィスのNATゲートウェイや多忙なAPIとして機能するサーバーでは、すぐにこれらの設定が不足します。ここでは、sysctlを使用してシステムを最適化する方法を紹介します。
1. テーブル容量の拡張
変更を加える前に、現在の制限を確認してください。多くのシステムではデフォルトで65,536エントリに設定されていますが、これは現代のワークロードに対しては低すぎます。
# 現在の最大制限を表示
sysctl net.netfilter.nf_conntrack_max
# 現在追跡されている接続数を確認
cat /proc/sys/net/netfilter/nf_conntrack_count
16GBのRAMを搭載したサーバーの場合、通常は制限を100万エントリまで引き上げます。また、検索速度を維持するためにハッシュテーブルのサイズも増やす必要があります。適切な比率は、8エントリに対して1バケットです。
# 最大接続数を 1,048,576 に設定
sysctl -w net.netfilter.nf_conntrack_max=1048576
# hashsize を調整 (1048576 / 8 = 131072)
echo 131072 > /sys/module/nf_conntrack/parameters/hashsize
2. タイムアウトの短縮
デフォルトでは、Linuxは「Established」状態のTCP接続をテーブルに5日間保持します。これは過剰です。アプリケーションが数千の短寿命な接続を生成する場合、テーブルはすでにアクティブではない「ゴースト」エントリで埋め尽くされてしまいます。これらの値を大幅に下げることをお勧めします。
# 1時間通信がない確立済みセッションを破棄
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
# クローズされた接続をより早くクリーンアップ (FIN_WAIT 状態)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=60
トラブルシューティングのツールとテクニック
リアルタイムで何が起きているかを確認するには、conntrack-toolsパッケージをインストールします。DebianやUbuntuでは、sudo apt install conntrackを実行します。
大量接続元の特定
テーブルの使用率が急増している場合は、次のコマンドを使用して、どの送信元IPが最も多くの接続を開いているかを確認します。
conntrack -L -o extended | awk '{print $7}' | cut -d= -f2 | sort | uniq -c | sort -nr | head -n 10
これによりIPのランキングリストが表示され、ビジーな内部サービスなのか、外部からのボットネット攻撃なのかを判別するのに役立ちます。
緊急フラッシュ
ファイアウォールのルールを更新しても、既存の「不正な」接続がすでにテーブルにあるため、生き残り続けることがあります。特定のセッションを終了させるか、テーブル全体をクリアすることができます。
# 特定の攻撃者IPからのすべての接続を終了
conntrack -D -s 203.0.113.10
# テーブル全体をクリア (警告: 一時的にアクティブなトラフィックが切断されます)
conntrack -F
ドロップの監視
テーブルが上限に達すると、カーネルは特定のエラーをログに記録します。ドロップの監視を行う際、ログに次のメッセージがないか頻繁に確認してください。
dmesg | grep "nf_conntrack: table full, dropping packet"
これがログに表示されている場合、ユーザーはタイムアウトに直面しています。すぐにnf_conntrack_maxを増やす必要があります。
まとめ
ConntrackはLinuxネットワークの静かなエンジンです。ステートフルなセキュリティのためのインテリジェンスを提供しますが、その限界は有限です。テーブルサイズを拡張し、アグレッシブなタイムアウト設定に短縮することで、ほとんどの「原因不明」のネットワーク停止を防ぐことができます。サーバーの接続スロットを誰が占有しているかを特定するために、常にconntrack CLIツールを使えるようにしておきましょう。

