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ドメインには機密データが含まれることが多いため、厳格なアクセス制御が必要です。

修復方法

AWSコンソールでの修正手順

  1. Amazon OpenSearch Serviceに移動し、サイドバーからドメインを選択後、新しいドメインを作成します。
  2. 新しいドメインの作成ステップの内、ネットワークのセクションではVPCを選択してください。
  1. 設定したことを確認後、右側のサイドバーから「作成」をクリックしリソースを構築します。

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機能で簡単に検出及び管理する事が可能です。

運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る