パブリック公開されているEBSスナップショットの非公開対応の手順

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

ポリシーの説明
[EC2.1] Amazon EBS スナップショットはパブリックに復元できないようにすることをお勧めします
Amazon EC2 の Security Hub コントロール – AWS Security Hub
このコントロールは、Amazon Elastic Block Store スナップショットがパブリックではないかどうかをチェックします。Amazon EBS スナップショットを誰でも復元できる場合、コントロールは失敗します。
このポリシーは、Amazon EBS スナップショットがインターネットから誰でも復元できないようにすることを求めています。EBS スナップショットにはボリューム全体のデータコピーが含まれる可能性があり、機密データが含まれている場合、パブリックアクセスを許可するとデータ漏洩や不正アクセスのリスクが極めて高まります。
修復方法
AWSコンソールでの修正手順
- AWS マネジメントコンソールにログインし、EC2 コンソールを開きます。
- ナビゲーションペインで「Elastic Block Store」>「スナップショット」を選択します。
- パブリックに共有されている可能性のあるスナップショットを特定します。(必要であれば、各スナップショットを選択し、「アクション」>「アクセス権限を変更」で現在の共有設定を確認します)。
- パブリックになっている該当のスナップショットを選択し、「アクション」>「アクセス権限を変更」をクリックします。
- 「共有アカウント」セクションで、「パブリック」にチェックが入っている場合は、そのチェックを外します。「プライベート」が選択されている状態にします。(特定のアカウントとのみ共有したい場合は、「プライベート」を選択した上でアカウントIDを追加します)。
- 「変更を保存」をクリックします。

Terraformでの修復手順
Terraformには、既存のEBSスナップショットの共有属性(特にパブリック共有 all
の削除)を直接管理・修正するための標準的なリソースがありません。したがって、既にパブリックになっている既存のスナップショットを修正する場合は、上記のAWSコンソール手順、またはAWS CLI (aws ec2 modify-snapshot-attribute --attribute createVolumePermission --operation remove ...
) を使用するのが基本的な方法となります。(AWS CLIをTerraformのlocal-exec
で実行する回避策も考えられますが、状態管理が複雑になるため注意が必要です)。
以下に示すTerraformコードは、パブリックアクセスを防ぐための予防的措置や関連するベストプラクティスを実装する例です。具体的には、「新規ボリュームとスナップショットを暗号化付きで作成する」、「作成したスナップショットを特定アカウントとのみプライベート共有する」、「スナップショットの共有設定変更を監視する」方法を示しています。
# KMS key for EBS encryption
resource "aws_kms_key" "ebs" {
description = "KMS key for EBS volume encryption"
deletion_window_in_days = 7
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow EC2 to use the key"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = [
"kms:Decrypt",
"kms:GenerateDataKey*",
"kms:CreateGrant"
]
Resource = "*"
}
]
})
tags = var.tags
}
# KMS alias
resource "aws_kms_alias" "ebs" {
name = "alias/ebs-${var.environment}"
target_key_id = aws_kms_key.ebs.key_id
}
# EBS volume
resource "aws_ebs_volume" "example" {
availability_zone = var.availability_zone
size = var.volume_size
# ★重要: 暗号化の有効化
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
tags = merge(var.tags, {
Name = "ebs-volume-${var.environment}"
})
}
# ★重要: スナップショット作成時の設定
resource "aws_ebs_snapshot" "example" {
volume_id = aws_ebs_volume.example.id
# 説明に機密情報を含めない
description = "Snapshot for ${var.environment} - ${formatdate("YYYY-MM-DD", timestamp())}"
# ★重要: スナップショットの共有設定
# デフォルトではプライベート。特定のアカウントと共有する場合は別途設定が必要
tags = merge(var.tags, {
Name = "ebs-snapshot-${var.environment}"
Encrypted = "true"
Private = "true"
})
}
# ★重要: 特定のアカウントとのみ共有する場合の設定
resource "aws_snapshot_create_volume_permission" "example" {
count = length(var.shared_account_ids)
snapshot_id = aws_ebs_snapshot.example.id
account_id = var.shared_account_ids[count.index]
}
# CloudWatch Event Rule for monitoring snapshot sharing
resource "aws_cloudwatch_event_rule" "monitor_snapshot_sharing" {
name = "monitor-snapshot-sharing-${var.environment}"
description = "Monitor EBS snapshot sharing modifications"
event_pattern = jsonencode({
source = ["aws.ec2"]
detail-type = ["AWS API Call via CloudTrail"]
detail = {
eventSource = ["ec2.amazonaws.com"]
eventName = [
"ModifySnapshotAttribute",
"CreateSnapshot"
]
}
})
tags = var.tags
}
# CloudWatch Event Target
resource "aws_cloudwatch_event_target" "sns" {
rule = aws_cloudwatch_event_rule.monitor_snapshot_sharing.name
target_id = "SendToSNS"
arn = var.sns_topic_arn
input_transformer {
input_paths = {
snapshotId = "$.detail.requestParameters.snapshotId"
eventName = "$.detail.eventName"
userArn = "$.detail.userIdentity.arn"
}
input_template = "\\\\"EBS Snapshot sharing modification detected. Snapshot ID: <snapshotId>, Action: <eventName>, User: <userArn>\\\\""
}
}
# SNS Topic Policy
resource "aws_sns_topic_policy" "default" {
arn = var.sns_topic_arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCloudWatchEvents"
Effect = "Allow"
Principal = {
Service = "events.amazonaws.com"
}
Action = "SNS:Publish"
Resource = var.sns_topic_arn
}
]
})
}
# Variables
variable "environment" {
description = "Environment name"
type = string
}
variable "availability_zone" {
description = "Availability zone for EBS volume"
type = string
}
variable "volume_size" {
description = "Size of EBS volume in GB"
type = number
}
variable "shared_account_ids" {
description = "List of AWS account IDs to share the snapshot with"
type = list(string)
default = []
validation {
condition = can([for id in var.shared_account_ids : regex("^\\\\\\\\d{12}$", id)])
error_message = "Account IDs must be 12 digits."
}
}
variable "sns_topic_arn" {
description = "ARN of SNS topic for notifications"
type = string
}
variable "tags" {
description = "Tags for resources"
type = map(string)
default = {}
}
# Data sources
data "aws_caller_identity" "current" {}
Terraformコードのポイント:
- 暗号化の有効化:
aws_ebs_volume
でencrypted = true
とkms_key_id
を指定し、元となるボリュームと、そこから作成されるスナップショット (aws_ebs_snapshot
) が暗号化されるようにします。これはデータ保護の基本です。 - デフォルトでのプライベート設定:
aws_ebs_snapshot
リソースで作成されたスナップショットは、デフォルトでプライベートです。パブリックにするには明示的に共有設定を追加する必要があります。 - プライベート共有の設定:
aws_snapshot_create_volume_permission
リソースは、スナップショットを特定のAWSアカウントとのみ共有するために使用します。var.shared_account_ids
で指定したアカウントにのみリストア権限が付与され、パブリック共有を防ぐセキュアな共有方法です。 - 共有変更の監視:
aws_cloudwatch_event_rule
と関連リソースを使用して、スナップショットの共有設定が変更されたイベント (ModifySnapshotAttribute
) を検知し、SNSトピックに通知を送ることができます。これにより、意図しない共有変更(特にパブリック化)を早期に発見できます。
最後に
今回は、EBS スナップショットのパブリックアクセス設定について解説しました。パブリックアクセスを許可すると、データ漏洩や不正アクセスのリスクが高まります。必ずパブリックアクセスを無効にし、厳格なアクセス制御を実施してください。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。