Firewall rules allowing port ranges instead of discrete ports
What does ZopNight detect here?
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.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1253 |
| Category | compliance |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | vpc_firewall_range.go |
Where it applies
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
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.