Zero Trust on Google Cloud: BeyondCorp Enterprise Implementation Guide
Zero Trust assumes every request is potentially malicious until proven otherwise — no implicit trust based on network location. This guide implements Google's BeyondCorp Enterprise model on GCP: Identity-Aware Proxy, Access Context Manager, device trust policies, and application-level access controls.
Traditional network security is built on a perimeter model: the corporate network is trusted, everything outside is untrusted. VPN connects remote employees to the trusted network, firewall rules control what enters and exits, and internal applications are accessible to anyone inside the perimeter.
This model has two fundamental problems. First, it assumes that everything inside the network is safe — but breaches increasingly come from inside the network, whether from compromised internal accounts, malicious insiders, or lateral movement after a perimeter breach. Second, with cloud and remote work, the "inside" and "outside" distinction no longer maps cleanly to physical offices and on-premises servers.
Zero Trust replaces implicit trust based on network location with continuous verification of every request based on identity, device health, and context. Google developed BeyondCorp as its internal implementation of Zero Trust, and BeyondCorp Enterprise is the commercial version available on GCP.
This guide walks through implementing Zero Trust access controls for internal applications using Cloud IAP, Access Context Manager, and related GCP services.
The BeyondCorp Model
In BeyondCorp, access to an application depends on:
- Identity: Who is the user? Is their identity verified (MFA, FIDO2)?
- Device: Is the device managed and healthy? Is it compliant with security policies?
- Context: Is the request from an expected location? At an expected time? With normal behavior?
All three factors are evaluated for every request. A verified identity on a compromised device is denied. A company-managed device being used with stolen credentials is denied. This continuous verification is what makes Zero Trust fundamentally different from perimeter security.
Cloud Identity-Aware Proxy (IAP)
Cloud IAP is GCP's application-level access proxy. It sits in front of your internal applications and enforces authentication and authorization before requests reach your application servers.
# Enable IAP for your project
gcloud services enable iap.googleapis.com
# Enable IAP for a Cloud Run service
gcloud run services update my-internal-app --region=europe-west4 --no-allow-unauthenticated
# Grant IAP access to specific users
gcloud run services add-iam-policy-binding my-internal-app --region=europe-west4 --member=user:engineer@company.com --role=roles/run.invoker
# Or grant to a Google Group
gcloud run services add-iam-policy-binding my-internal-app --region=europe-west4 --member=group:engineering@company.com --role=roles/run.invoker
For GCE and GKE backends, IAP integrates with the HTTPS Load Balancer:
# Enable IAP on a backend service
gcloud compute backend-services update my-backend --global --iap=enabled,oauth2-client-id=CLIENT_ID,oauth2-client-secret=CLIENT_SECRET
Validating IAP Tokens in Your Application
IAP passes the authenticated user's identity to your application in the X-Goog-IAP-JWT-Assertion header. Validate this token server-side:
from flask import Flask, request, jsonify, abort
from google.auth.transport import requests as google_requests
from google.oauth2 import id_token
app = Flask(__name__)
# Your IAP client ID (from IAP configuration)
IAP_AUDIENCE = "/projects/PROJECT_NUMBER/apps/my-project"
def get_iap_user() -> dict:
"""Extract and validate the IAP-authenticated user from the request."""
iap_jwt = request.headers.get("X-Goog-IAP-JWT-Assertion")
if not iap_jwt:
abort(401, "Missing IAP assertion")
try:
payload = id_token.verify_token(
iap_jwt,
google_requests.Request(),
audience=IAP_AUDIENCE,
certs_url="https://www.gstatic.com/iap/verify/public_key",
)
return {
"email": payload["email"],
"user_id": payload["sub"],
}
except Exception as e:
abort(401, f"Invalid IAP token: {e}")
@app.route("/api/data")
def get_data():
user = get_iap_user()
# User is authenticated and authorized by IAP
# You can now make fine-grained authorization decisions based on email/group
return jsonify({"user": user["email"], "data": fetch_data_for_user(user["email"])})
Access Context Manager: Context-Based Access Policies
Access Context Manager lets you define access levels based on attributes of the request: IP address ranges, device OS and version, user identity attributes, and geographic location.
# Create an access policy for your organization
gcloud access-context-manager policies create --organization=ORGANIZATION_ID --title="BeyondCorp Access Policy"
# Create an access level: corporate managed device
gcloud access-context-manager levels create corporate-managed-device --policy=POLICY_ID --title="Corporate Managed Device" --basic-level-spec=device_policy.yaml
# device_policy.yaml
cat > device_policy.yaml << 'EOF'
devicePolicy:
requireScreenLock: true
allowedEncryptionStatuses:
- ENCRYPTED
osConstraints:
- osType: DESKTOP_MAC
minimumVersion: "13.0"
- osType: DESKTOP_WINDOWS
minimumVersion: "10.0.19041"
- osType: DESKTOP_CHROME_OS
minimumVersion: "107"
requireAdminApproval: false
requireCorpOwned: true
EOF
# Create access level: trusted IP range
gcloud access-context-manager levels create office-ip --policy=POLICY_ID --title="Office IP Range" --basic-level-spec=ip_condition.yaml
# ip_condition.yaml
cat > ip_condition.yaml << 'EOF'
conditions:
- ipSubnetworks:
- "203.0.113.0/24" # Your office IP range
- "10.0.0.0/8" # VPN range
EOF
Combining Access Levels with IAP
Apply access levels to IAP-protected resources:
# Create an IAP access policy that requires corporate device OR office IP
gcloud iap web add-iam-policy-binding --resource-type=backend-services --service=my-backend --member=group:all-employees@company.com --role=roles/iap.httpsResourceAccessor --condition='expression=request.auth.access_levels.exists(x, x == "accessPolicies/POLICY_ID/accessLevels/corporate-managed-device" || x == "accessPolicies/POLICY_ID/accessLevels/office-ip"),title=Corporate Access'
This policy grants access to all-employees@company.com only when the request satisfies at least one access level — they're on a managed device or they're on the office network.
BeyondCorp Enterprise: Device Trust Integration
BeyondCorp Enterprise extends basic IAP with endpoint verification — the Chrome browser extension that reports device security posture to Access Context Manager.
# Enable BeyondCorp Enterprise
gcloud services enable beyondcorp.googleapis.com
# Create a BeyondCorp application connector for on-premises apps
gcloud beyondcorp app connectors create my-connector --location=europe-west4 --display-name="Office Applications Connector"
# Create a BeyondCorp application gateway
gcloud beyondcorp app gateways create my-gateway --location=europe-west4 --display-name="Office App Gateway"
The BeyondCorp connector creates a secure tunnel from your on-premises environment to GCP without opening inbound firewall rules — the connector initiates outbound connections to Google's infrastructure.
Protecting Internal Applications Without VPN
The key BeyondCorp value proposition is eliminating VPN for internal application access. Here's the before and after:
Before (VPN model):
- User connects VPN → gains access to entire corporate network → accesses applications
- Lateral movement risk: VPN user can reach any application, not just the one they need
- Performance: VPN introduces latency for all traffic
- Management overhead: VPN server maintenance, certificate management
After (BeyondCorp model):
- User authenticates with Google Identity → Access Context Manager evaluates device/context → IAP grants access to specific application only
- No lateral movement: user gets access to Application A, not the network Application A is on
- Performance: traffic goes directly to the application via Google's network edge
- Management: no VPN infrastructure to maintain
Implementation steps for migrating an internal application:
# Step 1: Put the application behind a Load Balancer with IAP
gcloud compute url-maps create my-app-lb --default-service=my-backend-service
gcloud compute target-https-proxies create my-app-proxy --url-map=my-app-lb --ssl-certificates=my-app-cert
gcloud compute forwarding-rules create my-app-rule --global --target-https-proxy=my-app-proxy --ports=443
# Step 2: Enable IAP on the backend
gcloud compute backend-services update my-backend-service --global --iap=enabled,oauth2-client-id=CLIENT_ID,oauth2-client-secret=CLIENT_SECRET
# Step 3: Remove internal network access (after verifying IAP works)
# Update firewall rules to block direct internal access
gcloud compute firewall-rules update internal-allow-my-app --disabled
Cloud Armor Integration for Additional Protection
Combine IAP with Cloud Armor for WAF protection and rate limiting:
# Create a Cloud Armor security policy
gcloud compute security-policies create iap-protection --description="WAF rules for IAP-protected applications"
# Block known malicious IPs
gcloud compute security-policies rules create 1000 --security-policy=iap-protection --action=deny-403 --src-ip-ranges="198.51.100.0/24" # Known bad IP range
# Rate limit per IP to prevent credential stuffing
gcloud compute security-policies rules create 2000 --security-policy=iap-protection --action=throttle --rate-limit-threshold-count=100 --rate-limit-threshold-interval-sec=60 --conform-action=allow --exceed-action=deny-429 --enforce-on-key=IP --src-ip-ranges="*"
# Attach to backend service
gcloud compute backend-services update my-backend-service --global --security-policy=iap-protection
Monitoring Access Events
IAP generates audit logs for every access attempt. Query them to detect suspicious patterns:
# Find failed access attempts (good indicator of probing)
gcloud logging read 'resource.type="gce_backend_service" AND protoPayload.methodName="google.cloud.iap.v1.IdentityAwareProxyAdminService.GetIapSettings" AND protoPayload.status.code != 0' --freshness=24h --format="table(timestamp,protoPayload.authenticationInfo.principalEmail,protoPayload.requestMetadata.callerIp)"
# Find users accessing from new locations
gcloud logging read 'resource.type="gce_backend_service" AND protoPayload.serviceName="iap.googleapis.com"' --freshness=24h --format=json | jq '.[] | {user: .protoPayload.authenticationInfo.principalEmail, ip: .protoPayload.requestMetadata.callerIp}'
Zero Trust is not a product you buy and deploy once — it's an ongoing practice of continuous verification and least-privilege enforcement. Start with IAP protecting your highest-risk internal applications, add device trust verification, and gradually expand coverage.
For the complementary security monitoring platform, see our Security Command Center guide. For protecting GCP APIs from data exfiltration, see our VPC Service Controls guide.