[WIP] Keyless per-pod cloud identity for S3 and GCS - #23
Draft
achudnovskij wants to merge 1 commit into
Draft
Conversation
Add ambient credential resolution so a receiver can authenticate to object storage with per-pod identity instead of static keys: - S3: web-identity (IRSA) via STS AssumeRoleWithWebIdentity, and the container credentials endpoint (EKS Pod Identity / ECS). - GCS: the GKE/GCE metadata server (Workload Identity), replacing the previous "metadata-server auth not yet supported" hard error. The chain now mirrors the AWS SDKs: inherited source, static keys, web-identity, container credentials, EC2 IMDS. Static keys and GCS SA-JSON keep winning when present, so nothing changes for existing deployments. Both new S3 sources are one authenticated HTTP call plus the cache/refresh ImdsProvider already had, so they share a single AmbientProvider behind an AmbientKind enum rather than getting a provider and an enum variant each. The container endpoint returns the IMDS document shape, so parse_creds is reused; STS answers with Query-protocol XML, parsed via s3::first_tag_text (now pub(crate)). Rotating token files are re-read on every refresh. On the GCS side, emulator mode is already implied by host != STORAGE_HOST, so sa: None now means metadata auth and no auth enum is needed. Both token paths end in the same status check, TokenResp parse and cache write, so access_token keeps that once and only the request differs. Signing needed no changes: x-amz-security-token was already threaded and creds are fetched per request. identity() folds each refreshing source to a constant so rotation doesn't spuriously fail server-side-copy eligibility. GcsConfig gains a public metadata_endpoint field (GCE_METADATA_HOST), which is why this is 0.3.0 rather than a patch release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — not ready for review.
Lets a receiver authenticate to object storage with ambient per-pod identity instead of static keys, so each one can run under its own ServiceAccount bound to a scoped cloud role.
AssumeRoleWithWebIdentity, and the container credentials endpoint (EKS Pod Identity / ECS).metadata-server auth not yet supportedhard error.No new dependencies.
Credential chain
src(backup-copy inherit) → static keys → web-identity → container credentials → EC2 IMDS → error ifAWS_EC2_METADATA_DISABLED.This mirrors the AWS SDKs, which is where wal-g gets the same behavior — it writes essentially no credential code of its own (
session.NewSessionWithOptionsplus the SDK default chain;gcs.NewClient(ctx)for pure ADC on the GCS side). Static keys and GCS SA-JSON still win when present, so existing deployments are unaffected and this can ship independently of the operator work. Incomplete static keys remain a hard error.Env-var contract
AWS_WEB_IDENTITY_TOKEN_FILE+AWS_ROLE_ARN; optionalAWS_ROLE_SESSION_NAME,AWS_ENDPOINT_URL_STSAWS_CONTAINER_CREDENTIALS_FULL_URIor..._RELATIVE_URI; optionalAWS_CONTAINER_AUTHORIZATION_TOKEN_FILE/AWS_CONTAINER_AUTHORIZATION_TOKENGOOGLE_APPLICATION_CREDENTIALSselects it;GCE_METADATA_HOSToverrides the hostOn EKS/GKE the platform injects these itself once the pod runs under a bound ServiceAccount.
Notes for the reviewer
ImdsProvideralready had, so they share oneAmbientProviderbehind anAmbientKindenum — onecredentials()body, one client builder, one send/status/body block, and one newCredentialSourcearm instead of two. Container creds return the IMDS document shape, soparse_credsis reused; STS returns Query-protocol XML, parsed withs3::first_tag_text(nowpub(crate)).new()only rewriteshostaway fromSTORAGE_HOSTfor the emulator, sois_emulator()recovers that state andsa: Nonecan mean metadata auth. Both token paths end in the same status check →TokenResp→ cache write, soaccess_tokenkeeps that once and only the request differs.x-amz-security-tokenwas already threaded and creds are fetched per request.identity()folds each refreshing source to a constant, so rotation can't spuriously fail server-side-copy eligibility.StorageError::Http, so a 5xx stays retryable viais_transient.GOOGLE_APPLICATION_CREDENTIALS=""now falls back to metadata instead of failing on a read of"".Breaking change
GcsConfiggains a publicmetadata_endpointfield, which breaks external struct-literal construction — hence 0.3.0 rather than a patch. It carriesGCE_METADATA_HOSTand is the seam the metadata tests point at a mock.Verification
cargo fmt --check,cargo clippy --all-targets --locked -- -D warnings, andcargo test --lockedare all clean — 544 tests, 11 of them new (container creds incl. token-file precedence and HTTP errors, web-identity incl. caching, refresh-within-margin and missing-token-file, the STS XML parser, the GCS metadata token, and chain-order/precedence selection in config). The golden SigV4 signature tests are untouched.No end-to-end cloud validation is possible from a devcontainer, so the mock-HTTP tests are the contract; real-cloud validation happens downstream once a per-tenant ServiceAccount is bound.
🤖 Generated with Claude Code