Configuring Google Cloud Identity-Aware Proxy (IAP) to enforce per-request
SKILL.md
Configuring Identity-Aware Proxy with Google IAP
When to Use
When protecting Google Cloud applications (App Engine, Cloud Run, GKE, Compute Engine) with identity-based access
When implementing context-aware access requiring device posture and location verification
When providing secure access to internal tools without VPN or public IP exposure
When needing per-request authentication and authorization for web applications and TCP services
When configuring programmatic access to IAP-protected resources using service accounts
Do not use for non-HTTP applications that cannot be placed behind an HTTPS load balancer, for public-facing applications that need unauthenticated access, or when applications handle their own authentication and IAP would conflict with existing auth flows.
Prerequisites
Google Cloud project with billing enabled
IAP API enabled (gcloud services enable iap.googleapis.com)
Application deployed behind HTTPS Load Balancer, App Engine, or Cloud Run
Cloud Identity or Google Workspace for user management
Access Context Manager API enabled for access levels
OAuth consent screen configured for the project
Workflow
Step 1: Enable IAP on Backend Services
Configure IAP for different GCP compute platforms.
# Enable required APIs
gcloud services enable iap.googleapis.com
gcloud services enable accesscontextmanager.googleapis.com
# Create OAuth consent screen
gcloud iap oauth-brands create \
--application_title="Internal Applications" \
[email protected]
# Create OAuth client
gcloud iap oauth-clients create \
projects/PROJECT_ID/brands/BRAND_ID \
--display_name="IAP Web Client"
# === Enable IAP on Compute Engine Backend Service ===
gcloud compute backend-services update my-backend-service \
--iap=enabled,oauth2-client-id=CLIENT_ID,oauth2-client-secret=CLIENT_SECRET \
--global
# === Enable IAP on App Engine ===
gcloud iap web enable \
--resource-type=app-engine \
--oauth2-client-id=CLIENT_ID \
--oauth2-client-secret=CLIENT_SECRET
# === Enable IAP on Cloud Run ===
# First grant IAP service account the Cloud Run Invoker role
gcloud run services add-iam-policy-binding my-service \
--member="serviceAccount:[email protected]" \
--role="roles/run.invoker" \
--region=us-central1
# Enable IAP on the Cloud Run backend service
gcloud compute backend-services update my-cloud-run-backend \
--iap=enabled,oauth2-client-id=CLIENT_ID,oauth2-client-secret=CLIENT_SECRET \
--global
# === Enable IAP TCP Forwarding for SSH/RDP ===
# No load balancer needed - uses IAP tunnel
gcloud compute instances add-iam-policy-binding my-vm \
--member="group:[email protected]" \
--role="roles/iap.tunnelResourceAccessor" \
--zone=us-central1-a
# SSH through IAP tunnel
gcloud compute ssh my-vm --zone=us-central1-a --tunnel-through-iap
# RDP through IAP tunnel
gcloud compute start-iap-tunnel my-windows-vm 3389 \
--local-host-port=localhost:3390 \
--zone=us-central1-a
Step 2: Configure IAM Bindings for Access Control
Grant access to specific users and groups with optional access level conditions.
# Grant basic access to a group
gcloud iap web add-iam-policy-binding \
--resource-type=backend-services \
--service=my-backend-service \
--member="group:[email protected]" \
--role="roles/iap.httpsResourceAccessor"
# Grant access with access level condition
gcloud iap web add-iam-policy-binding \
--resource-type=backend-services \
--service=finance-app \
--member="group:[email protected]" \
--role="roles/iap.httpsResourceAccessor" \
--condition='expression=request.auth.access_levels.exists(x, x == "accessPolicies/POLICY_ID/accessLevels/corporate-device"),title=RequireCorporateDevice,description=Requires managed corporate device'
# Grant access only during business hours
gcloud iap web add-iam-policy-binding \
--resource-type=backend-services \
--service=admin-console \
--member="group:[email protected]" \
--role="roles/iap.httpsResourceAccessor" \
--condition='expression=request.time.getHours("America/New_York") >= 8 && request.time.getHours("America/New_York") <= 18 && request.time.getDayOfWeek("America/New_York") >= 1 && request.time.getDayOfWeek("America/New_York") <= 5,title=BusinessHoursOnly'
# Grant access to a specific URL path
gcloud iap web add-iam-policy-binding \
--resource-type=backend-services \
--service=internal-api \
--member="group:[email protected]" \
--role="roles/iap.httpsResourceAccessor" \
--condition='expression=request.path.startsWith("/api/v2/"),title=APIv2Access'
Step 3: Create Access Levels with Access Context Manager
Define context-based access requirements using device attributes and network conditions.
Scenario: Securing 15 Internal GCP Services with IAP
Context: An e-commerce company runs 15 internal services on GKE and Cloud Run (admin dashboards, internal APIs, monitoring tools). Currently, these services are protected only by VPN and firewall rules, creating excessive network-level access.
Approach:
Deploy all services behind an HTTPS Load Balancer with managed SSL certificates
Enable IAP on each backend service with per-service OAuth clients
Create IAM bindings mapping Google Groups to specific services (admin group -> admin dashboard, engineering -> monitoring)
Apply managed-device access level to admin dashboard and financial tools
Configure IAP TCP tunneling for SSH access to GKE nodes (replacing SSH bastion host)
Set re-authentication to 4 hours for admin tools, 8 hours for monitoring
Configure Cloud Audit Logs and create alerting for repeated denials
Pitfalls: IAP adds 10-50ms latency per request; test application performance. WebSocket connections through IAP require specific backend service configuration. Service-to-service calls within GKE should bypass IAP using internal service mesh, not external IAP endpoints. Break-glass access should use a separate IAM binding without access level conditions.