OpenSearch ドメインがVPC外に存在する場合にVPC内に再配置できるのか

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

ポリシーの説明
[Opensearch.2] OpenSearch ドメインがパブリックにアクセスできないようにする必要があります
OpenSearch Service の Security Hub コントロール – AWS Security Hub
このコントロールは、OpenSearch ドメインが VPC 内にあるかどうかをチェックします。このコントロールは、パブリックアクセスの可能性を判断するための VPC サブネットルーティング設定を評価しません。
このポリシーは、OpenSearch Serviceドメインがインターネットから直接アクセスできないようにすることを求めています。パブリックアクセスを許可すると、不正アクセスやデータ漏洩のリスクが高まります。OpenSearch Serviceドメインには機密データが含まれることが多いため、厳格なアクセス制御が必要です。
修復方法
すでに作成されているOpenSearchドメインの変更は出来ないため再作成が必要になります。
AWSコンソールでの修正手順
- Amazon OpenSearch Serviceに移動し、サイドバーからドメインを選択後、新しいドメインを作成します。
- 新しいドメインの作成ステップの内、ネットワークのセクションではVPCを選択してください。

- 設定したことを確認後、右側のサイドバーから「作成」をクリックしリソースを構築します。
Terraformでの修復手順
Terraformの場合も作成時にVPCをしてして作成する必要があります
# プロバイダー設定
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# データソース: 既存のVPC
data "aws_vpc" "existing" {
id = var.vpc_id
}
# データソース: 既存のサブネット
data "aws_subnets" "existing" {
filter {
name = "vpc-id"
values = [var.vpc_id]
}
filter {
name = "tag:SubnetGroup"
values = ["OpenSearch"]
}
}
# OpenSearchドメインのリソース
resource "aws_opensearch_domain" "main" {
domain_name = var.domain_name
engine_version = var.engine_version
cluster_config {
instance_type = var.instance_type
instance_count = var.instance_count
}
ebs_options {
ebs_enabled = true
volume_size = var.ebs_volume_size
}
vpc_options {
subnet_ids = data.aws_subnets.existing.ids
security_group_ids = [aws_security_group.opensearch.id]
}
encrypt_at_rest {
enabled = true
}
node_to_node_encryption {
enabled = true
}
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
}
}
access_policies = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = "*"
}
Action = "es:*"
Resource = "arn:aws:es:${var.aws_region}:${data.aws_caller_identity.current.account_id}:domain/${var.domain_name}/*"
Condition = {
IpAddress = {
"aws:SourceIp" = var.allowed_ip_ranges
}
}
}
]
})
tags = var.tags
depends_on = [aws_iam_service_linked_role.opensearch]
}
# セキュリティグループ
resource "aws_security_group" "opensearch" {
name = "${var.domain_name}-opensearch-sg"
description = "Security group for OpenSearch domain"
vpc_id = data.aws_vpc.existing.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.allowed_ip_ranges
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.tags, {
Name = "${var.domain_name}-opensearch-sg"
})
}
# IAMサービスリンクロール
resource "aws_iam_service_linked_role" "opensearch" {
aws_service_name = "es.amazonaws.com"
}
# 現在のアカウントID取得
data "aws_caller_identity" "current" {}
# 変数定義
variable "aws_region" {
description = "The AWS region to deploy to"
type = string
default = "ap-northeast-1"
}
variable "vpc_id" {
description = "The ID of the existing VPC"
type = string
}
variable "domain_name" {
description = "The name of the OpenSearch domain"
type = string
}
variable "engine_version" {
description = "The version of OpenSearch to deploy"
type = string
default = "OpenSearch_2.5"
}
variable "instance_type" {
description = "The instance type for OpenSearch nodes"
type = string
default = "t3.small.search"
}
variable "instance_count" {
description = "The number of instances in the OpenSearch cluster"
type = number
default = 1
}
variable "ebs_volume_size" {
description = "The size of the EBS volume per instance"
type = number
default = 10
}
variable "master_user_name" {
description = "The master user name for OpenSearch"
type = string
}
variable "master_user_password" {
description = "The master user password for OpenSearch"
type = string
sensitive = true
}
variable "allowed_ip_ranges" {
description = "List of IP ranges allowed to access the OpenSearch domain"
type = list(string)
}
variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
default = {}
}
# 出力
output "opensearch_endpoint" {
description = "The endpoint of the OpenSearch domain"
value = aws_opensearch_domain.main.endpoint
}
output "opensearch_dashboard" {
description = "The dashboard endpoint of the OpenSearch domain"
value = aws_opensearch_domain.main.dashboard_endpoint
}
最後に
今回は、OpenSearch Serviceドメインのパブリックアクセス設定について解説しました。パブリックアクセスを許可すると、不正アクセスやデータ漏洩のリスクが高まります。必ずVPC内に隔離し、厳格なアクセス制御を実施してください。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。