[Implementation] Backstage cataloger: AWS SigV4 auth (self-refreshing IAM-role creds)#232
Conversation
…M-role creds) Adds auth_mode (bearer|sigv4), aws_region, and aws_service inputs plus optional static-key AWS secrets to the backstage cataloger manifest, and documents SigV4 auth in the README. In sigv4 mode the cataloger resolves AWS credentials at runtime from the standard credential chain (IRSA / ECS task role / EC2 instance profile / static keys) and re-resolves them each run, so short-lived IAM-role creds self-refresh with no user-supplied secrets to rotate. curl's native --aws-sigv4 (already in the base image) does the signing. Spec only — no main.sh changes yet.
🌒 Earthly Lunar
|
There was a problem hiding this comment.
read the spec. design is coherent and the files match the spec-phase playbook. two things for the primary reviewer to weigh in on inline. the sidecar 'alternative' section doesnt actually fit lunar's snippet-pod model, and the aws_region failure mode when unset in sigv4 mode isnt pinned.
…ecar) + pin aws_region failure mode Addresses Fry review on #232: - The proxy 'alternative' framed aws-sigv4-proxy as a same-pod sidecar at localhost:8080, which doesn't fit Lunar's snippet-pod model — the operator builds the pod's container list (snippet container, which OPERATOR_SNIPPET_CONTAINER_SPEC_* replaces rather than appends, + the built-in sidecar) with no hook to add a container. Reworked to a standalone Deployment + Service reached via cluster DNS, with IRSA on the proxy's own SA. - Pinned aws_region behavior: in sigv4 mode with neither the input nor AWS_REGION set, the cataloger fails fast with a clear error instead of signing with an empty region.
|
Lets proceed to implementation but we should ideal test this somehow on cronos can you DM me instructions for how to this? Maybe doesn't have to be a real backstage but verifying the auth chain works. |
Adds the sigv4 implementation to main.sh behind auth_mode (default bearer,
unchanged). In sigv4 mode it resolves AWS credentials once at startup from
the standard provider chain and signs every request with curl --aws-sigv4:
1. static keys (LUNAR_SECRET_AWS_* or ambient AWS_* env)
2. IRSA / web identity -> STS AssumeRoleWithWebIdentity (token-auth POST,
XML parsed with python3 stdlib)
3. ECS task role -> container credentials endpoint (JSON)
4. EC2 instance profile -> IMDSv2
Session-token creds get the x-amz-security-token header. aws_region resolves
from the input or AWS_REGION/AWS_DEFAULT_REGION and fails fast with a clear
error if unset (per review). Zero new deps: curl 8.19 + jq + python3 are all
in base-main. Resolved once per run; each scheduled run re-resolves, so
role creds self-refresh with nothing to rotate.
Locally verified end-to-end against a header-capturing server: the sigv4
path emits a valid AWS4-HMAC-SHA256 signature; region fail-fast and the
unchanged bearer path both behave as expected.
|
Implementation pushed ( Credentials resolve once at startup from the standard chain (static keys → IRSA web-identity via STS Local test evidenceRan the actual
(AKIAIOSFODNN7EXAMPLE is AWS's public example key.)
The static-keys path shares the exact signing +
|
There was a problem hiding this comment.
the auth chain implementation is in shape but the ORDER inverts what the README says. static keys are #1 in the code (incl. ambient AWS_* env), but #4 in the docs as an 'escape hatch' fallback. so IRSA silently loses to any stray env var. one inline nit on the EKS Pod Identity token-file variant too.
…-file Addresses Fry review on #232: - Reorder the chain to match the README (and the plugin's self-refreshing-by- default promise): IRSA / EKS Pod Identity -> ECS -> EC2 IMDSv2 -> static LUNAR_SECRET_AWS_* keys LAST. Previously static was #1, so an attached IRSA role lost to any credential source ahead of it. - Drop the ambient AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env fallback entirely. A stray AWS_* env var (sidecar, Secret mount, dev tooling) must not silently preempt an annotated role — that would break self-refresh. Static is now the deliberate opt-in LUNAR_SECRET_AWS_* path only. - ECS branch now also handles EKS Pod Identity: read the rotating AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (fresh at resolve time) in addition to the direct-value AWS_CONTAINER_AUTHORIZATION_TOKEN env. Verified locally: static LUNAR_SECRET_* resolves + signs; a stray ambient AWS_ACCESS_KEY_ID is now ignored (no longer preempts role sources); no-cred case fails fast.
There was a problem hiding this comment.
addressed cleanly. chain reordered to IRSA -> container creds -> IMDS -> static (LUNAR_SECRET_AWS_* only, ambient AWS_* dropped so a stray env var cant preempt an attached role), and the container-creds branch now reads AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE for Pod Identity. matches the README ordering.
Combines the SigV4 auth work with main's backstage rewrite (by-query
cursor pagination + api_path_prefix). Conflicts were doc/echo-only:
- main.sh: kept main's URL echo (includes ${API_PATH_PREFIX}) + my auth-mode line
- README.md: kept both new sections (AWS SigV4 Authentication + API Path Prefix)
resolve_aws_credentials / AUTH_ARGS auto-merged onto the by-query fetch.
Re-verified locally: sigv4 path still emits a valid AWS4-HMAC-SHA256 sig
against the by-query {"items":[]} shape and exits 0.
What this is
Spec-only PR (per the plugin playbook) adding AWS SigV4 authentication to the live-API
catalogers/backstagecataloger, which today only supports Bearer tokens. For Backstage APIs fronted by AWS IAM auth (e.g. Amazon API Gateway), Bearer tokens are rejected — every request must be SigV4-signed.Tracks ENG-1119.
What's included
catalogers/backstage/lunar-cataloger.yml— newauth_mode(bearerdefault |sigv4),aws_region,aws_serviceinputs; optional static-key AWS secrets; landing-page note.catalogers/backstage/README.md— "AWS SigV4 Authentication" section (credential chain, one-time IAM-role setup, sidecar alternative).No
main.shchanges yet — implementation follows secondary-reviewer approval.Design summary
The driving requirement: it must work without the user supplying new credentials constantly — so static long-lived keys and manually-refreshed session tokens are out.
In
sigv4mode the cataloger resolves AWS credentials at runtime from the standard AWS credential provider chain, re-resolved on every run, so short-lived IAM-role creds always sign fresh with nothing for the user to rotate:Signing itself is
curl --aws-sigv4, already present in the base image (curl 8.19.0) — zero new dependencies (verified against the base image on ENG-1119: it emits a canonicalAWS4-HMAC-SHA256header withx-amz-security-token).One-time deploy prerequisite (documented in the README): the role annotation goes on the snippet-pod service account (
OPERATOR_POD_SERVICE_ACCOUNT, chart<release>-script-pod) that catalogers actually execute under — not the hub SA. Called out explicitly because annotating the hub SA is the easy mistake.Relationship to existing plugins
Enhances the existing
catalogers/backstage(live-API sync).bearerremains the default — existing installs are unaffected. No Component JSON schema change; output paths are identical.Testing plan
curl --aws-sigv4against the base image (ENG-1119). At implementation we'll add credential-chain resolution (STSAssumeRoleWithWebIdentityfor IRSA is a token-authenticated POST; ECS/IMDS return JSON) and cover the resolver with fixtures/mocked endpoints.statusstaysexperimental. To validate end-to-end we'd need either a test API-Gateway-fronted Backstage or coordination with a deployment that has one.Open questions
aws-sigv4-proxysidecar as the no-plugin-code alternative. Both self-refresh. Preference?aws_servicedefaultexecute-apiassumes API Gateway. Fine as the default?