aws-redshift-execute-as-via-pass-role

Pass a privileged IAM role to a Redshift cluster (iam:PassRole + ModifyClusterIamRoles), then submit a COPY/UNLOAD/UDF query that uses that role's AWS permissions. Covers both already-attached and attach-new-then-execute paths.

derived aws emits CanExecuteAs

match

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

{'principal': None} CanPassIdentity {'role': None} {'principal': None} CanModifyConfiguration {'cluster': None}

where

node_type(?cluster) == DataWarehouse ?cluster.provider_type in ['AWS::Redshift::Cluster', 'AWS::RedshiftServerless::Namespace'] ?role trust policy allows redshift.amazonaws.com as service principal iam:PassRole for ?role permits iam:PassedToService including redshift.amazonaws.com (or unconditioned)

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} HasPermission {'cluster': None}

emit

source typeIdentity
target typeMachineIdentity
source?principal
target?role
permissionsiam:PassRole redshift:ModifyClusterIamRoles redshift-data:ExecuteStatement
conditionsiam_permission role_compatibility service_state
state logicACTIVE when all three are confirmed: (1) iam:PassRole effective for ?role with iam:PassedToService admitting redshift.amazonaws.com; (2) redshift:ModifyClusterIamRoles (or redshift-serverless:UpdateNamespace for Serverless) effective on ?cluster; (3) a query-submission capability exists (redshift-data:ExecuteStatement or GetClusterCredentials on ?cluster). CONDITIONAL(iam_permission) when iam:PassedToService condition exists but its redshift.amazonaws.com admission is unresolved. CONDITIONAL(service_state) when the cluster is paused (Serverless: no active workgroup). BLOCKED when an SCP or permission boundary denies iam:PassRole or ModifyClusterIamRoles. This rule covers BOTH already-attached roles (which also have ExecutesAs edges) AND new roles being attached for the first time.

Narrative

{principal.name} can pass {role.name} to Redshift cluster {cluster.name} (iam:PassRole + redshift:ModifyClusterIamRoles) and then submit a COPY/UNLOAD or CREATE EXTERNAL FUNCTION query that uses that role, executing arbitrary AWS API calls as {role.name}. This covers attaching a NEW role or exercising an already-attached role.

Raw rule rules/derived/aws/redshift.yaml

id: aws-redshift-execute-as-via-pass-role
emits: CanExecuteAs
description: Pass a privileged IAM role to a Redshift cluster (iam:PassRole + ModifyClusterIamRoles),
  then submit a COPY/UNLOAD/UDF query that uses that role's AWS permissions. Covers both already-attached
  and attach-new-then-execute paths.
match:
- - principal: null
  - CanPassIdentity
  - role: null
- - principal: null
  - CanModifyConfiguration
  - cluster: null
where:
- node_type(?cluster) == DataWarehouse
- ?cluster.provider_type in ['AWS::Redshift::Cluster', 'AWS::RedshiftServerless::Namespace']
- ?role trust policy allows redshift.amazonaws.com as service principal
- iam:PassRole for ?role permits iam:PassedToService including redshift.amazonaws.com (or unconditioned)
optional:
- - principal: null
  - HasPermission
  - cluster: null
emit:
  source_type: Identity
  target_type: MachineIdentity
  source: ?principal
  target: ?role
  permissions:
  - iam:PassRole
  - redshift:ModifyClusterIamRoles
  - redshift-data:ExecuteStatement
  conditions:
  - iam_permission
  - role_compatibility
  - service_state
  state_logic: 'ACTIVE when all three are confirmed: (1) iam:PassRole effective for ?role with iam:PassedToService
    admitting redshift.amazonaws.com; (2) redshift:ModifyClusterIamRoles (or redshift-serverless:UpdateNamespace
    for Serverless) effective on ?cluster; (3) a query-submission capability exists (redshift-data:ExecuteStatement
    or GetClusterCredentials on ?cluster). CONDITIONAL(iam_permission) when iam:PassedToService condition
    exists but its redshift.amazonaws.com admission is unresolved. CONDITIONAL(service_state) when the
    cluster is paused (Serverless: no active workgroup). BLOCKED when an SCP or permission boundary denies
    iam:PassRole or ModifyClusterIamRoles. This rule covers BOTH already-attached roles (which also have
    ExecutesAs edges) AND new roles being attached for the first time.'
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - ?principal CanPassIdentity ?role
  - ?principal CanModifyConfiguration ?cluster (redshift:ModifyClusterIamRoles or redshift-serverless:UpdateNamespace)
  - 'can-execute-as linchpin pattern (identity-swap via config + pass): generic CanPassIdentity + CanModifyConfiguration
    for Data workloads'
  false_positive_note: "TWO permissions are independently required: iam:PassRole on the specific role\
    \ ARN (scoped by iam:PassedToService if present) AND redshift:ModifyClusterIamRoles on the cluster.\
    \ Either alone is insufficient \u2014 PassRole without ModifyClusterIamRoles cannot attach the role;\
    \ ModifyClusterIamRoles without PassRole is rejected by the IAM service. Honor iam:PassedToService:\
    \ a role passable only to ec2.amazonaws.com does NOT become a Redshift attached role (downgrade to\
    \ POTENTIAL(role_compatibility)). For Serverless, ModifyClusterIamRoles is replaced by redshift-serverless:UpdateNamespace.\
    \ The query-submission step (ExecuteStatement / GetClusterCredentials) is an optional gate: without\
    \ it the role is attached but no query yet exercises it (CONDITIONAL(service_state)). The linchpin\
    \ pattern can-execute-as owns the escalation roll-up \u2014 do not re-derive CanEscalateTo here."
  narrative: '{principal.name} can pass {role.name} to Redshift cluster {cluster.name} (iam:PassRole +
    redshift:ModifyClusterIamRoles) and then submit a COPY/UNLOAD or CREATE EXTERNAL FUNCTION query that
    uses that role, executing arbitrary AWS API calls as {role.name}. This covers attaching a NEW role
    or exercising an already-attached role.'
move · open · esc close