Back to Checks
aws

Amazon OpenSearch Service domain is not publicly accessible

opensearch_service_domains_not_publicly_accessible
Service: opensearchSeverity: critical
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:AwsOpenSearchServiceDomain

Amazon OpenSearch domains are assessed for public exposure via their resource-based access policies. Domains inside a VPC are treated as privately reachable; domains with overly permissive policies that allow broad, unauthenticated access are identified as publicly accessible.

Risk

Public exposure lets anyone query, index, or delete data, impacting confidentiality (record disclosure), integrity (unauthorized writes, index tampering), and availability (disruption, deletion). Attackers can harvest sensitive logs/PII, alter analytics, or wipe indices, enabling lateral movement and operational outage.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks opensearch_service_domains_not_publicly_accessible

Recommendation

Apply least privilege and defense in depth:

  • Place domains in a VPC and restrict reachability with security groups
  • Use narrow resource policies; avoid Principal:"*"
  • Require authenticated access (fine-grained controls); if unavoidable, limit public endpoints by IP and roles
  • Monitor access with logs and alerts

Remediation

CLI

aws opensearch update-domain-config --domain-name <DOMAIN_NAME> --access-policies '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::<ACCOUNT_ID>:root"},"Action":"es:*","Resource":"arn:aws:es:<REGION>:<ACCOUNT_ID>:domain/<DOMAIN_NAME>/*"}]}'

Terraform

# Restrict OpenSearch access policy to your account only
resource "aws_opensearch_domain" "<example_resource_name>" {
  domain_name = "<example_resource_name>"

  # critical: limits access to the owning account, removing public access
  access_policies = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { AWS = "arn:aws:iam::<ACCOUNT_ID>:root" }
      Action    = "es:*"
      Resource  = "arn:aws:es:<REGION>:<ACCOUNT_ID>:domain/<example_resource_name>/*"
    }]
  })
}

NativeIaC

# CloudFormation: restrict OpenSearch access policy to your account only
Resources:
  <example_resource_name>:
    Type: AWS::OpenSearchService::Domain
    Properties:
      DomainName: <example_resource_name>
      AccessPolicies:  # critical: restricts access to your account only, removing public access
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: arn:aws:iam::<ACCOUNT_ID>:root  # critical: only this account can access
            Action: es:*
            Resource: arn:aws:es:<REGION>:<ACCOUNT_ID>:domain/<example_resource_name>/*

Other

1In the AWS console, open Amazon OpenSearch Service and select your domain
2Go to Security configuration > Edit
3Choose Access policy > JSON
4Replace the policy with the following (use your values) and Save changes:
5```json
6{
7"Version": "2012-10-17",
8"Statement": [
9{
10"Effect": "Allow",
11"Principal": {"AWS": "arn:aws:iam::<ACCOUNT_ID>:root"},
12"Action": "es:*",
13"Resource": "arn:aws:es:<REGION>:<ACCOUNT_ID>:domain/<DOMAIN_NAME>/*"
14}
15]
16}
17```
18Verify the domain endpoint is no longer accessible publicly except by your account's IAM principals

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.lib.policy import is_policy_public
from prowler.providers.aws.services.opensearch.opensearch_client import (
    opensearch_client,
)


class opensearch_service_domains_not_publicly_accessible(Check):
    def execute(self):
        findings = []
        trusted_ips = opensearch_client.audit_config.get("trusted_ips", [])
        for domain in opensearch_client.opensearch_domains.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=domain)
            report.status = "PASS"
            report.status_extended = (
                f"Opensearch domain {domain.name} is not publicly accessible."
            )

            if domain.vpc_id:
                report.status_extended = f"Opensearch domain {domain.name} is in a VPC, then it is not publicly accessible."
            elif domain.access_policy is not None and is_policy_public(
                domain.access_policy,
                opensearch_client.audited_account,
                trusted_ips=trusted_ips,
            ):
                report.status = "FAIL"
                report.status_extended = f"Opensearch domain {domain.name} is publicly accessible via access policy."

            findings.append(report)

        return findings