GEN4: GCE Networking: VPC Networks, Firewalls, IAP Tunneling & PGA

Earlier this year we explored how Google Compute Engine (GCE)'s VPC networks and especially GCP's "Private Google Access" (PGA) allows VMs that lack external IP addresses continue to communicate with GCP's various APIs that require external connectivity. VMs that lack external IP addresses significantly reduce their attack service, but historically were cut off from accessing external GCP APIs like BigQuery or Vertex AI APIs due to those services being external services that must be accessed via their external IP addresses. While PGA is disabled by default on new VPC networks, it is a one-click enablement that instantly allows GCE VMs without external IP addresses to access any GCP API as if it did have an external IP. This offers the best of both worlds: a VM that has a reduced attack surface and still has full access to all GCP APIs. Critically, if a node regularly removes its public IP address and operates for times with only an internal IP address, PGA permits it to continue communicating with GCP APIs during those periods.

Not all VMs can operate without an external IP address. For example, web servers, web crawlers, ingest nodes, API gateways and other kinds of services all require the ability to connect with the outside world. For these nodes, VPC firewall rules make it trivial to greatly reduce their attack surface by limiting both ingress and egress traffic to authorized patterns and using least-privilege principles, walling off access to only the bare minimum ports and flows required for functionality.

Sometimes, however, there are VMs where administrators or developers require ongoing continuous SSH access from the outside world, which poses a very unique risk. How can a VM support SSH logins, but still reduce its risk surface?

It turns out GCP has native support for this in the form of IAP TCP Forwarding. This service is built into GCP and can be used as-is without any major configuration or project changes to transparently enable what amounts to a proxy in front of your VM instances. Instead of VMs receiving SSH traffic directly, users must first authenticate to the GCP IAP proxy which then forwards their SSH traffic to the node only after the user has authenticated. Since only authorized users who have already been authenticated by IAP can actually connect to the SSH port of the VM, this instantly silences the deluge of daily SSH probes and significantly reduces the node's exposed surface area. Of course, this doesn't stop malicious insider threats from those authorized to connect to a VM, but it at least halts the flood of ongoing external probing that can strain highly taxed smaller VMs. It also means that if a user is removed from a project, all of their SSH connections tunneled through IAP are instantly terminated at that precise moment, rather than lingering as they would through a traditional SSH connection.

Using IAP TCP Forwarding for SSH quite literally requires just three small changes. The first is to add two firewall rules to the VPC Firewall (this works for both legacy "default" networks and modern VPC networks):

allow-ingress-from-iap Ingress IP ranges: 35.235.240.0/20 tcp:22 Allow 999
deny-external-ssh Ingress IP ranges: 0.0.0.0/0 tcp:22 Deny 1000

The two rules are listed in descending order of priority. The second rule ensures that all incoming SSH connections are rejected: the VM never sees the connection attempt. The first rule overrides this ONLY for traffic from "35.235.240.0/20", which is the IP range of the IAP TCP Forwarding service.

Set "deny-external-ssh" to apply project-wide to all VMs and set "allow-ingress-from-iap" to apply only to a specific network tag, name it "allow-ingress-from-iap" and apply that tag only to nodes that should allow external SSH connections. Using a network tag it is easy to assign IAP SSH permissions to only selected nodes that absolutely require external SSH access and make it trivial to revoke that access when it is no longer needed.

To SSH into one of these nodes, you simply add "–tunnel-through-iap" to your gcloud command:

gcloud compute ssh --zone "ZONE" "VMNAME" --tunnel-through-iap --project "PROJECTNAME"

That's literally all there is to it! The end result is that all incoming SSH connections to all VMs in the project are rejected outright, without the VM ever being aware that a connection was attempted, with only IAP-proxied and preauthorized connections ever making it to the VM.

The IAP proxy is a Google-managed service, so there is nothing to be managed on your end, you simply add the two firewall rules and "–tunnel-through-iap" to your gcloud compute ssh command and that's it! It really is that simple.