Back to Checks
openstack

Security groups do not allow RDP from the Internet

networking_security_group_allows_rdp_from_internet
Service: networkingSeverity: high
OfficialBuilt-inVerified
Categories:internet-exposedtrust-boundaries
Resource Type:OS::Neutron::SecurityGroup

OpenStack security groups are evaluated to verify that RDP (port 3389) is not exposed to the Internet (0.0.0.0/0 or ::/0). Security groups act as virtual firewalls controlling Windows instance traffic. Unrestricted RDP access violates least privilege and creates significant attack surface. Best practices recommend restricting RDP to known IP ranges, RD Gateway, or VPN.

Risk

Unrestricted RDP exposes Windows instances to brute-force attacks, password spraying, and RDP vulnerabilities (BlueKeep, DejaBlue). Compromised sessions enable ransomware deployment, credential theft, privilege escalation, and lateral movement. Successful compromise leads to domain controller access, Active Directory enumeration, and organization-wide data exfiltration.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler openstack --checks networking_security_group_allows_rdp_from_internet

Recommendation

Restrict RDP access to known IP ranges using Remote Desktop Gateway or VPN instead of allowing 0.0.0.0/0. Enable Network Level Authentication (NLA) and implement multi-factor authentication for all RDP sessions. Deploy LAPS for unique local administrator passwords and patch Windows systems regularly to address RDP vulnerabilities.

Remediation

Terraform

# Terraform: restrict RDP to known IP ranges, never 0.0.0.0/0

resource "openstack_networking_secgroup_v2" "windows_servers" {
  name        = "windows-servers-sg"
  description = "Security group for Windows application servers"
}

# GOOD: RDP restricted to corporate VPN IP range
resource "openstack_networking_secgroup_rule_v2" "rdp_from_vpn" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 3389
  port_range_max    = 3389
  remote_ip_prefix  = "10.8.0.0/24"  # VPN gateway range
  security_group_id = openstack_networking_secgroup_v2.windows_servers.id
}

# GOOD: RDP from RD Gateway security group (reference-based rule)
resource "openstack_networking_secgroup_rule_v2" "rdp_from_rd_gateway" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 3389
  port_range_max    = 3389
  remote_group_id   = openstack_networking_secgroup_v2.rd_gateway.id  # Reference
  security_group_id = openstack_networking_secgroup_v2.windows_servers.id
}

# Remote Desktop Gateway security group (least privilege)
resource "openstack_networking_secgroup_v2" "rd_gateway" {
  name        = "rd-gateway-sg"
  description = "Security group for Remote Desktop Gateway"
}

# RD Gateway accepts RDP from corporate network only
resource "openstack_networking_secgroup_rule_v2" "rd_gateway_rdp" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 3389
  port_range_max    = 3389
  remote_ip_prefix  = var.corporate_cidr  # e.g., 198.51.100.0/24
  security_group_id = openstack_networking_secgroup_v2.rd_gateway.id
}

# RD Gateway also needs HTTPS for external RDG clients
resource "openstack_networking_secgroup_rule_v2" "rd_gateway_https" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 443
  port_range_max    = 443
  remote_ip_prefix  = var.corporate_cidr  # Restrict to corporate IPs
  security_group_id = openstack_networking_secgroup_v2.rd_gateway.id
}

# BAD: Never do this - RDP from anywhere (0.0.0.0/0)
# resource "openstack_networking_secgroup_rule_v2" "rdp_from_anywhere" {
#   direction         = "ingress"
#   ethertype         = "IPv4"
#   protocol          = "tcp"
#   port_range_min    = 3389
#   port_range_max    = 3389
#   remote_ip_prefix  = "0.0.0.0/0"  # DANGEROUS - DO NOT USE
#   security_group_id = openstack_networking_secgroup_v2.windows_servers.id
# }

Other

1Navigate to Network > Security Groups
2Locate the security group with unrestricted RDP access
3Click Manage Rules
4Delete RDP rules with Remote 0.0.0.0/0 or ::/0
5Click Add Rule and create a new RDP rule
6Set Remote to CIDR with your trusted IP (e.g., 198.51.100.50/32)
7Save changes and verify connectivity

Check Code

from typing import List

from prowler.lib.check.models import Check, CheckReportOpenStack
from prowler.providers.openstack.lib.security_groups import check_security_group_rule
from prowler.providers.openstack.services.networking.networking_client import (
    networking_client,
)


class networking_security_group_allows_rdp_from_internet(Check):
    """Ensure security groups do not allow RDP from the Internet."""

    def execute(self) -> List[CheckReportOpenStack]:
        findings: List[CheckReportOpenStack] = []

        for sg in networking_client.security_groups:
            report = CheckReportOpenStack(metadata=self.metadata(), resource=sg)
            report.resource_id = sg.id
            report.resource_name = sg.name
            report.region = sg.region

            # Check if any rule allows RDP from 0.0.0.0/0 or ::/0
            rdp_exposed = False
            exposed_rules = []

            for rule in sg.security_group_rules:
                if check_security_group_rule(
                    rule=rule,
                    protocol="tcp",
                    ports=[3389],
                    any_address=True,
                    direction="ingress",
                ):
                    rdp_exposed = True
                    cidr = rule.remote_ip_prefix or "0.0.0.0/0"
                    exposed_rules.append(
                        f"rule {rule.id} ({rule.protocol}/{cidr}:{rule.port_range_min}-{rule.port_range_max})"
                    )

            if rdp_exposed:
                report.status = "FAIL"
                rules_str = ", ".join(exposed_rules)
                report.status_extended = f"Security group {sg.name} ({sg.id}) allows unrestricted RDP access (port 3389) from the Internet via {rules_str}."
            else:
                report.status = "PASS"
                report.status_extended = f"Security group {sg.name} ({sg.id}) does not allow RDP (port 3389) from the Internet."

            findings.append(report)

        return findings