gcp-dataproc-jobs-create-execute-command

dataproc.jobs.create on an existing cluster lets the principal submit arbitrary Spark/PySpark/Hive code that runs on cluster VMs -> RCE as the cluster SA.

derived gcp emits CanExecuteCommand

match (effective permission)

{ "action": "dataproc.jobs.create", "resource_type": "google.dataproc.Cluster" }

where

cluster is RUNNING (state == RUNNING); submit to a STOPPED cluster returns an error the effective permission may be scoped at project OR cluster level (per-resource IAM)

emit

source typeIdentity
target typeAnalyticsService
source<principal>
target<Dataproc cluster>
permissionsdataproc.jobs.create
conditionsiam_permission service_state
state logicACTIVE when dataproc.jobs.create is confirmed (effective permission; honor project- and cluster-level IAM) and the cluster is RUNNING. CONDITIONAL(service_state) when the cluster is STOPPED or CREATING (job submit fails). BLOCKED if an IAM deny or org policy denies dataproc.jobs.create at the effective scope. Per-resource IAM on the cluster (dataproc.clusters.use or jobs.create binding) narrows or expands access — resolve both project-level and cluster-level bindings.

Narrative

{principal.name} can submit a Dataproc job (dataproc.jobs.create) to {cluster.name}, executing arbitrary Spark/PySpark/Hive/Pig code on the cluster VMs as cluster SA {sa.name}. CITE can-execute-as execute-as-via-command for the CanExecuteAs collapse.

Raw rule rules/derived/gcp/dataproc.yaml

id: gcp-dataproc-jobs-create-execute-command
emits: CanExecuteCommand
description: dataproc.jobs.create on an existing cluster lets the principal submit arbitrary Spark/PySpark/Hive
  code that runs on cluster VMs -> RCE as the cluster SA.
match_effective_permission:
  action: dataproc.jobs.create
  resource_type: google.dataproc.Cluster
where:
- cluster is RUNNING (state == RUNNING); submit to a STOPPED cluster returns an error
- the effective permission may be scoped at project OR cluster level (per-resource IAM)
emit:
  source_type: Identity
  target_type: AnalyticsService
  source: <principal>
  target: <Dataproc cluster>
  permissions:
  - dataproc.jobs.create
  conditions:
  - iam_permission
  - service_state
  state_logic: "ACTIVE when dataproc.jobs.create is confirmed (effective permission; honor project- and\
    \ cluster-level IAM) and the cluster is RUNNING. CONDITIONAL(service_state) when the cluster is STOPPED\
    \ or CREATING (job submit fails). BLOCKED if an IAM deny or org policy denies dataproc.jobs.create\
    \ at the effective scope. Per-resource IAM on the cluster (dataproc.clusters.use or jobs.create binding)\
    \ narrows or expands access \u2014 resolve both project-level and cluster-level bindings."
  false_positive_note: "Only ACTIVE for RUNNING clusters; STOPPED/CREATING clusters reject job submissions.\
    \ Resolve the cluster-level IAM policy (dataproc.clusters.getIamPolicy) \u2014 a cluster-level DENY\
    \ for the principal overrides a project-level allow. dataproc.jobs.create alone gives RCE but only\
    \ yields privilege escalation if the cluster SA is more privileged than the attacker (resolve actual\
    \ SA roles). Submit to the CORRECT cluster \u2014 cross-cluster job submission is not possible with\
    \ only a project-level binding if a cluster-level IAM deny blocks it. CITE can-control escalate-via-execute-as\
    \ for the privilege-level check; CanEscalateTo is derived automatically when the cluster SA privilege_level\
    \ > principal.privilege_level."
  narrative: '{principal.name} can submit a Dataproc job (dataproc.jobs.create) to {cluster.name}, executing
    arbitrary Spark/PySpark/Hive/Pig code on the cluster VMs as cluster SA {sa.name}. CITE can-execute-as
    execute-as-via-command for the CanExecuteAs collapse.'
move · open · esc close