Back to blog
Best PracticesCloud SecurityDatabasesGCPNetworking

Firewall Allows Public Oracle: Closing Open GCP Database Ports

Learn why a GCP firewall rule exposing Oracle port 1521 to the public internet is dangerous, and how to fix and prevent it with CLI, Terraform, and policy-as-code.

TL;DR

This check flags any GCP firewall rule that exposes Oracle's database listener (port 1521) to 0.0.0.0/0. Public database listeners are prime targets for brute force and exploitation, so restrict the source ranges to known internal CIDRs or remove the rule entirely.

Database ports do not belong on the public internet. It sounds obvious, yet open Oracle listeners show up regularly in cloud audits, usually because someone needed "quick access" during a migration and never tightened the rule afterward. The Lensix Firewall Allows Public Oracle check (vpc_openoracle) catches exactly this: a GCP VPC firewall rule that permits inbound traffic to TCP port 1521 from any source address.


What this check detects

The check scans your VPC firewall rules and looks for ingress rules that meet all of the following conditions:

  • Direction is INGRESS
  • Action is ALLOW
  • The rule targets TCP port 1521 (the default Oracle TNS listener port), either directly or via a port range that includes it
  • The source range includes 0.0.0.0/0, meaning any IP address on the internet

Port 1521 is the default port for the Oracle Transparent Network Substrate (TNS) listener, the process that brokers client connections to an Oracle database instance. If a firewall rule opens it to the world, anyone who can reach your VPC can attempt to talk to that listener.

Note: GCP firewall rules are stateful and apply at the VPC network level, not per instance. A single overly permissive rule can expose every VM that matches its target tags or service accounts, so the blast radius is often larger than people expect.


Why it matters

An exposed Oracle listener is a direct line to your data. Attackers do not need to find a clever foothold elsewhere first. The listener itself becomes the entry point.

Real attack scenarios

  • TNS listener enumeration. Tools like tnscmd10g and the Oracle modules in Metasploit can query an unauthenticated listener to leak the database SID, version, and service names. That information shapes the next stage of an attack.
  • Credential brute forcing. Once an attacker knows the SID, they can hammer default and common accounts (SYS, SYSTEM, SCOTT, DBSNMP) with password lists. Legacy Oracle installations are notorious for default credentials that were never changed.
  • Known CVE exploitation. Older listener versions have well documented vulnerabilities, including the long-lived TNS Poison flaw (CVE-2012-1675) that allows man-in-the-middle hijacking of database sessions. An internet-reachable listener gives an attacker unlimited time to probe for these.
  • Automated scanning. Mass scanners like Shodan and Censys index open database ports continuously. A public 1521 listener will be found within hours, not days.

Danger: A public Oracle listener combined with an unpatched database or default credentials is effectively a public data breach waiting to be discovered. Treat any open 1521 finding as an active incident until you confirm no unauthorized access occurred.

Business impact

Oracle databases tend to hold the data that matters most: financial records, customer PII, ERP and HR systems. A compromise here is not a contained incident. It triggers breach notification obligations under regulations like GDPR, HIPAA, and PCI DSS, and it almost always involves regulated data. The cost is measured in fines, remediation, and lost trust, not just engineering hours.


How to fix it

The fix is to stop the rule from accepting traffic from the entire internet. You have two main options: scope the source ranges down to trusted networks, or remove the rule if nothing legitimately needs it.

Step 1: Find the offending rule

List firewall rules and filter for ones touching port 1521:

gcloud compute firewall-rules list \
  --format="table(name, network, direction, sourceRanges.list():label=SRC_RANGES, allowed[].map().firewall_rule().list():label=ALLOW)" \
  --filter="allowed.ports:1521 AND direction=INGRESS"

Inspect a specific rule to see its full definition before you change anything:

gcloud compute firewall-rules describe RULE_NAME --format=json

Step 2: Identify who actually needs access

Before tightening, work out which clients legitimately connect to the database. This is usually a known set of application servers, a bastion host, or an on-prem range reaching in over a VPN or Interconnect. Collect those CIDR blocks.

Warning: Removing or narrowing a firewall rule takes effect immediately and can drop live database connections. Confirm the legitimate source ranges with the application team and change the rule during a maintenance window if the database serves production traffic.

Step 3: Restrict the source ranges

Update the rule so it only allows the internal networks that need it. For example, to limit access to a private subnet and a management range:

gcloud compute firewall-rules update RULE_NAME \
  --source-ranges=10.0.1.0/24,10.0.2.0/24

If you prefer to scope by workload identity rather than IP, target the database VMs with a service account and only allow traffic from the client service account:

gcloud compute firewall-rules update RULE_NAME \
  --source-ranges=10.0.1.0/24 \
  --target-service-accounts=oracle-db@PROJECT_ID.iam.gserviceaccount.com

Step 4: Or delete the rule entirely

Danger: Deleting a firewall rule is irreversible and severs all traffic that depended on it. Verify nothing legitimate uses this path before running the command below.

gcloud compute firewall-rules delete RULE_NAME

Console steps

  1. Open VPC network → Firewall in the Google Cloud Console.
  2. Find the rule allowing TCP 1521. Look in the Source filters column for 0.0.0.0/0.
  3. Click the rule, then Edit.
  4. Under Source IPv4 ranges, replace 0.0.0.0/0 with your trusted CIDRs.
  5. Click Save, or delete the rule if it is no longer needed.

Tip: For long-term access to a private database, skip public exposure entirely. Use Identity-Aware Proxy (IAP) TCP forwarding to reach the instance over an authenticated tunnel, no public IP or open port required. The source range becomes Google's IAP range (35.235.240.0/20) instead of the open internet.


How to prevent it from happening again

Fixing one rule is not enough. The same mistake will reappear unless you put guardrails in the path of whoever creates firewall rules next.

Define firewall rules in Terraform

Manage firewall rules as code so every change is reviewed and the source ranges are explicit:

resource "google_compute_firewall" "oracle_internal" {
  name      = "allow-oracle-internal"
  network   = google_compute_network.main.id
  direction = "INGRESS"

  allow {
    protocol = "tcp"
    ports    = ["1521"]
  }

  # Internal ranges only, never 0.0.0.0/0
  source_ranges           = ["10.0.1.0/24"]
  target_service_accounts = ["[email protected]"]
}

Block the bad pattern in CI

Use a policy-as-code tool to fail the pipeline before a public database port ever reaches production. With OPA Conftest, a rule like this rejects any plan that opens 1521 to the world:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "google_compute_firewall"
  rule := resource.change.after
  rule.source_ranges[_] == "0.0.0.0/0"
  allow := rule.allow[_]
  allow.ports[_] == "1521"
  msg := sprintf("Firewall '%s' exposes Oracle port 1521 to the internet", [rule.name])
}

Wire it into your pipeline against a Terraform plan:

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json

Tip: Pair the CI gate with continuous monitoring in Lensix. CI catches new mistakes in IaC, but Lensix catches drift, manual console changes, and rules created outside your pipelines. You want both layers.

Enforce org-level constraints

GCP Organization Policy lets you block public exposure across every project at once. The constraints/compute.vmExternalIpAccess constraint and firewall hierarchical policies can prevent risky ingress rules from being created in the first place, rather than relying on each team to remember.


Best practices

  • Never expose database ports publicly. Oracle (1521), MySQL (3306), PostgreSQL (5432), MSSQL (1433), MongoDB (27017), and Redis (6379) should all live behind private networking.
  • Default to deny. Start with no ingress and add narrow rules for specific needs. The opposite approach, opening broadly and trimming later, leaves gaps.
  • Use bastions or IAP for human access. Engineers who need to reach a database should go through an authenticated jump path, not a port open to their home IP.
  • Tag and scope every rule. Target rules at service accounts or network tags so they apply to the intended workloads only, not every VM in the network.
  • Review firewall rules on a schedule. Temporary rules created during migrations and incidents are the usual source of these findings. Audit them quarterly and remove anything stale.
  • Patch the listener. Even with tight firewalls, keep the Oracle listener patched. Defense in depth means an exposure is not automatically a breach.

An open Oracle listener is one of the highest-severity network findings you can have, because it puts your most sensitive data one weak password away from the internet. Lock the source ranges down, codify the rule, and gate the pattern in CI so it never comes back.

Fix Public Oracle Port 1521 on GCP Firewalls | Lensix | Lensix