Back to Checks
EMR cluster is not publicly accessible
emr_cluster_publicly_accesible
Service: emrSeverity: medium
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:Other
Amazon EMR clusters are assessed for public network exposure by examining master and core/task node security groups for inbound rules that allow any source (0.0.0.0/0 or ::/0).
Only active clusters are considered, and findings identify exposure via the specific security groups attached to the cluster nodes.
Risk
Open Internet ingress to EMR nodes enables direct access to services and UIs, facilitating brute force, RCE, and data theft. Adversaries can pivot inside the VPC, alter jobs and outputs (integrity), exfiltrate datasets (confidentiality), or abuse compute for mining, degrading availability.
Run this check with Prowler
Run in Prowler CloudRun
prowler aws --checks emr_cluster_publicly_accesibleRecommendation
Apply least privilege and defense in depth:
- Place clusters in private subnets; avoid public IPs
- Deny
0.0.0.0/0and::/0in node security groups; allow trusted CIDRs only - Keep EMR Block Public Access enabled with minimal exceptions
- Use bastion/SSM, private connectivity, and logging for hardened access
Remediation
Terraform
# Restrict EMR SG ingress to avoid 0.0.0.0/0 or ::/0
resource "aws_security_group_rule" "<example_resource_name>" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
security_group_id = "<example_resource_id>" # EMR master/core SG
cidr_blocks = ["10.0.0.0/8"] # CRITICAL: restrict source; not 0.0.0.0/0 or ::/0
}NativeIaC
# CloudFormation: Security Group without public ingress for EMR nodes
Resources:
<example_resource_name>:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: SG for EMR without public access
VpcId: <example_resource_id>
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/8 # CRITICAL: restrict source; do not use 0.0.0.0/0 or ::/0 to avoid public accessOther
1In AWS Console, go to EMR > Clusters and open the affected cluster
2In the cluster details, note the Security Groups for Master and Core/Task under Network and security
3Open the EC2 Console > Security Groups and select each noted group
4Edit Inbound rules and remove any rule with Source 0.0.0.0/0 or ::/0
5If access is required, re-add only from specific CIDR(s) you control, then Save
Check Code
from copy import deepcopy
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
from prowler.providers.aws.services.emr.emr_client import emr_client
from prowler.providers.aws.services.emr.emr_service import ClusterStatus
class emr_cluster_publicly_accesible(Check):
def execute(self):
findings = []
for cluster in emr_client.clusters.values():
if cluster.status not in (
ClusterStatus.TERMINATED,
ClusterStatus.TERMINATED_WITH_ERRORS,
):
report = Check_Report_AWS(metadata=self.metadata(), resource=cluster)
report.status = "PASS"
report.status_extended = (
f"EMR Cluster {cluster.id} is not publicly accessible."
)
# If EMR cluster is Public, it is required to check
# their Security Groups for the Master,
# the Slaves and the additional ones
if cluster.public:
# Check Public Master Security Groups
master_node_sg_groups = deepcopy(
cluster.master.additional_security_groups_id
)
if master_node_sg_groups:
master_node_sg_groups.append(cluster.master.security_group_id)
else:
master_node_sg_groups = [cluster.master.security_group_id]
master_public_security_groups = []
for master_sg in master_node_sg_groups:
master_sg_public = False
for sg in ec2_client.security_groups.values():
if sg.id == master_sg:
for ingress_rule in sg.ingress_rules:
if check_security_group(ingress_rule, "-1"):
master_sg_public = True
break
if master_sg_public:
master_public_security_groups.append(sg.id)
break
# Check Public Slave Security Groups
slave_node_sg_groups = deepcopy(
cluster.slave.additional_security_groups_id
)
if slave_node_sg_groups:
slave_node_sg_groups.append(cluster.slave.security_group_id)
else:
slave_node_sg_groups = [cluster.slave.security_group_id]
slave_public_security_groups = []
for slave_sg in slave_node_sg_groups:
slave_sg_public = False
for sg in ec2_client.security_groups.values():
if sg.id == slave_sg:
for ingress_rule in sg.ingress_rules:
if check_security_group(ingress_rule, "-1"):
slave_sg_public = True
break
if slave_sg_public:
slave_public_security_groups.append(sg.id)
break
if master_public_security_groups or slave_public_security_groups:
report.status = "FAIL"
report.status_extended = f"EMR Cluster {cluster.id} is publicly accessible through the following Security Groups:"
report.status_extended += (
f" Master Node {master_public_security_groups}"
if master_public_security_groups
else ""
)
report.status_extended += (
f" Slaves Nodes {slave_public_security_groups}"
if slave_public_security_groups
else ""
)
findings.append(report)
return findings