How to get the full domain allowlist for pulling Docker images
Corporate HTTP proxies and security gateways (Squid, Zscaler and friends) love blocking unknown destinations. That’s a problem when your CI/CD pipeline or Kubernetes cluster pulls container images, because a “simple” pull talks to far more hosts than just the registry. Here is a repeatable way to find every single domain your images need, so you can build a minimal allowlist.
crane pull --verbose, then extract the unique hostnames from the log and allowlist them.
Why one registry domain is not enough
When you pull registry.k8s.io/external-dns/external-dns, the manifest comes from registry.k8s.io, but the blobs (the actual layers) are often served from a backing store such as prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. If that host isn’t allowlisted, the pull just hangs.
Prepare your image list
Put one fully qualified image reference per line in a plain text file. Lines starting with # are ignored.
# images.txt
registry.k8s.io/external-dns/external-dns:v0.14.2
ghcr.io/kyverno/kyverno:v1.12.0
quay.io/prometheus/node-exporter:v1.8.2If you are migrating from one Kubernetes cluster to another, you can dump the images already running there:
kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -uInclude any mirrored or private registry endpoints you consume.
Pull images with verbose logging
crane from the go-containerregistry project can pull images and print all HTTP traffic in verbose mode. The script below loops over your image file, pulls each image for one platform and appends every request and response to out.log. The pull destination is /dev/null, so only the network interaction matters.
Examine the logs
Find the hostnames that show up in the requests.
Extract the unique domains
Turn the log into a deduplicated list and add it to your proxy.
linux/amd64 and linux/arm64). Different architectures may be served from different storage buckets or regions.The pull script
#!/usr/bin/env bash
set -u
set -o pipefail
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <images-file>"
exit 1
fi
IMAGES_FILE="$1"
if [[ ! -f "$IMAGES_FILE" ]]; then
echo "Error: file not found: $IMAGES_FILE"
exit 1
fi
ARCH="amd64"
OS="linux"
LOG="out.log"
: > "$LOG"
TOTAL=$(grep -vc '^\s*$\|^\s*#' "$IMAGES_FILE")
COUNT=0
URL_COUNT=0
FAIL_COUNT=0
echo "[*] Platform : ${OS}/${ARCH}"
echo "[*] Images to scan: $TOTAL"
echo
resolve_image() {
local image="$1"
local before after delta
[[ -z "$image" ]] && return
[[ "$image" =~ ^# ]] && return
COUNT=$((COUNT + 1))
echo "[${COUNT}/${TOTAL}] Pulling $image"
crane pull \
--platform="${OS}/${ARCH}" \
--verbose \
"$image" /dev/null \
2>>"$LOG"
}
while read -r image; do
resolve_image "$image"
done < "$IMAGES_FILE"
echo "Completed"Run the script behind your proxy so all HTTP requests go through it. If you deploy multiple architectures, change ARCH accordingly or loop over the architectures.
Examine the logs
out.log interleaves requests and responses. A request entry has a timestamp, a direction arrow (-->), the HTTP method and the full URL, followed by the Host header and other metadata:
2025/12/20 12:20:31 --> GET https://prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com/containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 [body redacted: omitting binary blobs from logs]
2025/12/20 12:20:31 GET /containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 HTTP/1.1
Host: prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com
User-Agent: crane/0.20.7 go-containerregistry/(devel)
Referer: https://registry.k8s.io/v2/external-dns/external-dns/blobs/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565
Accept-Encoding: gzip
2025/12/20 12:20:31 <-- 200 https://prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com/containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 (140.98475ms) [body redacted: omitting binary blobs from logs]
2025/12/20 12:20:31 HTTP/1.1 200 OK
Content-Length: 40334622
Accept-Ranges: bytes
Content-Type: binary/octet-stream
Date: Sat, 20 Dec 2025 11:20:32 GMT
Etag: "a32f7a524e8925d6477109e1a4ee6d2a-8"
Last-Modified: Tue, 02 Sep 2025 09:37:17 GMT
Server: AmazonS3
X-Amz-Id-2: 4738bNwUTkuPUFgEmL8FHxIzbF1AfFMwBR5Ny3mOU0oBErx5i9/qlYU3Y/oWt80FGN+W0heUf3mqGapUQMvhj4bzp+8T7rtbczH86SmKxbo=
X-Amz-Replication-Status: REPLICA
X-Amz-Request-Id: BHYDQY4KTZR0T2X4
X-Amz-Server-Side-Encryption: AES256
X-Amz-Version-Id: EFFEydHI6sT.arWAQdr.XDbPkk0a7AZdprod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. Note the Referer pointing back to registry.k8s.io: the registry redirected the blob download to S3. You must allow these storage domains even though they look nothing like the registry.Extract the unique domains
Once the log is populated, this pipeline pulls out just the hostnames:
cat out.log | grep http | awk '{print $5}' | awk -F '/' '{print $3}' | sort -uIt keeps lines containing http, takes the URL field, splits it on / to get the domain and deduplicates the result. A run against the example images produced:
auth.docker.io
cdn01.quay.io
europe-west3-docker.pkg.dev
ghcr.io
index.docker.io
mcr.microsoft.com
pkg-containers.githubusercontent.com
prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com
production.cloudflare.docker.com
quay.io
reg.kyverno.io
registry.k8s.io
westeurope.data.mcr.microsoft.comAdd these domains to your proxy configuration. Depending on what your proxy supports, you may be able to allow wildcard subdomains instead of listing each entry.
Tips and caveats
- Run per platform: different CPU architectures may fetch layers from different storage regions. Repeat the process for each architecture you use.
- Include mirrors and caches: if you pull through private mirrors, caches or CDNs, put those endpoints in
images.txttoo. - Rerun as your image list grows: the allowlist is only as complete as the images you test. Regenerate it whenever you add images or bump versions.
- Know your proxy syntax: some proxies support patterns like
*.s3.amazonaws.com, others need exact domain names. Adjust the list accordingly.