You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(registry): authenticate to private ECR with the AWS credential chain (#40)
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.
Co-authored-by: Anmol Nagpal <ianmolnagpal@gmail.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Added
11
+
- Native authentication for private Amazon ECR destinations: when a destination host matches `<account>.dkr.ecr.<region>.amazonaws.com`, SyncerD calls `ecr:GetAuthorizationToken` through the standard AWS credential chain (environment variables, shared config, IRSA, instance role) instead of requiring a `docker login`. Tokens are cached per account and region and refetched before they expire, so a long-running sync no longer needs an external refresh loop, and a Kubernetes deployment using IRSA no longer needs a `dockerConfigSecret` for ECR. An existing docker credential entry for the same registry still wins, so setups that log in explicitly are unchanged. Public ECR (`public.ecr.aws`) is a different service and is not covered
12
+
13
+
### Fixed
14
+
- The GitHub Action could not authenticate to any destination registry that was logged into with `docker/login-action` or `aws-actions/amazon-ecr-login`: it runs as a Docker container action, which does not receive the runner's `~/.docker/config.json`, so every request failed with `401 Unauthorized`. ECR now works with AWS credentials alone, and the README documents the `DOCKER_CONFIG` workspace path that makes the other registries' login steps visible to the container
Copy file name to clipboardExpand all lines: README.md
+41-7Lines changed: 41 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -94,7 +94,7 @@ That's it. Use the same config in [GitHub Actions](#use-as-a-github-action-marke
94
94
|**GitHub Action**|[Marketplace action](https://github.com/marketplace) — drop into workflows |
95
95
|**Helm chart**| Run as a CronJob on Kubernetes; stateless by default (no PVC) |
96
96
|**Slack**| Optional branded Block Kit alerts (color-coded, per-destination grouping) on new syncs and failures (compact/detailed) |
97
-
|**Secure**| Docker Hub via env/secret; destinations via Docker credential config |
97
+
|**Secure**| Docker Hub via env/secret; destinations via Docker credential config, ECR via the AWS credential chain|
98
98
|**Git mirroring**| Mirror repositories across GitHub, GitLab, Bitbucket, Azure DevOps, and AWS CodeCommit (`git-sync`); filtered discovery, safe push modes, dry run |
99
99
100
100
---
@@ -129,7 +129,40 @@ Add SyncerD to your workflow:
129
129
130
130
Pin to the latest [release tag](https://github.com/clouddrove/syncerd/releases); a floating `v1` tag will exist once the project reaches a 1.0 release.
131
131
132
-
Add Docker credential steps (e.g. `docker/login-action`, `aws-actions/amazon-ecr-login`) *before* SyncerD so destination registries are authenticated.
132
+
**Destination registry auth in the Action:**
133
+
134
+
For **AWS ECR**, add `aws-actions/configure-aws-credentials` before SyncerD and nothing else: SyncerD requests an ECR token itself from the AWS credential chain, so `aws-actions/amazon-ecr-login` is not needed.
For **GHCR, ACR, GCR** (and for ECR if you prefer an explicit `docker login`), the login step has to write its credentials somewhere the Action can read. SyncerD runs as a Docker container action, and a container action does not get the runner's `~/.docker/config.json`, so a plain `docker/login-action` step is invisible to it and every request fails with `401 Unauthorized`. Point `DOCKER_CONFIG` at the workspace, which *is* mounted into the container at `/github/workspace`:
DOCKER_CONFIG: /github/workspace/.docker # same directory, path inside the container
163
+
with:
164
+
config: syncerd.yaml
165
+
```
133
166
134
167
**Inputs:**
135
168
@@ -213,10 +246,11 @@ Set `config.destinations` and `config.images` in `values.yaml` or via `--set`.
213
246
214
247
**Credentials:**
215
248
- Docker Hub (source): use `existingSecret` (recommended) or `secret.*` in values.
216
-
- Destination registries (ECR/ACR/GCR/GHCR): create a Docker config secret and set `dockerConfigSecret`. SyncerD automatically sets `DOCKER_CONFIG=/var/lib/syncerd/.docker` so the credentials are found even when the pod runs as a non-root user.
249
+
- Destination registries (ACR/GCR/GHCR): create a Docker config secret and set `dockerConfigSecret`. SyncerD automatically sets `DOCKER_CONFIG=/var/lib/syncerd/.docker` so the credentials are found even when the pod runs as a non-root user.
250
+
- Destination ECR registries: give the pod AWS credentials instead (IRSA, EKS Pod Identity, or an instance role with `ecr:GetAuthorizationToken` plus push permissions on the target repositories). SyncerD fetches and refreshes the ECR token itself, so no `dockerConfigSecret` and no refresh CronJob is needed.
217
251
- To pull the SyncerD image itself from a private registry, use `imagePullSecrets` — this is separate from `dockerConfigSecret`.
218
252
219
-
**ECR note:** ECR tokens expire every 12 hours. Refresh `dockerConfigSecret` before expiry:
253
+
**ECR without IAM:** if the pod cannot be given AWS credentials, a Docker config secret still works, but ECR tokens expire every 12 hours and the secret has to be refreshed before expiry:
220
254
221
255
```bash
222
256
aws ecr get-login-password --region <region> | \
@@ -255,7 +289,7 @@ Full example: [syncerd.yaml.example](syncerd.yaml.example).
255
289
| Section | Purpose |
256
290
|---------|---------|
257
291
| `source` | Docker Hub (username/password or token via env or config) |
258
-
| `destinations` | List of registries (ECR, ACR, GCR, GHCR); auth via Docker credential config |
292
+
| `destinations` | List of registries (ECR, ACR, GCR, GHCR); auth via Docker credential config, or the AWS credential chain for ECR |
259
293
| `images` | Images to sync; optional `tags`, `watch_tags` for new tag detection |
260
294
| `schedule` | Cron expression when running without `--once` |
261
295
| `state_path` | Optional state file for "already synced" tracking; leave empty for fully stateless |
@@ -273,8 +307,8 @@ Override with `SYNCERD_` prefix:
273
307
### Authentication
274
308
275
309
- **Docker Hub (source):** Username/password or Personal Access Token (env or config). Credentials are validated at startup.
276
-
- **Destinations (ECR/ACR/GCR/GHCR):** SyncerD uses the default Docker keychain — `docker login`, credential helpers, or GitHub Actions login steps.
277
-
- **ECR:** Tokens expire every 12 hours. Ensure credentials are refreshed before each scheduled sync.
310
+
- **Destinations (ACR/GCR/GHCR):** SyncerD uses the default Docker keychain — `docker login`, credential helpers, or GitHub Actions login steps.
311
+
- **ECR:** AWS credentials are enough. When a destination host is a private ECR registry (`<account>.dkr.ecr.<region>.amazonaws.com`), SyncerD calls `ecr:GetAuthorizationToken` through the standard AWS credential chain (environment variables, shared config, IRSA, instance role) and refreshes the token as it expires, so long-running syncs do not need a credential refresh loop. The account and region come from the registry host. A `docker login` for the same registry still takes precedence when one exists. Public ECR (`public.ecr.aws`) is not covered by this and needs a `docker login`.
Copy file name to clipboardExpand all lines: _helm/syncerd/README.md
+9-1Lines changed: 9 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,7 +75,15 @@ SyncerD uses the Docker credential config for destination registry auth. Create
75
75
76
76
#### AWS ECR
77
77
78
-
ECR tokens expire every **12 hours**. Refresh the secret before each sync window.
78
+
Give the pod AWS credentials (IRSA, EKS Pod Identity, or a node instance role) and skip `dockerConfigSecret` entirely: SyncerD requests an ECR token itself through the standard AWS credential chain and refreshes it as it expires. The role needs `ecr:GetAuthorizationToken` plus push permissions on the destination repositories.
Without AWS credentials, a Docker config secret still works, but ECR tokens expire every **12 hours**, so the secret has to be refreshed before each sync window.
0 commit comments