Skip to content

Support EKS Pod Identity credentials in the S3 repository - #151614

Merged
DaveCTurner merged 8 commits into
elastic:mainfrom
somaz94:repository-s3-eks-pod-identity
Jul 9, 2026
Merged

DaveCTurner merged 8 commits into
elastic:mainfrom
somaz94:repository-s3-eks-pod-identity

Conversation

@somaz94

@somaz94 somaz94 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What

repository-s3 already supports EKS IRSA (web identity) but not EKS Pod Identity. With the ES 9.x entitlements model, the AWS SDK's ContainerCredentialsProvider reads the Pod Identity auth token from AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token), which is outside the repository-s3 entitlement-grantable area, so credential resolution fails with Failed to read token file.

How

This mirrors the IRSA web-identity setup: repository-s3 grants read access to a fixed entitled location, and the operator points the credential source at it.

  • entitlement-policy.yaml grants read on a fixed config-relative symlink repository-s3/eks-pod-identity-token, alongside the existing IRSA repository-s3/aws-web-identity-token-file grant.
  • The operator points AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (or the aws.containerAuthorizationTokenFile system property) at that entitled path and symlinks the Kubernetes-injected token there, exactly as they already do for the IRSA web-identity token. S3Service does not override the env var or any system property, so there are no JVM-wide repercussions.

Token rotation needs no extra handling: ContainerCredentialsProvider re-reads the token on each resolution.

Testing

  • ./gradlew :modules:repository-s3:compileJava :modules:repository-s3:compileJavaRestTestJava :modules:repository-s3:checkstyleMain :modules:repository-s3:forbiddenApisMain — BUILD SUCCESSFUL
  • ./gradlew :modules:repository-s3:javaRestTest --tests "*RepositoryS3PodIdentityCredentialsRestIT" — BUILD SUCCESSFUL, 16 tests / 0 failures, run locally with Docker / testcontainers. The IT sets AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE to ${ES_PATH_CONF}/repository-s3/eks-pod-identity-token to mirror the operator setup.

Closes #106484


This change was implemented with the assistance of Claude Code (Opus 4.8). I have reviewed it and take responsibility for its correctness.

@cla-checker-service

cla-checker-service Bot commented Jun 18, 2026

Copy link
Copy Markdown

💚 CLA has been signed

@elasticsearchmachine elasticsearchmachine added v9.5.0 external-contributor Pull request authored by a developer outside the Elasticsearch team labels Jun 18, 2026
@somaz94
somaz94 marked this pull request as ready for review June 18, 2026 07:09
@elasticsearchmachine elasticsearchmachine added the needs:triage Requires assignment of a team area label label Jun 18, 2026
@john-wagster john-wagster added the :Distributed/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs label Jun 18, 2026
@elasticsearchmachine elasticsearchmachine added Team:Distributed Meta label for distributed team. and removed needs:triage Requires assignment of a team area label labels Jun 18, 2026
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-distributed (Team:Distributed)

@coderabbitai

This comment was marked as off-topic.

@DaveCTurner DaveCTurner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the IRSA identity setup, we need to ask users to set this up themselves with env vars or system properties. We can't override system properties as you propose because this has JVM-wide repercussions.

@somaz94

somaz94 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense. I've dropped the system-property override entirely.

The plugin no longer touches aws.containerAuthorizationTokenFile or AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE. It just keeps the read entitlement on the repository-s3/eks-pod-identity-token symlink, the same way the IRSA web-identity token works. Operators point AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (or the aws.containerAuthorizationTokenFile sysprop) at that entitled path themselves and symlink the Kubernetes-injected token there.

So S3Service loses the maybeOverrideContainerAuthTokenFile / clearPodIdentitySysprop methods (about 100 lines removed), and the entitlement policy drops the write_system_properties grant for that sysprop. The IT now sets the env var to ${ES_PATH_CONF}/repository-s3/eks-pod-identity-token to mirror the operator setup, and :modules:repository-s3:javaRestTest passes locally (16 tests).

@DaveCTurner DaveCTurner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, ok, with all that gone it seems we could use repository-s3/aws-web-identity-token-file for EKS pod identity too, avoiding the need for this change entirely. Can you explain why the filename matters? Or am I missing something?

@DaveCTurner DaveCTurner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On reflection I think I accept the need for a different file name - it's not really the same thing, and it'd be confusing to overload this one location with two different meanings.

I left a couple of comments about the test, but overall it's doing pretty much the right thing.

The code comments aren't particularly useful (are they LLM-generated?) as they don't really describe the "why" of anything, only reiterating the "how". I think you could reasonably delete them, although comments linking to the relevant AWS docs might be useful in their places.

We also need to add to the reference manual the instructions about how to set up the relevant symlink, similar to the IRSA instructions here. Unfortunately that's in a different repository so will need a separate PR, but I'll be able to merge them together.

private static final String BASE_PATH = PREFIX + "base_path";
private static final String CLIENT = "pod_identity_credentials_client";

private static final String POD_IDENTITY_TOKEN_FILE_CONTENTS = "test-pod-identity-auth-token-" + UUID.randomUUID();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This UUID.randomUUID() is not a deterministic function of the test seed - I'd rather we stuck to using deterministic randomness. A little tricky here because deterministic randomness isn't available in static context so you'll need to use a Supplier<String> to delay the generation of this token until later when the test is running.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — the token is now produced lazily by a memoized Supplier<String> (LazyInitializable over randomIdentifier()) instead of UUID.randomUUID() in static context, so it's a deterministic function of the test seed and isn't evaluated until the test runs. The same memoized supplier feeds both the config file and the fixture's verification (see the other thread), so they always observe the same token.

private static final Supplier<String> regionSupplier = new DynamicRegionSupplier();
private static final DynamicAwsCredentials dynamicCredentials = new DynamicAwsCredentials(regionSupplier, "s3");

private static final Ec2ImdsHttpFixture podIdentityCredentialsFixture = new Ec2ImdsHttpFixture(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be approximately right but I'm not sure it's quite the same shape of response (e.g. does it include the RoleArn field?) and it doesn't verify that the token is correct.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split into the two parts you raised:

  • Response shape: the credentials JSON the shared Ec2ImdsHttpHandler emits already includes RoleArn (alongside AccessKeyId/Expiration/SecretAccessKey/Token), which is what ContainerCredentialsProvider consumes — so the shape was already correct.
  • Token verification: added. Ec2ImdsServiceBuilder.authorizationTokenSupplier(...) makes the credentials endpoint require the exact Authorization header the SDK sends (the token it read from the entitled file) and return 403 otherwise, mirroring AwsStsHttpHandler. It's opt-in (null by default) so the other IMDS/ECS consumers are unaffected. The pod-identity test wires in the same memoized token, so it now genuinely verifies the SDK forwarded the entitled token. Also added a fixture unit test (testAlternativeCredentialsEndpointRequiresAuthorizationToken) covering the missing/wrong/correct-token cases, and confirmed the full RepositoryS3PodIdentityCredentialsRestIT still passes end-to-end.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the credentials JSON the shared Ec2ImdsHttpHandler emits already includes RoleArn

Indeed, but should it? Is this what the pod identity credentials endpoint returns? I couldn't find any docs confirming one way or the other.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it shouldn't be there. I checked the eks-pod-identity-agent source (EksCredentialsResponse in pkg/credentials/model.go) and the pod identity endpoint returns AccessKeyId, SecretAccessKey, Token, AccountId and Expiration, so an AccountId and no RoleArn. The RoleArn was just leaking in from reusing the shared IMDS/ECS handler.

I added a podIdentityCredentialsResponse() option to the fixture so this test now returns AccountId and drops RoleArn, with a unit test asserting the shape. Pushed as a new commit.

@DaveCTurner DaveCTurner self-assigned this Jul 1, 2026
@somaz94
somaz94 force-pushed the repository-s3-eks-pod-identity branch from d05a344 to 81d02b5 Compare July 2, 2026 03:10
@DaveCTurner

Copy link
Copy Markdown
Member

Thanks, I hope to get to this again next week. Please don't force-push to PRs under review -- we've lost the commits against which my earlier reviews were done.

@somaz94

somaz94 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Sorry about the force-push, I won't rebase this branch again while it's under review. Pushed the RoleArn fix above as a new commit on top instead.

Also opened the companion reference-manual PR you mentioned: elastic/docs-content#7182. It adds an EKS Pod Identity section next to the IRSA one.

@somaz94

somaz94 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @DaveCTurner. Pushed fc62a0e:

  • Trimmed the code comments — deleted the ones that just restated the adjacent code/asserts, and where a comment explains AWS-specific behavior I replaced the prose with a short note linking the EKS Pod Identity docs (pod-id-how-it-works.html).
  • The two inline threads were addressed earlier in 9e89b16: deterministic randomness now comes from randomIdentifier() (memoized via LazyInitializable, shared by the fixture and the config so both observe the same token) instead of UUID.randomUUID(); and the fixture returns the real Pod Identity response shape (AccountId, no RoleArn) with the credentials endpoint now verifying the Authorization token.

PTAL when you have a moment. The reference-manual docs are in the companion PR elastic/docs-content#7182.

@DaveCTurner DaveCTurner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@DaveCTurner
DaveCTurner merged commit b5cdd68 into elastic:main Jul 9, 2026
38 checks passed
DaveCTurner pushed a commit to elastic/docs-content that referenced this pull request Jul 10, 2026
#7182)

Documents how to use EKS Pod Identity credentials with the S3 snapshot
repository, as a companion to elastic/elasticsearch#151614. Adds an
"Using EKS Pod Identity for authentication" subsection alongside the
existing IRSA instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Distributed/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs >enhancement external-contributor Pull request authored by a developer outside the Elasticsearch team Team:Distributed Meta label for distributed team. v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S3 repository- support for AWS_CONTAINER_CREDENTIALS_FULL_URI

4 participants