gcp-lb-iap-policy-write

compute.backendServices.setIamPolicy rewrites the IAP resource-level allow policy on a backend service. Granting roles/iap.httpsResourceAccessor to allUsers bypasses IAP authentication - all requests pass through without a Google identity - without setting iap.enabled=false, so monitoring / logs show IAP still "enabled" while access is open.

derived gcp emits CanModifyConfiguration

match (effective permission)

{ "action": "compute.backendServices.setIamPolicy", "resource_type": "compute.googleapis.com/BackendService" }

where

backend service has iap.enabled=true (IAP is active — policy write is relevant)

emit

source typeIdentity
target typeLoadBalancer
source<principal>
target<IAP-protected BackendService in scope>
permissionscompute.backendServices.setIamPolicy
conditionsiam_permission

Narrative

{principal.name} can rewrite the IAP resource policy on backend service {target.name} (compute.backendServices.setIamPolicy). Granting roles/iap.httpsResourceAccessor to allUsers opens the IAP-protected application to any internet user while the IAP feature flag remains visually enabled, bypassing the authentication gate without triggering iap.enabled=false alerts.

Raw rule rules/derived/gcp/loadbalancing.yaml

id: gcp-lb-iap-policy-write
emits: CanModifyConfiguration
description: "compute.backendServices.setIamPolicy rewrites the IAP resource-level allow policy on a backend\
  \ service. Granting roles/iap.httpsResourceAccessor to allUsers bypasses IAP authentication \u2014 all\
  \ requests pass through without a Google identity \u2014 without setting iap.enabled=false, so monitoring\
  \ / logs show IAP still \"enabled\" while access is open."
match_effective_permission:
  action: compute.backendServices.setIamPolicy
  resource_type: compute.googleapis.com/BackendService
where:
- "backend service has iap.enabled=true (IAP is active \u2014 policy write is relevant)"
emit:
  source_type: Identity
  target_type: LoadBalancer
  source: <principal>
  target: <IAP-protected BackendService in scope>
  permissions:
  - compute.backendServices.setIamPolicy
  conditions:
  - iam_permission
  escalation: Grant roles/iap.httpsResourceAccessor to allUsers (or allAuthenticatedUsers for any Google-authenticated
    user) on the backend service's IAM policy. IAP passes all requests regardless of the caller's specific
    identity. The IAP feature flag remains enabled, making the open access harder to detect in configuration
    audits that only check iap.enabled rather than the IAP resource policy membership.
  false_positive_note: "Only impactful if the backend service has iap.enabled=true. If IAP is not enabled,\
    \ setIamPolicy is a no-op for authentication purposes (IAP resource policy is unused). Distinguish\
    \ broadening (granting allUsers \u2014 attack) from narrowing (revoking access \u2014 hardening) in\
    \ the narrative. compute.backendServices.setIamPolicy is NOT included in roles/compute.viewer; it\
    \ requires roles/compute.loadBalancerAdmin or a custom role."
  narrative: '{principal.name} can rewrite the IAP resource policy on backend service {target.name} (compute.backendServices.setIamPolicy).
    Granting roles/iap.httpsResourceAccessor to allUsers opens the IAP-protected application to any internet
    user while the IAP feature flag remains visually enabled, bypassing the authentication gate without
    triggering iap.enabled=false alerts.'
move · open · esc close