AWS Security Hub · ES
ES.3: ES should encrypt node-to-node traffic
Written and reviewed by Emnode · Last reviewed
What does AWS Security Hub ES.3 check?
ES.3 checks whether NodeToNodeEncryptionOptions.Enabled is true on a legacy Elasticsearch domain and reports FAILED when it is off. Domain nodes constantly shuffle data between themselves — replicating shards, rebalancing, and fanning out queries — and this option encrypts that intra-cluster traffic.
Why does ES.3 matter?
With the option off, data moves between nodes in plaintext. The traffic stays inside a service-managed VPC, so exploiting it needs an attacker already present in that network, but compliance regimes don't accept "it's inside a VPC" — an unencrypted channel is a control failure under PCI DSS, HIPAA, FedRAMP, and SOC 2 regardless of the blast radius.
How do I fix ES.3?
- Enable node-to-node encryption on the domain where the engine version supports an in-place change.
- On older versions, create a new domain with the option on and migrate indices via snapshot restore.
- Pair it with HTTPS enforcement and encryption at rest for full coverage.
- Default the option on in new-domain templates.
Remediation script · bash
# 1. Bulk-enable free SSE-SQS on every unencrypted queue in the region.
for q in $(aws sqs list-queues --query 'QueueUrls[]' --output text); do
state=$(aws sqs get-queue-attributes --queue-url $q \
--attribute-names KmsMasterKeyId SqsManagedSseEnabled --query 'Attributes' --output text)
[ -z "$state" ] && aws sqs set-queue-attributes --queue-url $q \
--attributes '{"SqsManagedSseEnabled":"true"}' && echo "encrypted $q"
done
# 2. High-throughput stream: SSE-KMS with a 5-minute data-key reuse window to keep KMS cost flat.
aws kinesis start-stream-encryption --stream-name payment-events \
--encryption-type KMS \
--key-id arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
# 3. Find unencrypted recovery points (Backup.1 reads IsEncrypted per recovery point, not per vault).
aws backup list-recovery-points-by-backup-vault --backup-vault-name prod-backups \
--query 'RecoveryPoints[?IsEncrypted==`false`].[RecoveryPointArn,ResourceType]' --output table
# 4. Confirm an at-rest Config rule is evaluating so regressions are caught automatically.
aws configservice describe-compliance-by-config-rule --config-rule-names sqs-queue-encrypted \
--query 'ComplianceByConfigRules[].Compliance.ComplianceType' Full walkthrough (console steps, edge cases and verification) in the lesson Encrypt other services at rest (queues, streams, logs, ML).
Is ES.3 a false positive?
On older Elasticsearch versions node-to-node encryption can't be toggled on an existing domain — it has to be set at creation, so the fix may be a migration rather than an edit.
More ES controls
- ES.1 ES domains should encrypt at rest
- ES.2 A legacy Elasticsearch domain is publicly accessible
- ES.4 ES error logging to CW should be enabled
- ES.5 ES domains should have audit logging
- ES.6 ES domains should have >= 3 data nodes
- ES.7 ES domains should have >= 3 dedicated master nodes
- ES.8 ES should use latest TLS policy