ECSタスク定義でログ設定を有効化する手順

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

ポリシーの説明
ECSタスク定義では、各コンテナ定義にログドライバーを設定することで、コンテナの標準出力・標準エラー出力をCloudWatch Logs、FireLens、Splunkなどのログ管理システムに送信できます。ログ設定が存在しないコンテナは、運用時の可視性が著しく低下し、インシデント対応やトラブルシューティングが困難になります。本ポリシーでは、すべてのECSタスク定義でログ設定が適切に構成されているかを確認します。AWS Well-Architected Frameworkの信頼性の柱でも、ログの収集は重要なベストプラクティスとして位置づけられています。
修復方法
コンソールでの修復手順
AWSのコンソールを使用して、ECSタスク定義にログ設定を追加します。
ステップ1: タスク定義の確認
- AWSマネジメントコンソールにログイン
- Amazon ECSサービスに移動
- 左側メニューから 「タスク定義」 を選択
- ログ設定が必要なタスク定義をクリック
- 現在のリビジョンを確認
ステップ2: 新しいリビジョンの作成
- タスク定義の詳細画面で 「新しいリビジョンの作成」 ボタンをクリック
- 「JSON」 タブをクリックして、タスク定義のJSONを表示
- 必要に応じて現在の設定をバックアップ

ステップ3: コンテナ定義のログ設定
- 「コンテナ定義」 セクションまでスクロール
- 編集するコンテナ名をクリック
- 「ストレージとログ」 セクションまでスクロール
- 「ログ設定」 で最適なログ設定を選択してください。※ 以下はCloudWatchでの設定例です
ステップ4: CloudWatch Logsロググループの作成
- 別タブで CloudWatchコンソールを開く
- 左側メニューから 「ロググループ」 を選択
- 「ロググループの作成」 をクリック
- ロググループ名に
/ecs/タスク定義名
を入力 - 保持期間: 要件に応じて設定
- 開発環境: 7日
- ステージング環境: 14日
- 本番環境: 30日以上(コンプライアンス要件に応じて最大90日以上)
- KMS暗号化(オプション): 機密情報を含む場合はKMSキーを選択
- 「作成」 をクリック
ステップ5: タスク実行ロールの権限確認
- ECSコンソールに戻る
- 「タスク実行ロール」 を確認
- IAMコンソールで該当ロールを開く
- 以下のポリシーがアタッチされていることを確認:
AmazonECSTaskExecutionRolePolicy
または- カスタムポリシーに以下の権限が含まれていること:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:log-group:/ecs/*" } ] }
ステップ6: タスク定義の更新と展開
- すべての設定を確認後、「作成」 をクリック
- 新しいリビジョンが作成されたことを確認
- ECSサービスを更新して新しいタスク定義リビジョンを使用
- 「サービスの更新」 → 新しいタスク定義リビジョンを選択
- 「サービスの更新」 をクリック
Terraformでの修復手順
ECSタスク定義のログ設定を有効にするTerraformコードと、主要な修正ポイントを説明します。
# CloudWatch Logsロググループの作成
resource "aws_cloudwatch_log_group" "ecs_logs" {
name = "/ecs/${var.task_family}"
retention_in_days = var.log_retention_days # 環境ごとに変数化
kms_key_id = var.kms_key_id # 暗号化キー(オプション)
tags = {
Environment = var.environment
Purpose = "ECS Container Logs"
ManagedBy = "Terraform"
}
}
# ログストリームの事前作成(オプション)
resource "aws_cloudwatch_log_stream" "ecs_container" {
name = "${var.container_name}/container"
log_group_name = aws_cloudwatch_log_group.ecs_logs.name
}
# タスク実行ロール
resource "aws_iam_role" "ecs_execution_role" {
name = "${var.task_family}-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
tags = {
Environment = var.environment
Purpose = "ECS Task Execution"
}
}
# AWS管理ポリシーのアタッチ(ECRアクセス等)
resource "aws_iam_role_policy_attachment" "ecs_execution_role_policy" {
role = aws_iam_role.ecs_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
# CloudWatch Logsへの書き込み権限
resource "aws_iam_role_policy" "ecs_execution_logs" {
name = "${var.task_family}-logs-policy"
role = aws_iam_role.ecs_execution_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "${aws_cloudwatch_log_group.ecs_logs.arn}:*"
}
]
})
}
# ECSタスク定義(ログ設定を含む)
resource "aws_ecs_task_definition" "app" {
family = var.task_family
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.task_cpu
memory = var.task_memory
execution_role_arn = aws_iam_role.ecs_execution_role.arn
task_role_arn = aws_iam_role.ecs_task_role.arn
container_definitions = jsonencode([
{
name = var.container_name
image = var.container_image
# ログ設定(重要)
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.ecs_logs.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
"awslogs-datetime-format" = "%Y-%m-%d %H:%M:%S" # タイムスタンプフォーマット(オプション)
}
# FireLensを使用する場合の例
# logDriver = "awsfirelens"
# options = {
# "Name" = "cloudwatch"
# "region" = var.aws_region
# "log_group_name" = aws_cloudwatch_log_group.ecs_logs.name
# "log_stream_prefix" = "ecs/"
# }
}
# 環境変数
environment = var.environment_variables
# ポートマッピング
portMappings = [
{
containerPort = var.container_port
protocol = "tcp"
}
]
# ヘルスチェック
healthCheck = {
command = ["CMD-SHELL", "curl -f <http://localhost>:${var.container_port}/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
}
])
# 複数コンテナの場合の例
# container_definitions = jsonencode([
# {
# name = "app"
# ...
# logConfiguration = { ... }
# },
# {
# name = "sidecar"
# ...
# logConfiguration = { ... } # 各コンテナにログ設定が必要
# }
# ])
}
# VPCエンドポイント(プライベートサブネット利用時)
resource "aws_vpc_endpoint" "logs" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.logs"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoints.id]
tags = {
Name = "${var.environment}-logs-endpoint"
}
}
# variables.tf
variable "task_family" {
description = "ECSタスクファミリー名"
type = string
validation {
condition = can(regex("^[a-zA-Z][a-zA-Z0-9-]{0,254}$", var.task_family))
error_message = "Task family name must start with a letter and contain only letters, numbers, and hyphens."
}
}
variable "aws_region" {
description = "AWSリージョン"
type = string
default = "ap-northeast-1"
}
variable "environment" {
description = "環境名(dev/staging/prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "log_retention_days" {
description = "CloudWatch Logsログ保持期間"
type = number
default = 30
validation {
condition = contains([1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653], var.log_retention_days)
error_message = "Log retention days must be a valid CloudWatch Logs retention period."
}
}
variable "kms_key_id" {
description = "CloudWatch Logs暗号化用KMSキーID"
type = string
default = null # nullの場合はデフォルト暗号化
}
# 出力値
output "log_group_name" {
description = "CloudWatch Logsロググループ名"
value = aws_cloudwatch_log_group.ecs_logs.name
}
output "log_group_arn" {
description = "CloudWatch LogsロググループARN"
value = aws_cloudwatch_log_group.ecs_logs.arn
}
主要な修正ポイント
- ロググループの事前作成: タスク定義作成前にCloudWatch Logsロググループを作成
- 適切な権限設定: タスク実行ロールにCloudWatch Logsへの書き込み権限を付与
- 保持期間の設定: コンプライアンス要件に応じて適切な保持期間を設定(30日以上推奨)
- VPCエンドポイント: プライベートサブネット利用時はVPCエンドポイントを設定してコスト最適化
- マルチコンテナ対応: サイドカーコンテナも含め、すべてのコンテナにログ設定を適用
最後に
この記事では、ECSタスク定義でログ設定を有効化する手順について、リスクと対策を解説しました。
ログ設定を有効にすることで、コンテナの可視性が大幅に向上し、セキュリティインシデントの早期発見、迅速なトラブルシューティング、コンプライアンス要件の遵守が可能になります。特に本番環境では、すべてのECSタスク定義でログ設定を必須とし、定期的な監査プロセスを確立することを強く推奨します。ログデータは単なる記録ではなく、システムの健全性を維持し、ビジネス価値を向上させるための重要な資産です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。