【AWSネットワークの深層】VCPフローログのログフィールドを完全解剖する:パケットの「おしゃべり」を聴き取る技術
夜中の2時、pagerが鳴り響く。「本番環境のマイクロサービス間で、なぜか一部のAPIリクエストがタイムアウトしている。セキュリティグループやルートテーブルは完璧に見えるのに……」。
こんな修羅場をくぐり抜けてきたSREやインフラエンジニアなら、誰もが一度はVPCフローログの海にダイブした経験があるはずです。AWSの仮想ネットワーク(VPC)を流れるパケットは、目に見えないブラックボックスではありません。その足跡を克明に記録しているのが「VPCフローログ(VPC Flow Logs)」です。
今回は、AWSネットワークの心臓部であるVPCフローログのレコードフィールドに焦点を当て、パケットがAWSのルーターをどう駆け巡っているのか、そのリアルな挙動を読み解いていきましょう。
—
1. VPCフローログのデフォルトフィールド:パケットは何を語っているか
VPCフローログを有効化し、S3やCloudWatch Logsに吐き出されたログを見たとき、そこに並ぶスペース区切りの文字列をただのテキストだと思ってはいけません。あれは、ネットワークの生態系そのものです。
標準フォーマットのバージョン2における主要なフィールドの並び順と意味を、実務的な視点で整理しておきましょう。
2 123456789012 eni-0123456789abcdef0 10.0.1.15 10.0.2.20 54321 443 6 15 1500 1680000000 1680000060 ACCEPT OK
この1行のレコードが、以下の情報を雄弁に語っています。
version: フローログのフォーマットバージョン(通常は2)account-id: AWSアカウントID(マルチアカウント環境の監査で命綱になります)interface-id: ENI(Elastic Network Interface)のID。EC2やRDS、NATゲートウェイのインターフェースです。srcaddr: 送信元IPアドレスdstaddr: 宛先IPアドレスsrcport: 送信元ポート番号dstport: 宛先ポート番号protocol:IANAプロトコル番号packets: このフロー内で転送されたパケット数bytes: 転送されたバイト数の総計start: フローの集計期間の開始エポック秒end: フローの集計期間の終了エポック秒action: セキュリティグループやNACLによってACCEPT(許可)されたか、REJECT(拒否)されたかlog-status: ログが正常に記録されたか(OK、NODATA、SKIPDATA)
現場で直面する「プロトコル番号」の罠
特にインフラ初心者がハマりやすいのが protocol フィールドです。ここにはサービス名ではなく、IANA(Internet Assigned Numbers Authority)が定めた数値が入ります。
6: TCP(Web API、データベース接続など、信頼性が求められる通信)17: UDP(DNS、gRPCの一部、ログ転送など、速度重視の通信)1: ICMP(pingによる疎通確認など)
トラブルシューティングの最中に「あれ?この 17 ってなんだっけ?」と手が止まらないよう、主要な番号は体に叩き込んでおきましょう。
—
2. 通信フロー(シーケンス)とフローログ集約のリアル
ここで重要な事実を一つ。VPCフローログは、パケットが通過するたびにリアルタイムで1行ずつ出力されるわけではありません。AWSの基盤レイヤーで一定期間(デフォルトは約1分、最大60秒)集約(Aggregation)されてから出力されます。
例えば、クライアント(10.0.1.15)からバックエンドのAPIサーバー(10.0.2.20)へ、HTTPS(ポート 443)で大量のリクエストを投げた場合の通信とログの関係を見てみます。
[Client: 10.0.1.15] [ENI / AWS Router] [Server: 10.0.2.20]
| | |
| ----- SYN (Port 54321 -> 443) ----> | |
| <---- SYN-ACK --------------------- | |
| ----- ACK ------------------------> | |
| | |
| === HTTP Request / Response ======> | |
| | |
| |-- (1分間のバッファで集約) ---->|
| |-- 1レコードを出力 (bytes加算)|
この間、何百というパケットがやり取りされても、フローログ上は1つのレコードとして packets と bytes が合算されて記録されます。したがって、「瞬発的な接続の有無」を見るには action=REJECT のログを追い、「通信量やボトネック」を見るには bytes の大きさに着目するという使い分けが必要です。
—
3. 実務で役立つ!VPCフローログの構文解析・監査スクリプト
クラウドの規模が大きくなると、CloudWatch Logs InsightsやAmazon Athenaを使ってログを解析しますが、手元でサクッとパケットの傾向を掴みたいときや、独自の軽量な監査ツールを組むときはPythonが便利です。
以下に、CloudWatch Logsから取得した、あるいはS3からダウンロードしたVPCフローログのテキストをパースし、通信先ごとの総バイト数や拒否されたトラフィック(REJECT)を抽出する実用的なPythonスクリプトを紹介します。
from collections import defaultdict
import re
# サンプルのVPCフローログ(実際にはファイルやAWS APIから取得)
flow_logs_sample = """
2 123456789012 eni-111 10.0.1.15 10.0.2.20 54321 443 6 15 1500 1680000000 1680000060 ACCEPT OK
2 123456789012 eni-222 10.0.1.99 192.168.1.50 45000 22 6 2 100 1680000000 1680000060 REJECT OK
2 123456789012 eni-111 10.0.1.15 8.8.8.8 12345 53 17 1 76 1680000000 1680000060 ACCEPT OK
"""
def analyze_flow_logs(log_data):
# 統計情報の集計用データ構造
traffic_stats = defaultdict(lambda: {"packets": 0, "bytes": 0})
rejected_connections = []
# プロトコル番号のマッピング
proto_map = {6: "TCP", 17: "UDP", 1: "ICMP"}
# 行ごとにパース処理を実行
for line in log_data.strip().split("\n"):
parts = line.split()
if len(parts) < 14:
continue # フォーマットが不正な行はスキップ
version, account, eni, srcaddr, dstaddr, srcport, dstport, proto, packets, bytes_val, start, end, action, log_status = parts
proto_name = proto_map.get(int(proto), f"PROTO-{proto}")
packets = int(packets)
bytes_val = int(bytes_val)
# 1. 拒否されたトラフィック(セキュリティグループやNACLのブロック)を検知
if action == "REJECT":
rejected_connections.append({
"src": f"{srcaddr}:{srcport}",
"dst": f"{dstaddr}:{dstport}",
"protocol": proto_name
})
# 2. 宛先IPとポートごとのトラフィック量を集計
key = (srcaddr, dstaddr, dstport, proto_name)
traffic_stats[key]["packets"] += packets
traffic_stats[key]["bytes"] += bytes_val
# --- 分析結果のレポート出力 ---
print("=== 【セキュリティ監査】拒否されたトラフィック (REJECT) ===")
if rejected_connections:
for rej in rejected_connections:
print(f"[警告] 遮断検知: {rej['src']} -> {rej['dst']} ({rej['protocol']})")
else:
print("遮断されたトラフィックはありません。")
print("\n=== 【帯域監査】通信フロー別トラフィック総量 ===")
print(f"{'送信元IP':<15} | {'宛先IP':<15} | {'Port':<6} | {'Proto':<6} | {'総パケット':<10} | {'総バイト数':<10}")
print("-" * 75)
for (src, dst, dport, proto), stats in traffic_stats.items():
print(f"{src:<15} | {dst:<15} | {dport:<6} | {proto:<6} | {stats['packets']:<10} | {stats['bytes']:<10}")
if __name__ == "__main__":
analyze_flow_logs(flow_logs_sample)
このスクリプトを拡張すれば、例えば「社内から許可されていない外部IPへのSSH(ポート 22)通信の試行(REJECT)」や「特定のマイクロサービス間における異常なデータ転送量(bytes の高騰)」をリアルタイムでアラート検知する基盤の土台となります。
—
4. シニアSREが教える、現場のトラブルシューティングTips
最後に、VPCフローログを使ったインフラ障害シューティングで、現場のエンジニアが陥りがちな落とし穴と、素早く原因にたどり着くための実践的な知見をいくつか共有します。
1. 「REJECT」ログがないのに通信できない場合
- セキュリティグループやNACLにブロックされていれば
action=REJECTが記録されますが、宛先サーバー側のOS内ファイアウォール(iptables / ufw / firewalld)や、アプリケーションのバインドミス(ローカルループバックアドレスにしかリスンしていない等)でドロップされている場合、AWSのENI視点では正常にパケットが届いて処理されたとみなされるため、フローログ上はACCEPTになります。「ログはACCEPTなのに繋がらない」ときは、OSレイヤーやルーティングテーブル(ネクストホップの誤設定)を疑いましょう。
2. カスタムフォーマットの活用を惜しむな
- デフォルトフィールドだけでは、パケットの方向(トラフィックの向き:
flow-direction)や、AWSリソースのメタデータが不足することがあります。本番運用では、カスタムフォーマットを追加し、traffic-path(NATゲートウェイやインターネットゲートウェイをどう経由したか)を含めることで、複雑なルーティングのデバッグ効率が何倍にも跳ね上がります。
3. コストとサンプリング間隔のバランス
- フローログはCloudWatch LogsやS3への転送コスト、およびストレージコストが発生します。全てのENIで詳細なログを無期限に残すのは財布に優しくありません。開発環境や重要度の低いバッチ用サブネットではログ出力を絞り、APIゲートウェイやデータベースのフロントエンドなど、「絶対に落とせない境界線」のENIに絞って高精度なログ収集を行う設計が、プロフェッショナルなクラウドアーキテクトの腕の見どころです。
ネットワークの挙動に悩んだときは、パケットの叫び声であるVPCフローログに耳を傾けてみてください。そこには、必ず真実が記録されています。
コメント