aws-eks-irsa-workload-identity

A Kubernetes ServiceAccount annotated with eks.amazonaws.com/role-arn and a matching IAM role trust policy (Federated = cluster OIDC issuer) lets any pod running as that KSA exchange its projected token for IAM role credentials via sts:AssumeRoleWithWebIdentity. Cites container-chains workload-identity-federates and federation-chains federation-maps-to-identity.

derived aws emits CanFederateAs

match

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

{'ksa': None} ExternalIdentityMapsTo {'iam_role': None}

where

node_type(?ksa) in [WorkloadIdentity, ServiceAccount] ?ksa is an EKS Kubernetes ServiceAccount with IRSA annotation (eks.amazonaws.com/role-arn) node_type(?iam_role) in [Role, MachineIdentity] exists TrustsExternalIdentity(?iam_role -> ?ksa) backed by an AWS OIDC provider matching the cluster OIDC issuer URL the IAM role trust Federated principal = cluster OIDC issuer ARN + sts:AssumeRoleWithWebIdentity

emit

source typeWorkloadIdentity
target typeRole
source?ksa
target?iam_role
permissionssts:AssumeRoleWithWebIdentity
conditionstrust_condition
state logicDELEGATED to federation-chains federation-maps-to-identity and container-chains workload-identity-federates. The ExternalIdentityMapsTo edge already represents a MATCHED trust condition (the mapping was realized when the explicit rule verified sub matching system:serviceaccount:<ns>:<sa>). ACTIVE when the sub condition is SATISFIABLE by the attacker (broad/unpinned like system:serviceaccount:*:* or namespace-only without SA pin, or the attacker can schedule pods as the pinned SA). CONDITIONAL(trust_condition) when the sub is pinned to a specific SA the attacker cannot schedule. BLOCKED if the OIDC provider is deleted or issuer key rotated. This rule dedupes with container-chains workload-identity-federates on (CanFederateAs, ?ksa, ?iam_role) — emit once.

Narrative

Kubernetes ServiceAccount {ksa.name} (namespace {ksa.namespace}) is annotated with IAM role {iam_role.name}. The role's trust policy trusts the cluster OIDC issuer with condition sub matching this SA. A pod running as {ksa.name} presents its projected SA token to STS (sts:AssumeRoleWithWebIdentity) and obtains credentials for {iam_role.name} with no static key (federation-chains rolls up account foothold and impersonation).

Raw rule rules/derived/aws/eks.yaml

id: aws-eks-irsa-workload-identity
emits: CanFederateAs
description: A Kubernetes ServiceAccount annotated with eks.amazonaws.com/role-arn and a matching IAM
  role trust policy (Federated = cluster OIDC issuer) lets any pod running as that KSA exchange its projected
  token for IAM role credentials via sts:AssumeRoleWithWebIdentity. Cites container-chains workload-identity-federates
  and federation-chains federation-maps-to-identity.
match:
- - ksa: null
  - ExternalIdentityMapsTo
  - iam_role: null
where:
- node_type(?ksa) in [WorkloadIdentity, ServiceAccount]
- ?ksa is an EKS Kubernetes ServiceAccount with IRSA annotation (eks.amazonaws.com/role-arn)
- node_type(?iam_role) in [Role, MachineIdentity]
- exists TrustsExternalIdentity(?iam_role -> ?ksa) backed by an AWS OIDC provider matching the cluster
  OIDC issuer URL
- the IAM role trust Federated principal = cluster OIDC issuer ARN + sts:AssumeRoleWithWebIdentity
emit:
  source_type: WorkloadIdentity
  target_type: Role
  source: ?ksa
  target: ?iam_role
  permissions:
  - sts:AssumeRoleWithWebIdentity
  conditions:
  - trust_condition
  state_logic: "DELEGATED to federation-chains federation-maps-to-identity and container-chains workload-identity-federates.\
    \ The ExternalIdentityMapsTo edge already represents a MATCHED trust condition (the mapping was realized\
    \ when the explicit rule verified sub matching system:serviceaccount:<ns>:<sa>). ACTIVE when the sub\
    \ condition is SATISFIABLE by the attacker (broad/unpinned like system:serviceaccount:*:* or namespace-only\
    \ without SA pin, or the attacker can schedule pods as the pinned SA). CONDITIONAL(trust_condition)\
    \ when the sub is pinned to a specific SA the attacker cannot schedule. BLOCKED if the OIDC provider\
    \ is deleted or issuer key rotated. This rule dedupes with container-chains workload-identity-federates\
    \ on (CanFederateAs, ?ksa, ?iam_role) \u2014 emit once."
  confidence: min(contributing_confidences)
  derived_from:
  - ?ksa ExternalIdentityMapsTo ?iam_role
  - ?iam_role TrustsExternalIdentity ?ksa (cluster OIDC issuer trust)
  - 'container-chains: workload-identity-federates'
  - 'federation-chains: federation-maps-to-identity'
  - 'aws/identity-escalation: aws-assume-role-web-identity'
  false_positive_note: "CITE, DO NOT DUPLICATE federation-chains and container-chains. This rule dedupes\
    \ with container-chains workload-identity-federates on (CanFederateAs, ?ksa, ?iam_role) \u2014 emit\
    \ once. The CanEnterAccount / CanImpersonate / CanEscalateTo roll-ups are federation-chains' (federate-then-enter-account,\
    \ federate-inherits-capabilities) \u2014 never re-derive them here. The ExternalIdentityMapsTo explicit\
    \ rule already filtered for matching trust conditions: the edge only fires when sub condition is satisfied\
    \ by the specific KSA. This derived rule interprets whether that match is ATTACKER-SATISFIABLE: a\
    \ pinned sub the attacker cannot schedule as is CONDITIONAL, not ACTIVE. A wildcard or namespace-only\
    \ sub is ACTIVE. Each cluster has a DISTINCT OIDC issuer URL; do not conflate across clusters."
  narrative: Kubernetes ServiceAccount {ksa.name} (namespace {ksa.namespace}) is annotated with IAM role
    {iam_role.name}. The role's trust policy trusts the cluster OIDC issuer with condition sub matching
    this SA. A pod running as {ksa.name} presents its projected SA token to STS (sts:AssumeRoleWithWebIdentity)
    and obtains credentials for {iam_role.name} with no static key (federation-chains rolls up account
    foothold and impersonation).
move · open · esc close