ECR Cross-Account Pull Policies Without Source Conditions
An ECR repository policy grants pull access to an entire trusted AWS account rather than one specific role, so any principal that account later trusts inherits the same rights, and standard IAM boundaries on the registry side never see the grant to intervene.
At a glance
- Unsafe setting
- ECR repository policy grants pull permissions to an entire trusted account root ARN without aws:SourceArn or aws:PrincipalOrgID conditions.
- Failure trigger
- Any IAM principal within the trusted account, including later-added cross-account or third-party roles, calls BatchGetImage against the shared repository.
- Blast radius
- Every role the trusted account later assumes inherits pull access to the full repository, exposing baked-in secrets in image layers to unintended principals.
- Recommended control
- Scope the repository policy Principal to a specific role ARN and enforce aws:SourceArn and aws:PrincipalOrgID conditions on the grant.
Fix commands and configuration
aws ecr set-repository-policy --repository-name prod-api --policy-text file://policy.jsonpolicy.json"Condition": {"StringEquals": {"aws:PrincipalOrgID": "o-xxxxxxxx"}, "ArnLike": {"aws:SourceArn": "arn:aws:sts::<account>:assumed-role/ci-pull-role/*"}}The Trap
A cross-account ECR repository policy that grants ecr:GetDownloadUrlForLayer, ecr:BatchGetImage, and ecr:BatchCheckLayerAvailability to an external AWS account root ARN with no aws:SourceArn or aws:PrincipalOrgID condition attached.
The Default State
AWS’s own cross-account sharing examples, and most aws_ecr_repository_policy Terraform modules copied from them, set the Principal to {"AWS": "arn:aws:iam::<account-id>:root"} and stop there. The console’s Permissions tab reinforces this by defaulting to account-level scoping rather than role-level, so teams sharing a base image with a partner account or a separate CI account tick the box for “grant access” and never add a condition block restricting which role inside that account is doing the pulling.
The Blast Radius
Every IAM principal inside the trusted account inherits pull rights to the entire repository, not just the pipeline role you intended to authorise. That includes roles the account later assumes for third-party SaaS integrations, cross-account CI runners, or any Lambda execution role a developer spins up for testing. Because repository policies sit outside CloudTrail’s default event selectors unless you enable ECR data events explicitly, GuardDuty rarely flags anomalous cross-account pull volume until well after exfiltration. Base images frequently carry embedded secrets baked in during build stages — Dockerfile ARG values, cached .npmrc tokens, leftover AWS credentials from a multi-stage build — and ecr:BatchGetImage returns the full manifest, meaning any principal in the trusted account can retag and re-push those layers into its own registry, propagating the secret downstream with no further authorisation check.
The Lead Mechanic Fix
Scope the resource policy to specific calling identities and enforce organisational and source conditions:
aws ecr set-repository-policy --repository-name prod-api --policy-text file://policy.json
Where policy.json restricts the Principal to a named role ARN and adds:
"Condition": {"StringEquals": {"aws:PrincipalOrgID": "o-xxxxxxxx"}, "ArnLike": {"aws:SourceArn": "arn:aws:sts::<account>:assumed-role/ci-pull-role/*"}}
Enable CloudTrail data events for ecr.amazonaws.com and alert on any BatchGetImage call where the calling userIdentity.arn falls outside the known set of pipeline role ARNs.