Serverless Agentsを利用してECS Fargate環境でSysdig Secureを利用してみた〜Terraform編〜

2022.10.31

こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。

今回は、Sysdig Secureから提供されているServerless Agentsを利用して、ECS Fargate環境でデプロイされるコンテナのランタイム検知をしてみようと思います。

Sysdig Secureとは

Sysdig Secureとは、Sysdig社から提供されるセキュリティ製品の1つでクラウド環境やコンテナ環境の脆弱性の検知、保護等を行う製品です。

Sysdig Secure is part of Sysdig’s container intelligence platform. Sysdig uses a unified platform to deliver security, monitoring, and forensics in a cloud, container and microservices-friendly architecture integrated with Docker and Kubernetes. Sysdig Secure takes a services-aware approach to protect workloads while bringing deep cloud and container visibility, posture management (compliance, benchmarks, CIEM), vulnerability scanning, forensics and threat detection and blocking.

Sysdig Secureより引用

Serverless Agentsとは

今回利用するServerless Agentsとは、AWS Fargateのようなサーバレス環境でSysdig Secureを起動するために必要なエージェントになります。

ECS on Fargateの場合、ESC on EC2とは違いコンテナを起動するホストにエージェントをインストールできないため、Serverless Agentsをサイドカーコンテナの形式でデプロイする必要があります。

構成要素

Serverless Agentsは、「Agents」と名前がついている通り、2種類のエージェントで構成されています。

ECS on Fargate環境下でSysdig Secureを起動するには、各エージェントを必要な数、デプロイする必要があります。

  • The Sysdig serverless orchestrator agent(以後、orchestrator agent)
    • 各VPCごとに1つ必要
    • 後続のThe Sysdig serverless workload agentからデータを収集し、Sysdig SaaSのエンドポイントへ転送する
  • The Sysdig serverless workload agent(以後、workload agent)
    • 各タスクごとに必要
    • 同一タスク内に含まれるコンテナの情報をorchestrator agentに転送する

AWS Fargate Serverless Agents - Architectureより引用

今回はTerraformデプロイであることと、もう少し深掘りして全体像を表すと以下になります。

また、今回使用したコードは以下に格納しました。自由にカスタマイズしてご利用いただければと思います。

VPC周りの作成

では、VPCも含めて今回の構成をセットアップしてみます。

terraform-aws-modules/vpc/awsを利用してデプロイします。

どのような通信が行われているかも含めて確認したいため、今回はVPCフローログもデプロイします。

プロバイダー設定

providers.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.37.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region
data "aws_region" "current" {}
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity
data "aws_caller_identity" "self" {}

変数設定

variables.tf

variable "prefix" {
  type = string
  default = "sysdig-fargate"
}

S3(VPCフローログ用)

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
resource "aws_s3_bucket" "flowlog" {
  bucket = "${var.prefix}-flowlog-${data.aws_caller_identity.self.account_id}"

  force_destroy = true
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls
resource "aws_s3_bucket_ownership_controls" "flowlog" {
  bucket = aws_s3_bucket.flowlog.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy
resource "aws_s3_bucket_policy" "flowlog" {
  bucket = aws_s3_bucket.flowlog.id

  policy = templatefile("${path.module}/policy_document/bucket_flowlog.json", {
    bucket_arn = aws_s3_bucket.flowlog.arn
    account_id = data.aws_caller_identity.self.account_id
    region = data.aws_region.current.name
  })
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration
resource "aws_s3_bucket_server_side_encryption_configuration" "flowlog" {
  bucket = aws_s3_bucket.flowlog.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block
resource "aws_s3_bucket_public_access_block" "flowlog" {
  bucket = aws_s3_bucket.flowlog.id

  block_public_acls = true
  block_public_policy = true
  restrict_public_buckets = true
  ignore_public_acls = true

  depends_on = [
    aws_s3_bucket_policy.flowlog,
    aws_s3_bucket_ownership_controls.flowlog
  ]
}

/policy_document/bucket_flowlog.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AWSLogDeliveryWrite",
            "Effect": "Allow",
            "Principal": {
                "Service": "delivery.logs.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "${bucket_arn}/*",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "${account_id}",
                    "s3:x-amz-acl": "bucket-owner-full-control"
                },
                "ArnLike": {
                    "aws:SourceArn": "arn:aws:logs:${region}:${account_id}:*"
                }
            }
        }
    ]
}

vpc.tf

# https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = var.prefix
  cidr = "10.0.0.0/16"

  azs             = ["${data.aws_region.current.name}a", "${data.aws_region.current.name}c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = true

  enable_flow_log = true
  flow_log_destination_type = "s3"
  flow_log_destination_arn = aws_s3_bucket.flowlog.arn
  flow_log_traffic_type = "ALL"
}

全体像で表すと以下まで作成完了しました。

orchestrator agentの作成

Sysdig公式からorchestrator agentを作成するためにModuleが提供されています。

今回はこちらのModuleを利用して、orchestrator agentを作成してみようと思います。

orchestrator_agent.tf

# https://registry.terraform.io/modules/sysdiglabs/fargate-orchestrator-agent/aws/latest
module "fargate-orchestrator-agent" {
  source  = "sysdiglabs/fargate-orchestrator-agent/aws"
  version = "0.2.0"

  vpc_id = module.vpc.vpc_id
  subnets = module.vpc.private_subnets

  access_key = var.sysdig_access_key # Sysdig access key
  collector_host = "ingest.au1.sysdig.com" # Sysdig collector host (default:"collector.sysdigcloud.com")
  collector_port = "6443" # Sysdig collector port (default:"6443")

  name = "${var.prefix}-orchestrator" # Identifier for module resources
  agent_image = "quay.io/sysdig/orchestrator-agent:latest" # Orchestrator agent image

  assign_public_ip = false # Provisions a public IP for the service. Required when using an Internet Gateway for egress.
}

access_keyについて

module内で指定した「access_key」について補足します。

access_keyは、Sysdig Secureコンソール画面から取得できます。

「Your access key」からアクセスキーをコピーします。

コピーが完了したら、variables.tfterraform.tfvarsを利用して設定していきます。

variables.tf

variable "prefix" {
  type = string
  default = "sysdig-fargate"
}

variable "sysdig_access_key" {
  type = string
  default = ""
  sensitive = true
}

terraform.tfvars

sysdig_access_key = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

collector_hostについて

module内で指定した「collector_host」について補足します。

collector_hostは、Sysdigのアカウント作成時に指定したリージョンによって設定値が異なります。

私の場合、Asia Pacific (Sydney)を指定したため、「ingest.au1.sysdig.com」を指定する必要があります。

詳しくは以下をご覧ください。

orchestrator agentの作成が完了すると以下のような構成になっています。

workload agentとアプリケーションコンテナの作成

続いて、workload agentをサイドカー形式でアプリケーションコンテナと一緒にデプロイします。

今回、アプリケーションコンテナはFalco Securityより提供されているfalcosecurity/event-generatorを利用してみようと思います。

詳しい使い方は以下をご覧ください。

タスク定義の作成

タスク定義の作成を行います。

sysdig_fargate_workload_agentを利用して、workload agentを組み込んだタスク定義を作成します。

はじめに、sysdig_fargate_workload_agentはSysdigプロバイダーによって提供されているためプロバイダーの設定を更新します。

providers.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.37.0"
    }
    # ここ
    sysdig = {
      source = "sysdiglabs/sysdig"
      version = "0.5.40"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region
data "aws_region" "current" {}
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity
data "aws_caller_identity" "self" {}

# ここ
provider "sysdig" {
  sysdig_secure_url = "https://app.au1.sysdig.com"
  sysdig_secure_api_token = var.sysdig_access_key
}

次にworkload agentを利用しない場合のタスク定義を定めます。

今回のタスク定義は以下になります。(Terraformで値を受け渡しするため、マネジメントコンソール上で表示される内容と一部異なります。)

/task_definition/event_generator.json

[
  {
      "name": "event-generator",
      "essential": true,
      "image": "falcosecurity/event-generator",
      "entryPoint": ["/bin/event-generator"],
      "command": [
        "run",
        "syscall",
        "--loop"
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "${region}",
          "awslogs-group": "${log_group_name}",
          "awslogs-stream-prefix": "${log_stream_prefix}"
        }
      }
  }
]

上記のタスク定義にsysdig_fargate_workload_agentを利用して、workload agentを組み込みます。

ecs.tf

# https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/fargate_workload_agent
data "sysdig_fargate_workload_agent" "instrumented_envent_generator" {
  container_definitions = templatefile("${path.module}/task_definition/event_generator.json", {
    region = data.aws_region.current.name
    log_group_name = aws_cloudwatch_log_group.task.name
    log_stream_prefix = "${var.prefix}-event-generator"
  })

  sysdig_access_key     = var.sysdig_access_key
  workload_agent_image  = "quay.io/sysdig/workload-agent:latest"
  orchestrator_host     = module.fargate-orchestrator-agent.orchestrator_host
  orchestrator_port     = module.fargate-orchestrator-agent.orchestrator_port

  log_configuration {
    region = data.aws_region.current.name
    group = aws_cloudwatch_log_group.workload_agent.name
    stream_prefix = "${var.prefix}-workload-agent"
  }
}

最後にsysdig_fargate_workload_agentのアウトプットを利用して、タスク定義を作成します。

ecs.tf

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition
resource "aws_ecs_task_definition" "envent_generator" {
  family = "${var.prefix}-td-event-generator"

  requires_compatibilities = [ "FARGATE" ]
  network_mode = "awsvpc"
  cpu = "1024"
  memory = "2048"
  container_definitions = data.sysdig_fargate_workload_agent.instrumented_envent_generator.output_container_definitions
  execution_role_arn = aws_iam_role.task_exec.arn

  runtime_platform {
    operating_system_family = "LINUX"
    cpu_architecture = "X86_64"
  }
}

サービスの作成

タスク定義が作成できたため、ECSクラスター、サービスをデプロイしていきます。

ecs.tf

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role
resource "aws_iam_role" "task_exec" {
  name = "${var.prefix}-role-task-exection"
  assume_role_policy = file("${path.module}/policy_document/assume_ecs_task_exec.json")
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment
resource "aws_iam_role_policy_attachment" "managed_task_exec" {
  role = aws_iam_role.task_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group
resource "aws_cloudwatch_log_group" "task" {
  name = "${var.prefix}-logs"
}

resource "aws_cloudwatch_log_group" "workload_agent" {
  name = "${var.prefix}-workload-agent-logs"
}

# https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/fargate_workload_agent
data "sysdig_fargate_workload_agent" "instrumented_envent_generator" {
  container_definitions = templatefile("${path.module}/task_definition/event_generator.json", {
    region = data.aws_region.current.name
    log_group_name = aws_cloudwatch_log_group.task.name
    log_stream_prefix = "${var.prefix}-event-generator"
  })

  sysdig_access_key     = var.sysdig_access_key
  workload_agent_image  = "quay.io/sysdig/workload-agent:latest"
  orchestrator_host     = module.fargate-orchestrator-agent.orchestrator_host
  orchestrator_port     = module.fargate-orchestrator-agent.orchestrator_port

  log_configuration {
    region = data.aws_region.current.name
    group = aws_cloudwatch_log_group.workload_agent.name
    stream_prefix = "${var.prefix}-workload-agent"
  }
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition
resource "aws_ecs_task_definition" "envent_generator" {
  family = "${var.prefix}-td-event-generator"

  requires_compatibilities = [ "FARGATE" ]
  network_mode = "awsvpc"
  cpu = "1024"
  memory = "2048"
  container_definitions = data.sysdig_fargate_workload_agent.instrumented_envent_generator.output_container_definitions
  execution_role_arn = aws_iam_role.task_exec.arn

  runtime_platform {
    operating_system_family = "LINUX"
    cpu_architecture = "X86_64"
  }
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster
resource "aws_ecs_cluster" "cluster" {
  name = "${var.prefix}-cluster"
  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
resource "aws_security_group" "ecs_service" {
  name        = "${var.prefix}-ecs-service"
  description = "${var.prefix}-ecs-service"
  vpc_id      = module.vpc.vpc_id
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
resource "aws_security_group_rule" "ecs_any_egress" {
  security_group_id = aws_security_group.ecs_service.id
  type = "egress"
  description = "Allow to Any"
  from_port = 0
  to_port = 0
  protocol = "-1"
  cidr_blocks = ["0.0.0.0/0"]
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service
resource "aws_ecs_service" "service" {
  name = "${var.prefix}-service"
  cluster = aws_ecs_cluster.cluster.id
  launch_type = "FARGATE"
  task_definition = aws_ecs_task_definition.envent_generator.arn
  desired_count = 1

  network_configuration {
    security_groups = [ aws_security_group.ecs_service.id ]
    subnets = module.vpc.private_subnets
    assign_public_ip = false
  }
}

/policy_document/assume_ecs_task_exec.json

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Sysdig Secureで確認

Sysdig Secureで確認してみます。デフォルトでは重要度が「High」のルールのみ有効下されていたため、「Sysdig Runtime Notable Events」と「Sysdig Runtime Activity Logs」を有効にしてみます。

有効にするとルールで検知されたリソースがInshightに表示されていました。

Eventsタブでも、時系列順に検知されたルールが表示されるようになりました。

通信要件まとめ

Serverless Agentsの構成の場合、デフォルトでは以下の通信が必要でした。

どちらのポートも、orchestrator agent作成時に指定する値のため、ポート範囲に迷ったら、module "fargate-orchestrator-agent"で探してみてください。

fargate-orchestrator-agent - inputs

IPアドレスベースで絞りたい場合は、再掲になりますがSysdigの地域別で利用しているIP範囲をご覧いただければと思います。

参考情報

教えてケイティ! - AWS ECSでサーバレスエージェントをデプロイする!

AWS Fargate サーバーレスエージェント

まとめ

以上、Serverless Agentsを利用して、ECS Fargate環境でデプロイされるコンテナのランタイム検知でした。

引き続きランタイム保護等のポリシーについて調べてみようと思います。この記事がどなたかの参考になれば幸いです。

AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!