【テクニカル・上級編】 フローログ(VPC Flow Logs / NSG Flow Logs)を活用したNAT変換前後パケットのデバッグ手法 – クラウド&コンテナネットワーク実践ガイド

はじめに:NATの向こう側で見えなくなる「あのパケット」

深夜3時、鳴り響くPagerDutyのアラート。
「特定の外部APIエンドポイントへの通信が、突如としてタイムアウトし続けている」。
SREやインフラエンジニアであれば、誰もが冷や汗をかく瞬間だ。アプリケーションログには「Connection Timed Out」の無機質なエラーが並び、KubernetesのPodやクラウドの仮想マシン(EC2 / Compute Engine)に入って curl を叩いてみるものの、なぜかそこでは正常に応答が返ってくる――。

この「ローカル環境では再現しないが、本番のロードバランサーやNATゲートウェイを介した途端に死ぬ」という現象の多くは、トランスポート層におけるポート枯渇、非対称ルーティング、あるいはセキュリティグループやファイアウォールによる暗黙の破棄が原因だ。そして、この迷宮入りしかけたパケットの足取りを追うための最強にして唯一の武器が、「フローログ(VPC Flow Logs / NSG Flow Logs)」である。

今回は、パケットがパブリッククラウドのマネージドNATゲートウェイを通過する前後でどのようにその姿(IPアドレスとポート番号)を変えるのか。そして、LinuxカーネルのネットワークスタックとクラウドのSDN(Software-Defined Networking)の境界で何が起きているのかを、フローログのディープな解析手法とともに紐解いていこう。

—

1. パケットの変身劇:NATゲートウェイ前後におけるIP・ポートの位相幾何学

まずは、プライベートサブネットからインターネットへ向かうパケットが、NATゲートウェイ(AWSならNAT Gateway、GCPならCloud NAT)を通過する際の「身分証明書の書き換え」のメカニズムを正確に把握する必要がある。

プライベート空間からパブリック空間へのダイナミクス

KubernetesのPodやクラウドのワーカーノードが、プライベートサブネット(ルーティングテーブルにインターネットGWへの直接のルートを持たないサブネット)から外部APIへHTTPSリクエストを送出すると、パケットは次のような運命をたどる。

1. 送信元 (Pod/VM): srcaddr: 10.0.1.15, srcport: 51234
2. 宛先 (外部API): dstaddr: 198.51.100.50, dstport: 443
3. NATゲートウェイ通過時:
クラウドの分散NAT機構が、SNAT(Source Network Address Translation)およびNAPT(Network Address and Port Translation)を適用する。
4. インターネット側:
srcaddr: 203.0.113.10 (NAT GatewayのElastic IP / 外部IP), srcport: 40001 (動的に割り当てられたパブリックポート)

この変換の瞬間、Linuxカーネルのnetfilter(あるいはクラウド基盤のOVS / eBPFデータプレーン)のコネクション追跡テーブル(Conntrack)には、プライベートIPとパブリックIPの対応関係がミリ秒単位で刻まれる。

—

2. フローログが語る真実:srcaddr と dstaddr の迷宮を読み解く

通信障害のデバッグにおいて、AWSのVPC Flow Logs(バージョン5)やGCPのVPC Flow Logsを出力しているだけでは不十分だ。問題は、「どの瞬間に、どのフィールドがどう変化したか」のコンテキストを頭の中で完全にマッピングできているかどうかにある。

AWS VPC Flow Logsにおけるフィールドの解剖

AWSのデフォルトフォーマットにおける主要なフィールドを確認しておこう。

${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}

ここで重要なのは、NATゲートウェイの「内側(プライベート側)」と「外側(パブリック側)」で、どのENI(Elastic Network Interface)のフローログを見ているかによって、観測されるIPアドレスが完全に変わるという点だ。

  • プライベートインスタンスのENIで採取したログ:
  • srcaddr: 10.0.1.15 (Pod/VMのプライベートIP)
  • dstaddr: 198.51.100.50 (外部APIのIP)
  • NATゲートウェイのENIで採取したログ(外向き):
  • srcaddr: 203.0.113.10 (NAT GatewayのEIP)
  • dstaddr: 198.51.100.50 (外部APIのIP)

もしアプリケーションが「接続できない」と嘆いているとき、プライベートインスタンスのフローログで action: REJECT が出ていれば、それはセキュリティグループやNACL(Network ACL)のルール違反だ。しかし、ログの action が ACCEPT にもかかわらず通信が成立していない場合、問題はNATゲートウェイの先、あるいはNAPTのポート枯渇にある。

—

3. 実践:AthenaとPythonを用いたNAT変換前後のパケット追跡パイプライン

大規模なKubernetesクラスターでは、数千のPodが同時に外部通信を行うため、NATゲートウェイのポート枯渇(Port Exhaustion)が頻発する。AWSでは1つのNAT GatewayパブリックIPあたり最大64,000の同時接続(ポート)しか維持できない。

ここでは、Amazon Athenaを使ってS3に蓄積されたVPC Flow Logsから、特定の送信元IPとポートの変換前後を突き合わせ、パケットロスの原因を特定するクエリの例を示す。

Athenaクエリ:NATポート枯渇とパケットドロップの検知

-- NATゲートウェイのログとプライベートインスタンスのログを突き合わせ、
-- ポート変換の枯渇やリジェクトをあぶり出すクエリ
SELECT 
    f.start,
    f.srcaddr AS private_ip,
    f.srcport AS private_port,
    f.dstaddr AS external_ip,
    f.dstport AS external_port,
    f.protocol,
    f.action,
    f.packets,
    f.bytes
FROM 
    vpc_flow_logs f
WHERE 
    f.start >= 1711929600 -- 調査開始タイムスタンプ(Epoch)
    AND f.end <= 1711933200   -- 調査終了タイムスタンプ(Epoch)
    AND f.dstaddr = '198.51.100.50' -- 宛先外部APIのIP
    AND f.action = 'REJECT'
ORDER BY 
    f.start DESC;

このクエリで REJECT が多発している場合、クラウド側のファイアウォール、あるいは宛先サーバー側のステートフルインスペクション(例: Linuxの net.netfilter.nf_conntrack_max 溢れによるドロップ)が疑われる。

—

4. トランスポート層の最適化:TCPバッファチューニングとRTT削減

NATゲートウェイを通過する通信において、見落とされがちなのが「TCPコネクションの寿命とタイムアウトの設定」である。

クラウドのNATゲートウェイは、一定期間(通常、AWSではTCPコネクションの場合アイドル状態が350秒続くと)パケットが流れないと、Conntrackテーブルのエントリを破棄(Idle Timeout)する。この仕様を把握していないと、長時間のKeep-Alive接続を使っているつもりが、NATゲートウェイ側に勝手にコネクションを切断され、次の一撃で RST またはタイムアウトを食らうことになる。

Linuxカーネルパラメータの最適化 (/etc/sysctl.conf)

Kubernetesのノードや仮想マシン側で、TCPのキープアライブとバッファサイズを適切にチューニングし、NATゲートウェイのアイドルタイムアウトを逆手に取った設計を行う必要がある。

# コネクションが切断されたとみなすまでのアイドル時間を短縮し、無駄なエントリを保持しない
net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5

# 高速な広帯域ネットワークにおけるBDP (Bandwidth-Delay Product) の最大化
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# TIME_WAITバケットの枯渇を防ぐための高速リサイクル/再利用
net.ipv4.tcp_tw_reuse = 1

このようなカーネルチューニングを施すことで、NATゲートウェイのConntrackテーブルの輻輳を防ぎ、パケットがNAT変換のプレッシャーに負けない堅牢なネットワークトポロジを構築できる。

—

5. セキュリティと脆弱性の回避:非対称ルーティングとスプーフィング対策

最後に、フローログを用いたデバッグにおいて最もエンジニアを悩ませる「非対称ルーティング(Asymmetric Routing)」の罠について言及しておこう。

複数形のアベイラビリティゾーン(AZ)や、複数のNATゲートウェイ(マルチAZ配置)を持つ環境において、
1. 行きのパケットは AZ-a のNATゲートウェイを通過した (srcaddr が AZ-a のEIPに変換)
2. 戻りのパケットが誤ったルーティングテーブルやロードバランサーの挙動により、AZ-c のNATゲートウェイに戻ってきた

こうした現象が発生すると、NATゲートウェイのステートフルファイアウォールは「そんなセッション知らない」と判断し、容赦なくパケットをドロップ(Drop)する。

対策としての厳密なリバースパスフィルタリング(RPF)の確認

Linuxカーネル側で、不正なパケットや非対称な経路を通ってきたパケットを検知・破棄するために、以下の設定が有効になっていることを確認する。

# loose mode (2) ではなく strict mode (1) を推奨するケースもあるが、
# クラウドのマルチパス環境ではルーティング設計に依存するため注意が必要
sudo sysctl -w net.ipv4.conf.all.rp_filter=1
sudo sysctl -w net.ipv4.conf.default.rp_filter=1

フローログ上で srcaddr と dstaddr のトラフィック量が完全に非対称(送信はあるが受信がゼロ、あるいはその逆)である場合、パケットがNATの往路と復路で別のルートを通っていないか、クラウドのルートテーブル(Route Table)の伝播状況を直ちに疑うべきだ。

—

おわりに:パケットの声を聞け

クラウドネイティブなアーキテクチャの抽象化が進んだ現代においても、ネットワークの根底にあるのはIPパケットとTCPのステートマシン、そして無慈悲なまでのカーネルのルールだ。

「なぜ繋がらないのか」迷ったときは、複雑なアプリケーションコードをデバッグする手を一旦止め、フローログを開こう。そこには、パケットがNATゲートウェイを通過した瞬間の「変身の痕跡」が、嘘偽りのない客観的な事実として刻まれている。そのログを読み解くスキルこそが、真のインフラストラクチャ・エンジニアの武器なのである。

コメント

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