Skip to main content

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 AKS
  • CIS Critical Security Controls v8
  • 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

Manual Steps
  1. Inventory current image sources (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
    • Export a clean list of unique registries in use:
      kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' \
      | sed 's#^\([^/]\+\)/.*#\1#' | sort -u > /tmp/current-registries.txt
  2. Define and document the approved registries set (offline / console)

    • From security / platform standards, produce a list like:
      • myorg.azurecr.io (ACR)
      • mcr.microsoft.com (base images)
      • registry.k8s.io (Kubernetes system images, if needed)
    • Save as /tmp/approved-registries.txt for comparison.
  3. Compare actual vs approved registries (any machine with kubectl access)

    echo "=== Unapproved registries in use ==="
    comm -23 /tmp/current-registries.txt /tmp/approved-registries.txt || true
    • For each unapproved registry, identify dependent workloads:
      UNAPPROVED="<registry-hostname>"
      kubectl get pods -A -o wide \
      | awk -v r="$UNAPPROVED" 'NR==1 || $0 ~ r'
    • Decide per workload: migrate image to an approved registry or obtain formal exception.
  4. Lock down Azure Container Registry to AKS (Azure CLI / Portal)

    • For each approved ACR, restrict access (run from admin machine with Azure CLI):
      ACR_NAME="myregistry" # ACR name, not FQDN
      AKS_RG="my-aks-rg"
      AKS_NAME="my-aks-cluster"

      # Attach ACR to AKS (ensures identity permissions)
      az aks update -g "$AKS_RG" -n "$AKS_NAME" --attach-acr "$ACR_NAME"

      # Enable firewall and allow only AKS egress IPs (example: a single egress IP)
      ACR_RG="$(az acr show -n "$ACR_NAME" --query resourceGroup -o tsv)"
      ACR_IP="<aks-egress-public-ip>"

      az acr update -n "$ACR_NAME" --resource-group "$ACR_RG" --public-network-enabled false
      az acr network-rule add -n "$ACR_NAME" -g "$ACR_RG" --ip-address "$ACR_IP"
    • Alternatively, use Private Endpoints and ensure only the AKS virtual network/subnets are linked.
  5. Enforce registry restrictions via Azure Policy / admission control (cloud configuration)

    • In the Azure portal or via CLI, assign a built‑in policy (search for):
      • “Kubernetes clusters should only allow container images from trusted registries”
    • Scope it to the AKS cluster’s resource group or subscription and configure the allowed registries list to match /tmp/approved-registries.txt.
    • If using custom admission controllers (e.g., OPA/Gatekeeper), review the constraint templates to ensure they:
      • Deny images whose registry host is not in the approved list.
      • Are applied to all relevant namespaces (exclude only explicitly approved ones).
  6. Verify enforcement and absence of drift (any machine with kubectl access)

    • Attempt to deploy a pod from an unapproved registry and confirm it is blocked:
      cat << 'EOF' > /tmp/unapproved-test-pod.yaml
      apiVersion: v1
      kind: Pod
      metadata:
      name: unapproved-registry-test
      namespace: default
      spec:
      containers:
      - name: test
      image: unapproved.example.com/busybox:latest
      command: ["sh", "-c", "sleep 3600"]
      EOF

      kubectl apply -f /tmp/unapproved-test-pod.yaml
      kubectl describe pod unapproved-registry-test -n default
    • Confirm the pod is denied by policy or fails to pull due to ACR firewall/network rules, and re-run step 1’s inventory to ensure only approved registries remain in active workloads.
Using kubectl

kubectl cannot configure which container registries are approved for AKS workloads; this is controlled via Azure Container Registry firewall rules, Azure Policy/admission control, and network/egress settings in the Azure control plane and surrounding Azure resources. Use kubectl only to inspect current image usage; apply the actual changes using Azure configuration as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
set -euo pipefail

# Run this on any machine with kubectl access and correct context.
# Optional: comma‑separated list of approved registries (prefix match), e.g.:
# APPROVED_REGISTRIES="myregistry.azurecr.io,contoso.azurecr.io"
APPROVED_REGISTRIES="${APPROVED_REGISTRIES:-}"

echo "Collecting image usage from all namespaces..." >&2

# 1) Gather all unique images (including initContainers) across all namespaces
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: (
([.spec.containers[], (.spec.initContainers // [])[]]
| {name, image})
)
}
| .ns as $ns
| .pod as $pod
| .containers[]
| [$ns, $pod, .name, .image]
| @tsv
' | sort -u > /tmp/aks_images.tsv

echo "Found the following (namespace, pod, container, image) tuples:" >&2
cat /tmp/aks_images.tsv >&2
echo >&2

# 2) Extract the registry/host portion of each image (text before first '/')
# Note: images without an explicit registry default to Docker Hub
cut -f4 /tmp/aks_images.tsv \
| awk -F/ '
{
if (index($1, ".") || index($1, ":")) {
registry=$1
} else {
# No registry specified -> treat as Docker Hub library or docker.io
registry="docker.io (implicit)"
}
print registry
}
' \
| sort -u > /tmp/aks_registries.txt

echo "Unique registries (or implied registry) in use:" >&2
nl -ba /tmp/aks_registries.txt >&2
echo >&2

if [ -n "$APPROVED_REGISTRIES" ]; then
echo "Approved registries (prefix match): $APPROVED_REGISTRIES" >&2
IFS=',' read -r -a approved <<< "$APPROVED_REGISTRIES"

echo
echo "=== Potentially UNAPPROVED registries in use ==="
echo "(Any registry not matching one of the approved prefixes)"
while read -r reg; do
is_ok=0
for a in "${approved[@]}"; do
# simple prefix match
case "$reg" in
"$a"*) is_ok=1 ;;
esac
done
if [ "$is_ok" -eq 0 ]; then
echo "$reg"
fi
done < /tmp/aks_registries.txt | sort -u
echo
fi

# 3) Detailed report: list pods/containers using each registry
echo "=== Detailed pod/container -> registry mapping ==="
echo -e "NAMESPACE\tPOD\tCONTAINER\tIMAGE\tREGISTRY"
while IFS=$'\t' read -r ns pod cname img; do
reg=$(awk -F/ '
{
if (index($1, ".") || index($1, ":")) {
print $1
} else {
print "docker.io (implicit)"
}
}' <<< "$img")
printf "%s\t%s\t%s\t%s\t%s\n" "$ns" "$pod" "$cname" "$img" "$reg"
done < /tmp/aks_images.tsv | column -t

How to interpret the output:

  • Any registry listed under “Unique registries (or implied registry) in use” that is not on your approved list is a review item.
  • If APPROVED_REGISTRIES is set, anything printed under “Potentially UNAPPROVED registries in use” indicates a potential problem.
  • Pay particular attention to:
    • docker.io (implicit) or images without a registry (defaulting to Docker Hub) if Docker Hub is not approved.
    • Public cloud registries (e.g., mcr.microsoft.com, docker.io, gcr.io, etc.) if your policy requires only private/ACR registries.
  • Use the “Detailed pod/container -> registry mapping” section to identify exactly which workloads are pulling from each non‑approved registry for manual remediation and policy updates.

Additional Reading: