Back to Checks
aws

RDS instance is not publicly exposed to the Internet

rds_instance_no_public_access
Service: rdsSeverity: critical
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:AwsRdsDbInstance

RDS DB instances are assessed for internet exposure using the PubliclyAccessible setting, security group ingress to the DB port from any address, and whether subnets are public. Instances that combine an internet-facing endpoint, open ingress, and public subnets are identified.

Risk

An internet-reachable database invites:

  • Brute-force and vulnerability probing, risking availability and integrity
  • Unauthorized queries and dumps leading to confidentiality loss

Compromise can enable lateral movement and persistence within the VPC.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks rds_instance_no_public_access

Recommendation

Keep databases private by applying least privilege at the network layer:

  • Set PubliclyAccessible to false
  • Place instances in private subnets
  • Deny 0.0.0.0/0 and ::/0 on the DB port
  • Expose access via private endpoints, VPN, or an application tier/DB proxy

Adopt defense in depth with monitoring and strong auth.

Remediation

CLI

aws rds modify-db-instance --db-instance-identifier <db_instance_id> --no-publicly-accessible --apply-immediately

Terraform

# Ensure the RDS instance is not publicly accessible
resource "aws_db_instance" "<example_resource_name>" {
  publicly_accessible = false  # CRITICAL: disables public access (no public endpoint)
}

NativeIaC

# CloudFormation: make the RDS instance private
Resources:
  <example_resource_name>:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: <example_resource_id>
      PubliclyAccessible: false  # CRITICAL: disables public access so the instance is not Internet-facing

Other

1In the AWS console, go to RDS > Databases and select your DB instance
2Click Modify
3In Connectivity (Connectivity & security), set Public access to No (Not publicly accessible)
4Choose Continue, select Apply immediately (or during the next window), then click Modify DB instance

Check Code

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.rds.rds_client import rds_client
from prowler.providers.aws.services.vpc.vpc_client import vpc_client


class rds_instance_no_public_access(Check):
    def execute(self):
        findings = []
        for db_instance in rds_client.db_instances.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=db_instance)
            report.status = "PASS"
            report.status_extended = (
                f"RDS Instance {db_instance.id} is not publicly accessible."
            )
            if db_instance.public:
                report.status_extended = f"RDS Instance {db_instance.id} is set as publicly accessible, but is not publicly exposed."
                # Check if any DB Instance Security Group is publicly open
                if db_instance.security_groups:
                    public_sg = False
                    report.status_extended = f"RDS Instance {db_instance.id} is set as publicly accessible but filtered with security groups."
                    db_instance_port = db_instance.endpoint.get("Port")
                    if db_instance_port:
                        for security_group in ec2_client.security_groups.values():
                            if security_group.id in db_instance.security_groups:
                                for ingress_rule in security_group.ingress_rules:
                                    if check_security_group(
                                        ingress_rule,
                                        "tcp",
                                        [db_instance_port],
                                        any_address=True,
                                    ):
                                        report.status_extended = f"RDS Instance {db_instance.id} is set as publicly accessible and security group {security_group.name} ({security_group.id}) has {db_instance.engine} port {db_instance_port} open to the Internet at endpoint {db_instance.endpoint.get('Address')} but is not in a public subnet."
                                        public_sg = True
                                        if db_instance.subnet_ids:
                                            for subnet_id in db_instance.subnet_ids:
                                                if (
                                                    subnet_id in vpc_client.vpc_subnets
                                                    and vpc_client.vpc_subnets[
                                                        subnet_id
                                                    ].public
                                                ):
                                                    report.status = "FAIL"
                                                    report.status_extended = f"RDS Instance {db_instance.id} is set as publicly accessible and security group {security_group.name} ({security_group.id}) has {db_instance.engine} port {db_instance_port} open to the Internet at endpoint {db_instance.endpoint.get('Address')} in a public subnet {subnet_id}."
                                                    break
                                    if public_sg:
                                        break
                            if public_sg:
                                break

            findings.append(report)

        return findings