OpenSearch Service Domain Audit Logging
Overview
This check verifies that Amazon OpenSearch Service domains have audit logging enabled. Audit logs record user requests for access to your OpenSearch domain, helping you track who accessed what data and when.
Risk
Without audit logging enabled, you lose visibility into access patterns and potential security incidents:
- No accountability: You cannot trace who performed critical actions like data queries, configuration changes, or access attempts
- Undetectable threats: Unauthorized access, privilege misuse, and data exfiltration can occur without any record
- Compliance gaps: Many regulatory frameworks (PCI-DSS, HIPAA, SOC 2) require audit trails for data access
- Hindered incident response: If a breach occurs, forensic investigation becomes extremely difficult without logs
Remediation Steps
Prerequisites
- Access to the AWS Console with permissions to modify OpenSearch domains
- The OpenSearch domain must have Fine-Grained Access Control enabled (audit logs require this)
Required IAM permissions
Your IAM user or role needs these permissions:
es:UpdateDomainConfiges:DescribeDomainlogs:CreateLogGrouplogs:PutResourcePolicy
AWS Console Method
- Open the Amazon OpenSearch Service console at https://console.aws.amazon.com/aos/
- In the left navigation, click Domains
- Click on the domain name you want to configure
- Select the Logs tab
- Under Audit logs, click Enable
- For CloudWatch log group, either:
- Select an existing log group, or
- Click Create new and enter a name like
/aws/opensearch/domains/<your-domain-name>/audit-logs
- If prompted about a resource policy, click Confirm to allow OpenSearch to write to CloudWatch Logs
- Click Save changes
Note: If the Enable option is grayed out, you need to enable Fine-Grained Access Control first:
- Go to the Security configuration tab
- Click Edit
- Enable Fine-grained access control
- Configure a master user (either IAM ARN or internal user database)
- Save and wait for the domain to update (this can take 15-30 minutes)
AWS CLI (optional)
First, create a CloudWatch Logs group and resource policy to allow OpenSearch to write logs:
# Set your variables
DOMAIN_NAME="<your-domain-name>"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION="us-east-1"
LOG_GROUP_NAME="/aws/opensearch/domains/${DOMAIN_NAME}/audit-logs"
# Create the CloudWatch Logs group
aws logs create-log-group \
--log-group-name "$LOG_GROUP_NAME" \
--region "$REGION"
# Get the log group ARN
LOG_GROUP_ARN="arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:${LOG_GROUP_NAME}"
# Create a resource policy to allow OpenSearch to write logs
aws logs put-resource-policy \
--policy-name "${DOMAIN_NAME}-opensearch-audit-logs" \
--policy-document "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Effect\": \"Allow\",
\"Principal\": {
\"Service\": \"es.amazonaws.com\"
},
\"Action\": [
\"logs:PutLogEvents\",
\"logs:CreateLogStream\"
],
\"Resource\": \"${LOG_GROUP_ARN}:*\"
}
]
}" \
--region "$REGION"
Then enable audit logging on the domain:
aws opensearch update-domain-config \
--domain-name "$DOMAIN_NAME" \
--log-publishing-options "AUDIT_LOGS={CloudWatchLogsLogGroupArn=${LOG_GROUP_ARN},Enabled=true}" \
--region "$REGION"
CloudFormation (optional)
This template creates an OpenSearch domain with audit logging enabled, including the required CloudWatch Logs group and resource policy:
AWSTemplateFormatVersion: '2010-09-09'
Description: OpenSearch domain with audit logging enabled
Parameters:
DomainName:
Type: String
Description: Name for the OpenSearch domain
Default: my-opensearch-domain
Resources:
OpenSearchAuditLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/opensearch/domains/${DomainName}/audit-logs'
RetentionInDays: 365
OpenSearchLogResourcePolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: !Sub '${DomainName}-opensearch-log-policy'
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "es.amazonaws.com"
},
"Action": [
"logs:PutLogEvents",
"logs:CreateLogStream"
],
"Resource": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/opensearch/domains/${DomainName}/audit-logs:*"
}
]
}
OpenSearchDomain:
Type: AWS::OpenSearchService::Domain
DependsOn: OpenSearchLogResourcePolicy
Properties:
DomainName: !Ref DomainName
EngineVersion: OpenSearch_2.11
ClusterConfig:
InstanceType: t3.small.search
InstanceCount: 1
EBSOptions:
EBSEnabled: true
VolumeSize: 10
VolumeType: gp3
NodeToNodeEncryptionOptions:
Enabled: true
EncryptionAtRestOptions:
Enabled: true
DomainEndpointOptions:
EnforceHTTPS: true
AdvancedSecurityOptions:
Enabled: true
InternalUserDatabaseEnabled: true
MasterUserOptions:
MasterUserName: admin
MasterUserPassword: '{{resolve:secretsmanager:opensearch-master-password:SecretString:password}}'
LogPublishingOptions:
AUDIT_LOGS:
CloudWatchLogsLogGroupArn: !GetAtt OpenSearchAuditLogGroup.Arn
Enabled: true
Outputs:
DomainEndpoint:
Description: OpenSearch domain endpoint
Value: !GetAtt OpenSearchDomain.DomainEndpoint
AuditLogGroupArn:
Description: CloudWatch Log Group ARN for audit logs
Value: !GetAtt OpenSearchAuditLogGroup.Arn
Note: Before deploying, create a Secrets Manager secret named opensearch-master-password with a key password containing your desired master password.
Terraform (optional)
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
variable "domain_name" {
description = "Name for the OpenSearch domain"
type = string
default = "my-opensearch-domain"
}
variable "master_user_password" {
description = "Master user password for OpenSearch"
type = string
sensitive = true
}
# CloudWatch Log Group for audit logs
resource "aws_cloudwatch_log_group" "opensearch_audit_logs" {
name = "/aws/opensearch/domains/${var.domain_name}/audit-logs"
retention_in_days = 365
}
# Log resource policy to allow OpenSearch to write logs
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
resource "aws_cloudwatch_log_resource_policy" "opensearch_log_policy" {
policy_name = "${var.domain_name}-opensearch-log-policy"
policy_document = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "es.amazonaws.com"
}
Action = [
"logs:PutLogEvents",
"logs:CreateLogStream"
]
Resource = "${aws_cloudwatch_log_group.opensearch_audit_logs.arn}:*"
}
]
})
}
# OpenSearch domain with audit logging enabled
resource "aws_opensearch_domain" "main" {
domain_name = var.domain_name
engine_version = "OpenSearch_2.11"
cluster_config {
instance_type = "t3.small.search"
instance_count = 1
}
ebs_options {
ebs_enabled = true
volume_size = 10
volume_type = "gp3"
}
encrypt_at_rest {
enabled = true
}
node_to_node_encryption {
enabled = true
}
domain_endpoint_options {
enforce_https = true
}
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = "admin"
master_user_password = var.master_user_password
}
}
log_publishing_options {
cloudwatch_log_group_arn = aws_cloudwatch_log_group.opensearch_audit_logs.arn
log_type = "AUDIT_LOGS"
enabled = true
}
depends_on = [aws_cloudwatch_log_resource_policy.opensearch_log_policy]
}
output "domain_endpoint" {
description = "OpenSearch domain endpoint"
value = aws_opensearch_domain.main.endpoint
}
output "audit_log_group_arn" {
description = "CloudWatch Log Group ARN for audit logs"
value = aws_cloudwatch_log_group.opensearch_audit_logs.arn
}
Deploy with:
terraform init
terraform apply -var="master_user_password=YourSecurePassword123!"
Verification
After enabling audit logging, verify the configuration:
- In the OpenSearch console, navigate to your domain
- Click the Logs tab
- Confirm that Audit logs shows as Enabled
- Check that a CloudWatch log group is linked
To verify logs are being collected, perform an action on your domain (like a search query), then check CloudWatch Logs for entries in your audit log group.
CLI verification commands
# Check domain configuration for audit logging
aws opensearch describe-domain \
--domain-name "<your-domain-name>" \
--region us-east-1 \
--query "DomainStatus.LogPublishingOptions.AUDIT_LOGS" \
--output json
# Expected output shows Enabled: true
# {
# "CloudWatchLogsLogGroupArn": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/opensearch/domains/my-domain/audit-logs",
# "Enabled": true
# }
# Check for recent log events (after performing some domain activity)
aws logs filter-log-events \
--log-group-name "/aws/opensearch/domains/<your-domain-name>/audit-logs" \
--region us-east-1 \
--limit 5
Additional Resources
- AWS Documentation: Monitoring audit logs in Amazon OpenSearch Service
- AWS Documentation: Fine-grained access control in Amazon OpenSearch Service
- AWS Documentation: CloudWatch Logs for OpenSearch Service
Notes
-
Fine-grained access control required: Audit logging only works when Fine-grained Access Control is enabled on the domain. This is a prerequisite that cannot be bypassed.
-
Domain update time: Enabling audit logging triggers a blue/green deployment. The domain remains available, but the configuration change can take 15-30 minutes to complete.
-
Log retention costs: Audit logs can generate significant volume depending on your domain's activity. Configure an appropriate retention period on your CloudWatch Logs group to manage costs.
-
Categories to tune: By default, all audit events are logged. You can customize which categories are logged via the OpenSearch Dashboards security plugin to reduce noise and costs.
-
Existing domains: When enabling on an existing domain without Fine-grained Access Control, you must first enable Fine-grained Access Control, which is a significant change. Plan for potential application updates to use the new authentication model.