ECSタスク定義で秘匿情報を含む環境変数が存在する場合のSSMを活用した設定修正手順

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、秘匿情報を含む環境変数が存在するECSタスク定義について、そのリスクと対策を解説します。

ポリシーの説明
まず、AWS Security Hubによるポリシーの説明は以下の通りです。
Amazon ECS の Security Hub コントロール – AWS Security Hub
このコントロールは、コンテナ定義の
environment
パラメータにある、任意の変数のキー値に、AWS_ACCESS_KEY_ID
、AWS_SECRET_ACCESS_KEY
、ECS_ENGINE_AUTH_DATA
のいずれかが含まれているかどうかをチェックします。任意のコンテナ定義内の単一の環境変数が、AWS_ACCESS_KEY_ID
、AWS_SECRET_ACCESS_KEY
、ECS_ENGINE_AUTH_DATA
のいずれかである場合、このコントロールは失敗します。
リスクとしてはParameter StoreやSystems Managerの値を復号化するKMSキーを使用する権限を持っていないユーザーでも、ECSで使用されているアクセスキー等の秘匿情報を覗き見ることが可能となり、悪意のあるユーザーの場合は漏洩させたり設定変更等を行うリスクがあります。 そのため、秘匿情報はParameter StoreやSystems Managerで保管するようにしましょう。
修復方法
AWSコンソールでの修正手順
以下Systems Managerのパラメータストアを使用した修正手順です。
① AWS コンソールにログイン後、Systems Manager > パラメータストアへ移動します。
② 移動後、パラメータの作成をクリック

③ 名前、タイプ、値は最低限入力し、 パラメータを作成します。

④ 作成後、ECSのタスク定義 > 新しいタスク定義の作成で環境変数に以下の通り入力します。
キー: コンテナ内で変数として使用する名前を入力
値のタイプ:ValueFrom
値:パラメータストアで作成したパラメータのARNを記載

Terraformでの修復手順
ECSタスク定義での安全な認証情報管理のためのTerraformコードを作成します。
# Secrets Managerでのシークレット作成
resource "aws_secretsmanager_secret" "app_secrets" {
name = "${var.environment}-app-secrets"
description = "アプリケーション用の認証情報"
tags = {
Environment = var.environment
Application = var.app_name
}
}
# シークレット値の設定
resource "aws_secretsmanager_secret_version" "app_secrets" {
secret_id = aws_secretsmanager_secret.app_secrets.id
secret_string = jsonencode({
DATABASE_URL = var.database_url
API_KEY = var.api_key
AWS_ACCESS_KEY_ID = var.aws_access_key
AWS_SECRET_KEY = var.aws_secret_key
})
}
# ECSタスク実行ロール
resource "aws_iam_role" "ecs_task_execution_role" {
name = "${var.environment}-ecs-task-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
}
# タスク実行ロールへのポリシー付与
resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" {
role = aws_iam_role.ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
# Secrets Managerアクセス用のカスタムポリシー
resource "aws_iam_role_policy" "secrets_policy" {
name = "${var.environment}-secrets-policy"
role = aws_iam_role.ecs_task_execution_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"kms:Decrypt"
]
Resource = [
aws_secretsmanager_secret.app_secrets.arn
]
}
]
})
}
# ECSタスク定義(安全な設定)
resource "aws_ecs_task_definition" "app" {
family = "${var.environment}-${var.app_name}"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = var.container_name
image = var.container_image
# 環境変数を直接指定しない
secrets = [
{
name = "DATABASE_URL"
valueFrom = "${aws_secretsmanager_secret.app_secrets.arn}:DATABASE_URL::"
},
{
name = "API_KEY"
valueFrom = "${aws_secretsmanager_secret.app_secrets.arn}:API_KEY::"
},
{
name = "AWS_ACCESS_KEY_ID"
valueFrom = "${aws_secretsmanager_secret.app_secrets.arn}:AWS_ACCESS_KEY_ID::"
},
{
name = "AWS_SECRET_KEY"
valueFrom = "${aws_secretsmanager_secret.app_secrets.arn}:AWS_SECRET_KEY::"
}
]
# 非機密な環境変数のみ直接指定
environment = [
{
name = "APP_ENV"
value = var.environment
},
{
name = "LOG_LEVEL"
value = "info"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/${var.environment}-${var.app_name}"
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "ecs"
}
}
}
])
}
# ECSサービス
resource "aws_ecs_service" "app" {
name = "${var.environment}-${var.app_name}"
cluster = var.ecs_cluster_id
task_definition = aws_ecs_task_definition.app.arn
desired_count = var.service_desired_count
launch_type = "FARGATE"
network_configuration {
security_groups = [aws_security_group.ecs_tasks.id]
subnets = var.private_subnet_ids
}
}
# セキュリティグループ
resource "aws_security_group" "ecs_tasks" {
name = "${var.environment}-${var.app_name}-ecs-tasks"
description = "Security group for ECS tasks"
vpc_id = var.vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# CloudWatch Logsグループ
resource "aws_cloudwatch_log_group" "app" {
name = "/ecs/${var.environment}-${var.app_name}"
retention_in_days = 30
}
# 必要な変数定義
variable "environment" {
type = string
description = "環境名(production, staging等)"
}
variable "app_name" {
type = string
description = "アプリケーション名"
}
variable "container_name" {
type = string
description = "コンテナ名"
}
variable "container_image" {
type = string
description = "コンテナイメージURI"
}
variable "cpu" {
type = number
description = "タスクのCPU単位"
default = 256
}
variable "memory" {
type = number
description = "タスクのメモリ(MiB)"
default = 512
}
# 現在のリージョンを取得
data "aws_region" "current" {}
# 出力
output "task_definition_arn" {
value = aws_ecs_task_definition.app.arn
}
output "secrets_arn" {
value = aws_secretsmanager_secret.app_secrets.arn
}
重要な修正ポイントと実装手順を説明します:
- 認証情報の安全な管理(最重要):
# 環境変数を直接指定する代わりに、Secrets Managerを使用
secrets = [
{
name = "AWS_ACCESS_KEY_ID"
valueFrom = "${aws_secretsmanager_secret.app_secrets.arn}:AWS_ACCESS_KEY_ID::"
}
]
# 非機密情報のみ環境変数として設定
environment = [
{
name = "APP_ENV"
value = var.environment
}
]
2. 適切なIAMロールとポリシーの設定:
resource "aws_iam_role_policy" "secrets_policy" {
policy = jsonencode({
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue"
]
Resource = [aws_secretsmanager_secret.app_secrets.arn]
}
]
})
}
3. ログとモニタリング:
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/${var.environment}-${var.app_name}"
awslogs-stream-prefix = "ecs"
}
}
適用手順:
- 変数の設定:
# terraform.tfvars
environment = "production"
app_name = "myapp"
container_name = "api"
container_image = "xxx.dkr.ecr.region.amazonaws.com/myapp:latest"
2. Terraformの実行:
terraform init
terraform plan
terraform apply
最後に
今回は、秘匿情報をそのままECSで使用している場合のリスクとその対処方法についてご紹介しました。 ECSで秘匿情報を扱う際は十分注意していただき、本記事を参考にセキュリティリスクが高い状態で使用しないよう設定を行ってみてください。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。