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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
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) — 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).