Minimize Container Registries To Only Those Approved
More Info:
Restrict image pulls to an approved set of container registries so that only vetted, trusted registries can supply container images.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory all registries in use by EKS workloads
- On any machine with kubectl access:
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 image registry domain (before the first
/) from each image and build a unique list of registries currently used by the cluster.
- On any machine with kubectl access:
-
Define and document your approved registry list and criteria
- Outside the cluster (in your security/design docs or IaC repo), write down:
- The approval criteria (security scanning, signing, compliance, network location, etc.).
- The explicit list of approved registries (for example:
*.dkr.ecr.<region>.amazonaws.com,public.ecr.aws/<your-org>, internal mirror domains).
- Store this in version control and treat it as policy.
- Outside the cluster (in your security/design docs or IaC repo), write down:
-
Compare actual registries vs. approved list and decide disposition
- For each registry discovered in step 1:
- If it is not on the approved list, decide whether to:
- Add it to the approved list after review against the criteria, or
- Plan to migrate images away from it and prohibit future use.
- If it is not on the approved list, decide whether to:
- Record decisions per registry (approved / to-be-phased-out / banned) in your documentation.
- For each registry discovered in step 1:
-
Implement enforcement via AWS IAM and ECR configuration
- On any admin machine with AWS CLI configured, restrict ECR access to approved accounts/regions (example for one approved account/region; adjust ARNs/regions as per your policy):
aws ecr put-registry-policy --policy-text '{"Version": "2012-10-17","Statement": [{"Sid": "AllowApprovedPull","Effect": "Allow","Principal": {"AWS": ["arn:aws:iam::<APPROVED_ACCOUNT_ID>:role/<EKS_NODE_ROLE_NAME>"]},"Action": ["ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","ecr:GetAuthorizationToken"]}]}'
- For external registries (Docker Hub, third‑party, etc.), configure AWS VPC endpoints, private connectivity, or organization policies (e.g., AWS Organizations service control policies) to only allow network egress and credentials for approved registries; remove or tighten any IAM policies that grant
ecr:*or registry logins not aligned with your approval list.
- On any admin machine with AWS CLI configured, restrict ECR access to approved accounts/regions (example for one approved account/region; adjust ARNs/regions as per your policy):
-
Update cluster/IaC to reference only approved registries
- Search your manifests/IaC (helm charts, kustomize, Terraform, CD pipelines) locally:
grep -RIn "image:" . | grep -v "<your-approved-registry-pattern>"
- For each non‑approved image reference, update the image to one hosted in an approved ECR registry (or other approved registry), then redeploy the workloads through your normal CI/CD process.
- Search your manifests/IaC (helm charts, kustomize, Terraform, CD pipelines) locally:
-
Verify only approved registries are currently used
- After changes deploy, re-run on any machine with kubectl access:
kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' \| sed 's#/.*##' \| sort -u
- Confirm that the resulting list of registries matches your approved registry list and that IAM/egress controls prevent pulling from any other registries.
- After changes deploy, re-run on any machine with kubectl access:
Using kubectl
kubectl cannot be used to restrict image pulls to an approved set of ECR registries because this control is implemented at the cloud provider / managed control plane and IAM level, not via Kubernetes API objects. Make these changes in your AWS console/CLI/IaC as described in the Manual Steps section, then use kubectl only to verify workload image sources if needed.
Automation
#!/usr/bin/env bash
# Purpose: Report which container registries are used by workloads in the cluster
# Scope: Run from any machine with kubectl access and correct context set
set -euo pipefail
# Optional: set a regex of approved registries (edit to match your org policy)
# Examples:
# APPROVED_REGEX='^(123456789012\.dkr\.ecr\.us-west-2\.amazonaws\.com|public\.ecr\.aws/my-team)/'
# APPROVED_REGEX='^123456789012\.dkr\.ecr\.(us-west-2|us-east-1)\.amazonaws\.com/'
APPROVED_REGEX='^$' # empty (matches nothing) by default so everything is "unapproved" for review
echo "Collecting images from all namespaces..."
echo
# Get all pods and their container images, including initContainers, de-duplicated
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| .metadata.namespace as $ns
| .metadata.name as $pod
| [
(.spec.containers[]? | {type:"container", name:.name, image:.image}),
(.spec.initContainers[]? | {type:"initContainer", name:.name, image:.image})
][]
| [$ns, $pod, .type, .name, .image]
| @tsv
' | sort -u > /tmp/k8s-images.tsv
if [ ! -s /tmp/k8s-images.tsv ]; then
echo "No pods found."
exit 0
fi
echo "Per-pod image usage:"
echo -e "NAMESPACE\tPOD\tKIND\tCONTAINER\tIMAGE"
cat /tmp/k8s-images.tsv
echo
# Extract just the image field
cut -f5 /tmp/k8s-images.tsv | sort -u > /tmp/k8s-images-unique.txt
echo "Unique images in cluster:"
cat /tmp/k8s-images-unique.txt
echo
# Derive the registry domain (or 'docker.io' for images with no explicit registry)
awk '
function get_registry(img) {
split(img, parts, "/")
if (split(parts[1], hostparts, "\\.") >= 2 || index(parts[1], ":") || parts[1] ~ /localhost/) {
return parts[1]
}
# no explicit registry, default docker.io
return "docker.io"
}
{
img=$0
reg=get_registry(img)
print reg "\t" img
}' /tmp/k8s-images-unique.txt | sort -u > /tmp/k8s-images-by-registry.tsv
echo "Registries in use and associated images:"
echo -e "REGISTRY\tIMAGE"
cat /tmp/k8s-images-by-registry.tsv
echo
echo "Summary: registries in use (count of images per registry):"
cut -f1 /tmp/k8s-images-by-registry.tsv | sort | uniq -c | sort -nr
echo
if [ -n "${APPROVED_REGEX}" ]; then
echo "Detecting images from registries NOT matching approved regex: ${APPROVED_REGEX}"
echo -e "REGISTRY\tIMAGE"
awk -v re="${APPROVED_REGEX}" '
{
reg=$1
img=$2
# rebuild full image (in case of spaces, though unlikely)
for (i=3; i<=NF; i++) img=img FS $i
if (img !~ re) {
print reg "\t" img
}
}' /tmp/k8s-images-by-registry.tsv | sort -u > /tmp/k8s-unapproved.tsv || true
if [ -s /tmp/k8s-unapproved.tsv ]; then
cat /tmp/k8s-unapproved.tsv
echo
echo "Found images that do NOT match the approved registry pattern."
echo "Each listed IMAGE should be reviewed against your approval criteria."
else
echo "No images found outside the approved registry pattern."
fi
else
echo "APPROVED_REGEX is empty; skipping automatic approved/unapproved classification."
fi
Explanation of what indicates a problem:
- Any registry in the “Summary: registries in use” section that is not part of your approved ECR list (e.g.,
docker.io,gcr.io, other AWS accounts’ ECRs) is a candidate issue. - If you set
APPROVED_REGEXto your allowed ECR registries, any entries printed under “Found images that do NOT match the approved registry pattern” are using unapproved registries and must be reviewed and either:- Migrated to an approved ECR registry, or
- Explicitly granted an exception as part of your policies and procedures.