★★ 中級

12. KMS(暗号化キー管理)

S3 / RDS / EBS / Secrets Manager すべての暗号化の中心にいるのが KMS(Key Management Service)。AWS 管理キーと顧客管理キー (CMK) の違い、いつ自前で持つかを整理します。

AWS 管理キー vs カスタマー管理キー

AWS 管理キー (aws/s3 等)カスタマー管理キー (CMK)
作成AWS が自動で作成自分で作成
料金無料$1/月/key + 利用 API 数
キーポリシー編集不可自由に編集
ローテーション自動(不可視)有効/無効を選択
削除不可(永続)7-30 日の猶予期間後に削除可
クロスアカウント共有不可可能

判断基準: 個人開発/検証なら AWS 管理キーで十分。本番運用・コンプライアンス要件・別アカウントとの共有が必要なら CMK。

CMK の作成

resource "aws_kms_key" "app" {
  description             = "Customer-managed key for myapp"
  deletion_window_in_days = 30
  enable_key_rotation     = true

  tags = {
    Name = "myapp-cmk"
  }
}

deletion_window_in_days は 7〜30。削除コマンド後、この期間内なら復活可。本番は 30 日推奨。

エイリアスで参照しやすく

resource "aws_kms_alias" "app" {
  name          = "alias/myapp"
  target_key_id = aws_kms_key.app.key_id
}

# 他リソースから「alias/myapp」で参照できる
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
  bucket = aws_s3_bucket.data.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_alias.app.arn
    }
  }
}

自動ローテーション

enable_key_rotation = true1 年に 1 回自動でキーマテリアルが切り替わる。古いマテリアルは AWS が保持するので、過去に暗号化されたデータの復号も継続可。ON にして害はほぼないので原則 ON。

キーポリシー

キーポリシーは「このキーを使える人」を IAM とは独立に定義。デフォルトポリシーは「ルートユーザーが全権限」のみ。実用にはアプリやサービスからの利用権限を追加する必要があります。

data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "kms_app" {
  # ルートユーザーフル権限(必須・誤って権限を失わないため)
  statement {
    sid     = "EnableIAMUserPermissions"
    effect  = "Allow"
    actions = ["kms:*"]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
  }

  # ECS タスクロールに使用許可
  statement {
    sid    = "AllowECSTaskRole"
    effect = "Allow"
    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:GenerateDataKey",
      "kms:DescribeKey",
    ]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = [aws_iam_role.ecs_task.arn]
    }
  }

  # CloudWatch Logs に使用許可(KMS 暗号化されたロググループ用)
  statement {
    sid    = "AllowCloudWatchLogs"
    effect = "Allow"
    actions = [
      "kms:Encrypt*",
      "kms:Decrypt*",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:Describe*",
    ]
    resources = ["*"]
    principals {
      type        = "Service"
      identifiers = ["logs.${data.aws_region.current.name}.amazonaws.com"]
    }
  }
}

resource "aws_kms_key" "app" {
  description             = "myapp CMK"
  deletion_window_in_days = 30
  enable_key_rotation     = true
  policy                  = data.aws_iam_policy_document.kms_app.json
}

他リソースから使う

# RDS
resource "aws_db_instance" "main" {
  storage_encrypted = true
  kms_key_id        = aws_kms_key.app.arn
  # ...
}

# EBS
resource "aws_ebs_volume" "data" {
  encrypted  = true
  kms_key_id = aws_kms_key.app.arn
  # ...
}

# CloudWatch Logs
resource "aws_cloudwatch_log_group" "app" {
  name              = "/myapp"
  retention_in_days = 30
  kms_key_id        = aws_kms_key.app.arn
}

# Secrets Manager(次章)
resource "aws_secretsmanager_secret" "db" {
  name       = "myapp/db"
  kms_key_id = aws_kms_key.app.arn
}

マルチリージョンキー

同じキーマテリアルを複数リージョンで使いたい時。DR / グローバル配信に。

resource "aws_kms_key" "primary" {
  description  = "Multi-region primary"
  multi_region = true
}

resource "aws_kms_replica_key" "secondary" {
  provider                  = aws.us_west_2
  description               = "Replica in us-west-2"
  primary_key_arn           = aws_kms_key.primary.arn
  deletion_window_in_days   = 30
}