ECR Repository Not Publicly Accessible
Overview
This check examines Amazon Elastic Container Registry (ECR) repositories and flags any that allow public access through their repository policies. A repository is considered publicly accessible when its policy contains a statement with Principal: "*", which permits anyone (including unauthenticated users) to access the repository.
Risk
A publicly accessible ECR repository creates serious security vulnerabilities:
- Secrets exposure: Container images often contain embedded credentials, API keys, database connection strings, or configuration files that become accessible to anyone
- Intellectual property leakage: Proprietary application code and business logic packaged in images can be pulled and analyzed by competitors or attackers
- Supply chain attacks: If write permissions are granted publicly, attackers could push malicious images to your repository, potentially compromising your deployment pipeline
- Infrastructure fingerprinting: Attackers can analyze your images to understand your software stack, dependencies, and potential vulnerabilities
- Unexpected costs: Unrestricted pulls can lead to significant data transfer charges
- Compliance violations: Public repositories may violate data protection regulations (GDPR, HIPAA, PCI-DSS)
This check is rated Critical severity because public ECR repositories can directly expose your application code and secrets to the internet.
Remediation Steps
Prerequisites
- AWS account access with permissions to modify ECR repository policies
- The repository name that needs to be made private
Required IAM permissions
You will need the following permissions:
ecr:GetRepositoryPolicy- View current repository policyecr:SetRepositoryPolicy- Modify the repository policyecr:DeleteRepositoryPolicy- Remove the repository policy entirelyecr:DescribeRepositories- List and view repository details
AWS Console Method
Step 1: Find your publicly accessible repositories
- Sign in to the AWS Management Console
- Navigate to Amazon ECR (search for "ECR" in the search bar)
- Make sure you are in the correct region (e.g., us-east-1)
- Click Repositories in the left sidebar
- Select each repository and check its Permissions tab for policies containing
"Principal": "*"
Step 2: Remove public access from the repository
Option A: Delete the entire repository policy (simplest approach if no policy is needed)
- Select the repository by clicking its name
- Click the Permissions tab
- Click Delete policy (or Edit policy JSON > select all > delete)
- Confirm the deletion
Option B: Edit the policy to remove public access (if you need to keep some permissions)
- Select the repository by clicking its name
- Click the Permissions tab
- Click Edit policy JSON
- Remove any statements that contain
"Principal": "*" - Replace with specific AWS account IDs or IAM role ARNs that need access
- Click Save
Step 3: (Optional) Grant access to specific accounts
If you need to share this repository with specific AWS accounts or roles:
- On the Permissions tab, click Edit policy JSON
- Add a policy statement with specific principals:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountPull",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
}
]
}
- Replace
123456789012with the actual AWS account ID - Click Save
AWS CLI (optional)
View the current repository policy:
aws ecr get-repository-policy \
--repository-name <your-repository-name> \
--region us-east-1
Replace <your-repository-name> with the actual repository name.
Delete the repository policy entirely:
aws ecr delete-repository-policy \
--repository-name <your-repository-name> \
--region us-east-1
This removes all permissions, making the repository accessible only to the owning account.
Set a new policy with specific account access:
First, create a policy file named ecr-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountPull",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
]
}
]
}
Then apply the policy:
aws ecr set-repository-policy \
--repository-name <your-repository-name> \
--policy-text file://ecr-policy.json \
--region us-east-1
List all repositories and check policies in bulk:
# List all repositories
aws ecr describe-repositories \
--query 'repositories[*].repositoryName' \
--output text \
--region us-east-1
# Check each repository's policy (will error if no policy exists)
for repo in $(aws ecr describe-repositories --query 'repositories[*].repositoryName' --output text --region us-east-1); do
echo "Repository: $repo"
aws ecr get-repository-policy --repository-name "$repo" --region us-east-1 2>/dev/null || echo " No policy set"
echo ""
done
CloudFormation (optional)
When creating ECR repositories with CloudFormation, ensure repository policies do not include Principal: "*".
Create a private ECR repository (no policy needed for private access):
AWSTemplateFormatVersion: '2010-09-09'
Description: Private ECR repository without public access
Parameters:
RepositoryName:
Type: String
Description: Name of the ECR repository
Default: my-private-repo
Resources:
PrivateECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref RepositoryName
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration:
EncryptionType: AES256
ImageTagMutability: IMMUTABLE
Tags:
- Key: Environment
Value: Production
- Key: Public
Value: 'false'
Outputs:
RepositoryArn:
Description: ARN of the ECR repository
Value: !GetAtt PrivateECRRepository.Arn
RepositoryUri:
Description: URI of the ECR repository
Value: !GetAtt PrivateECRRepository.RepositoryUri
Create an ECR repository with cross-account access (not public):
AWSTemplateFormatVersion: '2010-09-09'
Description: ECR repository with specific cross-account access
Parameters:
RepositoryName:
Type: String
Description: Name of the ECR repository
Default: shared-repo
TrustedAccountId:
Type: String
Description: AWS Account ID to grant access to
AllowedPattern: '[0-9]{12}'
ConstraintDescription: Must be a valid 12-digit AWS account ID
Resources:
SharedECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref RepositoryName
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration:
EncryptionType: AES256
ImageTagMutability: IMMUTABLE
RepositoryPolicyText:
Version: '2012-10-17'
Statement:
- Sid: AllowCrossAccountPull
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${TrustedAccountId}:root'
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Tags:
- Key: Environment
Value: Production
- Key: SharedWith
Value: !Ref TrustedAccountId
Outputs:
RepositoryArn:
Description: ARN of the ECR repository
Value: !GetAtt SharedECRRepository.Arn
RepositoryUri:
Description: URI of the ECR repository
Value: !GetAtt SharedECRRepository.RepositoryUri
Important: Never use "Principal": "*" in RepositoryPolicyText. Always specify explicit AWS account IDs or IAM role ARNs.
Terraform (optional)
Create a private ECR repository (default - no policy needed):
provider "aws" {
region = "us-east-1"
}
resource "aws_ecr_repository" "private_repo" {
name = "my-private-repo"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "AES256"
}
tags = {
Environment = "Production"
Public = "false"
}
}
# No aws_ecr_repository_policy resource = repository is private by default
Create an ECR repository with specific cross-account access:
provider "aws" {
region = "us-east-1"
}
variable "trusted_account_id" {
description = "AWS Account ID to grant access to"
type = string
default = "123456789012"
}
resource "aws_ecr_repository" "shared_repo" {
name = "shared-repo"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "AES256"
}
tags = {
Environment = "Production"
SharedWith = var.trusted_account_id
}
}
# Grant access to specific accounts only - NOT public
resource "aws_ecr_repository_policy" "cross_account_policy" {
repository = aws_ecr_repository.shared_repo.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCrossAccountPull"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${var.trusted_account_id}:root"
}
Action = [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
]
}
]
})
}
Fix an existing public repository by removing the policy:
# Import and remove the policy from an existing repository
# First, import: terraform import aws_ecr_repository.existing_repo my-repo-name
resource "aws_ecr_repository" "existing_repo" {
name = "my-repo-name"
# ... other configuration
}
# Do NOT create an aws_ecr_repository_policy resource
# If one exists from before, use: terraform state rm aws_ecr_repository_policy.old_policy
Important: Never use "Principal": "*" in ECR repository policies. Always specify explicit AWS account IDs, IAM role ARNs, or IAM user ARNs.
Verification
After completing the remediation:
- Go to Amazon ECR > Repositories in the AWS Console
- Select your repository and click the Permissions tab
- Verify that the policy either does not exist or does not contain
"Principal": "*" - Re-run the Prowler check to confirm the issue is resolved
CLI verification commands
Verify the repository policy is removed or updated:
aws ecr get-repository-policy \
--repository-name <your-repository-name> \
--region us-east-1
If the policy was deleted, you will see an error: RepositoryPolicyNotFoundException. This means the repository is private.
If a policy exists, verify the output does not contain "Principal": "*".
Check all repositories for public access:
for repo in $(aws ecr describe-repositories --query 'repositories[*].repositoryName' --output text --region us-east-1); do
policy=$(aws ecr get-repository-policy --repository-name "$repo" --query 'policyText' --output text --region us-east-1 2>/dev/null)
if echo "$policy" | grep -q '"Principal"[[:space:]]*:[[:space:]]*"\*"'; then
echo "WARNING: $repo has public access!"
fi
done
Additional Resources
- Amazon ECR Repository Policies
- Amazon ECR Repository Policy Examples
- Cross-Account Access to ECR
- ECR Security Best Practices
- AWS CLI: ecr delete-repository-policy
- AWS CLI: ecr set-repository-policy
Notes
- Private vs. Public ECR: This check applies to private ECR repositories with overly permissive policies. AWS also offers Amazon ECR Public for intentionally public images. If you need public container distribution, use ECR Public with appropriate content governance.
- VPC endpoints: For enhanced security, configure VPC endpoints for ECR to keep all traffic within your VPC and avoid internet exposure.
- Image scanning: Enable image scanning on push to detect vulnerabilities in your container images before deployment.
- Immutable tags: Use immutable image tags to prevent image tampering and ensure deployment consistency.
- Lifecycle policies: Implement ECR lifecycle policies to automatically clean up old images and reduce storage costs and attack surface.
- Cross-account sharing: When sharing images across accounts, always specify explicit account IDs or role ARNs rather than using wildcards.
- Audit regularly: Periodically review ECR repository policies to ensure no accidental public exposure has occurred.