Article 15 – Identity-Aware Proxy (IAP)

Let me paint you a picture. You have built this fantastic internal web application. It is hosted on Compute Engine or GKE, and your employees use it every day. Now, the traditional way to keep this thing secure is to shove it behind a VPN. Every employee connects to the VPN first, then accesses the app. Sounds reasonable, right?

Here is the problem. VPNs are slow. They are a pain to manage at scale. And the bigger issue — they create a “moat and castle” security model. Once someone is inside the VPN, they can potentially access everything on that network. If that VPN gets breached, your entire internal network is exposed.

The modern security approach is zero-trust: never trust, always verify. Every single request should be authenticated and authorized, regardless of where it comes from. You do not get a free pass just because you are on the “right network.”

So the question becomes: how do you provide secure access to your internal applications without a VPN, while embracing zero-trust? You need something that checks every single request at the door. That is Identity-Aware Proxy (IAP).

What Exactly is IAP?

IAP is a Google Cloud service that sits in front of your web applications and controls who can access them. Before any request reaches your application, IAP verifies the user’s identity and checks whether they are authorized.

Here is what makes it special:

  • Zero-Trust by Design — IAP does not care where the request comes from. Internal network, coffee shop WiFi, a hotel in Tokyo — does not matter. It authenticates and authorizes every user on every request.

  • Context-Aware — It integrates with Google’s identity system (Google accounts, Workspace, Cloud Identity) and checks IAM permissions in real time.

  • No VPN Required — Your application can have a public URL, but IAP makes sure only authorized users get through. The app is “public” in the sense that anyone can reach the door, but only authorized people get past the bouncer.

How the Request Flow Works

Let me walk you through what actually happens when a user tries to access an IAP-protected application:

  1. User sends a request — Someone navigates to https://my-internal-app.example.com

  2. IAP intercepts — The request hits your Google Cloud Load Balancer first, which forwards it to IAP

  3. Authentication — IAP checks if the user is signed in with a Google Identity. If they are not, it redirects them to the Google login page

  4. Authorization (IAM Check) — Once the user is authenticated, IAP checks the IAM policy on the backend service. It looks for the role roles/iap.httpsResourceAccessor — this is the specific role that says “this person is allowed through IAP”

  5. Decision time:

  6. Authorized? IAP adds a special HTTP header with the user’s identity information and forwards the request to your backend
  7. Not authorized? IAP blocks the request and returns an “Access Denied” page

  8. Application receives the request — Your app gets the request already knowing who the user is. The identity info is right there in the IAP headers

The big takeaway here: Your application does not need to handle authentication itself. IAP does it all before the request even reaches your code. This simplifies your application security dramatically.

What Can IAP Protect?

IAP is not just for one type of workload. It is surprisingly versatile:

  • App Engine — Standard and Flexible environments
  • Compute Engine — VMs behind a load balancer (via instance groups or NEGs)
  • GKE — Kubernetes services via Ingress or GKE load balancers
  • Cloud Run — Serverless container services
  • Internal Load Balancers — You can even put IAP on an internal LB to secure internal microservices
  • SSH/RDP to VMs — This one is really useful. You can use IAP to provide secure SSH or RDP access to individual VMs without giving them public IPs and without a VPN

Let me show you that last one, because it is practical:

# SSH into a VM through IAP tunnel — no public IP needed
gcloud compute ssh my-private-vm \
    --zone=us-central1-a \
    --tunnel-through-iap

Output:

WARNING: The private key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
Updating project ssh metadata...done.
Waiting for SSH key to propagate.
Linux my-private-vm 5.10.0-28-cloud-amd64 #1 SMP Debian x86_64
user@my-private-vm:~$

The VM has no public IP. The SSH traffic is tunneled through IAP. The user needs the iap.tunnelInstances.accessViaIAP permission. Clean and secure.

Setting Up IAP — The Commands

Let me show you how to enable IAP on a backend service and grant access:

# Step 1: Enable IAP on a backend service
gcloud compute backend-services update my-backend-service \
    --global \
    --iap=enabled

Output:

Updated [https://compute.googleapis.com/compute/v1/projects/my-project/global/backendServices/my-backend-service].
# Step 2: Grant a specific user access through IAP
gcloud compute backend-services add-iam-policy-binding my-backend-service \
    --global \
    --member=user:[email protected] \
    --role=roles/iap.httpsResourceAccessor

Output:

Updated IAM policy for backend service [my-backend-service].
# Step 3: Grant access to an entire group (better practice)
gcloud compute backend-services add-iam-policy-binding my-backend-service \
    --global \
    --member=group:[email protected] \
    --role=roles/iap.httpsResourceAccessor

Now only users with that role can get through. Everyone else gets a 403.

Common Pitfalls and Best Practices

Pitfall: Thinking IAP is a firewall.
Best Practice: It is not a network-level firewall. It is application-level access control. You still need network firewalls to block unauthenticated traffic from reaching your backends directly. IAP works best when your application backends are private and IAP is the only entry point.

Pitfall: Granting the iap.httpsResourceAccessor role to allUsers.
Best Practice: Only grant it to specific user accounts or Google Groups. This is the entire point of IAP’s security model. If you grant it to allUsers, you have essentially disabled IAP.

Pitfall: Not understanding the role of the load balancer.
Best Practice: IAP works with a Cloud Load Balancer, not as a replacement for one. When setting up IAP for web applications, you will almost always be configuring it on the backend service of an External HTTP(S) Load Balancer.

Pitfall: Using IAP headers as your only authorization mechanism.
Best Practice: IAP tells you who the user is. But your application should still handle its own fine-grained authorization. IAP gets them to the door — your app decides what they can do inside. For example, “Is this user an admin? Can they delete records?” That logic stays in your application.

Quick Reference
# Enable IAP on a backend service
gcloud compute backend-services update [BACKEND_SERVICE] \
    --global --iap=enabled

# Grant IAP access to a user
gcloud compute backend-services add-iam-policy-binding [BACKEND_SERVICE] \
    --global \
    --member=user:[EMAIL] \
    --role=roles/iap.httpsResourceAccessor

# Grant IAP access to a group
gcloud compute backend-services add-iam-policy-binding [BACKEND_SERVICE] \
    --global \
    --member=group:[GROUP_EMAIL] \
    --role=roles/iap.httpsResourceAccessor

# SSH through IAP tunnel (no public IP needed)
gcloud compute ssh [INSTANCE] --zone=[ZONE] --tunnel-through-iap