How Google IAP TCP forwarding works

IAP TCP forwarding opens a listening port on your own machine that is tunnelled, inside HTTPS, to a chosen port on a Compute Engine VM's internal IP. The VM needs no public address, and the only thing your firewall must allow is Google's IAP range. This page walks the path a packet takes and the two things you must configure.

1. The path, hop by hop

your app ──► localhost:LOCAL_PORT ──► gcloud (HTTPS) ──► tunnel.cloudproxy.app / IAP
        ──► identity + IAM check ──► VM internal IP (nic0), port INSTANCE_PORT
  1. gcloud compute start-iap-tunnel binds a local port and connects to Google over HTTPS. Google documents the tunnel endpoints as tunnel.cloudproxy.app and, when certificate-based access is enabled, mtls.tunnel.cloudproxy.app.
  2. IAP authenticates the caller and checks roles/iap.tunnelResourceAccessor (plus any IAM conditions) for that instance and port.
  3. IAP connects to the primary internal IPv4 address of nic0 on the VM from its own range and relays bytes both ways. Google states the connection is wrapped "in an HTTPS stream" before IAP applies access controls, and forwarded unwrapped to the remote port — so the VM sees a normal TCP connection to, say, port 3389.

Because the VM sees the connection arriving from IAP, not from you, the firewall rule below is all the exposure the port ever gets. The VM does not need a public IP, and load-balanced targets are not supported — the tunnel goes to one instance.

2. Configuration item one: the firewall rule

Allow ingress from IAP's range to the port you want to reach:

gcloud compute firewall-rules create allow-ssh-ingress-from-iap \
  --direction=INGRESS \
  --action=allow \
  --rules=tcp:22 \
  --source-ranges=35.235.240.0/20

35.235.240.0/20 "contains all IP addresses that IAP uses for TCP forwarding". For a VM with an IPv6 address the range is 2600:2d00:1:7::/64. Use tcp:3389 for RDP, or any other port. Scope the rule with --target-tags or --target-service-accounts if you don't want every VM in the network to accept IAP connections.

3. Configuration item two: the IAM role

The person opening the tunnel needs IAP-secured Tunnel User (roles/iap.tunnelResourceAccessor). Google's guide also lists roles/compute.instanceAdmin.v1 for the full gcloud compute ssh experience (it pushes SSH keys and needs compute.instances.get and compute.instances.list). Project-wide:

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=user:EMAIL --role=roles/iap.tunnelResourceAccessor

Per-instance scoping and conditions are covered in IAM roles for IAP.

4. Opening a tunnel

Generic TCP:

gcloud compute start-iap-tunnel INSTANCE_NAME INSTANCE_PORT \
  --local-host-port=localhost:LOCAL_PORT \
  --zone=ZONE

RDP (then point your RDP client at localhost:LOCAL_PORT):

gcloud compute start-iap-tunnel INSTANCE_NAME 3389 \
  --local-host-port=localhost:3390 \
  --zone=ZONE

SSH without a local port at all — gcloud wraps the SSH session directly:

gcloud compute ssh INSTANCE_NAME --tunnel-through-iap --project PROJECT_ID --zone ZONE

--tunnel-through-iap forces the tunnel even when the VM has an external IP; gcloud chooses it automatically when there is none. Add --listen-on-stdin to use the tunnel as a ProxyCommand for plain ssh, scp or PuTTY — see SSH to a Linux VM via IAP.

5. Limits to plan around

  • Not for bulk data. Google: "IAP's TCP forwarding feature isn't intended for bulk transfer of data. IAP reserves the right to rate-limit users abusing this service."

  • Idle timeout. "IAP automatically disconnects sessions after one hour of inactivity."

  • Throughput tip. Installing NumPy for gcloud's Python noticeably raises upload bandwidth:

    $(gcloud info --format="value(basic.python_location)") -m pip install numpy
    export CLOUDSDK_PYTHON_SITEPACKAGES=1
    

    (On Windows PowerShell: start (gcloud info --format="value(basic.python_location)") "-m pip install numpy".)

  • One instance per tunnel; no load-balanced targets; not callable from mobile devices.

  • Tags aren't supported for scoping IAP tunnel permissions — use instances, groups or conditions instead.

Common questions

What source IP does my VM see?

An address from 35.235.240.0/20 (IAP), not the user's. If an application on the VM allowlists by client IP, allowlist that range.

Can I forward a database port?

Yes — any TCP port, e.g. start-iap-tunnel INSTANCE 5432 --local-host-port=localhost:5432, with a matching firewall rule for tcp:5432.

Does the tunnel encrypt my traffic?

The tunnel itself runs inside HTTPS between gcloud and IAP, and inside Google's network to the VM. Your protocol's own encryption (SSH, RDP with TLS) still applies on top.

Why does my tunnel drop after a while?

Idle sessions are closed after one hour. Reconnect; keep-alives at the protocol level (for example ServerAliveInterval in SSH) prevent the idle state.

Sources