Amazon Elastic Container Registry
This page describes how to give Anchore Enterprise access to images stored in Amazon Elastic Container Registry (ECR).
Anchore Enterprise can scan ECR images two ways. In centralized analysis — the focus of this page — you register ECR credentials and the deployment pulls and analyzes images itself. In distributed analysis, a CI/CD job pulls the image, generates the SBOM locally, and uploads only the SBOM; see Distributed Analysis from ECR in CI/CD.
Authentication Modes
When you register an ECR registry for centralized analysis, the Username and Password you supply select one of three authentication modes. Which to use depends on where Anchore Enterprise runs and how its AWS access to the registry is configured. The registry --type is always awsecr.
| Mode | Username | Password | Use when |
|---|---|---|---|
| API Keys | AWS access key ID | AWS secret access key | You authenticate with long-lived access/secret keys, ideally from a dedicated, restricted IAM user. |
| Local Credentials | awsauto | awsauto | Anchore Enterprise should adopt the AWS credentials of its execution environment — an EC2 instance profile, ECS task role, or Kubernetes (IRSA/Pod Identity) service account role. |
| ECR Assume Role | _iam_role | Target role ARN | Anchore Enterprise should assume a role different from the one it runs under to reach the registry. |
iamauto option. Local Credentials also requires allow_awsecr_iam_auto: True in config.yaml (Helm: under anchoreConfig), which is disabled by default; ECR Assume Role does not.API Keys
Provide an access key ID and secret access key from an AWS account or IAM user — ideally a dedicated IAM user scoped to only the ECR permissions it needs. Pass the access key ID as the username and the secret access key as the password:
ANCHORECTL_REGISTRY_PASSWORD=<MY_AWS_SECRET_ACCESS_KEY> anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username <MY_AWS_ACCESS_KEY_ID> --type awsecr
The --type awsecr flag tells Anchore Enterprise to treat these as AWS credentials; if omitted, AnchoreCTL infers the type from the registry URL. Anchore Enterprise uses the keys to generate ECR authentication tokens and refreshes them automatically as they expire (typically every 12 hours). Do not store an aws ecr get-login token as the credential itself — it expires after 12 hours and would need constant manual updates.
Local Credentials
In this mode Anchore Enterprise adopts the AWS credentials from its own execution environment — environment variables, ~/.aws/credentials, or (most commonly) an IAM role inherited from the instance, task, or pod it runs in. Set both the username and password to awsauto:
ANCHORECTL_REGISTRY_PASSWORD=awsauto anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username awsauto --type awsecr
How that execution role is assigned depends on the deployment:
- Docker Compose or a self-managed install on EC2 — the deployment inherits the EC2 instance profile. See Grant an EC2 Instance Profile Role below.
- Helm on EKS (or other Kubernetes) — the deployment inherits a role from its service account via IRSA or EKS Pod Identity. Set
serviceAccountNamein your Helm values, as described in Amazon S3 IAM Role Authentication and the EKS deployment guide. If one role serves both S3 and ECR, combine their permissions into it.
Grant an EC2 Instance Profile Role
When Anchore Enterprise runs directly on EC2, give the instance a role that includes the AmazonEC2ContainerRegistryReadOnly policy (or an equivalent policy scoped to your registries). You can configure this manually from the launch-instance wizard, or with an automation tool such as Terraform or CloudFormation:
Step 1: Select Create new IAM role.

Step 2: Under type of trusted entity select EC2.

Ensure that the AmazonEC2ContainerRegistryReadOnly policy is selected.
Step 3: Attach Permissions to the Role.

Step 4: Name the role.
Give a name to the role and add this role to the instance you are launching.
On the running EC2 instance you can manually verify that the instance has inherited the correct role by running the following command:
curl http://169.254.169.254/latest/meta-data/iam/info
{
"Code": "Success",
"LastUpdated": "2018-01-12T18:45:12Z",
"InstanceProfileArn": "arn:aws:iam::123456789012:instance-profile/ECR-ReadOnly",
"InstanceProfileId": "ABCDEFGHIJKLMNOP"
}
Step 5: Enable IAM authentication in Anchore Enterprise by adding the following entry to config.yaml (it is disabled by default):
allow_awsecr_iam_auto: True
Step 6: Add the registry with awsauto for both the username and password, as shown above.
ECR Assume Role
Use this mode to have Anchore Enterprise assume a role different from the one it currently runs under. Anchore Enterprise uses the AWS credentials from its execution environment — the same ambient credentials as Local Credentials — to assume the role you specify. The execution credentials must be granted permission to assume the target role (sts:AssumeRole), and the target role must hold the ECR permissions. This mode does not require the allow_awsecr_iam_auto flag.
Set the username to _iam_role and the password to the ARN of the role to assume:
ANCHORECTL_REGISTRY_PASSWORD=arn:aws:iam::123456789012:role/anchore-ecr-access anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr
To require an external ID when assuming the role, append it to the ARN in the password, separated by a semicolon (<ROLE_ARN>;<EXTERNAL_ID>):
ANCHORECTL_REGISTRY_PASSWORD='arn:aws:iam::123456789012:role/anchore-ecr-access;my-external-id' anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr
Cross-Account Access
Assume-role is the standard way to scan a registry that lives in a different AWS account from the one Anchore Enterprise runs in. Point the registry URL at the ECR-owning account and set the password to the ARN of a role in that account — Anchore requests the authorization token for the registry account it parses from the URL, using the assumed role’s credentials.
The example below uses two accounts and two roles:
| Account | Role | |
|---|---|---|
| Anchore account — where Anchore Enterprise runs | 111111111111 | anchore-enterprise-execution (its execution role) |
| ECR account — where the registry and images live | 123456789012 | anchore-ecr-access (the role Anchore assumes) |
Three IAM pieces are required, across both accounts:
| IAM policy | In account | Attach to | Purpose |
|---|---|---|---|
| Identity policy (below) | 111111111111 (Anchore) | anchore-enterprise-execution | Allows Anchore’s execution role to assume the target role |
| Trust policy (below) | 123456789012 (ECR) | anchore-ecr-access | Allows the target role to be assumed by Anchore’s execution role |
| ECR permissions | 123456789012 (ECR) | anchore-ecr-access | Grants pull/inspect access to the registry — attach the AWS-managed AmazonEC2ContainerRegistryReadOnly policy, or a least-privilege equivalent. Add ecr:BatchImportUpstreamImage for uncached pull-through images (see Scanning Images from an ECR Pull-Through Cache). |
Identity Policy
Attach to anchore-enterprise-execution in the Anchore account (111111111111). It lets Anchore’s execution role call sts:AssumeRole on the target role:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeEcrAccessRole",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::123456789012:role/anchore-ecr-access"
}
]
}
Trust Policy
Set as the trust relationship on anchore-ecr-access in the ECR account (123456789012). It names Anchore’s execution role as a principal allowed to assume the role. For cross-account or third-party trust, AWS recommends requiring an external ID to guard against the confused-deputy problem; the Condition below requires the external ID my-external-id:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/anchore-enterprise-execution"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "my-external-id"
}
}
}
]
}
Register the Registry
With the policies in place, add the registry using _iam_role and the target role’s ARN, pointing the registry URL at the ECR account (123456789012). Because the trust policy requires an external ID, append it to the ARN after a semicolon (and quote the value so the shell does not split on the ;):
ANCHORECTL_REGISTRY_PASSWORD='arn:aws:iam::123456789012:role/anchore-ecr-access;my-external-id' anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr
Condition. If they differ, the sts:AssumeRole call is denied and Anchore Enterprise cannot obtain a registry token.Distributed Analysis from ECR in CI/CD
In a CI/CD pipeline you often want distributed analysis rather than a registered, stored credential: the job pulls the image, AnchoreCTL generates the SBOM locally, and only the SBOM is uploaded. The image bytes never leave the runner, and you do not register the registry with Anchore Enterprise at all.
This pairs naturally with short-lived ECR credentials. A stored credential must be long-lived — the reason the API Keys mode avoids the 12-hour get-login token — but a CI job is ephemeral, so a token minted at the start of the job and used immediately is a perfect fit.
A typical job assumes an IAM role, exchanges it for a short-lived ECR token, and hands that token to AnchoreCTL:
# 1. Assume the role that has ECR pull permissions (plus ecr:BatchImportUpstreamImage
# if you are analyzing uncached pull-through images). Skip this step if the runner
# already carries the role via an instance profile or IRSA.
CREDS=$(aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/anchore-ci-scanner \
--role-session-name anchore-scan)
export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Credentials.SessionToken')
# 2. Exchange the AWS credentials for a short-lived ECR registry token and hand it to
# AnchoreCTL. The username for an ECR token is always AWS.
export ANCHORECTL_REGISTRY_AUTH_AUTHORITY=123456789012.dkr.ecr.us-east-1.amazonaws.com
export ANCHORECTL_REGISTRY_AUTH_USERNAME=AWS
export ANCHORECTL_REGISTRY_AUTH_PASSWORD=$(aws ecr get-login-password --region us-east-1)
# 3. Pull and analyze locally, then upload only the SBOM.
anchorectl image add \
123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0 \
--from registry --wait
The --from registry flag is what makes this distributed: AnchoreCTL pulls the image itself using the ANCHORECTL_REGISTRY_AUTH_* credentials, so no docker login or local Docker daemon is required. Set the ANCHORECTL_URL and ANCHORECTL_USERNAME/ANCHORECTL_PASSWORD (or ANCHORECTL_API_KEY) variables that point AnchoreCTL at your deployment as usual; they are omitted here for brevity. See AnchoreCTL Configuration for all registry-auth settings.
anchorectl image one-time-scan <image> --from registry --fail-on-policy-error in place of image add. See One-Time Scan.Scanning Images from an ECR Pull-Through Cache
Amazon ECR pull-through cache rules let ECR transparently cache images from an upstream registry (such as Docker Hub) under a local namespace. For example, an upstream image pulled through a cache rule with the prefix docker-hub:
docker.io/anchore/enterprise:v6.1.0
appears in ECR as:
123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0
Anchore Enterprise can scan pull-through cached images directly, including images that have not yet been cached — there is no need for a separate step to pre-warm the cache. This does, however, require the scanning principal to hold the permission that triggers the upstream import.
Grant the Import Permission
When Anchore inspects a tag that has never been cached, ECR must import it from the upstream registry on that first request. If the principal cannot perform the import, the inspect fails and ECR returns an error that looks like a missing image rather than a missing permission:
manifest unknown: Requested image not found
The underlying cause is that the principal could read already-cached images but lacked permission to import uncached ones. Granting ecr:BatchImportUpstreamImage allows that first inspect or pull to trigger the upstream import; the image is cached from then on.
The following policy grants import permission, scoped to the pull-through cache prefix:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowECRPullThrough",
"Effect": "Allow",
"Action": [
"ecr:BatchImportUpstreamImage",
"ecr:CreateRepository"
],
"Resource": [
"arn:aws:ecr:us-east-1:123456789012:repository/docker-hub/*"
]
}
]
}
Keeping ecr:BatchImportUpstreamImage scoped to the docker-hub/* prefix (rather than *) follows least-privilege and is the recommended scope.
Analyze Uncached Images by Tag
Granting the import permission is what allows the upstream import, but it does not by itself pull anything. ECR only imports an uncached image when something requests that specific tag. To have Anchore import and analyze an image that has never been cached, add the specific tag for analysis:
anchorectl image add 123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0
That request triggers the upstream import for the tag, ECR caches it, and Anchore analyzes it.
A registry or repository watch (subscription) does not force uncached images to import. A watch discovers images that already exist in the ECR repository — those that have been cached — and it will pick up new images as they become cached going forward. It does not enumerate the upstream registry, so a tag that has never been pulled through the cache will not appear until it is imported at least once by an explicit tag analysis.
anchorectl image add <tag> to bring a never-cached image into ECR the first time. Rely on a repository or registry watch to keep already-cached images and future cached images under continuous analysis.Auto-Creating Repositories for New Images
The prefix scope above (docker-hub/*) covers images whose repository already exists under that prefix. The very first pull of an upstream image whose repository has never been created also requires that repository to be created automatically. If this is not permitted, ECR returns an error such as:
repository ... does not exist (name unknown)
There are two ways to allow the repository to be created:
- Grant
ecr:CreateRepository, as shown in the policy above.CreateRepositoryis a registry-level action, so it is typically scoped toarn:aws:ecr:us-east-1:123456789012:repository/*rather than a sub-prefix. - Configure a repository creation template for the pull-through prefix. This lets ECR auto-create repositories without granting the principal
CreateRepositorydirectly, and also lets you enforce scan-on-push, tags, and encryption on the cached repositories.
manifest unknown: Requested image not found, the cause is almost always a missing ecr:BatchImportUpstreamImage permission rather than the cache itself. A repository ... does not exist (name unknown) error instead points to a net-new repository that needs ecr:CreateRepository or a repository creation template.Troubleshooting Authentication Errors
When Anchore Enterprise (or AnchoreCTL) cannot authenticate to ECR, the errors it surfaces often read like the image is missing rather than like an auth failure — ECR and skopeo report an authorization denial as authentication required or Not Authorized. An image that scans fine in one account can therefore fail in another that lacks the credential. The messages below all trace back to a missing or invalid registry credential, expired keys, or a failed role assumption.
| Where you see it | Error | Root cause |
|---|---|---|
anchorectl registry add … --type awsecr | cannot ping supplied registry with supplied credentials [HTTP 406] | The AWS keys or role supplied at registration failed to authenticate to ECR. |
anchorectl image add (centralized) | cannot fetch image digest/manifest from registry [HTTP 400] | No valid ECR credential for this registry in the account you are using. |
Event system.image_analysis.registry_lookup_failed | Message Referenced image not found in registry; detail contains authentication required and error_code=REGISTRY_PERMISSION_DENIED | Same as above — the “not found” wording is misleading; the deployment could not authenticate to pull the manifest. |
anchorectl image add … --from registry (distributed) | 401 Unauthorized: Not Authorized, or DENIED: Your authorization token has expired. Reauthenticate and try again. | AnchoreCTL’s local ECR credentials (ANCHORECTL_REGISTRY_AUTH_*) are missing or the token has expired. |
To diagnose and resolve:
- Confirm a credential exists in the right account. Registry credentials are per-account — run
anchorectl registry listin the same account you are analyzing under. An image that fails in one account but succeeds in another almost always means the failing account has no ECR credential. - Read the full cause. Fetch the event detail with
anchorectl event get <EVENT_ID> -o json, or check thecatalogservice logs, where the underlyingskopeo inspectfailure (authentication required,REGISTRY_PERMISSION_DENIED) is logged. - By mode: for API Keys, verify the access key and secret and that the IAM principal holds ECR read permissions; for Local Credentials, confirm the execution role is attached and
allow_awsecr_iam_auto: Trueis set; for ECR Assume Role, confirm thests:AssumeRolegrant, the target role’s trust policy, and any external ID. - Distributed jobs: re-run
aws ecr get-login-passwordto mint a fresh token — ECR tokens expire after 12 hours.
authentication required / REGISTRY_PERMISSION_DENIED detail in the failure event distinguishes an auth problem from a real missing image.