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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
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) |
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.'