cluster-rbac-exec

Exec into a pod (cluster RBAC / credential / run-command) inherits the pod's mounted ServiceAccount/IRSA/Workload-Identity token, executing as that identity.

derived any cloud emits CanExecuteAs

match

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

{'principal': None} CanExecuteCommand {'pod': None} {'pod': None} ExecutesAs {'workload_identity': None}

where

node_type(?pod) in [KubernetesWorkload, Container, ContainerService, ContainerTask] node_class(?workload_identity) == Identity node_type(?workload_identity) in [ServiceAccount, WorkloadIdentity, MachineIdentity, ManagedIdentity, ServiceIdentity, Identity, Role]

emit

source typeIdentity
target typeIdentity
source?principal
target?workload_identity
permissions<pods/exec create | kubectl exec | run-command into container>
conditionstoken_mounted
state logicACTIVE — exec into a running pod immediately exposes the mounted SA token and the cloud credential relay to the attacker's shell, so the attacker holds ?workload_identity. CONDITIONAL(token_mounted) when the SA token is NOT projected into the pod (automountServiceAccountToken: false AND no IRSA/WI projected volume) — then exec yields OS access but not the token. Inherit weakest with the contributing CanExecuteCommand: a CONDITIONAL/BLOCKED exec (RBAC verb scoped to a different namespace/pod, pods/exec denied, PodSecurity/OPA gate) caps or blocks this edge.

Narrative

{principal.name} can exec into {pod.name} ({perm:CanExecuteCommand}), which runs as {workload_identity.name}; the pod's mounted ServiceAccount/IRSA/ Workload-Identity token is readable from inside, so {principal.name} executes as {workload_identity.name}.

Raw rule rules/derived/container-chains.yaml

id: cluster-rbac-exec
emits: CanExecuteAs
description: Exec into a pod (cluster RBAC / credential / run-command) inherits the pod's mounted ServiceAccount/IRSA/Workload-Identity
  token, executing as that identity.
applies_to:
- '*'
match:
- - principal: null
  - CanExecuteCommand
  - pod: null
- - pod: null
  - ExecutesAs
  - workload_identity: null
where:
- node_type(?pod) in [KubernetesWorkload, Container, ContainerService, ContainerTask]
- node_class(?workload_identity) == Identity
- node_type(?workload_identity) in [ServiceAccount, WorkloadIdentity, MachineIdentity, ManagedIdentity,
  ServiceIdentity, Identity, Role]
emit:
  source_type: Identity
  target_type: Identity
  source: ?principal
  target: ?workload_identity
  permissions:
  - <pods/exec create | kubectl exec | run-command into container>
  conditions:
  - token_mounted
  state_logic: "ACTIVE \u2014 exec into a running pod immediately exposes the mounted SA token and the\
    \ cloud credential relay to the attacker's shell, so the attacker holds ?workload_identity. CONDITIONAL(token_mounted)\
    \ when the SA token is NOT projected into the pod (automountServiceAccountToken: false AND no IRSA/WI\
    \ projected volume) \u2014 then exec yields OS access but not the token. Inherit weakest with the\
    \ contributing CanExecuteCommand: a CONDITIONAL/BLOCKED exec (RBAC verb scoped to a different namespace/pod,\
    \ pods/exec denied, PodSecurity/OPA gate) caps or blocks this edge."
  confidence: min(contributing_confidences)
  derived_from:
  - ?principal CanExecuteCommand ?pod
  - ?pod ExecutesAs ?workload_identity
  false_positive_note: "This dedupes with can-execute-as execute-as-via-command on (CanExecuteAs, ?principal,\
    \ ?workload_identity) \u2014 SAME collapse, k8s/pod-exec framing; emit once, do not double-count weight.\
    \ The escalation consequence is produced by can-execute-as / can-control off this CanExecuteAs \u2014\
    \ cite, do not duplicate. The token-mount gate matters: a pod with automountServiceAccountToken:false\
    \ and no IRSA/WI projected volume gives OS access but not the identity => CONDITIONAL(token_mounted).\
    \ Scope the exec verb to the ACTUAL pod/namespace: a `pods/exec` grant in namespace A does not exec\
    \ a pod in namespace B. Cluster-admin kubeconfig / a broad pods/exec on all namespaces reaches every\
    \ pod's identity \u2014 the fan-out is real but each edge is per (pod, workload_identity). Distinct\
    \ from rule 1: exec is runtime access to an already-running pod, not a supply-chain code push."
  narrative: '{principal.name} can exec into {pod.name} ({perm:CanExecuteCommand}), which runs as {workload_identity.name};
    the pod''s mounted ServiceAccount/IRSA/ Workload-Identity token is readable from inside, so {principal.name}
    executes as {workload_identity.name}.'
move · open · esc close