This check flags GCP firewall rules that allow RDP (TCP 3389) from 0.0.0.0/0, exposing Windows servers to the entire internet. Lock RDP down to known IP ranges or move it behind Identity-Aware Proxy, then delete the open rule.
RDP exposed to the public internet is one of the most reliably exploited misconfigurations in cloud environments. Attackers do not need to find your servers, they scan the entire IPv4 space continuously and your open port 3389 will show up in their results within minutes of going live. This check catches GCP firewall rules that hand that door wide open.
What this check detects
The vpc_openrdp check inspects every firewall rule in your GCP VPC networks and flags any rule that meets all of these conditions:
- Direction is
INGRESS - Action is
ALLOW - The rule covers TCP port
3389(either explicitly, as part of a range, or via an allow-all rule) - The source range includes
0.0.0.0/0
RDP, the Remote Desktop Protocol, is how administrators log into Windows servers over the network. When the source range is 0.0.0.0/0, anyone on the internet can attempt to connect. That is the problem.
Note: GCP firewall rules are evaluated per-network and applied to instances by target tags, service accounts, or by applying to all instances in the network. A single overly broad rule can expose far more machines than you expect, so the presence of the rule matters even if you think only one VM is affected.
Why it matters
Public RDP is a top entry point for ransomware. The attack pattern is well documented and depressingly consistent:
- Automated scanners (Shodan, Censys, and countless botnets) find your open 3389 port.
- Credential-stuffing and brute-force tools hammer the login with leaked or common username and password combinations.
- Once in, the attacker has an interactive session on a Windows host. From there they move laterally, dump credentials, disable backups, and deploy ransomware.
The FBI and CISA have repeatedly named exposed RDP as a leading initial access vector for ransomware crews. Groups like Ryuk, Dharma, and SamSam built entire operations around scanning for open 3389. This is not a theoretical risk, it is a daily occurrence.
An RDP port left open to the internet will receive brute-force login attempts within minutes. The question is not whether you will be targeted, but whether your credentials hold.
Even with strong passwords, public RDP exposes you to protocol-level vulnerabilities. BlueKeep (CVE-2019-0708) was a wormable remote code execution flaw in RDP that required no authentication at all. A patched server today can become a vulnerable server tomorrow when the next CVE lands, and a public attack surface means you are racing the clock every time.
Warning: Many teams assume a strong Windows admin password is enough. It is not. Brute-force is only one attack path. Unauthenticated RDP CVEs, NLA bypasses, and stolen session tokens all bypass password strength entirely.
How to fix it
The goal is to stop accepting RDP connections from arbitrary internet addresses. You have three solid options, in order of preference.
Option 1: Use Identity-Aware Proxy (recommended)
IAP for TCP forwarding lets you reach RDP without any public exposure at all. Connections are brokered through Google's infrastructure and gated by IAM, so you authenticate with your Google identity rather than punching a hole in the firewall.
First, allow ingress only from the IAP forwarding range instead of the public internet:
gcloud compute firewall-rules create allow-rdp-from-iap \
--network=YOUR_NETWORK \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:3389 \
--source-ranges=35.235.240.0/20 \
--target-tags=rdp-server
Then grant a user permission to tunnel through IAP:
gcloud projects add-iam-policy-binding YOUR_PROJECT \
--member="user:[email protected]" \
--role="roles/iap.tunnelResourceAccessor"
Now connect through the tunnel from your workstation:
gcloud compute start-iap-tunnel YOUR_INSTANCE 3389 \
--local-host-port=localhost:3389 \
--zone=YOUR_ZONE
Point your RDP client at localhost:3389 and you are in, with no public port open anywhere.
Option 2: Restrict the source range to known IPs
If you cannot use IAP, narrow the rule to your office or VPN egress addresses. Update the existing rule rather than leaving the broad one in place:
gcloud compute firewall-rules update OPEN_RDP_RULE_NAME \
--source-ranges=203.0.113.10/32,198.51.100.0/24
Option 3: Delete the rule entirely
If nothing legitimately needs RDP through this rule, remove it.
Danger: Deleting a firewall rule can lock you out of instances that depend on it for management access. Confirm you have an alternate path (IAP, a bastion host, or Serial Console access) before running this. There is no undo for an active session that drops mid-command.
gcloud compute firewall-rules delete OPEN_RDP_RULE_NAME
Fixing it in the console
- Go to VPC network → Firewall in the GCP Console.
- Find the rule allowing TCP 3389 with source
0.0.0.0/0. - Click the rule, then Edit.
- Under Source IPv4 ranges, replace
0.0.0.0/0with35.235.240.0/20(for IAP) or your specific corporate ranges. - Click Save.
How to prevent it from happening again
Fixing one rule is good. Making sure nobody can create another open RDP rule is better. Bake the guardrail into your infrastructure pipeline.
Define firewall rules in Terraform with locked-down sources
resource "google_compute_firewall" "rdp_iap_only" {
name = "allow-rdp-from-iap"
network = google_compute_network.main.id
allow {
protocol = "tcp"
ports = ["3389"]
}
# IAP forwarding range only, never 0.0.0.0/0
source_ranges = ["35.235.240.0/20"]
target_tags = ["rdp-server"]
direction = "INGRESS"
}
Block open RDP at the org level with a policy constraint
Use an Organization Policy or a custom constraint to reject firewall rules that open 3389 to the world. You can also enforce it as a pre-merge check with Policy Controller or OPA Gatekeeper. Here is the idea expressed as a Rego rule for IaC scanning:
deny[msg] {
rule := input.resource.google_compute_firewall[name]
rule.direction == "INGRESS"
rule.source_ranges[_] == "0.0.0.0/0"
port := rule.allow[_].ports[_]
port == "3389"
msg := sprintf("Firewall %v exposes RDP (3389) to the internet", [name])
}
Tip: Run tfsec, checkov, or terrascan in your CI pipeline as a required status check. These tools already ship with rules for public RDP and SSH, so you get coverage with almost no custom config. A failed scan blocks the merge before the rule ever reaches production.
For environments that already exist, schedule continuous monitoring so a manual console change does not quietly reintroduce the problem. Lensix re-runs vpc_openrdp on every scan and alerts you the moment an open rule appears, which closes the gap between someone clicking the wrong button and you finding out.
Best practices
- Never expose management ports publicly. RDP (3389) and SSH (22) should never accept
0.0.0.0/0. Treat any such rule as an incident. - Prefer IAP over bastion hosts. IAP removes the public attack surface entirely and ties access to IAM, so you get audit logs and centralized revocation for free.
- Use target tags or service accounts, not network-wide rules. Scope each rule to the instances that actually need it, limiting blast radius.
- Enable Cloud Logging for firewall rules. Turn on
--enable-loggingso you can audit who is connecting and from where. - Layer your defenses. Even behind IAP, require Network Level Authentication, enforce strong unique credentials, and keep Windows patched.
- Review firewall rules on a schedule. Rules accumulate. A quarterly audit catches the temporary rule that someone forgot to remove six months ago.
The fix here is cheap and the risk is severe. Closing public RDP is one of the highest-return security changes you can make in a GCP environment, so move it to the top of the list.

