Minimize User Access To Amazon Ecr
More Info:
Scan images being deployed to Amazon EKS for vulnerabilities.
Risk Level
High
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS EKS
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory who can access ECR (users, roles, groups, policies)
- On any machine with AWS CLI configured for the account:
# List all IAM usersaws iam list-users# List all IAM rolesaws iam list-roles# List all IAM groupsaws iam list-groups# List all IAM policies (paginate or filter as needed)aws iam list-policies --scope Local- In the AWS Console, go to IAM → Access analyzer → Policy analyzer and search for policies containing
ecr:actions.
-
Identify all IAM principals with ECR permissions and evaluate scope
- For each relevant IAM user, role, and group, on any machine with AWS CLI:
aws iam list-attached-user-policies --user-name <USER_NAME>aws iam list-user-policies --user-name <USER_NAME>aws iam list-attached-role-policies --role-name <ROLE_NAME>aws iam list-role-policies --role-name <ROLE_NAME>aws iam list-attached-group-policies --group-name <GROUP_NAME>aws iam list-group-policies --group-name <GROUP_NAME>- For each policy found, retrieve and inspect it for
ecr:*or broad permissions:
aws iam get-policy --policy-arn <POLICY_ARN>aws iam get-policy-version \--policy-arn <POLICY_ARN> \--version-id <VERSION_ID_FROM_GET_POLICY>- Flag policies that:
- Use
Action: "ecr:*"instead of specific actions. - Use
Resource: "*"instead of specific repositories. - Are attached directly to users instead of roles or groups.
- Use
-
Map ECR access to EKS usage and expected personas
- On any machine with kubectl access to the cluster, list namespaces and service accounts that pull images from ECR (assuming standard IAM roles for service accounts (IRSA) or node roles):
kubectl get serviceaccounts -A -o widekubectl get nodes -o wide- In AWS Console, go to EKS → Clusters →
<your-cluster>→ Configuration → Compute and note:- Node instance profile IAM role(s).
- IAM roles used via IRSA (under Configuration → Access and by inspecting service account annotations).
- Verify that only:
- Node IAM roles and IRSA roles used by workloads require
ecr:GetAuthorizationToken,ecr:BatchCheckLayerAvailability,ecr:GetDownloadUrlForLayer,ecr:BatchGetImage. - Human users or CI roles with push rights are limited to specific repos and least-privilege actions.
- Node IAM roles and IRSA roles used by workloads require
-
Review ECR repository policies and public access
- On any machine with AWS CLI:
# List repositoriesaws ecr describe-repositories# For each repositoryaws ecr get-repository-policy --repository-name <REPO_NAME>- In AWS Console, go to ECR → Repositories →
<REPO>→ Permissions and:- Ensure no public access unless explicitly required.
- Ensure repository policies do not grant wildcard principals (
"*"or wide account ranges) orecr:*on that repo. - Confirm that principals listed match the expected roles/users from step 3.
-
Tighten IAM and repository permissions (IaC/CLI/Console)
- For any over-privileged policies you identified:
- Replace
ecr:*with only the required actions. - Replace
Resource: "*"with specific ECR repository ARNs (fromaws ecr describe-repositories). - Prefer attaching policies to IAM roles used by EKS nodes/IRSA/CI, not directly to users.
- Replace
- Example of a read-only ECR policy document (adapt and apply via Console or IaC):
{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": ["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage"],"Resource": "*"}]}- Ensure only CI/CD or designated operators have push (
ecr:PutImage,ecr:InitiateLayerUpload, etc.) and that it is scoped to required repositories.
- For any over-privileged policies you identified:
-
Verify minimized access and ongoing monitoring
- Re-run IAM and ECR policy inspection to confirm no broad
ecr:*orResource: "*"remain except where justified:
# Find customer-managed policies mentioning ecraws iam list-policies --scope Local --query 'Policies[?contains(PolicyName, `ecr`) == `true`]'- Optionally, enable or review AWS CloudTrail and Access Analyzer to:
- Confirm ECR is not accessed by unexpected principals.
- Detect unintended public or cross-account access.
- Re-run IAM and ECR policy inspection to confirm no broad
Using kubectl
kubectl cannot be used to change Amazon ECR IAM permissions or access policies because this finding is entirely in the AWS cloud/provider configuration surface. Make the necessary changes using AWS IAM/ECR configuration via the AWS Console, CLI, or IaC, and refer to the Manual Steps section for detailed guidance.
Automation
#!/usr/bin/env bash
#
# Purpose: Report ECR image usage in an EKS cluster and highlight places where
# direct IAM user credentials are likely in use instead of roles.
#
# Run on: Any machine with kubectl and AWS CLI configured and access to the cluster.
#
# Requirements:
# - kubectl
# - aws CLI
# - jq
#
# Notes:
# - This does NOT change anything; it only reports for review.
# - Focus is on:
# * Which ECR registries are used
# * What Kubernetes identities pull those images
# * Where IAM users are referenced in ECR policies
set -euo pipefail
echo "=== 1. List all container images in the cluster and identify ECR usage ==="
echo "[*] Gathering all images from pods in all namespaces..."
IMAGES_JSON="$(kubectl get pods -A -o json)"
ALL_IMAGES="$(echo "${IMAGES_JSON}" | jq -r '
.items[]
| .spec.containers[]?.image,
.spec.initContainers[]?.image
' | sort -u)"
echo
echo "All unique images in cluster:"
echo "-----------------------------"
echo "${ALL_IMAGES}"
echo
echo "ECR images (aws_account_id.dkr.ecr.region.amazonaws.com/...):"
echo "-------------------------------------------------------------"
ECR_IMAGES="$(echo "${ALL_IMAGES}" | grep -E '^[0-9]{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com/' || true)"
if [ -z "${ECR_IMAGES}" ]; then
echo "No ECR images detected."
else
echo "${ECR_IMAGES}"
fi
echo
echo "=== 2. Map ECR images to namespaces and service accounts ==="
if [ -z "${ECR_IMAGES}" ]; then
echo "Skipping mapping step because no ECR images were found."
else
echo "[*] Pods using ECR images and the identities they run as:"
echo "---------------------------------------------------------"
echo "${IMAGES_JSON}" | jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
sa: (.spec.serviceAccountName // "default"),
node: (.spec.nodeName // "N/A"),
containers: (
(.spec.containers // []) + (.spec.initContainers // [])
)
}
| . as $pod
| $pod.containers[]
| select(.image
| test("^[0-9]{12}\\.dkr\\.ecr\\.[a-z0-9-]+\\.amazonaws\\.com/"))
| "\($pod.ns)\t\($pod.pod)\t\($pod.sa)\t\($pod.node)\t\(.image)"
' | sort -u \
| awk 'BEGIN { print "NAMESPACE\tPOD\tSERVICEACCOUNT\tNODE\tIMAGE" }1'
fi
echo
echo "=== 3. List imagePullSecrets used by pods (possible access path to ECR) ==="
echo "[*] Pods with imagePullSecrets and the secrets they reference:"
echo "-------------------------------------------------------------"
kubectl get pods -A -o json \
| jq -r '
.items[]
| select(.spec.imagePullSecrets != null)
| . as $pod
| .spec.imagePullSecrets[]
| "\($pod.metadata.namespace)\t\($pod.metadata.name)\t\(.name)"
' \
| awk 'BEGIN { print "NAMESPACE\tPOD\tIMAGEPULLSECRET" }1' \
| sort -u || echo "No pods with imagePullSecrets found."
echo
echo "=== 4. Inspect Docker config secrets likely used for ECR ==="
echo "[*] Checking all secrets for dockerconfigjson/dockerconfig ECR credentials..."
echo "-------------------------------------------------------------------------------"
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
for sec in $(kubectl -n "${ns}" get secrets -o jsonpath='{.items[?(@.type=="kubernetes.io/dockerconfigjson")].metadata.name}'); do
echo
echo "Namespace: ${ns} Secret: ${sec}"
echo "---------------------------------------"
DOCKERCFG=$(kubectl -n "${ns}" get secret "${sec}" -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d 2>/dev/null || true)
if [ -n "${DOCKERCFG}" ]; then
echo "${DOCKERCFG}" | jq .
echo
echo "ECR servers referenced in this secret:"
echo "${DOCKERCFG}" | jq -r '.auths | keys[]' | grep -E 'dkr\.ecr\.' || echo " (none)"
else
echo "Unable to decode .dockerconfigjson"
fi
done
for sec in $(kubectl -n "${ns}" get secrets -o jsonpath='{.items[?(@.type=="kubernetes.io/dockercfg")].metadata.name}'); do
echo
echo "Namespace: ${ns} Secret: ${sec}"
echo "---------------------------------------"
DOCKERCFG=$(kubectl -n "${ns}" get secret "${sec}" -o jsonpath='{.data.\.dockercfg}' | base64 -d 2>/dev/null || true)
if [ -n "${DOCKERCFG}" ]; then
echo "${DOCKERCFG}" | jq .
echo
echo "ECR servers referenced in this secret:"
echo "${DOCKERCFG}" | jq -r 'keys[]' | grep -E 'dkr\.ecr\.' || echo " (none)"
else
echo "Unable to decode .dockercfg"
fi
done
done
echo
echo "=== 5. (Optional) Review ECR repository policies for IAM users ==="
echo
echo "If you know which AWS accounts/regions host your ECR images, you can inspect"
echo "their repository policies to find IAM users with access. This requires AWS CLI"
echo "access to those accounts. Example commands you can run manually:"
cat <<'EOF'
# Example: list all repositories in a region
aws ecr describe-repositories --region us-east-1 --query 'repositories[].repositoryName'
# Example: show repository policy and highlight IAM users
aws ecr get-repository-policy \
--region us-east-1 \
--repository-name <REPO_NAME> \
--query 'policyText' \
--output text | jq -r '
.Statement[]
| {
Sid,
Effect,
Principal: .Principal,
Action
}
'
# Problem indicators in repository policies:
# - "AWS": "arn:aws:iam::<account-id>:user/<user-name>"
# - Wide principals like:
# "AWS": "*"
# - Use of IAM groups that include IAM users with broad permissions.
EOF
echo
echo "=== INTERPRETING THE OUTPUT: WHAT INDICATES A PROBLEM? ==="
cat <<'EOF'
Look for the following patterns to flag for manual review:
1) From sections 1–2:
- Business‑critical workloads using ECR images where the associated
serviceAccount is not mapped to an IRSA role (no AWS IAM role via
eks.amazonaws.com/role-arn annotation on its ServiceAccount).
- Shared or default service accounts pulling many different ECR images,
which often implies broad, non‑minimal access.
To check a specific ServiceAccount for IRSA:
kubectl -n <namespace> get sa <serviceaccount> -o yaml | \
yq '.metadata.annotations."eks.amazonaws.com/role-arn"'
Absence of this annotation suggests the pod may rely on node‑level roles
or legacy access patterns instead of fine‑grained roles.
2) From sections 3–4:
- imagePullSecrets that contain auth entries for ECR registries, especially
if:
* The username is 'AWS' with long‑lived password tokens created by
aws ecr get-login-password and baked into secrets.
* The secret is used by many pods/namespaces (shared credentials).
- Any credentials that are not clearly tied to a dedicated, scoped IAM role.
3) From section 5 (AWS CLI, run separately):
- ECR repository policies that reference:
"arn:aws:iam::<account-id>:user/<user-name>"
or other IAM users directly.
- Very broad principals ("AWS": "*") or policies that allow access from
shared roles/groups used by humans rather than workloads.
These findings mean IAM access to ECR is not minimized and must be
manually redesigned toward:
- Role‑based access (IRSA) per workload.
- Least‑privilege repository policies.
- Elimination of direct IAM user access and long‑lived static credentials.
EOF