How to get the full domain allowlist for pulling Docker images How to get the full domain allowlist for pulling Docker images

How to get the full domain allowlist for pulling Docker images

Corporate HTTP proxies and security gateways (Squid, Zscaler, corporate gateways) often block unknown destinations. When your CI/CD pipelines or Kubernetes cluster attempt to pull container images, they don’t just talk to a single registry domain – images are frequently stored in backing object stores on completely different hosts (for example, AWS S3 or Google Cloud Storage domains). If those domains aren’t allowlisted, every pull will hang. The rest of this article walks through a repeatable process for discovering every domain used when pulling your specific images so that you can build a minimal allowlist for your proxy.

Prepare your image list

Start by assembling the set of images you plan to use. Put one fully qualified image reference per line in a plain text file and ignore comment lines (those starting with #). For example:

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.2

or use following command if you already have images in k8s (in case you are migrating from one kubernetes to another): kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -u

Include any mirrored or private registry endpoints you consume. Repeat this procedure for each platform (e.g. linux/amd64 and linux/arm64) because different architectures may come from different storage buckets or regions.

Pull images with verbose logging

crane from the go-containerregistry project can pull container images and print HTTP traffic in verbose mode. The script below loops over each line in your image file, pulls the image for a specific platform, and writes all HTTP requests and responses to a log file (out.log). The destination of the pull is /dev/null, so only network interactions are recorded:

#!/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 to force all HTTP requests through it. If you deploy multiple architectures, change ARCH accordingly or loop over architectures.

Examine the logs

The resulting out.log file interleaves request and response lines. A request entry includes a timestamp, direction arrow (—>), HTTP method and full URL. Subsequent lines include the Host header and other metadata. For example:

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.XDbPkk0a7AZd

The important part is the hostname – in this case, prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. Registries often store layers in cloud object storage; you need to allow these domains even though they differ from the registry root.

Extract the unique domains

Once the log file is populated, use this pipeline to extract just the hostnames and sort them:

Terminal window
cat out.log | grep http | awk '{print $5}' | awk -F '/' '{print $3}' | sort -u

This command filters lines containing http, isolates the URL field, splits on / to pick out the domain, and deduplicates the results. 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.com

Add these domains to your proxy configuration. Depending on your proxy’s capabilities, you may be able to allow wildcard subdomains rather than enumerating 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 content delivery networks, include those endpoints in images.txt.
• Rerun as your image list grows: The completeness of your allowlist is determined by the images you test. Whenever you introduce new images or update to new versions, regenerate the list.
• Understand your proxy syntax: Some proxies support patterns like *.s3.amazonaws.com; others require exact domain names. Adjust the list accordingly.

← Back to blog