Minimize Container Registries Only Those Approved
More Info:
Scan images being deployed to Amazon EKS for vulnerabilities.
Risk Level
Low
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 all registries your EKS workloads pull from
- On any machine with
kubectlaccess:kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' | sort -u - Extract the registry host for each image (text after
//and before the first/, or before/if no//), e.g.123456789012.dkr.ecr.us-east-1.amazonaws.com,docker.io,gcr.io.
- On any machine with
-
List all AWS ECR registries and repositories in your accounts
- On any machine with AWS CLI configured:
aws ecr describe-registryaws ecr describe-repositories --region us-east-1aws ecr describe-repositories --region us-west-2# repeat for each region you operate in
- Compare these to the registries found in step 1 to distinguish ECR vs non‑ECR sources.
- On any machine with AWS CLI configured:
-
Define and document “approved registry” criteria and the allowed list
- With your security/compliance team, decide: which AWS accounts/regions’ ECRs are allowed, and which third‑party registries (if any) are permitted and under what conditions (scanning enabled, private, signed images, etc.).
- Write a short policy stating: “Only the following registries may be used for EKS workloads: …”.
-
Evaluate current registry usage against the approved list
- From the image list in step 1, mark each registry as: approved, candidate (needs review), or disallowed.
- For AWS ECR registries:
aws ecr describe-image-scan-findings \--repository-name <repo-name> \--image-id imageTag=<tag> \--region <region>
- Use this to check whether images from candidate/disallowed registries meet your security criteria (scanning, severity levels, etc.).
-
Implement or tighten controls to enforce only approved registries
- Using AWS IAM / Organizations (console, CLI, or IaC), adjust policies so that:
- Only specific AWS accounts/roles can pull from approved ECR registries.
- Pull/push actions to any non‑approved registries (e.g., internet egress) are restricted via IAM, VPC egress controls, or organizational SCPs.
- Optionally, add admission controls (e.g., OPA/Gatekeeper, Kyverno) that deny Pods whose image registry is not in your approved list (implemented via your chosen IaC/tooling, not
kubectldirectly for the control plane).
- Using AWS IAM / Organizations (console, CLI, or IaC), adjust policies so that:
-
Verify and periodically review
- Re-run the workload image inventory:
kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u
- Confirm that all registries now appear on your approved list and that IAM/egress controls prevent use of unapproved registries. Repeat this review on a defined schedule (e.g., quarterly) or when adding new registries.
- Re-run the workload image inventory:
Using kubectl
kubectl cannot be used to restrict or approve container registries for Amazon EKS, because this control is enforced at the cloud provider / managed control plane and IAM/ECR policy layer. Make the required changes in AWS (ECR, IAM, organization policies, and any admission controls configured via cloud tooling) as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
# Purpose: Report all container image registries used in an EKS cluster so they
# can be reviewed against your approved ECR registry list.
# Run on: any machine with kubectl access and the correct kubeconfig for the EKS cluster.
set -o errexit
set -o nounset
set -o pipefail
# ---------- CONFIGURATION ----------
# Comma-separated list of approved registry prefixes (edit for your org).
# Examples:
# 123456789012.dkr.ecr.us-east-1.amazonaws.com
# public.ecr.aws
# 602401143452.dkr.ecr.${AWS_REGION}.amazonaws.com
APPROVED_REGISTRIES_CSV="123456789012.dkr.ecr.us-east-1.amazonaws.com,public.ecr.aws"
# -----------------------------------
IFS=',' read -r -a APPROVED_REGISTRIES <<< "$APPROVED_REGISTRIES_CSV"
timestamp() {
date -u +"%Y-%m-%dT%H:%M:%SZ"
}
echo "[$(timestamp)] Collecting images from all namespaces..."
# Collect all unique images (includes pods, daemonsets, statefulsets, jobs, cronjobs, replicasets, deployments, replicas, replicationcontrollers)
ALL_IMAGES_JSON=$(kubectl get pods,daemonsets,statefulsets,jobs,cronjobs,replicasets,deployments,replicationcontrollers --all-namespaces -o json 2>/dev/null || echo '{"items":[]}')
# Extract all container images (containers + initContainers) with owner references and namespace.
# This produces lines: "<namespace> <workloadKind>/<workloadName> <podName> <containerType> <containerName> <image>"
IMAGES_REPORT=$(
echo "$ALL_IMAGES_JSON" \
| jq -r '
.items[]
| . as $pod
| ($pod.metadata.ownerReferences[0].kind // "Pod") as $ownerKind
| ($pod.metadata.ownerReferences[0].name // $pod.metadata.name) as $ownerName
| ($pod.metadata.namespace // "default") as $ns
| [ "containers", "initContainers" ][]
| . as $ctype
| ($pod.spec[$ctype] // [])[]
| [$ns,
($ownerKind + "/" + $ownerName),
$pod.metadata.name,
$ctype,
.name,
.image]
| @tsv
' 2>/dev/null || true
)
if [[ -z "$IMAGES_REPORT" ]]; then
echo "[$(timestamp)] No images found (cluster may be empty)."
exit 0
fi
echo "[$(timestamp)] Unique registry usage:"
# Extract registry (first path component before first '/') and count
echo "$IMAGES_REPORT" \
| awk '{print $6}' \
| sed 's#^#//##' \
| awk -F/ '{print $2}' \
| sort -u \
| while read -r registry; do
[[ -z "$registry" ]] && continue
echo " - $registry"
done
echo
echo "[$(timestamp)] Checking images against approved registries:"
echo "Approved registry prefixes:"
for r in "${APPROVED_REGISTRIES[@]}"; do
echo " - $r"
done
echo
# Function: return 0 if image is from an approved registry, 1 otherwise
is_approved_image() {
local image="$1"
local registry
# Extract registry: if image has '/', take first part; otherwise treat as "docker.io" style (no explicit registry)
if [[ "$image" == *"/"* ]]; then
registry="${image%%/*}"
else
registry="docker.io"
fi
# Check against approved registry prefixes (simple prefix match)
for approved in "${APPROVED_REGISTRIES[@]}"; do
if [[ "$registry" == "$approved"* ]]; then
return 0
fi
done
return 1
}
# Build a report of non-approved images
NON_APPROVED_FILE="$(mktemp)"
APPROVED_FILE="$(mktemp)"
while IFS=$'\t' read -r ns owner pod ctype cname image; do
if is_approved_image "$image"; then
printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$ns" "$owner" "$pod" "$ctype" "$cname" "$image" >> "$APPROVED_FILE"
else
printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$ns" "$owner" "$pod" "$ctype" "$cname" "$image" >> "$NON_APPROVED_FILE"
fi
done <<< "$IMAGES_REPORT"
echo "[$(timestamp)] Summary:"
echo " Total image references: $(echo "$IMAGES_REPORT" | wc -l | xargs)"
echo " Approved image references: $(wc -l < "$APPROVED_FILE" | xargs)"
echo " Non-approved image references: $(wc -l < "$NON_APPROVED_FILE" | xargs)"
echo
echo "[$(timestamp)] Detailed non-approved image usage:"
if [[ -s "$NON_APPROVED_FILE" ]]; then
printf "namespace\tworkload\tpod\tcontainerType\tcontainerName\timage\n"
sort -u "$NON_APPROVED_FILE"
else
echo " None detected (all images are from approved registries as per current list)."
fi
echo
echo "[$(timestamp)] NOTE:"
echo " - Lines listed under 'Detailed non-approved image usage' indicate potential policy violations."
echo " - For each such line, review whether the registry is intentionally allowed."
echo " - Update APPPROVED_REGISTRIES_CSV in this script as your approved ECR/public registries evolve."
What output indicates a problem:
- Any non-zero value for
Non-approved image referencesin the “Summary” section. - Any rows printed under “Detailed non-approved image usage” (each row shows a namespace, workload, pod, container type, container name, and image whose registry does not match your approved registry prefixes).