OpenSearch Serviceで保管時の暗号化の設定手順

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、Security Hubで検出された「[OpenSearch.1] OpenSearch ドメインは保管中の暗号化を有効にする必要があります」というセキュリティ課題の修正方法について解説します。

ポリシーの説明
OpenSearch Service の Security Hub コントロール – AWS Security Hub
このコントロールは、OpenSearch ドメインで保管中の暗号化設定が有効になっているかどうかをチェックします。保管中の暗号化が有効になっていない場合、チェックは失敗します。
Amazon OpenSearch Service(旧 Amazon Elasticsearch Service)は、ログ分析、リアルタイムアプリケーション監視、クリックストリーム分析などのユースケースに対応する、分散型の検索および分析サービスです。保管時の暗号化を有効にしない場合、OpenSearchドメインのノードに保存された機密データが不正にアクセスされるリスクがあります。データの機密性を保護するため、保管時の暗号化を有効にすることが推奨されます。
修復方法
AWSコンソールでの修正手順
- Amazon OpenSearch Service > Domainsに移動する
- ドメインを選択し「セキュリティ設定」のタブを開く
- 「編集」をクリックし、暗号化のセクションに移動する

- 「保管時のデータの暗号化の有効化」にチェックを入れ、変更の保存をクリックし反映する。
Terraformでの修復手順
OpenSearchドメインの保管時暗号化を有効にするためのTerraformコードと、重要な修正ポイントを説明します。
# KMS key for OpenSearch encryption
resource "aws_kms_key" "opensearch" {
description = "KMS key for OpenSearch 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
}
# OpenSearch domain
resource "aws_opensearch_domain" "main" {
domain_name = "opensearch-${var.environment}"
engine_version = "OpenSearch_${var.engine_version}"
# ★重要: 暗号化設定
encrypt_at_rest {
enabled = true # 保管時の暗号化を有効化
kms_key_id = aws_kms_key.opensearch.key_id
}
# ★重要: ノード間暗号化
node_to_node_encryption {
enabled = true # ノード間の暗号化を有効化
}
# クラスター設定
cluster_config {
instance_type = var.instance_type
instance_count = var.instance_count
dedicated_master_enabled = var.dedicated_master_enabled
zone_awareness_enabled = var.multi_az_enabled
# マルチAZ設定
dynamic "zone_awareness_config" {
for_each = var.multi_az_enabled ? [1] : []
content {
availability_zone_count = var.availability_zone_count
}
}
}
# ネットワーク設定
vpc_options {
subnet_ids = var.subnet_ids
security_group_ids = [aws_security_group.opensearch.id]
}
# Advanced security options
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = var.master_user_name
master_user_password = var.master_user_password
}
}
# HTTPS enforcement
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
# Logging options
log_publishing_options {
cloudwatch_log_group_arn = aws_cloudwatch_log_group.opensearch.arn
log_type = "INDEX_SLOW_LOGS"
enabled = true
}
access_policies = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = var.allowed_iam_arns
}
Action = "es:*"
Resource = "arn:aws:es:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:domain/${var.domain_name}/*"
}
]
})
tags = var.tags
}
# Security group for OpenSearch
resource "aws_security_group" "opensearch" {
name = "opensearch-${var.environment}-sg"
description = "Security group for OpenSearch domain"
vpc_id = var.vpc_id
ingress {
description = "HTTPS from allowed security groups"
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = var.allowed_security_group_ids
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.tags, {
Name = "opensearch-${var.environment}-sg"
})
}
# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "opensearch" {
name = "/aws/opensearch/${var.environment}"
retention_in_days = var.log_retention_days
tags = var.tags
}
# Variables
variable "environment" {
description = "Environment name"
type = string
}
variable "engine_version" {
description = "OpenSearch engine version"
type = string
default = "2.5"
}
variable "instance_type" {
description = "Instance type for OpenSearch nodes"
type = string
default = "t3.small.search"
}
variable "instance_count" {
description = "Number of instances in the cluster"
type = number
default = 2
}
variable "dedicated_master_enabled" {
description = "Enable dedicated master nodes"
type = bool
default = false
}
variable "multi_az_enabled" {
description = "Enable multiple availability zones"
type = bool
default = true
}
variable "availability_zone_count" {
description = "Number of availability zones"
type = number
default = 2
}
variable "vpc_id" {
description = "VPC ID where OpenSearch will be deployed"
type = string
}
variable "subnet_ids" {
description = "List of subnet IDs for OpenSearch"
type = list(string)
}
variable "allowed_security_group_ids" {
description = "List of security group IDs allowed to access OpenSearch"
type = list(string)
}
variable "master_user_name" {
description = "Master user name for OpenSearch"
type = string
}
variable "master_user_password" {
description = "Master user password for OpenSearch"
type = string
sensitive = true
}
variable "log_retention_days" {
description = "Number of days to retain CloudWatch logs"
type = number
default = 30
}
variable "tags" {
description = "Tags for resources"
type = map(string)
default = {}
}
# Data sources
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
主要な修正ポイントは以下の通りです:
- 保管時の暗号化設定(最重要):
encrypt_at_rest {
enabled = true # この設定が最も重要
kms_key_id = aws_kms_key.opensearch.key_id
}
ノード間の暗号化:
node_to_node_encryption {
enabled = true
}
追加のセキュリティ設定:
# HTTPS強制
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
# 高度なセキュリティオプション
advanced_security_options {
enabled = true
}
最後に
今回は、OpenSearchドメインの保管時の暗号化を有効にする方法についてご紹介しました。保管時の暗号化は、保存された検索および分析データを保護するための重要なセキュリティ対策です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。