Skip to content

Add ECR auto-refreshing upstream authentication - #278

Open
andrew wants to merge 3 commits into
mainfrom
andrew/ecr-auth
Open

Add ECR auto-refreshing upstream authentication#278
andrew wants to merge 3 commits into
mainfrom
andrew/ecr-auth

Conversation

@andrew

@andrew andrew commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Adds type: ecr to upstream.auth so the proxy can front private AWS ECR registries without a static credential in config. On first request (and again shortly before the 12-hour expiry) it calls ecr:GetAuthorizationToken via the AWS SDK default credential chain and sends the result as HTTP Basic to the ECR host; the existing OCI Bearer-challenge handling in internal/httpclient takes it from there.

upstream:
  oci:
    ecr: "https://123456789012.dkr.ecr.eu-west-1.amazonaws.com"
  auth:
    "https://123456789012.dkr.ecr.eu-west-1.amazonaws.com":
      type: ecr
      region: eu-west-1

AWS credentials come from the SDK default chain (IRSA on EKS, EC2/ECS instance profiles, AWS_* env vars, ~/.aws/credentials). The identity needs ecr:GetAuthorizationToken plus pull permissions on the target repositories. region is optional and falls back to the SDK default resolution.

Implementation: AuthConfig gains a Region field; Server.authForURL routes type: ecr to a small per-region token cache in internal/server/ecr_auth.go that mirrors the expiry-with-skew pattern already used by the OCI token cache in internal/httpclient/transport.go. AuthConfig.Header() and the AuthFunc signature are unchanged; on SDK error the provider logs and returns no header so the upstream 401 surfaces normally.

Dependency impact: aws-sdk-go-v2 is already linked via gocloud.dev/blob/s3blob, so this only adds github.com/aws/aws-sdk-go-v2/service/ecr. Stripped linux binary grows from 35,848,352 to 36,044,960 bytes (+192 KB, +0.55%); go.sum net +2 lines.

Fixes #276.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds automatic AWS ECR authentication for OCI upstreams with region-scoped token caching and refresh.

Changes:

  • Adds type: ecr with optional AWS region configuration.
  • Implements ECR token retrieval through the AWS SDK credential chain.
  • Adds tests, dependencies, documentation, and configuration examples.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Summary
internal/server/server.go Integrates ECR authentication routing.
internal/server/ecr_auth.go Fetches and caches ECR tokens. Critical: error handling can trigger an incorrect Bearer retry instead of preserving the original 401 (1 vote). Moderate: concurrent cache misses can cause redundant token requests; refreshes should be coordinated (4 votes).
internal/server/ecr_auth_test.go Tests ECR token behavior, caching, refresh, and routing.
internal/config/config.go Adds ECR authentication and region configuration.
go.sum Updates dependency checksums.
go.mod Adds AWS ECR SDK dependencies.
docs/configuration.md Documents ECR configuration and permissions.
config.example.yaml Adds an ECR configuration example.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/server/ecr_auth.go Outdated
Comment on lines +62 to +64
if err != nil {
e.logger.Error("fetching ECR authorization token", "region", region, "error", err)
return "", ""

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.

This is the existing behaviour of internal/httpclient.Transport.RoundTrip for every auth type, not something the ECR provider introduces: on a 401 with a Bearer challenge it drains the original response and returns registry authentication: %w when the token fetch fails (transport.go:89-93). A misconfigured type: basic against ECR or GHCR takes the identical path today. The provider logs the SDK error (no credentials, AccessDenied, etc) before returning empty, which is more actionable than the bare 401 would be. Changing the surfaced status means changing RoundTrip to return the original 401 when the token fetch fails, which affects every registry and is out of scope here; updated the doc comment on header() to describe the actual failure path.

Comment thread internal/server/ecr_auth.go Outdated
andrew added 2 commits August 21, 2026 09:34
- Add "ecr" auth type to upstream.auth config with optional region
- Cache ecr:GetAuthorizationToken results per region and refresh
  shortly before expiry via the AWS SDK default credential chain
- Route type: ecr through the token cache in Server.authForURL
- Document the new type in config.example.yaml and docs/configuration.md

Fixes #276
Drops the internal/server package below the goconst min-occurrences
threshold for the Authorization literal.
Concurrent cache misses for the same region now share a single
GetAuthorizationToken call instead of each issuing their own, avoiding
a request burst against the ECR API at cold start and at each 12-hour
refresh. golang.org/x/sync is already a direct dependency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ECR authentication

2 participants