Back to Checks
ElastiCache cluster is not using public subnets
elasticache_cluster_uses_public_subnet
Service: elasticacheSeverity: medium
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:Other
ElastiCache resources (Redis nodes and Memcached clusters) are assessed for placement in public subnets.
The finding identifies cache subnet groups that include subnets configured with Internet routing instead of private-only subnets.
Risk
Hosting caches in public subnets can permit direct or misconfigured Internet access, impacting CIA:
- Confidentiality: unauthorized reads and key dumps
- Integrity: cache poisoning or key tampering
- Availability: scanning and DDoS
Attackers may pivot from the cache to lateral movement within the VPC.
Run this check with Prowler
Run in Prowler CloudRun
prowler aws --checks elasticache_cluster_uses_public_subnetRecommendation
Place caches in private subnets only and ensure route tables lack Internet egress. Apply least privilege with tight security groups limited to required ports and trusted sources.
For external access, use VPC peering, VPN, or PrivateLink. Enable encryption in transit and Redis AUTH for layered controls.
Remediation
CLI
aws elasticache modify-cache-cluster --cache-cluster-id <example_resource_id> --cache-subnet-group-name <example_resource_name> --apply-immediatelyTerraform
# Terraform: ensure the cluster uses a subnet group with only private subnets
resource "aws_elasticache_subnet_group" "private" {
name = "<example_resource_name>"
subnet_ids = ["<example_resource_id>", "<example_resource_id>"] # private subnets only
}
resource "aws_elasticache_cluster" "cache" {
cluster_id = "<example_resource_id>"
engine = "redis"
node_type = "cache.t3.micro"
num_cache_nodes = 1
subnet_group_name = aws_elasticache_subnet_group.private.name # CRITICAL: restricts cluster to private subnets
}NativeIaC
# CloudFormation: move ElastiCache into private subnets via a private subnet group
Resources:
PrivateCacheSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
Description: Private subnets only
SubnetIds:
- <example_resource_id> # private subnet
- <example_resource_id> # private subnet
CacheCluster:
Type: AWS::ElastiCache::CacheCluster
Properties:
CacheClusterId: <example_resource_id>
Engine: redis
CacheNodeType: cache.t3.micro
NumCacheNodes: 1
CacheSubnetGroupName: !Ref PrivateCacheSubnetGroup # CRITICAL: forces the cluster to use only private subnetsOther
1In the AWS Console, go to ElastiCache > Subnet groups
2Click Create cache subnet group and select only private subnets (no route to an Internet Gateway)
3Go to ElastiCache > Redis or Memcached, select your cluster
4Click Modify, set Subnet group to the private subnet group
5Check Apply immediately and click Modify to save
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.elasticache.elasticache_client import (
elasticache_client,
)
from prowler.providers.aws.services.vpc.vpc_client import vpc_client
class elasticache_cluster_uses_public_subnet(Check):
def execute(self):
findings = []
for cluster in elasticache_client.clusters.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=cluster)
report.status = "PASS"
if cluster.engine == "redis":
report.status_extended = (
f"Elasticache Redis Node {cluster.id} is not using public subnets."
)
else:
report.status_extended = f"Elasticache Memcached Cluster {cluster.id} is not using public subnets."
public_subnets = []
for subnet in cluster.subnets:
if (
subnet in vpc_client.vpc_subnets
and vpc_client.vpc_subnets[subnet].public
):
public_subnets.append(vpc_client.vpc_subnets[subnet].id)
if len(public_subnets) > 0:
report.status = "FAIL"
if cluster.engine == "redis":
report.status_extended = f"Elasticache Redis Node {cluster.id} is using {', '.join(public_subnets)} public subnets."
else:
report.status_extended = f"Elasticache Memcached Cluster {cluster.id} is using {', '.join(public_subnets)} public subnets."
findings.append(report)
return findings