Amazon Redshiftクラスターの保存時暗号化設定について

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

ポリシーの説明
Amazon Redshiftは、ペタバイト規模のデータウェアハウスサービスとして、企業の重要なビジネスデータを格納・分析するために使用されます。金融データ、医療記録、個人情報などの機密性の高いデータを扱うサービスにおいて、保存時暗号化は必須のセキュリティ対策です。
Redshiftの保存時暗号化は、AES-256暗号化アルゴリズムを使用して、クラスター内のすべてのデータを暗号化します。
- FIPS 140-2 Level 2認定のHSMによるキー保護
暗号化はAES-256ハードウェアアクセラレーションを使用して実行されるため、パフォーマンスへの影響は最小限(1-3%程度)です。
修復方法
コンソールでの修復手順
重要:既存の非暗号化Redshiftクラスターでは暗号化を直接有効化できません。暗号化された新しいクラスターへの移行が必須です。
方法1: スナップショットを使用した移行
- Redshiftコンソール(https://console.aws.amazon.com/redshiftv2/)にアクセスし、対象のクラスターを選択します。
- 移行前の準備:
- 現在のクラスター設定を記録(ノードタイプ、ノード数、パラメータグループ等)
- メンテナンスウィンドウを設定し、アプリケーションへ通知
- 移行中のダウンタイムを最小化するための計画を立案
- スナップショットの作成:
- 「アクション」→「スナップショットの作成」を選択
- スナップショット識別子を入力(例:
encryption-migration-$(date +%Y%m%d-%H%M%S)
) - 「スナップショットの作成」をクリック
- スナップショットの完了を待機:
- スナップショットのステータスが「available」になるまで待ちます
- 大規模クラスターの場合、数十分から数時間かかる可能性があります
- 暗号化されたクラスターの作成:
- スナップショット一覧から作成したスナップショットを選択
- 「アクション」→「スナップショットから復元」を選択
- 新しいクラスター識別子を入力(例:
original-cluster-name-encrypted
) - 「データベース構成」セクション:
- データベース名、ポート、パラメータグループを確認
- 「暗号化」セクションで以下を設定:
- 「データベースの暗号化」: チェックを入れる
- 「AWS KMSキー」: 以下のいずれかを選択
- AWS管理キー:
aws/redshift
(簡単だが管理権限が限定的) - カスタマー管理キー:事前に作成したKMSキー(推奨)
- AWS管理キー:
- 「クラスターの詳細」セクション:
- ノードタイプ、ノード数は元のクラスターと同じ設定を使用
- 「ネットワークとセキュリティ」セクション:
- VPC、サブネットグループ、セキュリティグループを確認
- 「パブリックアクセス可能」: 無効(セキュリティ上推奨)
- 「クラスターを復元」をクリック
- 移行の検証と切り替え:
- 新しいクラスターのエンドポイントを取得
- テスト環境で接続テストを実施
- データ整合性を確認(レコード数、サンプルデータの照合)
- アプリケーションの接続文字列を更新
- パフォーマンスを監視し、問題がないことを確認
- 古いクラスターの削除:
- 完全な移行確認後、24-48時間待機
- 最終スナップショットを作成(ロールバック用)
- 古い非暗号化クラスターを削除
Terraformでの修復手順
暗号化を有効にしたRedshiftクラスターを作成するTerraformコードと、ベストプラクティスを説明します。
# データソース
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
# KMSキーの作成(カスタムキーを使用する場合)
resource "aws_kms_key" "redshift" {
description = "KMS key for Redshift cluster encryption"
deletion_window_in_days = 30 # 本番環境では30日を推奨(最小7日、最大30日)
enable_key_rotation = true # 年次で自動ローテーション
multi_region = false # コスト削減のためシングルリージョン
tags = merge(
var.common_tags,
{
Name = "redshift-encryption-key"
Purpose = "Redshift cluster encryption"
Environment = var.environment
}
)
}
resource "aws_kms_alias" "redshift" {
name = "alias/redshift-encryption"
target_key_id = aws_kms_key.redshift.key_id
}
# KMSキーポリシー
resource "aws_kms_key_policy" "redshift" {
key_id = aws_kms_key.redshift.id
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 Redshift to use the key"
Effect = "Allow"
Principal = {
Service = "redshift.amazonaws.com"
}
Action = [
"kms:Decrypt",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:CreateGrant",
"kms:DescribeKey"
]
Resource = "*"
Condition = {
StringEquals = {
"kms:ViaService" = "redshift.${data.aws_region.current.name}.amazonaws.com"
}
}
}
]
})
}
# Redshiftサブネットグループ
resource "aws_redshift_subnet_group" "main" {
name = "redshift-subnet-group"
subnet_ids = var.subnet_ids
tags = {
Name = "Redshift subnet group"
}
}
# Redshiftパラメータグループ(セキュリティ強化)
resource "aws_redshift_parameter_group" "secure" {
name = "${var.cluster_identifier}-secure-params"
family = "redshift-1.0"
parameter {
name = "require_ssl"
value = "true" # SSL/TLS接続を強制
}
parameter {
name = "enable_user_activity_logging"
value = "true" # ユーザーアクティビティログを有効化
}
parameter {
name = "statement_timeout"
value = "43200000" # 12時間(ミリ秒)
}
parameter {
name = "max_concurrency_scaling_clusters"
value = var.enable_concurrency_scaling ? "1" : "0"
}
parameter {
name = "use_fips_ssl"
value = "true" # FIPS 140-2準拠のSSLを使用
}
tags = var.common_tags
}
# セキュリティグループ
resource "aws_security_group" "redshift" {
name = "redshift-cluster-sg"
description = "Security group for Redshift cluster"
vpc_id = var.vpc_id
ingress {
from_port = 5439
to_port = 5439
protocol = "tcp"
cidr_blocks = var.allowed_cidr_blocks
description = "Redshift port"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Redshift Security Group"
}
}
# 暗号化されたRedshiftクラスター
resource "aws_redshift_cluster" "encrypted" {
cluster_identifier = var.cluster_identifier
database_name = var.database_name
master_username = var.master_username
master_password = var.master_password # AWS Secrets Managerの使用を推奨
node_type = var.node_type
cluster_type = var.number_of_nodes > 1 ? "multi-node" : "single-node"
number_of_nodes = var.number_of_nodes
# 暗号化設定(必須)
encrypted = true # 必ず true に設定
kms_key_id = aws_kms_key.redshift.arn # カスタムKMSキーを使用
# ネットワーク設定
cluster_subnet_group_name = aws_redshift_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.redshift.id]
publicly_accessible = false
# パラメータグループ
cluster_parameter_group_name = aws_redshift_parameter_group.secure.name
# バックアップ設定
automated_snapshot_retention_period = var.snapshot_retention_days
preferred_maintenance_window = var.maintenance_window
snapshot_cluster_identifier = var.source_cluster_id # スナップショットからの復元時に使用
# 監査ログ(コンプライアンスのため必須)
logging {
enable = true
bucket_name = var.audit_log_bucket
s3_key_prefix = "redshift-logs/${var.environment}/${var.cluster_identifier}/"
}
# 拡張VPC ルーティング(セキュリティ強化)
enhanced_vpc_routing = true
# スナップショットコピー(別リージョンへの暗号化コピー)
dynamic "snapshot_copy" {
for_each = var.enable_snapshot_copy ? [1] : []
content {
destination_region = var.snapshot_copy_region
retention_period = var.snapshot_copy_retention
grant_name = aws_redshift_snapshot_copy_grant.main[0].snapshot_copy_grant_name
}
}
tags = {
Name = "Encrypted Redshift Cluster"
Environment = var.environment
Encrypted = "true"
}
# 削除保護
skip_final_snapshot = false
final_snapshot_identifier = "${var.cluster_identifier}-final-${formatdate("YYYY-MM-DD-hhmm", timestamp())}"
allow_version_upgrade = true
apply_immediately = false # メンテナンスウィンドウで適用
lifecycle {
prevent_destroy = true
}
}
# スナップショットコピーグラント(クロスリージョン暗号化コピー用)
resource "aws_redshift_snapshot_copy_grant" "main" {
count = var.enable_snapshot_copy ? 1 : 0
snapshot_copy_grant_name = "${var.cluster_identifier}-copy-grant"
kms_key_id = var.snapshot_copy_kms_key_id
}
# 既存クラスターからの移行用モジュール
module "migration" {
source = "./modules/redshift-migration"
count = var.migrate_from_existing ? 1 : 0
source_cluster_id = var.source_cluster_id
target_cluster_id = aws_redshift_cluster.encrypted.id
snapshot_retention = 7
migration_window_start = "02:00"
migration_window_end = "06:00"
tags = var.common_tags
}
# 移行ステータスのチェック
data "aws_redshift_cluster" "source" {
count = var.migrate_from_existing ? 1 : 0
cluster_identifier = var.source_cluster_id
}
resource "aws_cloudwatch_metric_alarm" "migration_status" {
count = var.migrate_from_existing ? 1 : 0
alarm_name = "${var.cluster_identifier}-migration-status"
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "HealthStatus"
namespace = "AWS/Redshift"
period = "300"
statistic = "Average"
threshold = "1"
alarm_description = "Alert when migration cluster is unhealthy"
dimensions = {
ClusterIdentifier = aws_redshift_cluster.encrypted.id
}
alarm_actions = [var.sns_topic_arn]
}
# CloudWatchアラーム(暗号化ステータス監視)
resource "aws_cloudwatch_metric_alarm" "encryption_status" {
alarm_name = "redshift-encryption-check"
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "ClusterEncrypted"
namespace = "AWS/Redshift"
period = "300"
statistic = "Average"
threshold = "1"
alarm_description = "Alert when Redshift cluster is not encrypted"
dimensions = {
ClusterIdentifier = aws_redshift_cluster.encrypted.id
}
alarm_actions = [var.sns_topic_arn]
}
# Secrets Manager でパスワードを管理(推奨)
resource "aws_secretsmanager_secret" "redshift_master" {
name = "${var.cluster_identifier}-master-password"
rotation_rules {
automatically_after_days = 30
}
}
resource "aws_secretsmanager_secret_version" "redshift_master" {
secret_id = aws_secretsmanager_secret.redshift_master.id
secret_string = jsonencode({
username = var.master_username
password = random_password.master.result
engine = "redshift"
host = aws_redshift_cluster.encrypted.endpoint
port = 5439
dbname = var.database_name
})
}
resource "random_password" "master" {
length = 32
special = true
}
# 変数定義
variable "cluster_identifier" {
description = "Redshift cluster identifier"
type = string
}
variable "database_name" {
description = "Database name"
type = string
default = "mydb"
}
variable "master_username" {
description = "Master username"
type = string
default = "admin"
sensitive = true
}
variable "master_password" {
description = "Master password"
type = string
sensitive = true
}
variable "node_type" {
description = "Node type"
type = string
default = "ra3.xlplus" # RA3インスタンスを推奨(ストレージとコンピュートの分離)
validation {
condition = contains(["dc2.large", "dc2.8xlarge", "ra3.xlplus", "ra3.4xlarge", "ra3.16xlarge"], var.node_type)
error_message = "Invalid node type. Choose from dc2 or ra3 instance families."
}
}
variable "number_of_nodes" {
description = "Number of nodes"
type = number
default = 2
}
variable "snapshot_retention_days" {
description = "Automated snapshot retention period in days"
type = number
default = 7
validation {
condition = var.snapshot_retention_days >= 1 && var.snapshot_retention_days <= 35
error_message = "Snapshot retention must be between 1 and 35 days."
}
}
variable "maintenance_window" {
description = "Preferred maintenance window"
type = string
default = "sun:02:00-sun:06:00"
}
variable "enable_concurrency_scaling" {
description = "Enable concurrency scaling"
type = bool
default = false
}
variable "source_cluster_id" {
description = "Source cluster ID for migration"
type = string
default = ""
}
variable "audit_log_bucket" {
description = "S3 bucket for audit logs"
type = string
}
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "subnet_ids" {
description = "List of subnet IDs"
type = list(string)
}
variable "allowed_cidr_blocks" {
description = "CIDR blocks allowed to connect to Redshift"
type = list(string)
default = []
}
variable "common_tags" {
description = "Common tags for all resources"
type = map(string)
default = {}
}
variable "sns_topic_arn" {
description = "SNS topic ARN for alerts"
type = string
}
variable "enable_snapshot_copy" {
description = "Enable cross-region snapshot copy"
type = bool
default = false
}
variable "snapshot_copy_region" {
description = "Destination region for snapshot copy"
type = string
default = ""
}
variable "snapshot_copy_retention" {
description = "Retention period for copied snapshots"
type = number
default = 7
}
variable "snapshot_copy_kms_key_id" {
description = "KMS key ID in destination region"
type = string
default = ""
}
variable "migrate_from_existing" {
description = "Flag to migrate from existing cluster"
type = bool
default = false
}
# 出力
output "cluster_endpoint" {
value = aws_redshift_cluster.encrypted.endpoint
description = "Redshift cluster endpoint"
sensitive = true
}
output "cluster_encrypted" {
value = aws_redshift_cluster.encrypted.encrypted
description = "Encryption status of the cluster"
}
output "kms_key_id" {
value = aws_kms_key.redshift.id
description = "KMS key ID used for encryption"
}
主要な修正ポイント
- encrypted = true:
- Redshiftクラスターリソースで暗号化を明示的に有効化
- この設定はクラスター作成時のみ指定可能(後から変更不可)
- kms_key_id:
- カスタムKMSキーを使用することでキー管理の柔軟性を確保
- キーポリシーでRedshiftサービスに必要な権限を付与
- CloudTrailでキー使用を監査可能
- enhanced_vpc_routing:
- すべてのCOPY/UNLOADトラフィックがVPCを経由
- S3へのアクセスはVPCエンドポイント経由
- ネットワークトラフィックの完全な制御が可能
- logging:
- 監査ログをS3バケットに保存
- ユーザーアクティビティ、接続ログ、ユーザーログを記録
- AWS Athenaでログ分析が可能
- lifecycle prevent_destroy:
- Terraformでの誤削除を防止
- 本番環境では必須の設定
- 削除前に明示的な確認が必要
最後に
この記事では、Amazon Redshiftクラスターの保存時暗号化の有効化について、詳細なリスク分析と実践的な対策を解説しました。Redshiftの暗号化は、データウェアハウスに格納された機密データを保護するための基本的かつ重要なセキュリティ対策です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。
参考資料
- AWS公式: Amazon Redshiftデータベース暗号化
- AWS公式: Amazon Redshiftセキュリティベストプラクティス
- AWS KMSを使用したRedshift暗号化
- Redshift監査ログの設定