Skip to content

App composition: pods admitted before the PodIdentityAssociation propagates never get AWS credentials #1700

Description

@Smana

What

The App composition renders the Deployment and the EKSPodIdentity (→ PodIdentityAssociation) concurrently. Nothing orders them. If a pod is admitted before the association has propagated to the EKS Pod Identity webhook, that pod gets no AWS credentials — and because injection happens at admission, no amount of restarting fixes it. The pod crashloops forever against a correctly-configured IAM role.

Hit on xplane-image-gallery on the freshly rebuilt cluster (2026-08-02): 36 restarts, CrashLoopBackOff, dying on

{"level":"fatal","error":"Access Denied.","message":"Failed to connect to storage"}

Why it is easy to misdiagnose

Every piece of the IAM story looks healthy, so the investigation goes to AWS and finds nothing:

  • EKSPodIdentity/xplane-image-gallery-s3-pod-identitySynced=True Ready=True
  • PodIdentityAssociationReady=True, bound to exactly ns=apps sa=xplane-image-gallery
  • IAM policy — correctly scoped to the right bucket:
    s3:ListBucket           arn:aws:s3:::eu-west-3-ogenki-xplane-image-gallery
    s3:GetObject/PutObject/DeleteObject   .../*
    
  • Bucket MR — Ready=True, external name matches STORAGE_BUCKET exactly
  • Region matches, endpoint matches

The tell is in the pod spec, not in AWS:

$ kubectl get pod -n apps <pod> -o json | jq '.spec.containers[0].env[] | select(.name|test("AWS_CONTAINER"))'
# (empty — nothing injected)
$ kubectl get pod -n apps <pod> -o json | jq '.spec.volumes[] | select(.name=="eks-pod-identity-token")'
# (empty)

A working pod-identity workload (runlore-0) has all three:

AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
volume: eks-pod-identity-token

The timing

Event Timestamp
PodIdentityAssociation object created 07:35:51Z
Pod …-xbmpv admitted 07:35:49Z — 2s before the PIA object existed
PIA Ready=True 07:35:59Z
Pod …-67lg9 admitted 07:36:00Z — 1s after Ready, still inside webhook propagation lag

Both replicas lost the race. Neither ever recovered, across 36 restarts and ~3 hours.

Ruled out

automountServiceAccountToken: false (set by the claim, and a reasonable suspect since the token is a projected SA volume) is not the cause. Controlled admission test with the same ServiceAccount in the same namespace:

Pod automountServiceAccountToken Injected?
epi-test-automount-false false ✅ env + volume
epi-test-automount-default unset ✅ env + volume

The webhook injects regardless. The variable that mattered was when the pod was admitted.

Workaround

Delete the pods once the association is live — they are re-admitted and the webhook injects correctly:

kubectl delete pod -n apps -l app.kubernetes.io/instance=xplane-image-gallery

Confirmed: both replicas came back 1/1 Running, the AWS env vars are present, the app logs Storage client initialized instead of the fatal, and the XR is Synced=True Ready=True.

Proposed fix

Latch Deployment rendering on PodIdentityAssociation readiness for any App that declares an EPI — the same option("params").ocds pattern the composition already uses to gate AIGatewayRoute rendering on Deployment readiness (SPEC-002 FR-002). No Deployment until the association reports Ready=True, so pods can never be admitted into the gap.

Worth considering alongside it: a guard so this fails loudly rather than silently. An App that declares an EPI but whose pods have no AWS_CONTAINER_CREDENTIALS_FULL_URI is always a bug — right now the only signal is an application-level Access Denied that points at IAM.

Impact

Any App claim combining a workload with an EKSPodIdentity, on first creation — so it reproduces on every cluster rebuild, and is invisible on steady-state clusters where the association long predates any pod. xplane-image-gallery is the instance that surfaced it; xplane-harbor's S3 path uses a static IAM user and is unaffected, and app-wizard does not need AWS credentials at runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions