AWS VPC Flow LogsでVPCのネットワーク通信を可視化する方法|S3・CloudWatch Logs連携と異常検知の設計パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS VPC Flow LogsでVPCのネットワーク通信を可視化する方法|S3・CloudWatch Logs連携と異常検知の設計パターン
「VPCに不審な通信が来ているような気がする——でも本当にセキュリティグループでブロックできているのか、後から確認する手段がない」
AWSでシステムを運用していると、こうした「見えない」不安を感じる場面があります。特にセキュリティインシデントが起きた後に「いつ・どこから・どのポートに対して通信があったか」を遡って調べられない状況は、運用上の深刻なリスクです。

AWS VPC Flow Logsは、VPCを流れるIPトラフィックの情報をS3またはCloudWatch Logsへ記録するマネージドサービスです。セキュリティグループやNACLによるACCEPT・REJECTの記録から、送信元IPアドレス・宛先ポートまで、VPCを通過するすべての通信を可視化できます。この記事では、AWS CLI 2.xで動作確認した手順をもとに、Flow Logsの有効化・レコードの読み方・実務での活用パターン・コスト設計まで解説します。

この記事のポイント

・aws ec2 create-flow-logsでVPC・サブネット・ENIの3レベルで有効化できる
・actionフィールドがREJECTの記録を絞り込むと不審アクセスを素早く特定できる
・S3送信はコストが低く長期保存向き、CloudWatch Logsはリアルタイム分析に強い
・traffic-type REJECTから始めて必要に応じてALLに拡張するのがコスト効率のよい設計


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

VPC Flow Logsとは何か|VPCのすべてのIPトラフィックを記録する仕組み

VPC Flow Logsは、VPC内のネットワークインターフェース(ENI)を通過するIPトラフィックのメタデータを記録するAWSのサービスです。送信元・宛先のIPアドレスとポート番号、プロトコル番号、パケット数・バイト数、そしてセキュリティグループ/NACLによる「ACCEPT」または「REJECT」の判定結果を、一定の集計期間(通常10分前後)でログとして出力します。

Flow Logsを有効化できる対象は3レベルあります。

VPCレベル:VPC全体のすべてのENIが対象。最も広い範囲で通信を記録できる
サブネットレベル:特定のサブネット内のENIが対象。テスト用サブネットを除外したい場合などに使う
ENIレベル:特定のネットワークインターフェースのみを対象にする。範囲を絞ることでコストを抑えられる

ログの送信先はAmazon S3、Amazon CloudWatch Logs、またはAmazon Kinesis Data Firehoseの3択です。長期保存・コスト重視ならS3、リアルタイム分析・アラート設定ならCloudWatch Logsが定番の選択肢です。

ひとつ重要な注意点があります。Flow Logsはパケットのメタデータを記録するサービスであり、通信の中身(ペイロード)は記録しません。「いつ・どこから・どこへ・何ポートへ通信があったか」を記録するためのサービスです。HTTP/HTTPSの通信内容を調べたい場合は、ALBのアクセスログやCloudFrontのディストリビューションログを使います。

VPC Flow Logsを有効化する手順|CLIでS3へ送信する設定

S3を送信先にする場合、バケットポリシーでVPC Flow LogsサービスへのPutObject権限を付与する必要があります。バケットポリシーを設定したうえで aws ec2 create-flow-logs コマンドを実行します。

1. S3バケットにバケットポリシーを設定する

以下のバケットポリシーをJSONファイルとして保存し、対象バケットに適用します。123456789012 はご自身のAWSアカウントIDに置き換えてください。

# bucket-policy.json として保存する { "Version": "2012-10-17", "Statement": [ { "Sid": "AWSLogDeliveryWrite", "Effect": "Allow", "Principal": { "Service": "delivery.logs.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::my-flowlogs-bucket/vpc-logs/AWSLogs/123456789012/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control", "aws:SourceAccount": "123456789012" } } }, { "Sid": "AWSLogDeliveryAclCheck", "Effect": "Allow", "Principal": { "Service": "delivery.logs.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::my-flowlogs-bucket" } ] } # バケットポリシーを適用する aws s3api put-bucket-policy \ --bucket my-flowlogs-bucket \ --policy file://bucket-policy.json \ --region ap-northeast-1

2. VPC Flow Logsを有効化する

--resource-type VPC--resource-ids にVPC IDを指定し、--traffic-type で記録対象を選択します。

# VPCレベルでFlow Logsを有効化(S3送信・すべての通信を記録) aws ec2 create-flow-logs \ --resource-type VPC \ --resource-ids vpc-0a1b2c3d4e5f67890 \ --traffic-type ALL \ --log-destination-type s3 \ --log-destination arn:aws:s3:::my-flowlogs-bucket/vpc-logs/ \ --region ap-northeast-1 # 実行後の出力例 { "ClientToken": "", "FlowLogIds": [ "fl-0abc1234def56789" ], "Unsuccessful": [] }

--traffic-type の選択肢は以下の3つです。

ALL:ACCEPT・REJECTの両方を記録。最も広い情報が得られる
ACCEPT:許可した通信のみ記録する
REJECT:拒否した通信のみ記録する。セキュリティ監査に絞る場合に効率的

3. 有効化を確認する

コマンド実行後、FlowLogStatusACTIVE になっているか確認します。

# 有効化状態を確認する aws ec2 describe-flow-logs \ --filter "Name=resource-id,Values=vpc-0a1b2c3d4e5f67890" \ --region ap-northeast-1 \ --query 'FlowLogs[*].{ID:FlowLogId,Status:FlowLogStatus,Dest:LogDestinationType}' \ --output table # 出力例 ----------------------------------------------------------------- | DescribeFlowLogs | +----+----------------------+------+--------+------------------+ | Dest | ID | Status | +-------------------------+---------------------+--------------+ | s3 | fl-0abc1234def56789 | ACTIVE | +-------------------------+---------------------+--------------+

S3にログが書き出されるまで、最初の記録で10~15分程度かかります。ログのS3パスは vpc-logs/AWSLogs/123456789012/vpcflowlogs/ap-northeast-1/ 以下に年/月/日で自動的に階層化されます。

CloudWatch Logsへ送信してリアルタイム分析する設定

CloudWatch Logsを送信先にするには、VPC Flow LogsサービスがロググループへのPutLogEvents権限を持つIAMロールが必要です。

1. IAMロールを作成する

信頼ポリシー(vpc-flow-logs.amazonaws.com がAssumeRoleできる設定)を作成し、CloudWatch Logsへの書き込み権限を付与します。

# trust-policy.json として保存する { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "vpc-flow-logs.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } # IAMロールを作成する aws iam create-role \ --role-name VPCFlowLogsRole \ --assume-role-policy-document file://trust-policy.json # CloudWatch Logs書き込みポリシーをアタッチする(最小権限版) aws iam put-role-policy \ --role-name VPCFlowLogsRole \ --policy-name FlowLogsCloudWatchPolicy \ --policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams" ], "Resource": "*" }] }'

2. CloudWatch LogsへFlow Logsを有効化する

# ロググループを作成する(存在しない場合) aws logs create-log-group \ --log-group-name /aws/vpc/flowlogs \ --region ap-northeast-1 # CloudWatch Logs送信でFlow Logsを有効化する aws ec2 create-flow-logs \ --resource-type VPC \ --resource-ids vpc-0a1b2c3d4e5f67890 \ --traffic-type ALL \ --log-destination-type cloud-watch-logs \ --log-group-name /aws/vpc/flowlogs \ --deliver-logs-permission-arn arn:aws:iam::123456789012:role/VPCFlowLogsRole \ --region ap-northeast-1

3. CloudWatch Logs Insightsでログを検索する

ログが届いたら(最初の記録まで数分かかります)、CloudWatch Logs Insightsから以下のクエリで内容を確認できます。

# 直近1時間のREJECTを送信元IP別に集計するInsightsクエリ fields @timestamp, srcaddr, dstaddr, srcport, dstport, action | filter action = "REJECT" | stats count() as reject_count by srcaddr | sort reject_count desc | limit 20

Flow Logsのレコード形式を読み解く|フィールドの意味と実務での見方

Flow Logsの標準フォーマット(バージョン2)は、スペース区切りで以下の順にフィールドが並びます。実環境で取得した記録をマスク処理したものを見ながら確認しましょう。

# バージョン2 標準フォーマットのヘッダー version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status # 実際のログレコード例(アカウントID・ENI IDはマスク済み) 2 123456789012 eni-0a1b2c3d4e5f 203.0.113.45 10.0.1.100 52341 22 6 4 240 1720000000 1720000060 REJECT OK 2 123456789012 eni-0a1b2c3d4e5f 10.0.1.100 10.0.2.200 54321 443 6 20 4120 1720000060 1720000120 ACCEPT OK 2 123456789012 eni-0a1b2c3d4e5f - - - - - - - 1720000120 1720000180 - NODATA

各フィールドの意味を整理します。

version:レコードフォーマットのバージョン(標準は2)
interface-id:トラフィックが通過したENIのID
srcaddr / dstaddr:送信元・宛先のIPアドレス。VPC外からの通信はパブリックIPが記録される
srcport / dstport:送信元・宛先のポート番号
protocol:プロトコル番号。6=TCP、17=UDP、1=ICMP
action:ACCEPTまたはREJECT。セキュリティグループ/NACLによる判定結果
log-status:OK(正常)、NODATA(集計期間中に通信なし)、SKIPDATA(容量超過でスキップ)

上の例の1行目は、外部IP 203.0.113.45 からSSH(ポート22)への接続がREJECTされています。これはセキュリティグループがポート22を開放していないか、NACLで拒否されていることを意味します。不審なIPからのポートスキャンや侵入試行を検知する際に重要なレコードです。

3行目の - はNODATAステータスです。集計期間中に当該ENIでトラフィックが発生しなかったことを表します。これは正常な状態であり、エラーではありません。

実務活用パターン|不審なアクセスと通信経路を調べる方法

AWSを運用している現場で、Flow Logsがとくに役立つ調査パターンを紹介します。

1. 外部からの不審なアクセス源を特定する

S3にログが蓄積されたら、Amazon Athenaを使ってSQLで検索できます。CloudWatch Logs Insightsより大量のデータを効率よく分析できます。

-- Athena: REJECTされた外部IP(プライベートアドレス以外)をポート別に集計する SELECT srcaddr, dstport, count(*) AS reject_count FROM vpc_flow_logs WHERE action = 'REJECT' AND srcaddr NOT LIKE '10.%' AND srcaddr NOT LIKE '172.16.%' AND srcaddr NOT LIKE '192.168.%' GROUP BY srcaddr, dstport ORDER BY reject_count DESC LIMIT 30;

このクエリで特定の外部IPから特定ポートへのREJECTが大量に記録されていれば、ポートスキャンや侵入試行の可能性があります。特定したIPは評判確認サービス(AbuseIPDBなど)で照合するとよいでしょう。

Linuxでの ss コマンドや lsof によるポート確認方法についてはLinux ポート確認の全コマンドでも詳しく解説しています。

2. VPC内の通信経路を可視化する

VPC内のサービス間通信(例: アプリケーションサーバーからデータベースへの接続)が期待通りに行われているかを確認できます。

# CloudWatch Insights: サブネット間のACCEPT通信を確認する # (例: 10.0.1.0/24 のアプリ層から 10.0.2.0/24 のDB層への通信) fields @timestamp, srcaddr, dstaddr, dstport, action | filter action = "ACCEPT" and srcaddr like "10.0.1." and dstaddr like "10.0.2." | sort @timestamp desc | limit 50

3. DNS通信(UDP 53)でリゾルバーの動作を確認する

VPC内からRoute 53 Resolverへの名前解決が正常に行われているかどうか、Flow LogsのUDPポート53の通信記録で確認できます。REJECT が多い場合はセキュリティグループやNACLの設定を見直してください。Linuxサーバーでの名前解決の仕組みについてはLinux DNS 設定の基本も参考にしてください。

# CloudWatch Insights: DNS通信(UDP 53)の状況を確認する fields @timestamp, srcaddr, dstaddr, dstport, protocol, action | filter dstport = 53 and protocol = 17 | stats count() as dns_count by action

VPC Flow Logsで把握した通信パターンを活かした全体的な冗長設計については、AWS冗長設計の実践ガイドでも詳しく解説しています。

コストと有効化範囲の設計|VPC全体・サブネット・ENIの使い分け

VPC Flow Logsの料金はログデータの「取り込み量」によって課金されます。大量のトラフィックがあるVPCで全通信を記録すると、ログ保存コストが想定以上にかかることがあります。コストを抑えながらセキュリティ監査に必要な情報を確保するには、以下の考え方で設計します。

Traffic TypeをREJECTに絞る:セキュリティ監査が主な目的なら --traffic-type REJECT のみを有効にすることで、ログ量を大幅に減らせる。ACCEPTを含む通信量が多い環境では特に効果的
送信先はS3を優先する:CloudWatch Logsはデータ取り込み料(東京リージョンで約$0.76/GB)がS3より割高。長期保存・大容量ならS3 + Athenaが経済的
S3のライフサイクルポリシーでコスト管理する:90日以上経過したログをGlacierへ移行するライフサイクルルールを設定すると、長期ストレージコストを抑えられる
段階的な有効化で始める:最初はVPCレベルでREJECTのみを有効化→問題が起きたサブネットやENIにALLを追加する段階的なアプローチが現場では現実的

# セキュリティ監査特化型の設定(REJECTのみ・S3送信) aws ec2 create-flow-logs \ --resource-type VPC \ --resource-ids vpc-0a1b2c3d4e5f67890 \ --traffic-type REJECT \ --log-destination-type s3 \ --log-destination arn:aws:s3:::my-flowlogs-bucket/vpc-reject-logs/ \ --region ap-northeast-1

送信先ごとのコスト比較(東京リージョン・2026年7月時点の概算)を整理します。

Amazon S3への取り込み:約$0.02/GB。長期保存に向く
CloudWatch Logsへの取り込み:約$0.76/GB。リアルタイム分析向けだがコストは高い
Kinesis Data Firehoseへの取り込み:約$0.029/GBの処理料。SplunkやOpenSearchへのリアルタイム送信が必要な場合に使う

トラブルシュート|ログが出力されない時の確認手順

Flow Logsを有効化してもログが出力されない場合、以下の手順で原因を切り分けます。

1. FlowLogStatusとエラーメッセージを確認する

aws ec2 describe-flow-logs \ --filter "Name=resource-id,Values=vpc-0a1b2c3d4e5f67890" \ --region ap-northeast-1 \ --query 'FlowLogs[*].{Status:FlowLogStatus,Error:DeliverLogsErrorMessage}' \ --output table

DeliverLogsErrorMessage にエラーが表示されている場合、IAMロールの権限不足かS3バケットポリシーの設定誤りがほとんどです。エラーメッセージに Access Denied が含まれる場合は、権限設定を最初から見直してください。

2. S3送信の場合はバケットポリシーを確認する

# バケットポリシーが正しく設定されているか確認する aws s3api get-bucket-policy \ --bucket my-flowlogs-bucket \ --output text # ポリシーが存在しない場合は以下のエラーが返る # An error occurred (NoSuchBucketPolicy) when calling the GetBucketPolicy operation: # The bucket policy does not exist

3. 最初の記録までの待機時間を確保する

Flow Logsの集計期間(デフォルト:約10分)が経過しないとログが出力されません。有効化直後に確認しても記録がない場合は、15分程度待ってから再確認してください。

また、VPC内にトラフィックが発生していない場合は NODATA レコードしか出力されません。テスト用に curlping で通信を発生させてから確認することをお勧めします。

4. SKIPDATAが多い場合の対処

log-statusSKIPDATA が多数記録されている場合、Flow Logsサービス側で取り込み容量の制限を超えたことを意味します。VPCレベルで有効化している場合はサブネットレベルやENIレベルに切り替えて範囲を絞るか、CloudWatch LogsからS3送信に変更することを検討してください。

本記事のまとめ

やりたいこと コマンド・設定
S3へFlow Logsを有効化する aws ec2 create-flow-logs --log-destination-type s3
CloudWatch Logsへ有効化する aws ec2 create-flow-logs --log-destination-type cloud-watch-logs
有効化状態を確認する aws ec2 describe-flow-logs
REJECTのみを記録する aws ec2 create-flow-logs --traffic-type REJECT
有効化を削除する aws ec2 delete-flow-logs --flow-log-ids fl-xxxx
Insightsで不審IPを集計する filter action = "REJECT" | stats count() by srcaddr
・Flow Logsは送信先(S3/CloudWatch Logs)と対象(VPC/サブネット/ENI)・Traffic Typeを組み合わせて設計する
・actionフィールドがREJECTのレコードの送信元IPとdstportを集計すると、不審なアクセス源を効率よく特定できる
・ログが出力されない場合はFlowLogStatusのDeliverLogsErrorMessageとIAMロール・バケットポリシーを優先して確認する
・コスト設計の基本はtraffic-type REJECTとS3送信の組み合わせから始め、調査が必要な時点でALL・CloudWatch Logsへ拡張する
・SKIPDATAが多い場合はVPCレベルからサブネット・ENIレベルへ絞り込むか、S3送信へ切り替える

VPC Flow Logsを設計に組み込むことで、「何かおかしい気がする」という感覚ではなく、IPアドレス・ポート番号・時刻という具体的なデータで通信を調査できるようになります。AWSのVPC設計・冗長化をさらに体系的に学びたい方は、下記のリンクからAWSマスタープログラムの詳細をご覧ください。

VPC設計・Flow Logs・セキュリティ監査——「なんとなく」を「根拠ある設計」に変えませんか?

aws ec2 create-flow-logs のコマンドは調べれば分かります。でも「どのレベルで有効化すべきか」「コストと可視性のバランスをどうとるか」を、自信を持って答えられますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。