Using Identity-Aware Proxy to Secure Internal Services on Google Cloud Without a VPN
Learn how to protect internal HTTP services with Cloud IAP, set up OAuth credentials, attach IAP to a Cloud Run backend, and verify access with identity tokens.
19 Jan 2026, 21:38 UTC

Problem: Reaching internal services without a VPN
Many teams run internal web apps on Cloud Run, App Engine, or behind an internal load balancer. These services listen only on internal IP ranges, making them inaccessible to remote developers, CI pipelines, or third‑party tools unless you maintain a VPN or constantly adjust firewall rules. Managing VPNs adds operational overhead and can create a single point of failure.
Thesis: Cloud IAP gives you a zero‑trust, identity‑based gate for any HTTP(S) service
Identity‑Aware Proxy (IAP) sits in front of your backend and validates a Google identity before forwarding the request. It injects a signed JWT (X-Goog-IAP-Jwt-Assertion) that your application can verify to know the caller’s email, groups, and other claims. Because IAP works at the HTTP layer, you can protect Cloud Run, App Engine, Compute Engine, or GKE services without changing the application code.
How IAP works
When a user accesses the public URL of an IAP‑protected service:
- The request hits the global HTTP(S) load balancer (or Cloud Run’s fully managed domain).
- IAP checks for a valid identity token. If none is present, it redirects the browser to Google’s OAuth consent screen.
- After the user signs in and consents, Google issues an identity token, which IAP validates.
- IAP adds the
X-Goog-Iap-Jwt-Assertionheader (containing the user’s email, audience, and issuance time) and forwards the request to the backend. - The backend verifies the JWT signature (using Google’s public JWKS) and trusts the asserted identity.
Because the verification happens at the edge, the backend only sees traffic that has already passed IAP’s checks.
Step‑by‑step configuration
1. Enable the IAP API
Run the following command with a user that has the roles/serviceusage.serviceUsageViewer role on the project:
gcloud services enable iap.googleapis.com
2. Create an OAuth client ID (if you don’t already have one)
In the Cloud Console under APIs & Services → OAuth consent screen, set the user type to "Internal" (for Google Workspace) or "External" for consumer accounts. Then create an OAuth client ID of type "Web application". Note the CLIENT_ID and CLIENT_SECRET; you will need them only if you plan to use the gcloud auth print-identity-token flow for testing.
3. Deploy a test Cloud Run service
Deploy a simple hello‑world container (you can use the provided sample):
gcloud run deploy hello-iap \\
--image=gcr.io/cloudrun/hello \\
--allow-unauthenticated \\
--region=us-central1 \\
--platform=managed
The --allow-unauthenticated flag is required during deployment so the service can be reached by the load balancer; IAP will later enforce authentication.
4. Attach IAP to the backend
Identify the backend service URL (e.g., https://hello-iap-abcdefg-uc.a.run.app). In the Cloud Console, go to Security → Identity‑Aware Proxy, select the resource type "Cloud Run services", find your service, and click "Enable IAP". You will be prompted to select an OAuth client; choose the one you created in step 2.
5. Grant the IAP‑secured Web App User role
To let specific Google accounts access the service, grant them the role roles/iap.securedWebAppUser on the Cloud Run service:
gcloud run services add-iam-policy-binding hello-iap \\
--member="user:[contact removed]" \\
--role="roles/iap.securedWebAppUser" \\
--region=us-central1 \\
--platform=managed
Replace [contact removed] with the email of a tester.
Worked example: verifying access
Now test the protected endpoint.
Authenticated request
Obtain an identity token for your user (you must be logged in with gcloud auth login):
TOKEN=$(gcloud auth print-identity-token)
curl -H "Authorization: Bearer $TOKEN" \\
https://hello-iap-abcdefg-uc.a.run.app
You should see the hello‑world response (e.g., "Hello World!"). No 302 redirect occurs because IAP found a valid token and forwarded the request.
Unauthenticated request
curl -I https://hello-iap-abcdefg-uc.a.run.app
The response headers will include a 302 Found location pointing to https://accounts.google.com/..., indicating that IAP redirected the client to the login page.
Checking IAP injection in logs
In Cloud Logging, filter for resource.type="cloud_run_revision" and look for the field jsonPayload.requestMetadata.supportsIap. A value of true confirms that IAP processed the request. You can also extract the JWT from the X-Goog-Iap-Jwt-Assertion header and verify its signature using jwt.io or the gcloud iap decode-jwt command (available in the Cloud SDK).
Trade‑offs and limitations
- Latency: IAP adds roughly 50‑100 ms of round‑trip time because the request traverses Google’s global edge before reaching the backend.
- Public reachability: The backend must be accessible via a globally reachable HTTP(S) load balancer or Cloud Run’s fully managed domain. Purely internal‑only services (e.g., a private GKE service without an ingress) cannot be protected directly.
- Protocol scope: IAP only works for HTTP(S). Non‑HTTP protocols such as TCP‑based databases, SSH, or custom gRPC over plain TCP need other mechanisms (e.g., Identity‑Aware Proxy for TCP forwarding, which is a separate feature).
- Header trust: Applications must be configured to trust the
X-Goog-Iap-Jwt-Assertionheader and not rely onX-Forwarded-Forfor authentication unless they validate the JWT first.
To verify that latency is acceptable for your workload, run a simple benchmark before and after enabling IAP (e.g., using hey or wrk) and compare average response times.
Actionable closing
Start with a low‑risk service like the hello‑world example above:
- Enable the IAP API.
- Create or reuse an OAuth client.
- Deploy your service (allowing unauthenticated access temporarily).
- Attach IAP and select the OAuth client.
- Grant the
roles/iap.securedWebAppUserrole to the identities that need access. - Test with an identity token and confirm unauthenticated requests are redirected.
- Monitor Cloud Logging for
supportsIapentries and check latency.
Once you have validated the flow, you can replicate the same steps for production services, gradually tightening backend firewall rules to allow traffic only from Google’s load balancer health checks and IAP verification (see the --ingress flag on Cloud Run or VPC firewall rules). This gives you a zero‑trust perimeter without the operational burden of a VPN.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.