For AI agents: A markdown version of this page is available at https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/terraform-aws-ecr-repository-is-publicly-accessible.md. A documentation index is available at /llms.txt.
This product is not supported for your selected Datadog site. ().

Metadata

Id: terraform-aws-ecr-repository-is-publicly-accessible

Provider: AWS

Platform: Terraform

Severity: Critical

Category: Access Control

Learn More

Description

Amazon ECR repository policies that use a wildcard * in the Principal field grant public access to your container images, potentially exposing sensitive data or proprietary code. When left unaddressed, this vulnerability allows unauthorized users to pull, and in some cases push, container images, compromising the integrity and confidentiality of your container deployments.

To remediate this issue, specify explicit IAM principals in your repository policies, such as "Principal": { "AWS": "arn:aws:iam::account_number:root" } instead of using "Principal": "*".

Exception: statements that combine a wildcard principal with a Condition that genuinely scopes the caller — such as aws:PrincipalOrgID, aws:PrincipalAccount, or aws:SourceArn — are not flagged, because the condition effectively limits who can invoke the action. Conditions that restrict how (e.g. aws:SecureTransport) or check key presence (e.g. Null) rather than binding the caller’s identity are not considered scoping and the statement is still flagged.

Compliant Code Examples

resource "aws_ecr_repository" "negative1" {
  name = "bar"
}

resource "aws_ecr_repository_policy" "negative2" {
  repository = aws_ecr_repository.foo.name

  policy = <<EOF
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "new policy",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::##account_number##:root"
            },
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:DescribeRepositories",
                "ecr:GetRepositoryPolicy",
                "ecr:ListImages",
                "ecr:DeleteRepository",
                "ecr:BatchDeleteImage",
                "ecr:SetRepositoryPolicy",
                "ecr:DeleteRepositoryPolicy"
            ]
        }
    ]
}
EOF
}
resource "aws_ecr_repository" "negative_org_scoped" {
  name = "my-org-pull-repo"
}

resource "aws_ecr_repository_policy" "negative_org_scoped" {
  repository = aws_ecr_repository.negative_org_scoped.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "AllowOrgPull"
        Effect    = "Allow"
        Principal = "*"
        Action = [
          "ecr:BatchCheckLayerAvailability",
          "ecr:BatchGetImage",
          "ecr:GetDownloadUrlForLayer",
        ]
        Condition = {
          StringEquals = {
            "aws:PrincipalOrgID" = "o-abc12defgh"
          }
        }
      }
    ]
  })
}

Non-Compliant Code Examples

resource "aws_ecr_repository" "positive1" {
  name = "bar"
}

resource "aws_ecr_repository_policy" "positive2" {
  repository = aws_ecr_repository.foo.name

  policy = <<EOF
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "new policy",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:DescribeRepositories",
                "ecr:GetRepositoryPolicy",
                "ecr:ListImages",
                "ecr:DeleteRepository",
                "ecr:BatchDeleteImage",
                "ecr:SetRepositoryPolicy",
                "ecr:DeleteRepositoryPolicy"
            ]
        }
    ]
}
EOF
}
resource "aws_ecr_repository" "multi" {
  name = "multi-statement-repo"
}

resource "aws_ecr_repository_policy" "multi" {
  repository = aws_ecr_repository.multi.name

  policy = <<EOF
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "AllowSpecific",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    },
    {
      "Sid": "AllowAnyone",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    }
  ]
}
EOF
}
resource "aws_ecr_repository" "aws_wildcard" {
  name = "aws-wildcard-repo"
}

resource "aws_ecr_repository_policy" "aws_wildcard" {
  repository = aws_ecr_repository.aws_wildcard.name

  policy = <<EOF
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "AllowAnyAccount",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    }
  ]
}
EOF
}