This check flags Elasticsearch (OpenSearch) domains that send traffic between cluster nodes in plaintext. Without node-to-node encryption, anyone who can sniff your VPC traffic can read indexed data in transit. The fix is to enable node-to-node encryption on the domain, which on AWS requires a domain reconfiguration.
Amazon Elasticsearch Service (now Amazon OpenSearch Service) runs your search and analytics workloads across a cluster of nodes. Those nodes constantly talk to each other to replicate shards, route queries, and keep cluster state in sync. By default, that internal chatter can travel unencrypted across the network. This check, elasticsearch_notls, catches domains where node-to-node encryption is switched off.
If you store logs, customer records, security events, or anything sensitive in Elasticsearch, that data is moving between nodes constantly. Leaving it in cleartext is the kind of gap that passes unnoticed until an auditor or an attacker finds it.
What this check detects
The check inspects each Elasticsearch domain in your AWS account and reads its NodeToNodeEncryptionOptions configuration. If Enabled is false or absent, the domain is flagged.
Node-to-node encryption wraps the TLS layer around all communication between the data nodes, master nodes, and coordinating nodes inside a single domain. This is separate from two related settings that people often confuse it with:
- Encryption at rest protects data sitting on disk and in automated snapshots.
- HTTPS / enforce-TLS for endpoints protects traffic between clients and the domain endpoint.
- Node-to-node encryption protects traffic moving inside the cluster, between the nodes themselves.
You can have one without the others. A domain can enforce HTTPS at its public endpoint while still shipping shard data between nodes in plaintext. This check is specifically about that internal hop.
Note: Amazon renamed Elasticsearch Service to OpenSearch Service in 2021. The API field names, CLI commands, and the underlying behaviour for node-to-node encryption are identical, so everything here applies whether your domain shows up as es or opensearch in the console.
Why it matters
The whole point of running a cluster is that data is spread across nodes. Every write replicates to replica shards on other nodes. Every search that touches more than one shard pulls partial results across the wire and merges them. Cluster state, security metadata, and snapshot coordination all move node-to-node. When that traffic is unencrypted, all of it is readable to anything that can observe the network path.
Realistic attack scenarios
- Lateral movement inside the VPC. An attacker who lands on a compromised EC2 instance or container in the same VPC does not need to break into the domain itself. They can passively capture inter-node traffic and reconstruct indexed documents, including anything you thought was protected by access controls at the endpoint.
- A misconfigured or shared subnet. Cross-account VPC peering, overly broad security groups, or a noisy neighbour in a shared subnet widens the set of hosts that can reach the node interfaces.
- Snapshot and replication leakage. Because replication copies the full document body, sensitive fields you carefully redact at query time still travel in full between nodes.
Compliance impact
Encryption in transit is an explicit requirement in most frameworks teams have to answer to. PCI DSS requires strong cryptography for cardholder data in transit across any network. HIPAA expects transmission security for electronic protected health information. SOC 2 and ISO 27001 auditors will ask how you protect data moving between system components. A flagged domain is a finding you will have to explain or remediate before sign-off.
Warning: Node-to-node encryption can only be enabled on domains running Elasticsearch 6.0 or later, or any OpenSearch version. If you are on an older engine, you will need to upgrade the domain version first, which is itself a planned maintenance event.
How to fix it
You enable node-to-node encryption per domain. The setting can be turned on at creation time without disruption, or added to an existing domain through an update.
Danger: Enabling node-to-node encryption on an existing domain triggers a blue/green deployment. AWS provisions a new set of nodes, migrates your data, and cuts over. Expect elevated resource usage and a temporary capacity increase during the migration. Run it during a maintenance window and confirm you have headroom on storage and instance limits before starting.
Option 1: AWS CLI on an existing domain
First confirm the current state of the setting:
aws opensearch describe-domain \
--domain-name my-search-domain \
--query 'DomainStatus.NodeToNodeEncryptionOptions'
If it returns { "Enabled": false }, enable it:
aws opensearch update-domain-config \
--domain-name my-search-domain \
--node-to-node-encryption-options Enabled=true
For legacy Elasticsearch domains that still use the es command set, the equivalent is:
aws es update-elasticsearch-domain-config \
--domain-name my-search-domain \
--node-to-node-encryption-options Enabled=true
The update kicks off the blue/green deployment. Track progress with:
aws opensearch describe-domain \
--domain-name my-search-domain \
--query 'DomainStatus.{Processing:Processing,UpgradeProcessing:UpgradeProcessing}'
When Processing returns false, the cutover is complete.
Option 2: AWS Console
- Open the Amazon OpenSearch Service console and select your domain.
- Go to the Security configuration tab and choose Edit.
- Under Encryption, tick Node-to-node encryption.
- Save changes. The domain status moves to Processing while the blue/green deployment runs.
Option 3: Terraform
If your domains are managed as code, set the block explicitly so the setting is enforced on every apply:
resource "aws_opensearch_domain" "search" {
domain_name = "my-search-domain"
engine_version = "OpenSearch_2.11"
node_to_node_encryption {
enabled = true
}
encrypt_at_rest {
enabled = true
}
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
}
Tip: Turn on node-to-node encryption, encryption at rest, and enforce_https together. They cover three different network paths and changing them often means another reconfiguration event. Doing it in one pass saves you a second round of blue/green migration.
Option 4: CloudFormation
{
"Type": "AWS::OpenSearchService::Domain",
"Properties": {
"DomainName": "my-search-domain",
"EngineVersion": "OpenSearch_2.11",
"NodeToNodeEncryptionOptions": {
"Enabled": true
},
"EncryptionAtRestOptions": {
"Enabled": true
},
"DomainEndpointOptions": {
"EnforceHTTPS": true,
"TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07"
}
}
}
How to prevent it from happening again
Fixing the flagged domain solves today's problem. Stopping the next plaintext domain from ever being created is the part that actually moves the needle.
Gate it in CI/CD with policy-as-code
If you provision with Terraform, scan the plan before it applies. Here is a Conftest / OPA Rego policy that rejects any OpenSearch domain without node-to-node encryption:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_opensearch_domain"
not resource.change.after.node_to_node_encryption[0].enabled
msg := sprintf(
"OpenSearch domain '%s' must enable node_to_node_encryption",
[resource.change.after.domain_name]
)
}
Wire it into the pipeline so a non-compliant plan fails the build:
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy policy/
Enforce it organization-wide with AWS Config
For drift in accounts where engineers click around the console, AWS Config gives you continuous detection. The managed rule elasticsearch-node-to-node-encryption-check flags any non-compliant domain automatically:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "es-node-to-node-encryption-check",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "ELASTICSEARCH_NODE_TO_NODE_ENCRYPTION_CHECK"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::Elasticsearch::Domain"]
}
}'
Block it at creation with an SCP guardrail
Service Control Policies let you deny domain creation outright unless the encryption flag is set, which stops the problem before any resource exists:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedNodeToNode",
"Effect": "Deny",
"Action": [
"es:CreateElasticsearchDomain",
"opensearch:CreateDomain"
],
"Resource": "*",
"Condition": {
"Bool": {
"es:NodeToNodeEncryptionEnabled": "false"
}
}
}
]
}
Tip: Lensix runs the elasticsearch_notls check continuously, so a domain created outside your IaC pipeline gets caught on the next scan rather than at your next audit. Pair the platform scan with a CI gate and you cover both the resources you manage as code and the ones someone spins up by hand.
Best practices
- Treat the three encryption settings as one set. Node-to-node encryption, encryption at rest, and enforced HTTPS protect different segments of the data path. Enable all three on every domain rather than picking one.
- Set it at creation, not after. Enabling node-to-node encryption on a fresh domain is instant and free of disruption. Retrofitting it forces a blue/green migration. Bake it into your module defaults so new domains are born compliant.
- Keep the engine version current. Node-to-node encryption needs Elasticsearch 6.0 or newer. Staying on supported versions keeps the option available and gives you the security patches that come with newer releases.
- Lock down the network around the domain. Encryption in transit is a layer, not a replacement for network isolation. Run domains inside a VPC, scope security groups tightly, and use fine-grained access control so a sniffed packet is not the only thing standing between an attacker and your data.
- Audit continuously, not just at release. Configuration drifts. A continuous check beats a point-in-time review because it catches the domain someone created last Tuesday before it shows up in an audit finding.
Node-to-node encryption is a single boolean, but it closes a real gap in how your cluster moves data internally. Turn it on, enforce it in your pipeline, and verify it keeps catching new domains. The cost is one configuration change. The alternative is explaining to an auditor, or an incident responder, why your indexed data was travelling in cleartext.

