Skip to content

feat(registry): authenticate to private ECR with the AWS credential chain - #40

Merged
clouddrove-ci merged 1 commit into
masterfrom
feat/ecr-native-auth
Aug 14, 2026
Merged

feat(registry): authenticate to private ECR with the AWS credential chain#40
clouddrove-ci merged 1 commit into
masterfrom
feat/ecr-native-auth

Conversation

@clouddrove-ci

Copy link
Copy Markdown
Member

Problem

Destination registries were authenticated only through docker credential config (authn.DefaultKeychain), so an ECR destination required a docker login performed in the runtime environment.

The GitHub Action cannot satisfy that. It runs as a Docker container action, and a container action does not receive the runner's ~/.docker/config.json; the runner mounts /github/home as HOME instead. Credentials written by aws-actions/amazon-ecr-login on the host are therefore invisible to the container, and every request fails:

Failed to copy to <dest>: failed to copy image: HEAD https://<account>.dkr.ecr.eu-west-1.amazonaws.com/v2/.../manifests/v0.28.0:
unexpected status code 401 Unauthorized

Reproduced on a live workflow before this change, and confirmed fixed there by routing the login through DOCKER_CONFIG in the workspace.

Change

ECRKeychain recognises private ECR hosts by the registry host itself (<account>.dkr.ecr.<region>.amazonaws.com, including -fips, GovCloud, and the China partition), reads the account and region from it, and calls ecr:GetAuthorizationToken through the standard AWS credential chain: environment variables, shared config, IRSA, instance role.

  • Tokens are cached per account and region and refetched five minutes before expiry, so a continuously running sync does not need an external refresh loop.
  • An existing docker credential entry for the same registry still wins, so setups that log in explicitly are unchanged.
  • The keychain is used for both the existence check (GenericRegistry) and the copy (crane.Copy), and it activates on the host, not on destinations[].type, so a mistyped type cannot silently disable it.
  • Public ECR (public.ecr.aws) is a different service with a different token API and is not covered; documented as still needing a docker login.

The Helm chart gains serviceAccount.annotations, so a deployment can attach an IRSA role and drop dockerConfigSecret for ECR entirely.

Docs cover both halves of the original failure: ECR needs only aws-actions/configure-aws-credentials, and GHCR/ACR/GCR logins need DOCKER_CONFIG pointed at the workspace so the container action can read them.

Tests

internal/registry/ecr_test.go: host parsing (including hosts that merely embed an ECR host), delegation for non-ECR hosts, AWS token used when docker config is empty, docker config preferred when present, fallback when docker config errors, caching per account and region, refetch inside the expiry window, error message naming the registry and cause, user:password decoding with colons in the password.

verify-syncerd (full tier) passes: static/build, helm, live Docker Hub to local registry sync with state skip, Slack failure notification, docker build.

…hain

Destination registries were authenticated only through docker credential
config, so ECR required a docker login that had already been performed in the
runtime environment. The GitHub Action cannot satisfy that: it runs as a Docker
container action, which does not receive the runner's ~/.docker/config.json, so
credentials written by aws-actions/amazon-ecr-login on the host were invisible
to it and every request failed with 401 Unauthorized.

ECRKeychain recognises private ECR hosts by their registry host, reads the
account and region from it, and calls ecr:GetAuthorizationToken through the
standard AWS credential chain. Tokens are cached per account and region and
refetched inside a five minute window before expiry, so a continuously running
sync no longer needs an external refresh loop. An existing docker credential
entry for the same registry still wins, which keeps explicit logins working.

Public ECR is a separate service with a separate token API and is not covered.

The Helm chart gains serviceAccount.annotations so a deployment can attach an
IRSA role and drop dockerConfigSecret for ECR entirely.
@clouddrove-ci
clouddrove-ci merged commit 067ca25 into master Aug 14, 2026
3 checks passed
@clouddrove-ci
clouddrove-ci deleted the feat/ecr-native-auth branch August 14, 2026 23:22
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.

2 participants