Conntrack:ステートフルファイアウォールの内部
なぜ戻りパケットを許可ルール無しで通せるのかが腑に落ちます。フロー単位で接続を追跡する仕組みと、NAT との連携、そして本番で詰まるテーブル枯渇まで原理から押さえます。
- 1.ステートフルファイアウォールはパケット単位でなくフロー単位で判定する。接続追跡(conntrack)がフローの状態を表に持ち、戻りパケットを ESTABLISHED として自動許可する。
- 2.状態は NEW/ESTABLISHED/RELATED/INVALID の4種。RELATED は FTP データ接続や ICMP エラーなど、別フローだが既存接続に紐づくものを通す。
- 3.NAT は conntrack エントリにアドレス変換を記録して往復で一貫変換する。エントリ数には上限があり、超えると新規接続が落ちる(テーブル枯渇)。
パケット単位ではなく「フロー単位」で判定する
素朴なファイアウォールは1パケットずつヘッダを見て許可・拒否を決めます。だが TCP は双方向です。クライアントの発信を許可すると、サーバーからの戻りパケットも通さねばならない。これをパケット単位の静的ルールで書くと「外向きの新規接続は許可、内向きは戻りだけ許可」を手作業で対称に記述する羽目になり、ポート範囲ごとに穴を開けることになって破綻します。
ステートフルファイアウォール はこの問題を、判定の単位を フロー(コネクション) に引き上げて解決します。一度通した発信を「接続」として記憶し、その接続に属する戻りパケットを自動で許可する。Linux では netfilter の 接続追跡(connection tracking, conntrack) サブシステムがこの記憶を担います。フロー単位という発想の前提として、TCP/UDP の双方向性は /network/tcp-udp/ を参照してください。
conntrack は何を覚えているのか
conntrack はカーネル内に コネクション追跡テーブル を持ち、観測したフローを1エントリとして登録します。フローの同一性は概ね次の5要素(5タプル)で決まります。
- プロトコル(TCP/UDP/ICMP など)
- 送信元IP・送信元ポート
- 宛先IP・宛先ポート
各エントリは「往路(original)」と「復路(reply)」の2方向タプルを持ちます。復路タプルは往路を反転させたもので、これにより戻りパケットが来たとき「どのフローの戻りか」を逆引きできます。最初のパケットが入ると netfilter のフックでエントリが仮登録され、フロー終了やタイムアウトで除去されます。
conntrack 自体はパケットを通す/落とすを決めません。各パケットに「このフローの状態は ESTABLISHED だ」というラベルを付けるだけです。許可・拒否を決めるのは、そのラベルを参照するファイアウォールルール(iptables/nftables)側です。追跡と判定は分離されています。
状態:NEW / ESTABLISHED / RELATED / INVALID
ルールが参照する ctstate(接続状態)は4種類です。これは TCP のハンドシェイク状態とは別の、netfilter から見たフローの分類である点に注意してください。
| 状態 | 意味 | 典型例 |
|---|---|---|
| NEW | そのフローで初めて観測したパケット。テーブルに往路エントリが作られる | クライアントが送る最初の SYN |
| ESTABLISHED | 既存エントリに往復どちらかで合致するパケット | 確立後のデータ、サーバーからの戻り全般 |
| RELATED | 別フローだが既存接続に論理的に紐づくパケット | FTP のデータ接続、ICMP エラー(Port Unreachable 等) |
| INVALID | どのエントリにも合致せず状態整合もしないパケット | 確立前の素の ACK、寿命切れフローの遅延パケット |
実務のファイアウォールはこの分類を使い、たった2行で「発信は許可、戻りは自動許可」を実現します。
# 確立済み・関連フローの戻りパケットは無条件で通す(戻り用に穴を開けない)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 不整合パケットは捨てる
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# 以降、NEW の着信だけをサービスごとに個別許可すればよい
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
アクティブFTPはデータ転送用に別ポートで新しい接続を張ります。これは5タプルが違うので単独では NEW です。conntrack のヘルパ(ALG)が制御接続のやり取りを覗いてデータ接続を予約し、それを RELATED として扱うことで穴を開けずに通します。同様に「宛先不達」などの ICMP エラーは元フローを参照する RELATED であり、これを落とすと PMTUD 失敗などの不可解な不具合を招きます。
TCP の状態はもっと細かく追う
TCP については conntrack はさらに内部状態(SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT, TIME_WAIT など)を独立に持ち、フラグの整合性を検査します。例えばハンドシェイク前に届いた裸の ACK は INVALID 扱いになります。これにより、確立シーケンスを踏まない注入パケットを弾けます。conntrack の TCP 状態は TCP プロトコル本来の状態機械を模した別実装であり、両者を混同しないことが重要です。本来の状態遷移は /network/tcp-state-machine/ を参照してください。
状態ごとにタイムアウトが設定され、確立済み TCP は既定で非常に長い(数日規模)一方、UDP は応答が無いと短時間(数十秒〜数分)で消えます。UDP は本来コネクションレスですが、conntrack は往復タプルの観測でフローを擬似的に張り、戻りを通します。
NAT との連携
NAT は conntrack の上に構築されます。アドレス・ポート変換は フローの最初のパケット に対してだけ決定され、その変換内容が conntrack エントリに刻まれます。以後のパケットは状態照合でエントリに合致し、記録済みの変換が往復で一貫適用されます。
内側 192.168.0.10:51000 ──► (NAT) ──► 203.0.113.1:40001 ──► 宛先サーバー
往路: src=192.168.0.10:51000 dst=server:443 を
復路: dst=203.0.113.1:40001 に書き換えて記録
戻り: src=server:443 dst=203.0.113.1:40001 を
エントリ逆引きで dst=192.168.0.10:51000 へ復元
つまり PAT/マスカレードで「ポートで端末を見分ける」あの対応表の正体が、conntrack エントリそのものです。NAT の外形的な振る舞いとポートフォワーディングは /network/nat/ に、対称NATで直結が破綻する理由は /network/nat-traversal-hole-punching/ にまとめています。
変換の一貫性はエントリの存在に依存します。エントリが消える・作られないと、戻りパケットの逆変換ができず通信が片方向で死にます。だから NAT ルータでは conntrack を無効化できず、エントリ数が直接スケール上限になります。
ステートレスにしたい高速経路(ルーティングのみで NAT 不要なトラフィック)には notrack(raw テーブルで追跡対象から除外)を使い、conntrack の負荷とエントリ消費を避ける設計も取られます。
テーブル枯渇:本番で最も刺さる罠
conntrack テーブルにはエントリ数の上限(nf_conntrack_max)があります。エントリはハッシュテーブルで管理され、上限に達すると 新規フローのパケットがドロップ され、ログに nf_conntrack: table full, dropping packet が出ます。既存接続は生きているのに新規接続だけが落ちるため、「一部のユーザーだけ繋がらない」という再現性の低い障害として現れます。
枯渇しやすいのは次のような状況です。
- 短命接続を大量に張る構成(ロードバランサ配下、リバースプロキシ、ベンチマーク)
- UDP フラッドや SYN フラッド。確立しないフローでもエントリは消費される
- タイムアウトが長く、消えないエントリが積み上がる(特に既定の長い TCP ESTABLISHED)
# 現在のエントリ数と上限を確認
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
# 状態の偏りを集計(どの状態のフローが溜まっているか)
conntrack -L 2>/dev/null | awk '{print $1, $4}' | sort | uniq -c | sort -rn | head
# 上限とハッシュバケットを引き上げる(メモリと引き換え)
sysctl -w net.netfilter.nf_conntrack_max=1048576
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
枯渇の根本がタイムアウト設計や攻撃トラフィックなら、nf_conntrack_max を増やしてもメモリを食い潰すだけで遅延を招きます。確立済み TCP のタイムアウト短縮、不要フローの早期 INVALID ドロップ、攻撃元のレート制限、そして「追跡が本当に要らない経路」の notrack 化を併用するのが筋です。エントリ1件あたり数百バイトを消費するため、100万エントリは無視できないメモリ量になります。
「ステートフルが戻りを通せる理由は」→ conntrack が往復タプルを記録し ESTABLISHED と分類するから。「RELATED とは」→ 別フローだが既存接続に紐づくもの(FTPデータ、ICMPエラー)。「NAT と conntrack の関係は」→ 変換は初回パケットで決定しエントリに記録、往復で一貫適用。「table full の症状は」→ 既存は生き新規だけ落ちる。この4点を即答できれば上級。
まとめ
ステートフルファイアウォールの核心は、判定単位をパケットからフローへ引き上げ、その状態を conntrack という表に外出しした点にあります。NEW で発信を覚え、ESTABLISHED/RELATED で戻りと関連フローを自動許可し、INVALID で不整合を弾く。NAT はこの表に変換を相乗りさせて往復の一貫性を担保します。そして表である以上、エントリ数という有限資源を持ち、それが枯渇すると新規接続だけが静かに落ちる――この性質を理解しているかどうかが、ステートフルな経路の障害切り分けを分けます。
ネットワーク Article
Conntrack:ステートフルファイアウォールの内部を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
conntrack
比較で見る軸
難易度: advanced / カテゴリ: ネットワーク / タグ数: 6
導入後に効く点
状態は NEW/ESTABLISHED/RELATED/INVALID の4種。RELATED は FTP データ接続や ICMP エラーなど、別フローだが既存接続に紐づくものを通す。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ネットワーク
- タグ数
- 6
判断チェックリスト
- 自社の用途が「conntrack / ファイアウォール」に近いか確認する。
- 強みである「ステートフルファイアウォールはパケット単位でなくフロー単位で判定する。接続追跡(conntrack)がフローの状態を表に持ち、戻りパケットを ESTABLISHED として自動許可する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。