gcp-iap-disable-on-backend-service

A principal with compute.backendServices.update can set iap.enabled=false on a backend service that currently has IAP enabled, disabling the authentication gate. If the backend service is internet-facing (via an external HTTPS load balancer), this immediately exposes the protected application to unauthenticated internet access. This is a weaken-control primitive: IAP removal + internet-facing LB = ExposedToInternet on the backend app.

derived gcp emits CanModifyConfiguration

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} HasPermission {'backend_service': None}

where

node_type(?backend_service) == LoadBalancer OR node_type(?backend_service) == PublicEndpoint effective_permission(?principal, ?backend_service) contains 'compute.backendServices.update' ?backend_service.iap_enabled == true

emit

source typeIdentity
target typeLoadBalancer
source?principal
target?backend_service
permissionscompute.backendServices.update
conditionsiam_permission service_state
state logicif effective_permission(?principal, ?backend_service) contains 'compute.backendServices.update' AND ?backend_service.iap_enabled == true AND NOT iam_deny_policy blocks compute.backendServices.update: ACTIVE elif iap_enabled state not confirmed by collector: CONDITIONAL(service_state) else: POTENTIAL(iam_permission)

Narrative

{principal.name} holds compute.backendServices.update on {backend_service.name}, which currently has IAP enabled (iap.enabled=true). The principal can disable IAP (set iap.enabled=false), removing the Google authentication gate. If this backend service sits behind an external HTTPS load balancer, the application becomes directly accessible without IAP authentication.

Raw rule rules/derived/gcp/iap.yaml

id: gcp-iap-disable-on-backend-service
emits: CanModifyConfiguration
description: 'A principal with compute.backendServices.update can set iap.enabled=false on a backend service
  that currently has IAP enabled, disabling the authentication gate. If the backend service is internet-facing
  (via an external HTTPS load balancer), this immediately exposes the protected application to unauthenticated
  internet access. This is a weaken-control primitive: IAP removal + internet-facing LB = ExposedToInternet
  on the backend app.'
match:
- - principal: null
  - HasPermission
  - backend_service: null
where:
- node_type(?backend_service) == LoadBalancer OR node_type(?backend_service) == PublicEndpoint
- effective_permission(?principal, ?backend_service) contains 'compute.backendServices.update'
- ?backend_service.iap_enabled == true
emit:
  source_type: Identity
  target_type: LoadBalancer
  source: ?principal
  target: ?backend_service
  permissions:
  - compute.backendServices.update
  conditions:
  - iam_permission
  - service_state
  state_logic: 'if effective_permission(?principal, ?backend_service) contains ''compute.backendServices.update''
    AND ?backend_service.iap_enabled == true AND NOT iam_deny_policy blocks compute.backendServices.update:
    ACTIVE elif iap_enabled state not confirmed by collector: CONDITIONAL(service_state) else: POTENTIAL(iam_permission)'
  confidence: 0.85
  derived_from:
  - HasPermission(?principal, ?backend_service) [compute.backendServices.update]
  - ?backend_service.iap_enabled == true (observed config)
  false_positive_note: "compute.backendServices.update is a broad permission \u2014 it is not IAP-specific.\
    \ The CanModifyConfiguration edge only has meaningful IAP-security impact when: (1) ?backend_service\
    \ currently has iap_enabled=true (confirm from collector data) (2) The backend service is internet-accessible\
    \ via an external HTTPS LB (scheme=EXTERNAL) Without (1), there is no IAP to disable. Without (2),\
    \ disabling IAP does not create internet exposure (internal LB backends still require internal network\
    \ access). The resulting ExposedToInternet edge (if the LB is external) is NOT emitted here \u2014\
    \ it is derived by the LB exposure rules (elb.yaml analog for GCP load balancing) once IAP is confirmed\
    \ disabled. This rule emits only the IAP-disabling capability itself. Also: compute.backendServices.update\
    \ is governed by the iam.googleapis.com/constraints/compute.restrictCloudArmorConfig org policy which\
    \ may prevent IAP config changes in hardened environments. NOTE: Companion rules for disabling IAP\
    \ on Cloud Run (run.services.update with iap_enabled=false) and App Engine (appengine.services.update\
    \ with iap_enabled=false) are documented in iap.md Section 6 but should be implemented as separate\
    \ rules in cloudrun.yaml and appengine.yaml respectively when those services model IAP state."
  narrative: '{principal.name} holds compute.backendServices.update on {backend_service.name}, which currently
    has IAP enabled (iap.enabled=true). The principal can disable IAP (set iap.enabled=false), removing
    the Google authentication gate. If this backend service sits behind an external HTTPS load balancer,
    the application becomes directly accessible without IAP authentication.'
move · open · esc close