ECSからの不審な外向き通信を見つけて遮断する ── VPCフローログとネットワークACL
記事の「実践」を 1 項目ずつ進めるためのページです。AWS マネジメントコンソールで操作したら、右の一覧にチェックを入れてください。チェックはこのブラウザに保存されます。
前提条件
- リージョン:
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 | フローログがロググループに書き込むためのロール |
> [!NOTE] > フローログと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 20
8888 番の宛先が 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番のままでは既存ルールと重複して追加できず、大きい番号にすると全許可ルールが先に一致して拒否されません。
「変更を保存」をクリックします。
手順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 @timestamp desc
| limit 20
8888 番に action が REJECT の行が現れていることを確認します。遮断前の ACCEPT の記録と、遮断後の REJECT の記録が両方残るので、遮断が効いた時点がフローログで分かります。
> [!NOTE] > NACLはステートレスなので、アウトバウンドを拒否すると戻りの通信も届かなくなります。今回は「発信そのものを止める」ことが目的なので問題ありませんが、業務通信に適用する場合は、戻りの通信(エフェメラルポート)への影響を必ず確認します。