ElastiCache Cluster Using Public Subnet
Overview
This check identifies ElastiCache clusters that are deployed in public subnets (subnets with routes to an Internet Gateway). ElastiCache clusters should always reside in private subnets to prevent direct internet exposure.
Risk
When an ElastiCache cluster sits in a public subnet, it becomes accessible from the internet, which creates several security risks:
- Data theft: Attackers could access cached data containing sensitive information
- Cache poisoning: Malicious actors could modify cached data to serve bad content
- Service disruption: The cluster becomes vulnerable to network scanning and DDoS attacks
- Lateral movement: A compromised cache can serve as a starting point to attack other resources in your VPC
Remediation Steps
Prerequisites
You need permission to view and modify ElastiCache subnet groups and VPC networking settings. You will also need at least one private subnet in your VPC (a subnet without a route to an Internet Gateway).
How to identify if a subnet is public or private
A public subnet has a route table entry pointing to an Internet Gateway (igw-xxxxx).
A private subnet either has no internet route or routes through a NAT Gateway.
To check in the console:
- Go to VPC > Subnets
- Select the subnet
- Click the Route table tab
- Look for a route with destination
0.0.0.0/0pointing toigw-xxxxx(Internet Gateway = public subnet)
AWS Console Method
-
Open the ElastiCache console at https://console.aws.amazon.com/elasticache/
-
Identify the affected cluster:
- Click Redis caches or Memcached caches in the left menu
- Find your cluster and note its Subnet group name
-
Review the current subnet group:
- Click Subnet groups in the left menu
- Click on the subnet group name used by your cluster
- Note the subnets listed and their IDs
-
Create a new subnet group with private subnets (recommended approach):
- Click Create subnet group
- Enter a name (e.g.,
my-app-private-subnets) - Enter a description
- Select your VPC
- Under Subnets, select only private subnets (subnets without Internet Gateway routes)
- Click Create
-
Migrate the cluster to the new subnet group:
- Go back to your cluster
- Click Modify
- Under Subnet group, select your new private subnet group
- Choose when to apply the change (immediately or during next maintenance window)
- Click Modify
Important: Changing the subnet group causes a brief outage during the migration. Plan accordingly.
AWS CLI (optional)
List your current subnet groups:
aws elasticache describe-cache-subnet-groups \
--region us-east-1
Check if a subnet is public by examining its route table:
aws ec2 describe-route-tables \
--filters Name=association.subnet-id,Values=<subnet-id> \
--region us-east-1 \
--query 'RouteTables[].Routes[?GatewayId!=`null` && starts_with(GatewayId, `igw-`)]'
If the output contains an Internet Gateway route, the subnet is public.
Create a new subnet group with private subnets:
aws elasticache create-cache-subnet-group \
--cache-subnet-group-name my-app-private-subnets \
--cache-subnet-group-description "Private subnets for ElastiCache" \
--subnet-ids subnet-aaaa1111 subnet-bbbb2222 \
--region us-east-1
Modify the cluster to use the new subnet group:
aws elasticache modify-replication-group \
--replication-group-id <your-replication-group-id> \
--cache-subnet-group-name my-app-private-subnets \
--apply-immediately \
--region us-east-1
For standalone Memcached clusters:
aws elasticache modify-cache-cluster \
--cache-cluster-id <your-cluster-id> \
--cache-subnet-group-name my-app-private-subnets \
--apply-immediately \
--region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: ElastiCache cluster in private subnets
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where ElastiCache will be deployed
PrivateSubnet1:
Type: AWS::EC2::Subnet::Id
Description: First private subnet
PrivateSubnet2:
Type: AWS::EC2::Subnet::Id
Description: Second private subnet
Resources:
PrivateCacheSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
CacheSubnetGroupName: my-app-private-subnets
Description: Private subnets for ElastiCache
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
CacheSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for ElastiCache
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 6379
ToPort: 6379
SourceSecurityGroupId: !Ref AppSecurityGroup
Tags:
- Key: Name
Value: elasticache-sg
RedisCluster:
Type: AWS::ElastiCache::ReplicationGroup
Properties:
ReplicationGroupDescription: Redis cluster in private subnets
CacheNodeType: cache.t3.micro
Engine: redis
NumCacheClusters: 2
CacheSubnetGroupName: !Ref PrivateCacheSubnetGroup
SecurityGroupIds:
- !Ref CacheSecurityGroup
AtRestEncryptionEnabled: true
TransitEncryptionEnabled: true
Outputs:
SubnetGroupName:
Description: Name of the private subnet group
Value: !Ref PrivateCacheSubnetGroup
Terraform (optional)
# Private subnet group for ElastiCache
resource "aws_elasticache_subnet_group" "private" {
name = "my-app-private-subnets"
subnet_ids = var.private_subnet_ids
tags = {
Name = "my-app-private-subnets"
}
}
# Security group allowing access only from application tier
resource "aws_security_group" "elasticache" {
name = "elasticache-sg"
description = "Security group for ElastiCache"
vpc_id = var.vpc_id
ingress {
description = "Redis from app servers"
from_port = 6379
to_port = 6379
protocol = "tcp"
security_groups = [var.app_security_group_id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "elasticache-sg"
}
}
# Redis replication group in private subnets
resource "aws_elasticache_replication_group" "redis" {
replication_group_id = "my-redis-cluster"
description = "Redis cluster in private subnets"
node_type = "cache.t3.micro"
num_cache_clusters = 2
engine = "redis"
engine_version = "7.0"
subnet_group_name = aws_elasticache_subnet_group.private.name
security_group_ids = [aws_security_group.elasticache.id]
at_rest_encryption_enabled = true
transit_encryption_enabled = true
tags = {
Name = "my-redis-cluster"
}
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "private_subnet_ids" {
description = "List of private subnet IDs"
type = list(string)
}
variable "app_security_group_id" {
description = "Security group ID for application servers"
type = string
}
Verification
After making changes, verify the remediation:
-
In the AWS Console:
- Go to ElastiCache > Subnet groups
- Click on the subnet group used by your cluster
- For each subnet listed, go to VPC > Subnets and verify the route table has no Internet Gateway routes
-
Re-run the Prowler check:
prowler aws --checks elasticache_cluster_uses_public_subnet --region us-east-1
Additional Resources
- ElastiCache Subnet Groups Documentation
- Accessing ElastiCache Resources from Outside AWS
- ElastiCache Security Best Practices
Notes
-
Downtime consideration: Moving a cluster to a different subnet group typically requires a brief outage. Schedule changes during a maintenance window if possible.
-
Application connectivity: Ensure your applications can reach the private subnets. Applications outside the VPC will need VPC peering, AWS PrivateLink, or VPN connectivity.
-
Multi-AZ: When selecting private subnets, choose subnets in different Availability Zones for high availability.
-
Security groups: After moving to private subnets, tighten security group rules to allow connections only from your application servers, not entire CIDR blocks.
-
If you cannot change subnet groups: As a temporary mitigation, restrict the security group to allow connections only from specific internal IP addresses, but this is not a complete solution. Plan to migrate to private subnets.