概要
コンテナが侵害されたとき、最初に現れる兆候は「見慣れない宛先への外向き通信」です。マイニングプールへの接続、外部への大量のデータ送信、見慣れないポートへの定期的なアクセス。攻撃者がコンテナの中で何をしているかは見えなくても、通信が起きた事実はネットワーク側に残ります。
この事実を記録するのがVPCフローログです。そして、見つけた通信をサブネット単位で止めるのがネットワークACL(NACL)です。CloudTrailはAPIコールの記録なので、コンテナからインターネットへの通信は写りません。AWS WAFやShieldは受信側の防御であって、外向きの通信には効きません。
この記事では、ECSタスクから外部への通信をフローログで観測し、CloudWatch Logs Insightsで宛先ポートごとに集計して不審な通信を特定し、NACLで遮断してREJECTに変わることを確認します。

クラウドおさるECSタスクの外向き通信をフローログで捕まえ、宛先ポートごとに集計し、NACLで止めて REJECT に変わるところまで確かめるでござる。検出はフローログ、遮断はNACL、この対応を手で覚えるのでござるな。
この記事のメリット
- 外向き通信の調査に、なぜCloudTrailやWAFではなくフローログを使うのかを説明できるようになる
- フローログのレコード形式と
actionフィールドの読み方が分かるようになる - CloudWatch Logs Insightsで宛先ポート別に集計するクエリが書けるようになる
- セキュリティグループとネットワークACLの使い分け(ステートフル / ステートレス)を判断できるようになる
- SCS試験でネットワークレベルの検知と遮断を問う設問に答えられるようになる
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
外向き通信の調査に何を使うか
| サービス | 記録・制御するもの | この用途で使えるか |
|---|---|---|
| VPCフローログ | ネットワークインターフェイスを出入りするIPトラフィックのメタデータ | 使える。送信元・宛先・ポート・許可/拒否が分かる |
| CloudTrail | AWS APIの呼び出し | 使えない。コンテナから外部への通信はAPIコールではない |
| AWS WAF | ALB・CloudFrontなどへの受信リクエスト | 使えない。外向きの通信は対象外 |
| AWS Shield | DDoS攻撃からの保護(受信) | 使えない |
| GuardDuty | 脅威検出(フローログ・DNSログ・CloudTrailを分析) | 検知には使える。ただし遮断はしない |
設問では「不正な外向き通信を検出して遮断する」という形で問われます。検出はフローログ、遮断はNACLかセキュリティグループ、という対応で覚えます。
フローログのレコードを読む
既定の記録形式は14個のフィールドで構成されます。
| フィールド | 内容 |
|---|---|
version / account-id / interface-id | バージョン・アカウントID・ENI ID |
srcaddr / dstaddr | 送信元・宛先のIPアドレス |
srcport / dstport | 送信元・宛先のポート |
protocol | IANAのプロトコル番号(6=TCP、17=UDP) |
packets / bytes | パケット数・バイト数 |
start / end | フローの開始・終了時刻(UNIX秒) |
action | ACCEPT または REJECT |
log-status | ログの記録状態 |
CloudWatch Logs Insightsでは、これらのフィールドが srcAddr / dstAddr / dstPort / action / bytes のような名前で自動的に検出されます。クエリでは、レコード形式の srcaddr ではなく srcAddr のように大文字を含む名前で書く点に注意が必要です。
action の意味は次のとおりです。
| 値 | 意味 |
|---|---|
ACCEPT | トラフィックが許可された |
REJECT | セキュリティグループやネットワークACLで許可されなかった、接続終了後にパケットが届いた、など |
遮断が効いたかどうかは、この action が ACCEPT から REJECT に変わることで確認できます。
集約間隔は既定で最大10分、オプションで最大1分を指定できます(Nitroベースのインスタンスでは、指定に関わらず1分以下に短縮されます)。集約後、CloudWatch Logsへはおよそ5分、S3へはおよそ10分で配信されます。フローログはリアルタイムではない、という点は運用でも試験でも押さえておきます。





遮断が効いたかどうかは、action が ACCEPT から REJECT に変わることで分かるでござる。ただし猿山の門番の帳面と同じで、集約してから数分遅れで届くので、フローログはリアルタイムではないのでござるな。
セキュリティグループとネットワークACL
見つけた通信を止める手段は2つあります。性質が違うので使い分けます。
| 観点 | セキュリティグループ | ネットワークACL |
|---|---|---|
| 適用先 | ENI(インスタンス・タスク単位) | サブネット |
| 状態 | ステートフル(戻りの通信は自動的に許可) | ステートレス(戻りの通信も明示的に許可が要る) |
| ルール | 許可のみ | 許可と拒否の両方 |
| 評価 | すべてのルールを評価する | ルール番号の小さい順に評価し、最初に一致したもので決まる |
「特定の宛先ポートへの通信を拒否したい」という要件では、拒否ルールが書けるNACLを使います。セキュリティグループには拒否ルールがなく、「許可を外す」ことしかできないためです。
NACLはステートレスなので、アウトバウンドを拒否するときは戻りの通信にも注意が必要です。ただし今回のように「特定の宛先ポートへの発信を止める」用途では、アウトバウンドの拒否ルールを1本入れれば足ります。





セキュリティグループには拒否ルールが無く、許可を外すことしかできぬでござる。特定の宛先ポートを拒否したいならNACL、そしてNACLはステートレスで戻りの通信にも効く点に気をつけるでござるぞ。
調査の流れ
- フローログをCloudWatch Logsに出す(対象はVPC・サブネット・ENIのいずれか)
- CloudWatch Logs Insightsで、宛先ポートや宛先IPごとに集計する
- 想定していない宛先・ポートを特定する
- NACLの拒否ルールで遮断する
- フローログで
REJECTに変わったことを確認する
恒久対策としては、そもそもタスクをプライベートサブネットに置いてNATゲートウェイ経由に限定する、必要な宛先だけVPCエンドポイントで通す、といった構成に寄せます。NACLでの遮断は「いま出ている通信を止める」応急処置です。
実践
パブリックサブネットのECSタスクから外部へ定期的に通信させ、フローログで観測してからNACLで遮断します。
前提条件
- リージョン:
ap-northeast-1(東京) - ECS・VPC・CloudWatch Logsの操作権限を持つIAMユーザーまたはロールでコンソールにログイン済みであること
- 以下のリソースが作成済みであること
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| VPC | ecs-egress-lab-vpc(10.40.0.0/16) | 検証用VPC |
| パブリックサブネット | ecs-egress-lab-public(10.40.1.0/24) | ECSタスクの配置先 |
| セキュリティグループ | ecs-egress-lab-sg | タスク用(インバウンドなし・アウトバウンド全許可) |
| ECSクラスター | ecs-egress-lab-cluster | Fargateタスクの実行環境 |
| タスク定義 | ecs-egress-lab-task | 外部の 1.1.1.1 の TCP 8888 番へ、5秒ごとに接続を試みるコンテナ |
| CloudWatchロググループ | /aws/vpc/ecs-egress-lab | フローログの出力先 |
| IAMロール | ecs-egress-lab-flowlog-role | フローログがロググループに書き込むためのロール |
フローログとFargateタスクは課金対象です。
このハンズオンで作成するリソースは以下のとおりです。
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| VPCフローログ | ecs-egress-lab-flowlog | VPC全体のトラフィックを記録 |
| ネットワークACLのルール | アウトバウンド 90番 | 特定ポートへの通信を拒否 |
手順1: VPCフローログを作成する
AWSマネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。
上部の検索バーに VPC と入力し、「VPC」を選択します。左側ナビゲーションの「お使いのVPC」から ecs-egress-lab-vpc を選択します。
下部の「フローログ」タブを開き、「フローログの作成」をクリックして以下を設定します。
- 名前:
ecs-egress-lab-flowlog - 最大集計間隔:
1 分(既定は 10 分) - 送信先ロググループ:
/aws/vpc/ecs-egress-lab - サービスロール:
ecs-egress-lab-flowlog-role
フィルター(すべて)、送信先(CloudWatch Logs に送信)、サービスアクセス(既存のサービスロールを使用)、ログレコードの形式(AWS のデフォルト形式)は既定のままにします。


その他の項目はデフォルトのままにして、「フローログを作成」をクリックします。
手順2: ECSタスクを起動する
上部の検索バーに ECS と入力し、「Elastic Container Service」を選択します。
ecs-egress-lab-cluster を開き、「タスク」タブから「新しいタスクの実行」をクリックします。
「コンピューティングオプション」で 起動タイプ(起動タイプは FARGATE)を選び、「タスク定義ファミリー」で ecs-egress-lab-task を選択します(リビジョンは「最新」のまま)。
「ネットワーキング」で以下を設定します。
- VPC:
ecs-egress-lab-vpc - サブネット:
ecs-egress-lab-public - セキュリティグループ:
ecs-egress-lab-sg(VPC を選ぶと自動で入るdefaultは外します) - パブリック IP:
オンになっています(既定のまま)


「作成」をクリックします。タスク一覧で、30秒ほどでステータスが「実行中」になることを確認します。


手順3: フローログで外向き通信を確認する
タスクは起動すると、外部の 1.1.1.1 の 8888 番ポートに向けて定期的に接続を試み続けます。集計間隔とCloudWatch Logsへの配信の都合で、記録が現れるまで数分かかります。
上部の検索バーに CloudWatch と入力し、「CloudWatch」を選択します。左側ナビゲーションの「ログ」→「ロググループ」から /aws/vpc/ecs-egress-lab を開き、eni- で始まるログストリームを開きます。
フローログのレコードが、14個のフィールドをスペースで区切った形で並んでいます。インターネット側からタスクのパブリック IP に届いた通信(セキュリティグループで拒否された REJECT)も多く記録されるため、フィルターに 1.1.1.1 と入力して、タスクからの外向き通信だけに絞り込みます。


手順4: Logs Insightsで宛先ポート別に集計する
左側ナビゲーションの「ログ」→「ログのインサイト」を開きます。クエリ画面の「ロググループ」で /aws/vpc/ecs-egress-lab を選択すると、クエリ欄の1行目に SOURCE "arn:aws:logs:…:log-group:/aws/vpc/ecs-egress-lab" が自動で入ります。
2行目以降を次のクエリにして「実行」をクリックします。タスクのプライベートIPを送信元とする通信を、宛先ポートごとに集計します。
fields @timestamp, srcAddr, dstAddr, dstPort, action, bytes
| filter srcAddr like /^10\.40\./
| stats sum(bytes) as totalBytes, count(*) as flows by dstPort, action
| sort totalBytes desc
| limit 20結果には 443 番と 8888 番が並びます。443 番はタスク起動時のコンテナイメージの取得など、ECS が動くために必要な通信です。想定していないのは 8888 番で、継続的に通信が発生していることを確認します。


宛先IPも確認しておきます。
fields @timestamp, srcAddr, dstAddr, dstPort, action
| filter srcAddr like /^10\.40\./ and action = "ACCEPT"
| stats count(*) as flows by dstAddr, dstPort
| sort flows desc
| limit 208888 番の宛先が 1.1.1.1 であることが分かります。


手順5: ネットワークACLで遮断する
VPCコンソールの左側ナビゲーションから「ネットワーク ACL」を選択し、ecs-egress-lab-public サブネットに関連付けられているNACLを選択します。
下部の「アウトバウンドルール」タブを開き、「アウトバウンドルールを編集」をクリックします。既存のルール 100 番(すべてのトラフィックを許可)が表示されています。
「新しいルールを追加」をクリックし、追加された行に以下を設定します。
- ルール番号:
90 - タイプ:
カスタム TCP - ポート範囲:
8888(手順4で特定した宛先ポート) - 送信先:
0.0.0.0/0 - 許可/拒否:
拒否
既存の全許可ルール(100番)より小さい番号にする点が重要です。NACLはルール番号の小さい順に評価し、最初に一致したルールで決まります。100番のままでは既存ルールと重複して追加できず、大きい番号にすると全許可ルールが先に一致して拒否されません。


「変更を保存」をクリックします。



拒否ルールは、既存の全許可ルールより小さい番号にするのが肝でござる。NACLは番号の小さい順に評価し、最初に一致したルールで決まるのでござる。
手順6: REJECTに変わったことを確認する
数分待ってから、CloudWatch Logs Insightsで次のクエリを実行します。今回は保存から3分ほどで最初の REJECT が記録されました。
fields @timestamp, srcAddr, dstAddr, dstPort, action
| filter srcAddr like /^10\.40\./
| stats count(*) as flows by dstPort, action
| sort flows desc
| limit 208888 番に action が REJECT の行が現れていることを確認します。遮断前の ACCEPT の記録と、遮断後の REJECT の記録が両方残るので、遮断が効いた時点がフローログで分かります。


NACLはステートレスなので、アウトバウンドを拒否すると戻りの通信も届かなくなります。今回は「発信そのものを止める」ことが目的なので問題ありませんが、業務通信に適用する場合は、戻りの通信(エフェメラルポート)への影響を必ず確認します。



外向き通信の検知と遮断に、CloudTrailやWAF・Shieldではなくフローログと NACL を選べるかが試験の観点でござるな。GuardDutyは検知まで、遮断はしない、という線引きも取り違えやすいでござる。
まとめ
| やりたいこと | 使うもの |
|---|---|
| 外向き通信が起きた事実を知る | VPCフローログ(CloudWatch Logs / S3) |
| 宛先・ポートごとに集計する | CloudWatch Logs Insights |
| 特定の宛先ポートへの通信を拒否する | ネットワークACLの拒否ルール |
| タスク単位で許可を絞る | セキュリティグループ(拒否は書けない) |
| 脅威として検知させる | GuardDuty(検知のみ。遮断はしない) |
- CloudTrailはAPIコールの記録なので、コンテナから外部への通信は写らない
- WAFやShieldは受信側の防御であり、外向きの通信には効かない
- フローログの
actionがACCEPTからREJECTに変わることで、遮断が効いたと確認できる - NACLはステートレスかつルール番号順の評価。拒否ルールを書けるのはNACLだけ
- フローログはリアルタイムではない(集約最大10分・配信に数分)。即時遮断の仕組みではない
参照先
- フローログレコード – Amazon Virtual Private Cloud
- VPC フローログを使用した IP トラフィックのログ記録 – Amazon Virtual Private Cloud
- ネットワーク ACL を使用してサブネットへのトラフィックを制御する – Amazon Virtual Private Cloud
- セキュリティグループを使用したリソースへのトラフィックの制御 – Amazon Virtual Private Cloud
- サポートされるログと検出されるフィールド – Amazon CloudWatch Logs
- CloudWatch Logs Insights クエリ構文 – Amazon CloudWatch Logs











