Minimize Container Registries To Only Those Approved
More Info:
Restrict container image pulls to an approved set of registries using firewall rules, admission controllers, or Azure Policy, and limit egress traffic to those registries.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS AKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory current image registries in use
- Run 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 registry hostnames (first path segment before
/) and compile a list of all registries currently used by workloads (including system namespaces likekube-systemandazure-*).
- Run on any machine with kubectl access:
-
Define and document the approved registry list
- Using your organization’s security policy, decide which registries are approved (for example, specific Azure Container Registries and any required Microsoft/AKS system registries).
- Document them as explicit FQDNs, e.g.:
mycorpprod.azurecr.iomycorptest.azurecr.io- Required Microsoft endpoints as per AKS / egress docs.
-
Restrict access at the registry and network level (Azure side)
- For each Azure Container Registry:
- In the Azure Portal: ACR → Networking → enable Selected networks and add only approved VNets/subnets or private endpoints as per:
https://learn.microsoft.com/azure/container-registry/container-registry-firewall-access-rules - Or via Azure CLI, run on any admin machine:
az acr update \--name mycorpprod \--resource-group <RG-NAME> \--public-network-enabled false
- In the Azure Portal: ACR → Networking → enable Selected networks and add only approved VNets/subnets or private endpoints as per:
- For cluster egress control:
- If you use a firewall/NVA/Azure Firewall: restrict outbound rules from AKS subnets to only the approved registry FQDNs/IPs according to:
https://learn.microsoft.com/azure/aks/limit-egress-traffic
- If you use a firewall/NVA/Azure Firewall: restrict outbound rules from AKS subnets to only the approved registry FQDNs/IPs according to:
- For each Azure Container Registry:
-
Enforce registry usage with Azure Policy / admission control
- In Azure Portal: Policy → Definitions and locate policies such as:
Kubernetes cluster containers should only use allowed images
- Assign to the AKS cluster’s scope and configure the list of allowed registries (FQDNs from step 2).
- If using Gatekeeper/another admission controller (via IaC), create/update a policy that rejects images whose registry host is not in the approved list. Example (conceptual) for Gatekeeper ConstraintTemplate/Constraint; apply via your existing GitOps/IaC pipeline.
- In Azure Portal: Policy → Definitions and locate policies such as:
-
Verify enforcement and impact
- Attempt to deploy a pod pulling from an unapproved registry, on any machine with kubectl access:
cat <<'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata:name: test-unapproved-registryspec:containers:- name: cimage: unapproved.example.com/namespace/image:tagEOF
- Confirm that:
- The admission policy rejects the pod (check events):
kubectl describe pod test-unapproved-registry
- Or, if admitted, that image pull fails due to network/egress restrictions (image pull errors in events).
- The admission policy rejects the pod (check events):
- Attempt to deploy a pod pulling from an unapproved registry, on any machine with kubectl access:
-
Re-audit running workloads and system behavior
- Re-run the inventory command (step 1) to confirm that all new pods only use approved registries.
- Review AKS egress logs/firewall logs (Azure Firewall, NSG flow logs, etc.) to ensure no successful outbound connections from AKS subnets to non-approved registries.
Using kubectl
kubectl cannot be used to restrict container image pulls to approved registries because this control is enforced at the cloud/provider configuration level (Azure Container Registry firewall rules, Azure Policy, and egress controls), not via Kubernetes API objects. Apply the remediation in the cloud console or IaC as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
# Purpose: Report container registries in use across the cluster for review
# Requirements: kubectl configured with access to the target AKS cluster
set -euo pipefail
echo "==[ 1/4 ] Cluster-scoped image usage (Pods, Deployments, etc.)=="
# List all images referenced by core workload types in all namespaces
kubectl get pods,deployments,daemonsets,statefulsets,replicasets,cronjobs,jobs \
-A -o jsonpath='{range ..image}{.}{"\n"}{end}' 2>/dev/null \
| sort -u \
| awk 'NF' \
| tee /tmp/aks-images-all.txt
echo
echo "==[ 2/4 ] Unique image registries/domains in use (derived from above)=="
# Extract registry portion (text before first /); if no /, mark as "dockerhub_implicit"
awk '
{
img=$0
split(img,parts,"/")
if (NF==1 || img !~ /\./ && img !~ /:/) {
# Images like "nginx:1.21" => implicit Docker Hub
print "dockerhub_implicit"
} else {
print parts[1]
}
}' /tmp/aks-images-all.txt \
| sort -u \
| tee /tmp/aks-registries-all.txt
echo
echo "==[ 3/4 ] Report by namespace and workload=="
# Show each pod, its namespace, workload owner (if any), and images
kubectl get pods -A -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
owner: ( .metadata.ownerReferences[0].kind + "/" + .metadata.ownerReferences[0].name ) // "POD/standalone",
images: ([.spec.containers[]?.image] + [.spec.initContainers[]?.image]) | unique
}
| .ns + "\t" + .owner + "\t" + .pod + "\t" + ( .images | join(",") )
' \
| column -t -s $'\t' \
| tee /tmp/aks-image-usage-by-pod.txt
echo
echo "==[ 4/4 ] OPTIONAL: Compare against an approved registry list (if provided)=="
APPROVED_FILE="./approved-registries.txt"
if [[ -f "${APPROVED_FILE}" ]]; then
echo "Using approved registry list from: ${APPROVED_FILE}"
echo "Approved registries:"
cat "${APPROVED_FILE}"
echo
echo "Non-approved registries in use:"
# Show any registry not present in approved-registries.txt
grep -Fvx -f "${APPROVED_FILE}" /tmp/aks-registries-all.txt || true
else
cat <<'EOF'
No ./approved-registries.txt found.
To enable automatic comparison, create a file named approved-registries.txt in the current directory
with one approved registry/hostname per line, for example:
myteam.azurecr.io
another-approved-registry.io
dockerhub_implicit
Then re-run this script to see which registries are outside the approved set.
EOF
fi
echo
echo "==[ SUMMARY ]=="
echo "All unique images: /tmp/aks-images-all.txt"
echo "All unique registries: /tmp/aks-registries-all.txt"
echo "Per-pod image usage: /tmp/aks-image-usage-by-pod.txt"
if [[ -f "${APPROVED_FILE}" ]]; then
echo "Compare non-approved registries above with your policies, ACR firewall rules,"
echo "Azure Policy / admission controller configurations, and egress controls."
fi
How to run (any machine with kubectl access)
chmod +x report-registries.sh
./report-registries.sh
Interpreting the output (what indicates a problem)
/tmp/aks-registries-all.txtlists every registry/domain in use.- Any entry that is not on your formally approved list (for example, your ACR FQDNs) is a potential problem.
dockerhub_implicitindicates images likenginx:latestwith no explicit registry, typically Docker Hub. Treat this as unapproved unless your policy explicitly allows it.
- The “Non-approved registries in use:” section (when
approved-registries.txtis present) shows registries that:- Must be blocked via ACR firewall rules, Azure Policy/admission controls, or
- Must be reviewed and either added to the approved list or removed/migrated.
/tmp/aks-image-usage-by-pod.txtlets you trace unapproved registries back to the exact namespaces/workloads to drive remediation decisions.