cis_extensions.internet_ssh_rdp — version 1.2
Source: presets
Purpose
CIS 3.6 / 3.7 — SSH and RDP closed to the internet, for every network in the organisation at once.
HOW IT WORKS. A hierarchical firewall policy attached to the organisation is read
before any VPC firewall rule. It passes TCP 22 and 3389 from
admin_port_source_ranges on to the VPC rules, which still decide, and denies them
from every other address, IPv4 and IPv6 — so a VPC rule that opens them to 0.0.0.0/0
opens nothing.
ON BY DEFAULT, one of two fragments here that are. An organisation that has not closed
22 and 3389 to the internet is one scan away from the finding it will be asked about,
and the two controls this satisfies are about internet exposure, not about internal
administration. An estate that switches it off writes a deviates claim, whose
reason is mandatory and which an auditor reads.
WHAT THE DEFAULT RANGES MEAN, and why there are four. The deny matches 0.0.0.0/0,
which is every IPv4 address — private ones included — and a hierarchical policy is
read before the VPC rules that would otherwise allow internal traffic. A pass list of
only the IAP range would therefore deny SSH between two instances in one subnet. So
the three RFC1918 blocks are in the list beside IAP: what is denied is the internet,
and internal administration is handed to the VPC rules, which is where it belongs.
An estate that wants internal SSH denied as well removes those three entries — a
deliberate narrowing of its own policy, not a fork of this pack.
WHAT IT BREAKS. A session that reaches an instance from a PUBLIC address on 22 or 3389 ends the moment the policy is attached. A bastion with an external IP needs its range listed here, or its access moved to IAP TCP forwarding, which reaches instances without an open admin port at all. Google says the same in its own SSH guidance: do not expose SSH directly over the public internet, and reach a bastion through IAP. Google's published example attaches such a policy to FOLDERS and allows only selected VMs; this one is organisation-wide and allows the listed ranges everywhere, which is the stricter reading and the one an estate can narrow per folder if it needs to.
WHAT IS DENIED, IN FULL. SSH is TCP 22 and SCTP 22; RDP is TCP 3389 and UDP 3389 — Google's own detectors check all four, so a deny written for TCP alone leaves UDP 3389 open and the finding standing. Each rule here carries the whole set.
THE TWO PASS RULES DO NOT LOG, AND CANNOT. Google allows logging on allow and deny
rules only: "logging cannot be enabled for goto_next rules". So the record is of what
was REFUSED — each denied admin-port connection writes one entry, charged as logging
volume — and an accepted IAP session leaves no firewall log at all. That asymmetry is
Google's, not a choice made here, and it is worth knowing before someone goes looking
for proof that IAP access works.
presets carries these to a SIEM,
and its firewall sink is empty until this pack is on.
IPv4 AND IPv6 ARE SEPARATE RULES because a rule's sources may not mix the two. Each family gets a pass rule and a deny: 1000/1002 for IPv4, 1001/1003 for IPv6. IAP has its own IPv6 range for IPv6 VMs, which is why the pass list is in two params.
PROWLER READS THE VPC RULES, NOT THIS POLICY: a VPC rule allowing 0.0.0.0/0 on port 22 still fails there, and report-compliance shows the row CONTESTED. Such a rule has no effect while the policy is attached; deleting it settles the row.
Use it
params {
cis_block_internet_ssh_rdp = true
// the pack's own defaults — copy only the lines you change
admin_port_source_ranges = [
"35.235.240.0/20",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
]
admin_port_source_ranges_ipv6 = [ "2600:2d00:1:7::/64", "fc00::/7" ]
}
use "presets/cis/internet-ssh-rdp.satz" when cis_block_internet_ssh_rdp
cis_block_internet_ssh_rdp is the gate: while it is falsy the pack contributes nothing — no resources, no params, no claims. It is declared by CIS_GCP_Foundation_4_0, and the estate's own binding wins.
Needs from outside the pack:
customer_organization_id— declared byestate_core; use one of them alongside, or declare it in the estate.
Params
| param | default |
|---|---|
admin_port_source_ranges |
[ "35.235.240.0/20", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ] |
admin |
[ "2600:2d00:1:7::/64", "fc00::/7" ] |
Questions
What to ask before this pack is configured, and what changing the answer costs.
| asks about | prompt | reversal | blast | recommends |
|---|---|---|---|---|
admin |
Which IPv6 ranges may still reach SSH and RDP? | edit | low | — |
admin_port_source_ranges |
Which IPv4 ranges may still reach SSH and RDP (the VPC firewall rules decide for them)? | edit | high | — |
admin_port_source_ranges_ipv6 — The IPv6 half of the same decision, separate because a firewall rule's sources are IPv4 or IPv6 and never both. The default is Google's IAP range for IPv6 VMs and the unique-local block that plays RFC1918's part. An organisation with no IPv6 VMs can leave it as it is: the rule matches nothing and costs nothing.
admin_port_source_ranges — Every address outside this list is denied on ports 22 and 3389 before any VPC rule is read. The default lists Google's IAP TCP-forwarding range — IAP reaches an instance without an open admin port — and the three private blocks, because the deny matches 0.0.0.0/0 and would otherwise cut SSH between two instances in one subnet. A bastion with a PUBLIC address needs its range here or it loses access the moment the policy attaches. Removing the private blocks denies internal SSH too, which is a narrowing some estates want.
Contributes
| resource type | labels |
|---|---|
google |
cis_admin_ports |
google |
cis_admin_ports |
google |
cis_admin_ports_listed, cis_admin_ports_listed_ipv6, cis, cis |
Claims
| framework | control | title | kind | witnesses | interpretation |
|---|---|---|---|---|---|
| cis-gcp 4.0 | 3.6 | SSH restricted from the internet | implements | googlegooglegoogle |
A firewall policy attached to the organisation denies TCP 22 from the internet before any VPC rule is read; only the listed ranges pass to the VPC rules. |
| cis-gcp 4.0 | 3.7 | RDP restricted from the internet | implements | googlegooglegoogle |
As for §3.6, for TCP 3389. |
| cis-gcp 5.0 | 3.6 | SSH restricted from the internet | implements | googlegooglegoogle |
As for 4.0 §3.6 (5.0 §3.6, unchanged). |
| cis-gcp 5.0 | 3.7 | RDP restricted from the internet | implements | googlegooglegoogle |
As for 4.0 §3.7 (5.0 §3.7, unchanged). |
What the controls ask (this project's paraphrase from the catalog — never the framework's own text):
- RDP restricted from the internet (cis-gcp 4.0 §3.7, cis-gcp 5.0 §3.7) — No firewall path lets an internet address reach TCP 3389 on an instance.
- SSH restricted from the internet (cis-gcp 4.0 §3.6, cis-gcp 5.0 §3.6) — No firewall path lets an internet address reach TCP 22 on an instance.
Manual duties (a control stays partial until each is attested):
review-ranges(cis-gcp 4.0 §3.6, cis-gcp 4.0 §3.7, cis-gcp 5.0 §3.6, cis-gcp 5.0 §3.7) — The listed ranges are the exception to the control: re-read them when a bastion or partner range changes.
Not every resource this pack contributes is a witness:
google— contributed, but no claim names it._compute _firewall _policy .cis _admin _ports google— contributed, but no claim names it._compute _firewall _policy _rule .cis _admin _ports _listed google— contributed, but no claim names it._compute _firewall _policy _rule .cis _admin _ports _listed _ipv6
History
| version | date | change |
|---|---|---|
| 1.2 | 2026-09-30 | every claim states its measure without satz: gcloud (the commands that meet the control the way the claim's resources do), gcloud_check (the commands that show whether it is met) and risk (what goes wrong without it); require and report-compliance carry them in each control's measures. Nothing emitted changes |
| 1.1 | 2026-09-12 | ON by default (CIS pack 2.10), with the corrections a default-on pack needs. The pass list gains the three RFC1918 blocks beside the IAP range, because the deny matches 0.0.0.0/0 — every address, private ones included — and a hierarchical policy is read before the VPC rules: with IAP alone, SSH between two instances in one subnet was denied. IPv6 gets its own pass rule (IAP's 2600:2d00:1:7::/64 and fc00::/7), since a rule's sources may not mix families. Every rule now carries the control's whole protocol set — SSH on TCP 22 and SCTP 22, RDP on TCP 3389 and UDP 3389 — a TCP-only deny left UDP 3389 open. And the two DENY rules log: Google forbids logging on goto_next, so an accepted IAP session leaves no firewall record and only refusals do |
| 1.0 | 2026-09-11 | CIS 3.6 and 3.7, opt-in: a hierarchical firewall policy on the organisation denies TCP 22 and 3389 from the IPv4 and IPv6 internet and passes the listed ranges (IAP by default) to the VPC rules |
The whole library's history: the changelog in presets/CHANGELOG.md.
Notes
Prowler's checks for these two controls read VPC firewall rules, not the hierarchical firewall policy this pack writes, so report-compliance --prowler shows the rows CONTESTED while a permissive VPC rule still exists. Deleting that 0.0.0.0/0 rule settles it — the policy here denies the traffic either way.