Serverless Agentsを利用してECS Fargate環境でSysdig Secureを利用してみた〜Terraform編〜
こんにちは!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の場合、ECS 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フローログもデプロイします。
プロバイダー設定
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" {}
変数設定
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
]
}
{
"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}:*"
}
}
}
]
}
# 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を作成してみようと思います。
# 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.tf
とterraform.tfvars
を利用して設定します。
variable "prefix" {
type = string
default = "sysdig-fargate"
}
variable "sysdig_access_key" {
type = string
default = ""
sensitive = true
}
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プロバイダーによって提供されているためプロバイダーの設定を更新します。
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で値を受け渡しするため、マネジメントコンソール上で表示される内容と一部異なります。)
[
{
"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を組み込みます。
# 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
のアウトプットを利用して、タスク定義を作成します。
# 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クラスター、サービスをデプロイします。
# 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
}
}
{
"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でサーバレスエージェントをデプロイする!
まとめ
以上、Serverless Agentsを利用して、ECS Fargate環境でデプロイされるコンテナのランタイム検知でした。
引き続きランタイム保護等のポリシーについて調べてみようと思います。この記事がどなたかの参考になれば幸いです。
AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!