ECS Fargate ワークロードを Sysdig Serverless Agent で保護する (2)

Amazon Elastic Container Service (ECS) の実行環境に AWS Fargate を用いた構成(以下、ECS Fargate )は、AWS でコンテナの実行環境を整える際の強力な選択肢のひとつです。
Fargate を用いることで、ユーザが考慮すべきセキュリティリスクは( EC2 を用いるよりも)少なくなりますが、すべてがカバーされるものではありません。

この連載記事では、ECS Fargate で実行されるワークロードを Sysdig の Serverless Agent を使って保護するための方法をご紹介します。
第 2 回となる今回は、Serverless Agent のセットアップとイベントキャプチャに触れます。

  1. Sysdig と Serverless Agent の概要
  2. Serverless Agent のセットアップとイベントキャプチャ (この記事)
  3. Serverless Agent がアプリケーションに与える性能影響

Sysdig Serverless Agent のセットアップ

Sysdig Serverless Agent を ECS Fargate 環境にセットアップするまでの流れを見てゆきます。
公式ドキュメント の手順に沿って、セットアップ時の注意点やスクリーンショットを交えながらご紹介します。
セットアップに使用するサンプルアプリケーションの CloudFormation テンプレートを こちら に公開しましたので、必要に応じてご参照ください。

Orchestrator Agent のインストール

まず、Sysdig から配布されている Orchestrator Agent の CloudFormation テンプレート をダウンロードし、CloudFormation で対象の VPC にデプロイします。

このとき、パラメータとして指定する Sysdig Collector Host は、Sysdig の SaaS をどのリージョンで利用しているかでエンドポイントの URL が変わってくる点に注意が必要です。
今回は US West (Oregon) リージョンで作成した Sysdig アカウントを利用します。
そのため、公式ドキュメントの SaaS エンドポイントの一覧 から、Sysdig Collector Host に設定すべきエンドポイントは ingest-us2.app.sysdig.com であることがわかります。

Orchestrator Agent のデプロイが完了すると、コンソールの ECS のページからエージェントのサービスやタスクの状態を確認することができます。

以降の Workload Agent のインストール準備に必要となるため、CloudFormation の出力 (Output) タブの OrchestratorHost と OrchestratorPort の値を控えておきます。

Workload Agent のインストール

次に、Workload Agent をインストール(保護対象のワークロードのタスク定義に追加)します。
Workload Agent をタスク定義に追加する方法は以下の 4 つがありますが、今回は Kilt を使った自動挿入の流れをご紹介します。

  • Kilt によるタスク定義の自動挿入
  • Terraform Sysdig Provider によるタスク定義の自動挿入
  • 手動でタスク定義に Workload Agent と一連の設定を加える
  • コンテナイメージのビルド時に Workload Agent を同梱する

Kilt によるタスク定義の自動挿入を行うためには、まず公式ドキュメントの リンク から端末環境に合わせたインストーラをダウンロードします。
リンクの URL を見るに、Mac 向けのインストーラamd64 版のみの提供となっており、M1 Mac (arm64) には対応していないようですね。

実行時引数に OrchestratorHost と OrchestratorPort の値を与えて、ダウンロードしたインストーラを実行します。

% ./installer-macos-amd64 cfn-macro install -r us-east-1 MySysdigMacro sysdi-Sysdi-1H9B3SZS3U6J-3ab0b532f7c361ef.elb.us-east-1.amazonaws.com 6667
Kilt detected the following:
 * account id: ************
 * region: us-east-1
kilt bucket name: sysdig-cloud-************-us-east-1
Installing macro MySysdigMacro...
Uploading lambda code. This might take a while...DONE
Uploading kilt definition cfn-macro-MySysdigMacro.kilt.cfg...DONE
Submitted cloudformation stack 'KiltMacroMySysdigMacro'. Follow creation progress in AWS console
After installation is completed you will be able to use "Transform: ["MySysdigMacro"]" in your template to automatically instrument it

実行すると、インストール先のタスク定義の CloudFormation テンプレートに Transform セクションを加えるよう案内されます。
また、コンソールで確認すると、自動挿入を行うための Lambda が CloudFormation でデプロイされていることがわかります。

これで自動挿入の準備は完了です。

サンプルアプリケーションの実行と Sysdig による保護

Serverless Agent を利用する準備が整ったので、サンプルアプリケーションを CloudFormation でデプロイします。

今回、サンプルアプリケーションには Falco の GitHub リポジトリで公開されている event-generator を利用しました。
Sysdig で検出されるような様々なシステムコールの実行、ファイルの参照や変更といったアクションを延々と行うアプリケーションとなっています。
作成したサンプルアプリケーションの CloudFormation テンプレートの Transform セクションに、Kilt のマクロを指定してデプロイします。

github.com

タスク定義を参照すると、前述のマクロによって Workload Agent や一連の設定が自動挿入されたことを確認できます。
具体的には、以下のような設定が追加されます。

  • Entrypoint に /opt/draios/bin/instrument を追加する
  • SYS_PTRACE の Linux Capability を追加する
  • 環境変数に Orchestrator Agent の宛先情報を追加する
  • WorkloadAgent のコンテナ定義を追加する
{
  "ipcMode": null,
  "executionRoleArn": "arn:aws:iam::************:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "dnsSearchDomains": [],
      "environmentFiles": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "secretOptions": [],
        "options": {
          "awslogs-group": "KiltMacroMySysdigMacro-SysdigServerlessLogGroup-asDctJPbI8uU",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "TaskDefinition"
        }
      },
      "entryPoint": [
        "/opt/draios/bin/logwriter"
      ],
      "portMappings": [],
      "command": [],
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "resourceRequirements": null,
      "ulimits": [],
      "dnsServers": [],
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": [],
      "dockerSecurityOptions": [],
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "stopTimeout": null,
      "image": "quay.io/sysdig/workload-agent:latest",
      "startTimeout": null,
      "firelensConfiguration": null,
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": [],
      "hostname": null,
      "extraHosts": [],
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": {},
      "systemControls": [],
      "privileged": null,
      "name": "SysdigInstrumentation"
    },
    {
      "dnsSearchDomains": [],
      "environmentFiles": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "secretOptions": [],
        "options": {
          "awslogs-group": "/ecs/event-generator",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "app"
        }
      },
      "entryPoint": [
        "/opt/draios/bin/instrument"
      ],
      "portMappings": [],
      "command": [
        "/bin/event-generator",
        "run",
        "syscall",
        "--loop"
      ],
      "linuxParameters": {
        "capabilities": {
          "add": [
            "SYS_PTRACE"
          ],
          "drop": []
        },
        "sharedMemorySize": null,
        "tmpfs": [],
        "devices": [],
        "maxSwap": null,
        "swappiness": null,
        "initProcessEnabled": null
      },
      "cpu": 0,
      "environment": [
        {
          "name": "SYSDIG_ACCESS_KEY",
          "value": ""
        },
        {
          "name": "SYSDIG_COLLECTOR",
          "value": ""
        },
        {
          "name": "SYSDIG_COLLECTOR_PORT",
          "value": ""
        },
        {
          "name": "SYSDIG_LOGGING",
          "value": ""
        },
        {
          "name": "SYSDIG_ORCHESTRATOR",
          "value": "sysdi-Sysdi-1H9B3SZS3U6J-3ab0b532f7c361ef.elb.us-east-1.amazonaws.com"
        },
        {
          "name": "SYSDIG_ORCHESTRATOR_PORT",
          "value": "6667"
        }
      ],
      "resourceRequirements": null,
      "ulimits": [],
      "dnsServers": [],
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": [],
      "dockerSecurityOptions": [],
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [
        {
          "sourceContainer": "SysdigInstrumentation",
          "readOnly": true
        }
      ],
      "stopTimeout": null,
      "image": "falcosecurity/event-generator:latest",
      "startTimeout": null,
      "firelensConfiguration": null,
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": [],
      "hostname": null,
      "extraHosts": [],
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": {},
      "systemControls": [],
      "privileged": null,
      "name": "event-generator"
    }
  ],
  "placementConstraints": [],
  "memory": "512",
  "taskRoleArn": "arn:aws:iam::************:role/task-role-for-event-generator",
  "compatibilities": [
    "EC2",
    "FARGATE"
  ],
  "taskDefinitionArn": "arn:aws:ecs:us-east-1:************:task-definition/event-generator:7",
  "family": "event-generator",
  "requiresAttributes": [
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.22"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.task-eni"
    }
  ],
  "pidMode": null,
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "runtimePlatform": null,
  "cpu": "256",
  "revision": 7,
  "status": "ACTIVE",
  "inferenceAccelerators": null,
  "proxyConfiguration": null,
  "volumes": []
}

CloudFormation によるデプロイが完了すると、コンソールの ECS のページからサービスやタスクの状態を確認することができます。
実行中のタスクでは、サンプルアプリケーションのコンテナとは別に、Workload Agent のコンテナが稼働していることがわかります。

セキュリティイベントの検出にあたり、Sysdig のランタイムポリシはデフォルトで用意されているルールセットを有効化した状態としました。
デフォルトの設定では、ポリシに抵触するイベントが発生した場合、通知のみを行う(実行は制限しない)設定となっています。

Workload Agent が保護しているワークロードで検出されたセキュリティイベントは、Sysdig の Web コンソールの Events ページで確認することができます。

上の図では、サンプルアプリケーションが bash を起動したりディレクトリを作成したため、定義済みポリシに抵触して Notable Filesystem Changes や Unexpected Spawned Processes のイベントが検出されていることがわかります。

ECS Fargate 環境における Sysdig Capture

Sysdig Capture によるセキュリティイベントのキャプチャは、Sysdig の特徴的な機能のひとつです。
Sysdig はワークロードがポリシに準拠していることを評価するためにシステムコールをモニタリングし続けており、セキュリティイベントが発生した際には、その前後(最大 300 秒)のシステムコールやその他の OS イベントをひとつのキャプチャファイルにまとめ、後からダウンロードしたり分析ができるようになっています。
ECS Fargate 環境に限った話ではないのですが、Serverless Agent でもキャプチャが実行できるか試してみます。

キャプチャはポリシーの一部として構成するか、モジュールを使って手動で作成することができます。
今回は以下のポリシでイベントキャプチャを有効化して生成します。

サンプルアプリケーションで該当するイベントが検出されました。

生成されたキャプチャファイルは、端末にダウンロードするなどして参照することもできますが、手っ取り早く可視化する手段として Sysdig Inspect という分析ツールが提供されています。
Sysdig Inspect に Web コンソールからアクセスする場合は、メニューの Investigate ページにある Captures タブを開きます。

Sysdig Inspect の Web UI を使って、セキュリティイベント発生時のシステムコールやプロセスの状態を確認することができました。

補足 (1) Activity Audit の参照

Investigate ページの Activity Audit タブでは Sysdig が収集したアクティビティ情報を時系列で確認することができますが、今回、Serverless Agent を上記のようにセットアップした状態では何も表示されませんでした。
Serverless Agent が Fargate のホストにアクセスができないため Activity Audit がサポートされていないものと思われますが、もしかすると設定次第では何かしら表示できるものもあるのかもしれません。

ちなみに、EC2 に通常の Sysdig Agent をインストールした場合は、Activity Audit の情報が表示されました。

補足 (2) Kilt による Workload Agent と諸設定の自動挿入

公式ドキュメントにも記載されていますが、Workload Agent と諸設定の自動挿入を行う Kilt は、挿入の際に Entrypoint と Command の設定を変更します。
そのため、(プライベートなイメージの場合は特に)Entrypoint と Command を明示的にタスク定義に含めておきましょう。
パブリックなイメージの場合は Kilt が自動的にデフォルトの Entrypoint と Command を取得してくれますが、正しく取得できなかった場合は自動挿入に失敗します。

docs.sysdig.com

つづく

今回は ECS Fargate 環境に Sysdig Serverless Agent をセットアップし、ワークロードを保護するまでの流れをご紹介しました。
次回 は Serverless Agent がアプリケーションの性能に与える影響に触れたいと思います。