パブリックアクセスが可能なRDSインスタンスの非公開化設定手順

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

ポリシーの説明
[RDS.2] RDS DB インスタンスは、PubliclyAccessible 設定によって判断される、パブリックアクセスを禁止する必要があります
Amazon RDS の Security Hub コントロール – AWS Security Hub
このコントロールは、インスタンス設定項目内の
PubliclyAccessible
フィールドを評価して、Amazon RDS インスタンスがパブリックにアクセスできるかどうかをチェックします。
このポリシーは、RDSインスタンスがインターネットから誰でもアクセスできないようにすることを求めています。パブリックアクセスを許可すると、データベースへの不正アクセスやデータ漏洩のリスクが大幅に高まります。RDSインスタンスには機密データが保存されることが多いため、厳格なアクセス制御(VPC内部からのアクセスに限定するなど)が必要です。
修復方法
- AWSマネジメントコンソールにログインし、RDSコンソールを開きます。
- 修正対象のインスタンスを選択し、「変更」をクリックします。
- 「接続」セクションまでスクロールし、「パブリックアクセス」の設定で「なし」(または「パブリックアクセス不可」)を選択します。
- ページ下部の「続行」をクリックし、変更内容の概要を確認します。

Terraformでの修復手順
以下は、パブリックアクセスを無効にし、その他のセキュリティ設定(暗号化、プライベートサブネット配置など)を施した新しいRDSインスタンスをTerraformで作成するコード例です。
# KMS key for RDS encryption
resource "aws_kms_key" "rds" {
description = "KMS key for RDS 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 = "*"
}
]
})
tags = var.tags
}
# DB subnet group in private subnets
resource "aws_db_subnet_group" "private" {
name = "private-subnet-group-${var.environment}"
description = "Private subnet group for RDS instances"
subnet_ids = var.private_subnet_ids
tags = merge(var.tags, {
Name = "private-subnet-group-${var.environment}"
})
}
# Security group for RDS
resource "aws_security_group" "rds" {
name = "rds-private-sg-${var.environment}"
description = "Security group for RDS instances"
vpc_id = var.vpc_id
# Allow inbound access only from application security groups
ingress {
description = "Access from application layer"
from_port = var.db_port
to_port = var.db_port
protocol = "tcp"
security_groups = var.app_security_group_ids
}
# No outbound access needed for RDS
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow outbound access for updates"
}
tags = merge(var.tags, {
Name = "rds-private-sg-${var.environment}"
})
}
# Parameter group
resource "aws_db_parameter_group" "main" {
name = "rds-params-${var.environment}"
family = var.db_parameter_family
description = "Custom parameter group for RDS instances"
# Security related parameters
parameter {
name = "ssl"
value = "1"
}
parameter {
name = "require_ssl"
value = "1"
}
tags = var.tags
}
# Option group
resource "aws_db_option_group" "main" {
name = "rds-options-${var.environment}"
option_group_description = "Custom option group for RDS instances"
engine_name = var.engine
major_engine_version = var.engine_version
# SSL/TLS options if needed
dynamic "option" {
for_each = var.engine == "mysql" ? [1] : []
content {
option_name = "MARIADB_AUDIT_PLUGIN"
}
}
tags = var.tags
}
# RDS instance
resource "aws_db_instance" "main" {
identifier = "rds-${var.environment}"
# Engine settings
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
# Storage settings
allocated_storage = var.allocated_storage
storage_type = "gp3"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# Database settings
db_name = var.db_name
username = var.db_username
password = var.db_password
port = var.db_port
# Network settings
db_subnet_group_name = aws_db_subnet_group.private.name
vpc_security_group_ids = [aws_security_group.rds.id]
publicly_accessible = false # Ensure instance is not publicly accessible
# Parameter and option groups
parameter_group_name = aws_db_parameter_group.main.name
option_group_name = aws_db_option_group.main.name
# Backup and maintenance
backup_retention_period = var.backup_retention_period
backup_window = "03:00-04:00"
maintenance_window = "Mon:04:00-Mon:05:00"
# Enhanced monitoring
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
# Performance insights
performance_insights_enabled = true
performance_insights_retention_period = 7
performance_insights_kms_key_id = aws_kms_key.rds.arn
# Auto minor version upgrade
auto_minor_version_upgrade = true
# Deletion protection
deletion_protection = true
skip_final_snapshot = false
final_snapshot_identifier = "${var.environment}-final-snapshot"
tags = var.tags
}
# RDS monitoring role
resource "aws_iam_role" "rds_monitoring" {
name = "rds-monitoring-role-${var.environment}"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "monitoring.rds.amazonaws.com"
}
}
]
})
managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"]
}
# CloudWatch alarms
resource "aws_cloudwatch_metric_alarm" "rds_cpu" {
alarm_name = "rds-cpu-utilization-${var.environment}"
alarm_description = "RDS CPU utilization is too high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/RDS"
period = "300"
statistic = "Average"
threshold = "80"
alarm_actions = var.alarm_sns_topic_arns
dimensions = {
DBInstanceIdentifier = aws_db_instance.main.id
}
tags = var.tags
}
# Variables
variable "environment" {
description = "Environment name"
type = string
}
variable "vpc_id" {
description = "VPC ID where RDS will be deployed"
type = string
}
variable "private_subnet_ids" {
description = "List of private subnet IDs for RDS"
type = list(string)
}
variable "app_security_group_ids" {
description = "List of security group IDs that can access RDS"
type = list(string)
}
variable "engine" {
description = "RDS engine type"
type = string
default = "mysql"
}
variable "engine_version" {
description = "RDS engine version"
type = string
default = "8.0"
}
variable "db_parameter_family" {
description = "RDS parameter group family"
type = string
default = "mysql8.0"
}
variable "instance_class" {
description = "RDS instance class"
type = string
default = "db.t3.medium"
}
variable "allocated_storage" {
description = "Allocated storage in GB"
type = number
default = 20
}
variable "db_name" {
description = "Database name"
type = string
}
variable "db_username" {
description = "Database username"
type = string
sensitive = true
}
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
variable "db_port" {
description = "Database port"
type = number
default = 3306
}
variable "backup_retention_period" {
description = "Backup retention period in days"
type = number
default = 7
}
variable "tags" {
description = "Tags for all resources"
type = map(string)
default = {}
}
variable "alarm_sns_topic_arns" {
description = "List of SNS topic ARNs for CloudWatch alarms"
type = list(string)
default = []
}
# Data sources
data "aws_caller_identity" "current" {}
Terraformでの修復手順 以下は、パブリックアクセスを無効にし、その他のセキュリティ設定(暗号化、プライベートサブネット配置など)を施した新しいRDSインスタンスをTerraformで作成するコード例です。
(Terraformコードブロックは変更なし)
Terraformコードのポイント:
- **パブリックアクセス無効化 (最重要):**Terraform
aws_db_instance
リソース内の以下の設定が、このセキュリティポリシーに準拠するための中心的な箇所です。
publicly_accessible = false # Ensure instance is not publicly accessible
publicly_accessible
をfalse
に設定することで、RDSインスタンスにパブリックIPアドレスが割り当てられなくなり、インターネットからの直接アクセスが不可能になります。
- ネットワーク分離:
db_subnet_group_name
でプライベートサブネットのみを含むDBサブネットグループを指定します。vpc_security_group_ids
で、アプリケーションサーバーなど、許可されたリソースからのアクセスのみを許可するセキュリティグループ (aws_security_group.rds
) を指定します。
- その他のセキュリティ設定:
storage_encrypted = true
とkms_key_id
で保管時の暗号化を有効化しています。- パラメータグループ (
aws_db_parameter_group
) でSSL接続を強制する設定例を含んでいます。 - IAMロール (
aws_iam_role.rds_monitoring
) を使ってEnhanced Monitoringを有効化しています。 deletion_protection = true
で誤削除を防止しています。
注意:
- このTerraformコードは新規にセキュアなRDSインスタンスを作成する例です。既存のTerraform管理下のインスタンスを変更する場合は、該当の
aws_db_instance
リソース定義内のpublicly_accessible
をfalse
に変更し、terraform apply
を実行します。(ネットワーク設定の変更には注意が必要です) - Terraform管理外の既存インスタンスを修正する場合は、コンソールまたはAWS CLIでの修正が基本となります。
最後に
今回は、RDSインスタンスのパブリックアクセス設定について解説しました。パブリックアクセスを許可すると、不正アクセスやデータ漏洩のリスクが高まります。必ずパブリックアクセスを無効にし、VPC内のプライベートサブネットに配置して保護してください。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。