# GCP Firewall Rule Uses Port Ranges

> Firewall rules whose allowed ports include a span like 8000-9000 rather than discrete ports are flagged medium by ZopNight rule RC-1253. A range admitting 1,001 ports to serve 2 services is standing attack surface. The discoverer parses every ports entry, so detection is proven from rule config.

Source: https://zop.dev/integrations/gcp/recommendations/gcp-firewall-rule-uses-port-ranges
Updated: 2026-08-19

---

## Spans reviewers cannot reason about

A firewall rule listing `tcp:443` makes a reviewable claim: the web tier is reachable. A
rule listing `tcp:8000-9000` makes a claim nobody can check. Which of those 1,001 ports
carry a real service, and which are simply open in case something binds there later?
Ranges usually enter a ruleset as an estimate ("the app uses some ports around 8080") and
then fossilize, because narrowing them requires the knowledge that writing them let the
team avoid gathering. Every change review after that inherits a span instead of a fact.

## How lo-hi parsing decides

The discoverer walks every `allowed[]` entry on the rule and parses each `ports[]` token.
A token of the form lo-hi where the two bounds differ (`1000-2000`) marks the rule as
range-using; a single port like `22` does not, and the scan covers *all* entries, not just
internet-facing ones, because range hygiene applies to internal rules too. The resulting
marker is written in both polarities, and RC-1253 fires only on an explicit true. A rule
whose port shape was never classified abstains. Provider config is the only input, read via
Cloud Asset Inventory under `roles/cloudasset.viewer`.

## Not the same finding as all-ports

A boundary worth knowing: an entry with no `ports[]` at all opens every port for its
protocol, but that is deliberately *not* counted as a range here, since it belongs to
the separate all-ports finding (RC-135), which is critical rather than medium. RC-1253 covers
the middle ground: rules that did enumerate, but enumerated with a span. The two findings
never double-report the same shape.

## Surface the ranges in your rule set

```bash
gcloud compute firewall-rules list \
  --format="table(name,network,allowed[].map().firewall_rule().list())" \
  | grep -E '[0-9]+-[0-9]+'
```

Each hyphenated entry in the output is a span this rule would flag.

## Shrinking a range safely

Replace estimation with observation: enable firewall rule logging on the ranged rule and
let a representative traffic cycle accumulate, then read off the discrete ports that
actually matched. Rewrite the rule to list exactly those, keep the ranged rule disabled,
not deleted, for a rollback window, and remove it once nothing breaks. The finding clears
automatically when the discoverer next scans the rule and finds only discrete ports.
