Amazon DocumentDB クラスターの削除保護の有効化について

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

ポリシーの説明
すべてのAmazon DocumentDBクラスターで削除保護を有効にすることを強く推奨します。これにより、偶発的なデータベース削除や不正なユーザーによる削除を防止できます。特に本番環境やミッションクリティカルなデータを扱うクラスターでは必須の設定です。削除保護の設定をAWS Configやカスタムスクリプトで定期的に監査し、重要なクラスターが確実に保護されていることを確認してください。また、クラスター削除のプロセスを文書化し、複数人の承認を必要とする手順を確立してください。
修復方法
AWSマネジメントコンソールでの修復手順
以下の手順で、DocumentDBクラスターの削除保護を有効にできます:
- AWSマネジメントコンソールにログインします。
- サービス一覧から「Amazon DocumentDB」を選択します。
- 左側のナビゲーションペインから「Clusters」を選択します。
- 削除保護を有効にしたいクラスターの名前をクリックします。
- 「Configuration」タブで現在の削除保護の状態を確認します。
- 「Actions」ドロップダウンメニューから「Modify」を選択します。
- 「Additional configuration」セクションまでスクロールします。
- 「Deletion protection」のチェックボックスをオンにします。
- ページ下部の「Continue」ボタンをクリックします。
- 変更内容を確認し、「Apply immediately」を選択して即座に変更を適用します。
- 「Modify cluster」ボタンをクリックして変更を確定します。
注意: 削除保護の有効化は即座に反映され、クラスターの再起動は不要です。
Terraformでの修復手順
DocumentDBクラスターの削除保護を有効にする包括的なTerraformコードです:
# DocumentDBクラスターパラメータグループ
resource "aws_docdb_cluster_parameter_group" "main" {
family = var.docdb_family # DocumentDBバージョンに応じて動的に設定
name = "${var.cluster_identifier}-params"
description = "DocumentDB cluster parameter group with comprehensive security settings"
parameter {
name = "tls"
value = "enabled" # TLS暗号化を必須に
}
parameter {
name = "audit_logs"
value = "enabled" # 監査ログを有効化
}
parameter {
name = "profiler"
value = "enabled" # パフォーマンスプロファイラーを有効化
}
parameter {
name = "profiler_threshold_ms"
value = var.profiler_threshold_ms # スロークエリのログ閾値
}
# TTL監視の有効化(DocumentDB 4.0以降)
dynamic "parameter" {
for_each = var.docdb_family == "docdb4.0" || var.docdb_family == "docdb5.0" ? [1] : []
content {
name = "ttl_monitor"
value = "enabled"
}
}
tags = var.tags
lifecycle {
create_before_destroy = true
}
}
# サブネットグループ
resource "aws_docdb_subnet_group" "main" {
name = "${var.cluster_identifier}-subnet-group"
subnet_ids = var.subnet_ids
tags = merge(
var.tags,
{
Name = "${var.cluster_identifier}-subnet-group"
}
)
}
# セキュリティグループ
resource "aws_security_group" "docdb" {
name_prefix = "${var.cluster_identifier}-docdb-"
description = "Security group for DocumentDB cluster"
vpc_id = var.vpc_id
ingress {
from_port = 27017
to_port = 27017
protocol = "tcp"
security_groups = var.allowed_security_groups
description = "Allow DocumentDB access from application security groups"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = merge(
var.tags,
{
Name = "${var.cluster_identifier}-docdb-sg"
}
)
lifecycle {
create_before_destroy = true
}
}
# DocumentDBクラスター
resource "aws_docdb_cluster" "main" {
cluster_identifier = var.cluster_identifier
engine = "docdb"
engine_version = var.engine_version
master_username = var.master_username
master_password = random_password.docdb_password.result
db_cluster_parameter_group_name = aws_docdb_cluster_parameter_group.main.name
# 削除保護を必ず有効化(最重要)
deletion_protection = var.environment == "production" ? true : var.deletion_protection
# バックアップ設定
backup_retention_period = var.backup_retention_period # 推奨: 7日以上
preferred_backup_window = var.preferred_backup_window
preferred_maintenance_window = var.preferred_maintenance_window
# スナップショット設定
skip_final_snapshot = false # 必ず最終スナップショットを作成
final_snapshot_identifier = "${var.cluster_identifier}-final-${formatdate("YYYYMMDD-hhmmss", timestamp())}"
copy_tags_to_snapshot = true # タグをスナップショットにコピー
# 暗号化設定
storage_encrypted = true
kms_key_id = var.kms_key_id != "" ? var.kms_key_id : aws_kms_key.docdb[0].arn
# 監査ログの有効化
enabled_cloudwatch_logs_exports = ["audit", "profiler"]
# ネットワーク設定
db_subnet_group_name = aws_docdb_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.docdb.id]
tags = merge(
var.tags,
{
Name = var.cluster_identifier
DeletionProtection = "Enabled"
Environment = var.environment
Backup = "Enabled"
}
)
lifecycle {
prevent_destroy = true # Terraformレベルでも削除を防止
}
}
# KMSキー(オプション)
resource "aws_kms_key" "docdb" {
count = var.kms_key_id == "" ? 1 : 0
description = "KMS key for DocumentDB cluster ${var.cluster_identifier}"
deletion_window_in_days = 30
enable_key_rotation = true
tags = merge(
var.tags,
{
Name = "${var.cluster_identifier}-docdb-kms"
}
)
}
resource "aws_kms_alias" "docdb" {
count = var.kms_key_id == "" ? 1 : 0
name = "alias/${var.cluster_identifier}-docdb"
target_key_id = aws_kms_key.docdb[0].key_id
}
# パスワードの安全な生成
resource "random_password" "docdb_password" {
length = 32
special = true
# DocumentDBで使用できない特殊文字を除外
override_special = "!#$%&*()-_=+[]{}<>:?"
}
# AWS Secrets Managerでパスワードを管理
resource "aws_secretsmanager_secret" "docdb_credentials" {
name_prefix = "${var.cluster_identifier}-docdb-"
description = "DocumentDB cluster master credentials"
recovery_window_in_days = var.secret_recovery_window_days
tags = var.tags
}
resource "aws_secretsmanager_secret_version" "docdb_credentials" {
secret_id = aws_secretsmanager_secret.docdb_credentials.id
secret_string = jsonencode({
username = var.master_username
password = random_password.docdb_password.result
engine = "docdb"
host = aws_docdb_cluster.main.endpoint
port = aws_docdb_cluster.main.port
dbClusterIdentifier = aws_docdb_cluster.main.cluster_identifier
})
}
# DocumentDBインスタンス
resource "aws_docdb_cluster_instance" "main" {
count = var.instance_count
identifier = "${var.cluster_identifier}-${count.index + 1}"
cluster_identifier = aws_docdb_cluster.main.id
instance_class = var.instance_class
performance_insights_enabled = var.performance_insights_enabled
performance_insights_kms_key_id = var.performance_insights_enabled ? var.kms_key_id : null
tags = merge(
var.tags,
{
Name = "${var.cluster_identifier}-${count.index + 1}"
}
)
}
# CloudWatchアラーム(削除保護監視)
# 注意: DocumentDBには標準のDeletionProtectionメトリクスがないため、
# AWS ConfigまたはLambda関数で定期的にチェックする必要があります
resource "aws_lambda_function" "check_deletion_protection" {
filename = "check_deletion_protection.zip"
function_name = "${var.cluster_identifier}-check-deletion-protection"
role = aws_iam_role.lambda_execution.arn
handler = "index.handler"
runtime = "python3.9"
timeout = 60
environment {
variables = {
CLUSTER_IDENTIFIER = var.cluster_identifier
SNS_TOPIC_ARN = var.sns_topic_arn
}
}
tags = var.tags
}
# EventBridgeルールで定期実行(1時間ごと)
resource "aws_cloudwatch_event_rule" "check_deletion_protection" {
name = "${var.cluster_identifier}-check-deletion-protection"
description = "Check DocumentDB deletion protection status"
schedule_expression = "rate(1 hour)"
tags = var.tags
}
resource "aws_cloudwatch_event_target" "lambda" {
rule = aws_cloudwatch_event_rule.check_deletion_protection.name
target_id = "CheckDeletionProtectionLambda"
arn = aws_lambda_function.check_deletion_protection.arn
}
resource "aws_lambda_permission" "allow_eventbridge" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.check_deletion_protection.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.check_deletion_protection.arn
}
# 変数定義
variable "cluster_identifier" {
type = string
description = "The cluster identifier"
validation {
condition = can(regex("^[a-z][a-z0-9-]*$", var.cluster_identifier)) && length(var.cluster_identifier) <= 63
error_message = "Cluster identifier must start with a letter, contain only lowercase letters, numbers, and hyphens, and be 63 characters or less."
}
}
variable "docdb_family" {
type = string
description = "DocumentDB parameter group family"
default = "docdb5.0" # 最新バージョンをデフォルトに
validation {
condition = contains(["docdb3.6", "docdb4.0", "docdb5.0"], var.docdb_family)
error_message = "DocumentDB family must be one of: docdb3.6, docdb4.0, docdb5.0."
}
}
variable "profiler_threshold_ms" {
type = number
description = "Profiler threshold in milliseconds for slow query logging"
default = 100
validation {
condition = var.profiler_threshold_ms >= 0 && var.profiler_threshold_ms <= 2147483647
error_message = "Profiler threshold must be between 0 and 2147483647 milliseconds."
}
}
variable "deletion_protection" {
type = bool
description = "Enable deletion protection (automatically true for production)"
default = true
}
variable "environment" {
type = string
description = "Environment (e.g., production, staging, development)"
validation {
condition = contains(["production", "staging", "development"], var.environment)
error_message = "Environment must be production, staging, or development."
}
}
variable "backup_retention_period" {
type = number
description = "The backup retention period in days"
default = 7
validation {
condition = var.backup_retention_period >= 1 && var.backup_retention_period <= 35
error_message = "Backup retention period must be between 1 and 35 days."
}
}
variable "instance_count" {
type = number
description = "Number of instances in the cluster"
default = 2
validation {
condition = var.instance_count >= 1
error_message = "Instance count must be at least 1."
}
}
variable "tags" {
type = map(string)
description = "A map of tags to assign to the resource"
default = {}
}
variable "sns_topic_arn" {
type = string
description = "SNS topic ARN for alarm notifications"
}
variable "engine_version" {
type = string
description = "DocumentDB engine version"
default = "5.0.0"
}
variable "master_username" {
type = string
description = "Master username for DocumentDB cluster"
default = "docdbadmin"
sensitive = true
}
variable "kms_key_id" {
type = string
description = "KMS key ID for encryption (if empty, a new key will be created)"
default = ""
}
variable "subnet_ids" {
type = list(string)
description = "List of subnet IDs for the DB subnet group"
}
variable "vpc_id" {
type = string
description = "VPC ID for the security group"
}
variable "allowed_security_groups" {
type = list(string)
description = "List of security group IDs allowed to access DocumentDB"
default = []
}
variable "instance_class" {
type = string
description = "Instance class for DocumentDB instances"
default = "db.r5.large"
}
variable "preferred_backup_window" {
type = string
description = "Preferred backup window"
default = "03:00-04:00"
}
variable "preferred_maintenance_window" {
type = string
description = "Preferred maintenance window"
default = "sun:04:00-sun:05:00"
}
variable "secret_recovery_window_days" {
type = number
description = "Recovery window for secret deletion"
default = 7
}
variable "performance_insights_enabled" {
type = bool
description = "Enable Performance Insights"
default = true
}
# 出力
output "cluster_endpoint" {
value = aws_docdb_cluster.main.endpoint
description = "The cluster endpoint"
}
output "cluster_reader_endpoint" {
value = aws_docdb_cluster.main.reader_endpoint
description = "The cluster reader endpoint"
}
output "deletion_protection_status" {
value = aws_docdb_cluster.main.deletion_protection
description = "The deletion protection status"
}
output "secret_arn" {
value = aws_secretsmanager_secret.docdb_credentials.arn
description = "The ARN of the secret containing the database credentials"
sensitive = true
}
重要なポイント
- 多層防御:
deletion_protection = true
とlifecycle { prevent_destroy = true }
の両方を設定 - バックアップ戦略: 自動バックアップと最終スナップショットの両方を設定
- セキュリティ強化: KMS暗号化、TLS必須、監査ログ有効化
- 監視とアラート: 削除保護が無効化された場合のCloudWatchアラーム
- 適切なバリデーション: 入力変数の検証で誤設定を防止
最後に
この記事では、Amazon DocumentDB クラスターの削除保護が有効化されていない場合のリスクと、具体的な修復方法について解説しました。削除保護は、重要なデータベースを偶発的または悪意のある削除から守る重要なセキュリティ機能です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理することが可能です。 運用が非常に楽にできる製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。