Skip to main content

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

Manual Steps
  1. 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.
  2. 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.
  3. 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.
    • Record decisions per registry (approved / to-be-phased-out / banned) in your documentation.
  4. 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.
  5. 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.
  6. 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.
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_REGEX to 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.