Security groups do not allow RDP from the Internet
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 Cloudprowler openstack --checks networking_security_group_allows_rdp_from_internetRecommendation
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
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 findingsReferences
- https://docs.openstack.org/neutron/latest/admin/intro-os-networking.html
- https://docs.openstack.org/security-guide/networking/architecture.html
- https://docs.openstack.org/api-ref/network/v2/
- https://www.cisa.gov/news-events/cybersecurity-advisories/aa19-168a
- https://www.ncsc.gov.uk/guidance/preventing-lateral-movement