Amazon GuardDuty ECS Runtime Monitoringで脅威を検出したECSタスクを自動停止してみた

検出だけでなく防御もしたい、そんなあなたへ。

こんにちは。AWS事業本部トクヤマシュンです。

先日、Amazon GuardDuty ECS Runtime Monitoringが発表されました。 詳細は下記エントリでまとめてますのでご参照ください。

本機能はECSのランタイムセキュリティに関する脅威の発生をGuardDutyで検知する、というものでした。
ただ、検知するだけではなく防御もしたい、という方もおられるのではないでしょうか?

そこでECS Runtime Monitoringで脅威を検出したECSタスクを自動停止する仕組みを構築しましたので、本エントリで紹介します。

構築の前提

構築にあたり、下記を前提としています。

  • Amazon GuardDuty ECS Runtime Monitoringを東京リージョンで有効化し、脅威検出が可能な状態になっている
  • ECSタスクは既に起動済み
  • 脅威を検出したECSタスクは全て自動停止する

構成図

下図のような構成を取りました。

赤い点線の部分が今回構築する範囲です。
下記GitHubに今回構築で利用したTerraformコードを公開しておりますので、ご参照ください。

構築した仕組みについて

AWSサービスごとに構築内容を説明します。

Amazon ECS

ECSサービス内でタスクを起動しています。
また、Amazon GuardDuty ECS Runtime Monitoringが有効化されているため、タスク内にGuardDuty Agentサイドカーコンテナが起動しています。

Amazon GuardDuty

ECSタスク内でランタイムセキュリティの脅威が検出されると、GuardDuty AgentからAmazon GuardDutyに脅威情報(Findings)が送られます。

Amazon EventBridgfe

Amazon GuardDutyで検出されたFindingsの情報はAmazon EventBridgeのDefault Event Busに送られます。

Default Event Busを通るデータのうち、ECS Runtime Monitoringの脅威検出をトリガーとするため、次のようなイベントパターンを持つAmazon EventBridge Ruleを作成します。

{
  "detail": {
    "resource": {
      "resourceType": ["ECSCluster"]
    },
    "service": {
      "featureName": ["RuntimeMonitoring"]
    }
  },
  "detail-type": ["GuardDuty Finding"],
  "source": ["aws.guardduty"]
}

上記はECS Runtime Monitoringで検出されたすべてのFindingsをトリガーとするよう設定していますが、
特定のクラスターに属するタスクのみを対象としたり、重要度でフィルタリングすることも可能ですので、
ご自身の環境に適したイベントパターンを設定してください。

参考:GuardDutyの通知が重要度でフィルター可能になりました

Amazon EventBridge Ruleのターゲットには後続のAWS Step Functionsを指定します。
その際のInput transformerは次のように指定します。

Input path

{
  "EcsClusterArn": "$.detail.resource.ecsClusterDetails.arn",
  "GuardDutyFindingTitle": "$.detail.title",
  "TaskArn": "$.detail.resource.ecsClusterDetails.taskDetails.arn",
  "TaskDefinitionArn": "$.detail.resource.ecsClusterDetails.taskDetails.definitionArn"
}

Input template

{
  "EcsClusterArn": <EcsClusterArn>,
  "TaskDefinitionArn":  <TaskDefinitionArn>,
  "TaskArn":  <TaskArn>,
  "GuardDutyFindingTitle": <GuardDutyFindingTitle>
}

これらの入力は後続のStep Functionsで停止対象のタスクを識別したり、SNS通知の内容に含めるために利用されます。

AWS StepFunctions

下図のように設定します。

コードですと次の通りです。

AWS Step Functionsコード(クリックで展開)
{
  "Comment": "Stop Task with runtime security threats detected by Amazon GuardDuty",
  "StartAt": "StopTask",
  "States": {
    "Notify Failure": {
      "End": true,
      "Parameters": {
        "Message.$": "States.Format('Step Functions failed to stop ECS Task with runtime security threats detected by GuardDuty.\nDetail:\n  Error: {}\n  Cause: {}',$.Error,$.Cause)\n",
        "Subject": "Step Functions failed to stop ECS Task with runtime security threats detected by GuardDuty",
        "TopicArn": "arn:aws:sns:ap-northeast-1:640028535582:ecs-runtime-monitoring-stop-task-dev-topic"
      },
      "Resource": "arn:aws:states:::aws-sdk:sns:publish",
      "Type": "Task"
    },
    "Notify Success": {
      "End": true,
      "Parameters": {
        "Message.$": "States.Format('Step Functions succeeded in stopping ECS Task with runtime security threats detected by GuardDuty.\nDetail:\n  Cluster: {}\n  TaskDefinition: {}\n  Group: {}\n  Task: {}\n  StoppedReason: {}',$.Task.ClusterArn, $.Task.TaskDefinitionArn, $.Task.Group, $.Task.TaskArn, $.Task.StoppedReason)\n",
        "Subject": "Step Functions succeeded in stopping ECS Task with runtime security threats detected by GuardDuty",
        "TopicArn": "arn:aws:sns:ap-northeast-1:640028535582:ecs-runtime-monitoring-stop-task-dev-topic"
      },
      "Resource": "arn:aws:states:::aws-sdk:sns:publish",
      "Type": "Task"
    },
    "StopTask": {
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "Notify Failure"
        }
      ],
      "Next": "Notify Success",
      "Parameters": {
        "Cluster.$": "$.EcsClusterArn",
        "Reason.$": "$.GuardDutyFindingTitle",
        "Task.$": "$.TaskArn"
      },
      "Resource": "arn:aws:states:::aws-sdk:ecs:stopTask",
      "Type": "Task"
    }
  },
  "TimeoutSeconds": 3600
}

各ステートの動作は下記の通りです。

  • StopTask
    • 入力で指定されたECSタスクを停止します。
    • 停止が成功すればNotify Success、失敗すればNotify Failureに遷移します。
  • Notify Success
    • SNS TopicにECSタスク停止が成功した旨の通知を行います。
    • 本文には停止したタスクのクラスターARN、タスク定義ARN、タスクARN、検出された脅威の内容を記載しています。
  • Notify Failure
    • SNS TopicにECSタスク停止が失敗した旨の通知を行います。
    • 本文には停止失敗時に発生したエラーとエラー理由を記載しています。

Amazon SNS

タスク停止の成功、失敗を通知するためのTopicを準備します。
別途メールアドレスでサブスクライブしておくことで、タスク停止が行われた時に成功/失敗のどちらかの通知をメールで受け取ります。

実際に動かしてみる

動作の流れ

私の環境で実際に動かしてみた時の動作フローと経過時間を示します。

ECS Execで脅威を模したふるまいを実行  
--(約2分半経過)--  
GuardDutyで脅威検出  
--(約1分経過)--  
EventBridge RuleでStepFunctions呼び出し  ←約3分半でECSタスク停止

今回は脅威発生から3分半程度経過すると自動でタスク停止されました。
経過時間は環境や状況によって変わってくるとは思いますが、脅威検出からタスク停止まで比較的迅速に行えているのではないでしょうか。

では、実際の動作を確認していきましょう。

ECS Execで脅威を模したふるまいを実行

まずは、ECSタスク内のコンテナに対してECS Execでログインし、セキュリティ上の脅威を模したふるまいをするため、
コインマイニングに関するドメインの名前解決をおこなってみます。

今回はコンテナイメージとしてpublic.ecr.aws/nginx/nginx:stableを利用しました。
こちらはDebianベースのイメージですので、ECS Execでログイン後、下記コマンドを実行しました。

# apt update
# apt install -y dnsutils
# nslookup pool.supportxmr.com

GuardDutyで脅威検出

コマンド実行から数分後、CryptoCurrency:Runtime/BitcoinTool.B!DNSというタイプをもつ、次のようなFindingsの発生をAmazon GuardDutyで確認することができました。

停止対象のタスクIDは「fa2100c571d448b7a977da8120a5d383」でした。

EventBridge RuleでStepFunctions呼び出し

さらに数分待つと、Amazon EventBridge Ruleが動作し、AWS Step Functionsが動作します。
StopTaskステートによってタスクが停止され、成功したのでNotify SuccessステートでSNSトピックにメッセージが送信されます。

SNSトピックに対してメールアドレスでサブスクライブしていたので、届いたメールを確認します。

停止したタスク関連の情報が記載されていますね。

該当するECSタスクをコンソール画面で見てみると、タスクが停止されていたことが確認できました。

なお、今回はECSサービスでECSタスクを起動していたので、タスク停止後には新規で別のタスクが立ち上がってきました。
ただ元々のECSサービスのタスク数が1だったので、タスク停止〜新規タスク立ち上がり完了までの間は一時的に起動中のタスクが0になりましたので、ご注意ください。

参考:タスク停止失敗時のSNS通知

タスク停止失敗時の動作も確認してみます。

次のように入力としてタスクIDにdummyと設定して、Step Functionsを手動実行してみます。

Step Functionsは次のように遷移していました。
タスク停止に失敗した後失敗通知を行なっています。 

次のようなメールが届いており、どういった理由でタスク停止が失敗したかを判別できました。
タスクIDの長さは32か36と決まっているんですね。

動作確認としては以上です。

まとめ

ECS Runtime Monitoringで脅威を検出したECSタスクを自動停止する仕組みをご紹介しました。
構築した内容は、脅威を検出したECSタスクを問答無用で停止するものとなっています。
停止したくないタスクがある時はEventBridge Ruleのイベントパターンを工夫するなどしてうまくフィルタリングしてください。

本エントリがどなたかのお役に立てば幸いです。