Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,10 @@ GET https://registry-1.docker.io/v2/
GET https://registry-1.docker.io/v2/library/hello-world/*
# Auth tokens — scoped to the hello-world repository.
GET https://auth.docker.io/token*
# Blob storage — the registry redirects layer downloads to either
# a Cloudflare R2 bucket or CloudFront CDN. Blob paths contain
# sha256 digests that can't be scoped per-image, but the registry
# only returns redirect URLs for layers belonging to images the
# client already resolved via the scoped manifest rules above.
# Blob storage — the registry redirects layer downloads to a CDN.
# Paths contain per-blob sha256 digests, so they are wildcarded, but
# scoped to the /registry-v2/ prefix both CDNs serve them under.
# Docker Hub hands out both of these depending on the request.
GET https://docker-images-prod.6aa30f8b08e16409b46e0173d6de2f56.r2.cloudflarestorage.com/registry-v2/*
GET https://production.cloudfront.docker.com/registry-v2/*

Expand Down
39 changes: 39 additions & 0 deletions tests/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import sys
from pathlib import Path
from unittest.mock import MagicMock
from urllib.parse import urlparse

import pytest

Expand Down Expand Up @@ -254,3 +255,41 @@ def test_allows_head_when_get_rule_matches(self, filter_env):
flow = _make_flow("HEAD", "example.com", "https://example.com/page")
addon.request(flow)
assert flow.response is None # HEAD permitted by GET rule


# ---------------------------------------------------------------------------
# The shipped allowlist.txt (not synthetic rules)
# ---------------------------------------------------------------------------

class TestShippedAllowlist:
"""Checks the real allowlist.txt covers what the e2e suite needs.

These are the URLs a `docker pull` actually requests. Docker Hub serves
blobs from more than one CDN and picks per request, so allowing only one of
them yields an intermittent-looking failure: the manifest fetch succeeds,
then the layer download dies with a proxy 418 and docker reports
`error pulling image configuration`, which reads as a network fault rather
than a missing allowlist rule.
"""

@pytest.mark.parametrize("url", [
# Registry API: manifests and config blobs.
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest",
# Auth token for the anonymous pull.
"https://auth.docker.io/token?scope=repository%3Alibrary%2Fhello-world%3Apull",
# Blob CDNs — both are in play; paths carry per-blob sha256 digests.
"https://docker-images-prod.6aa30f8b08e16409b46e0173d6de2f56."
"r2.cloudflarestorage.com/registry-v2/docker/registry/v2/blobs/"
"sha256/58/58dee6a49ef1/data",
"https://production.cloudfront.docker.com/registry-v2/docker/registry/"
"v2/blobs/sha256/58/58dee6a49ef1/data",
])
def test_docker_pull_urls_allowed(self, url):
rules = fm.parse_allowlist(fm.ALLOWLIST_PATH)
host = urlparse(url).hostname
assert fm.is_allowed(rules, "GET", host, url), (
f"allowlist.txt blocks a URL `docker pull hello-world` needs:\n"
f" {url}\n"
"test_docker_hello_world in the e2e suite will fail with a proxy "
"418 that looks like a network error."
)
Loading