Ensure that SSH access is restricted from the internet
A VPC ingress firewall rule that allows TCP port 22 from 0.0.0.0/0 lets any host on the internet open a connection to the SSH daemon on every instance the rule targets. The control requires that no ingress rule in a VPC network allows port 22 from 0.0.0.0/0, so SSH arrives only from the specific source IP ranges you name.
What the benchmark asks for
A rule fails the control when all four of these are true at once. The direction is ingress, the source range includes 0.0.0.0/0, the protocol is tcp or all protocols, and the port set includes 22. A rule that allows all TCP, or all protocols, carries no port list at all, and an empty port list means every port, including 22. A rule written as tcp:0-65535 fails for the same reason as one written as tcp:22.
The control is about the rule, not about any one instance. A rule that allows 0.0.0.0/0 on port 22 and targets a network tag that no instance currently carries still fails, because nothing stops an instance from carrying that tag tomorrow.
Exposure matters here because the thing listening on port 22 can be made to run attacker code without any credential being presented. CVE-2024-3094 placed a backdoor in sshd that allowed remote code execution against a server by an attacker who never authenticated. An instance reachable only from a jump host is not safe from such a flaw, but it is reachable by far fewer people.
How it works
Google's default VPC network arrives with a pre-populated rule named default-allow-ssh that allows inbound TCP 22 from any IPv4 address. Any project built on the default network is reachable over SSH from the internet until someone removes or narrows that rule, and nobody has to create a rule for this to be true.
That rule carries priority 65534, the least-preferred tier Google Cloud uses for its own pre-populated rules. Priority is a number from 0 to 65535 and the lowest number wins. Because default-allow-ssh sits near the bottom, a deny rule for port 22 at any lower number overrides it for the instances that deny rule targets, and the allow rule stays in place while having no effect.
The same ordering works against you in the other direction. When two rules could match the same packet, the lower priority number decides, and how specific a rule is counts for nothing. A broad allow-all-ingress rule at priority 1000 beats a carefully scoped deny on port 22 at priority 2000, even though the deny names an exact source range and the allow names none.
VPC firewall rules are stateful. Once an inbound SSH connection is allowed, return traffic on that connection is permitted automatically and no separate egress rule can block it. Restricting SSH is therefore entirely a question of ingress rules. There is no matching egress rule to write and none to forget.
Sources and targets do not have to be IP ranges. A rule can target instances by network tag or by service account, and a source can be expressed the same way for traffic inside the network. Scoping by service account is the stronger of the two, because an instance's service account is set at creation and changing it is a deliberate, privileged operation.
What the control does not cover
Network tags are editable. Any IAM principal with permission to edit an instance can add or remove a tag, and the instance does not have to be stopped for that to take effect. A rule that allows SSH to instances tagged bastion is only as tight as the set of people who can write tags on instances.
Tightening a rule stops new connection attempts and nothing else. An SSH session established before the change keeps running, and the firewall will not interrupt it. If you are closing an exposed port in response to an incident, closing the rule is step one and terminating live sessions is step two.
A firewall rule belongs to one VPC network. It does not extend to a peered network, and it does not extend across a Cloud VPN tunnel. Each network needs its own rules, because the rules you write in one network do not travel to the networks it connects to.
Finally, the control reads rules, not reachability. An instance with no external IP address and no forwarding rule in front of it is not reachable from the internet whatever the firewall says.
What it costs to comply
Narrowing the rule blocks every new SSH connection from an address outside the ranges you keep, while sessions already open when you make the change stay up. Before you narrow it, find out who uses it and what replaces it for them, because the failure mode is that somebody loses access to a production host at the moment they need it.
Replacing the rule with an allowlist of specific source IP ranges keeps the rule in place and cuts who can reach port 22. It grants SSH to everyone who can reach those ranges rather than to a named set of principals, and the rule records nothing about who connected unless you turn on logging for it, which writes an entry to Cloud Logging for each connection the rule matches.
Google's own recommendation goes further. Rather than narrowing the source range, remove the external IP address from the instance and front SSH with Identity-Aware Proxy, so there is no internet-facing address for a scanner to find. Instances with no external IP address have no outbound path to the internet either, so anything that needs to reach out needs a route provided for it.
How to check your environment
List the firewall rules in every VPC network, and read the name, direction, source ranges, and the allowed protocol and port list on each one. The listing covers one project at a time, so run it again for every project you hold. Treat an empty port list as port 22 included.
Then sort the matching rules by priority and read down the list. The rule that governs an instance is the lowest-numbered one that matches, so a deny you added last week tells you nothing until you have confirmed there is no allow above it. The console shows the effective rules for a single instance's network interface, which is the fastest way to settle an argument about what actually applies.
Check the default network separately in each project. If it exists and nobody has deleted or modified default-allow-ssh, the rule is still there and still allows TCP 22 from any IPv4 address.
How to implement it
For a rule you intend to keep, replace 0.0.0.0/0 with the specific CIDR blocks that need SSH. If the rule exists only for occasional maintenance, keep it disabled and enable it for the window, then disable it again. A disabled rule matches no traffic, so the exposure lasts as long as the work rather than as long as the project.
A deny rule for TCP 22 from 0.0.0.0/0 covering the whole network holds only while no allow rule carries a lower priority number, so check for one before you rely on the deny.
Delete default-allow-ssh where you find it. Removing it does not break instances that reach the internet outbound, because egress is governed separately and return traffic on outbound connections is already allowed by statefulness.
Remove the external IP and move SSH behind Identity-Aware Proxy. Reaching an instance then runs through IAP rather than through an address a scanner can find.
Further reading
- VPC firewall rules overview. Rule components, priority evaluation, statefulness, targets by tag and service account, and the pre-populated rules in the default network.
- Safeguarding SSH with Identity-Aware Proxy. Why Google recommends removing external IPs over narrowing source ranges, and how IAP changes SSH access from a network grant to an identity grant.
