Back to Checks
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 CloudRun
prowler aws --checks opensearch_service_domains_not_publicly_accessibleRecommendation
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