gcp-gke-create-nodepool-as-sa

container.nodePools.create + iam.serviceAccounts.actAs on a chosen SA => create a new node pool running as that SA; pods on those nodes read the node SA token via the metadata server (legacy metadata mode).

derived gcp emits CanCreateWorkloadAs

match

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

{'principal': None} CanPassIdentity {'sa': None}

where

?principal has EFFECTIVE container.nodePools.create on the cluster ?sa is the target node SA (config.serviceAccount in the new node pool) ?principal has iam.serviceAccounts.actAs on ?sa (GKE requires actAs to bind a SA to a node pool) the new node pool uses EXPOSE_ALL_SCOPES metadata mode OR WI is disabled on the cluster (so pods can reach the node SA metadata)

emit

source typeIdentity
target typeServiceAccount
source?principal
target?sa
permissionscontainer.nodePools.create iam.serviceAccounts.actAs
conditionsiam_permission role_compatibility service_state
state logicACTIVE when both container.nodePools.create AND iam.serviceAccounts.actAs are confirmed and the node pool will use EXPOSE_ALL_SCOPES (or the cluster has WI disabled). POTENTIAL(role_compatibility) if the node pool SA permissibility is uncertain (the target SA may already be in use and GKE may reject re-binding). CONDITIONAL(service_state) until the node pool nodes are running and pods are scheduled.

Narrative

{principal.name} can create a new node pool for cluster {cluster.name} bound to {sa.name} (container.nodePools.create + iam.serviceAccounts.actAs); pods scheduled on those nodes can read {sa.name}'s OAuth token from the GCE metadata server, executing with its cloud IAM permissions.

Raw rule rules/derived/gcp/gke.yaml

id: gcp-gke-create-nodepool-as-sa
emits: CanCreateWorkloadAs
description: container.nodePools.create + iam.serviceAccounts.actAs on a chosen SA => create a new node
  pool running as that SA; pods on those nodes read the node SA token via the metadata server (legacy
  metadata mode).
match:
- - principal: null
  - CanPassIdentity
  - sa: null
where:
- ?principal has EFFECTIVE container.nodePools.create on the cluster
- ?sa is the target node SA (config.serviceAccount in the new node pool)
- ?principal has iam.serviceAccounts.actAs on ?sa (GKE requires actAs to bind a SA to a node pool)
- the new node pool uses EXPOSE_ALL_SCOPES metadata mode OR WI is disabled on the cluster (so pods can
  reach the node SA metadata)
emit:
  source_type: Identity
  target_type: ServiceAccount
  source: ?principal
  target: ?sa
  permissions:
  - container.nodePools.create
  - iam.serviceAccounts.actAs
  conditions:
  - iam_permission
  - role_compatibility
  - service_state
  state_logic: ACTIVE when both container.nodePools.create AND iam.serviceAccounts.actAs are confirmed
    and the node pool will use EXPOSE_ALL_SCOPES (or the cluster has WI disabled). POTENTIAL(role_compatibility)
    if the node pool SA permissibility is uncertain (the target SA may already be in use and GKE may reject
    re-binding). CONDITIONAL(service_state) until the node pool nodes are running and pods are scheduled.
  confidence: 0.85
  derived_from:
  - <gcp/identity-escalation.yaml gcp-sa-act-as CanPassIdentity edge>
  - '<effective iam_permission: container.nodePools.create on cluster>'
  false_positive_note: "container.nodePools.create requires iam.serviceAccounts.actAs on the SA to bind\
    \ it to the node pool \u2014 without actAs GKE rejects the request. The node SA token is only accessible\
    \ to pods if workloadMetadataConfig is NOT set to GKE_METADATA (downgrade to POTENTIAL if WI is enforced\
    \ cluster-wide). Adding a node pool with GKE_METADATA still enabled means pods get scoped WI tokens,\
    \ not the node SA \u2014 so the token-theft step does not apply. Only emit when legacy metadata access\
    \ is confirmed (or will be configured on the new node pool). GKE Autopilot clusters do NOT allow custom\
    \ node pools \u2014 BLOCKED for Autopilot."
  narrative: '{principal.name} can create a new node pool for cluster {cluster.name} bound to {sa.name}
    (container.nodePools.create + iam.serviceAccounts.actAs); pods scheduled on those nodes can read {sa.name}''s
    OAuth token from the GCE metadata server, executing with its cloud IAM permissions.'
move · open · esc close