Skip to content

Docker: challenge a request without credentials for an image a selector hides, so containerd and Kubernetes pulls send theirs - #808

Merged
schmitch merged 1 commit into
mainfrom
claude/2-2-merge-read-review-6u8q5j
Nov 2, 2026
Merged

schmitch merged 1 commit into
mainfrom
claude/2-2-merge-read-review-6u8q5j

Conversation

@schmitch

@schmitch schmitch commented Nov 2, 2026

Copy link
Copy Markdown
Member

Fixes #787

Problem

#703 (#704) made a selector-hidden image return 404 NAME_UNKNOWN. That arm now runs before the one that challenges an unauthenticated caller. A request that sent no credential of its own got 404 with no WWW-Authenticate:

  • an anonymous request under anonymous mode all, where the anonymous role's grant has a selector;
  • an account a trusted-header realm names, whose grant has a selector.

containerd sends its credentials only after a 401. So a Kubernetes pull with an imagePullSecret failed with "not found" for an image the secret may pull. On 2.1 the same request got the challenge, and the pull worked.

Change

Registry::refusal (crates/server/src/images/dispatch.rs) answers Decision::Hidden with the Bearer challenge when the request presented neither a credential nor a registry token: !mutation_credential(principal) && claims.is_none().

Every other case answers 404 NAME_UNKNOWN, as #703 intends:

  • a request with a registry token, an anonymous one included;
  • a request with Basic credentials.

Pushes and deletes are unchanged (403).

#703's changelog fragment (unreleased) said an anonymous caller now gets 404 too. It, and docs/docker.md, now describe the challenge. The regression never shipped, so this change adds no fragment of its own.

Tests

a_hidden_image_challenges_a_request_without_credentials (crates/server/tests/registry.rs) checks:

  • 401 with a Bearer challenge for secret/app, on GET and HEAD, for an anonymous request and for a trusted-header account, whose selector shows public/*;
  • public/app is still served to both;
  • 404 NAME_UNKNOWN with an anonymous registry token;
  • 200 with the credentials of a user who may read the whole repository.

Without the fix the test fails with anonymous: GET of a hidden image, left: 404, right: 401.

Run locally:

  • cargo test -p forge-server --test registry --test trusted_header: 49 + 11 passed;
  • cargo clippy -p forge-server --all-targets -- -D warnings, cargo fmt --all --check and cargo xtask changelog --check: clean.

Generated by Claude Code

…or hides

Since #703, an image a content selector hides from a caller who may read
the repository answers 404 NAME_UNKNOWN, ahead of the arm that challenges
an unauthenticated caller. So a request that sent no credential of its own
(anonymous under anonymous mode `all`, or an account a trusted-header
realm names) got 404 and no WWW-Authenticate. containerd, and with it a
Kubernetes pull with an imagePullSecret, sends its credentials only after
a 401: the pull failed with "not found" where 2.1 challenged it and the
credentials pulled the image.

`refusal` now answers a hidden image with the Bearer challenge when the
request presented no credential and no registry token. A request with a
registry token, an anonymous one included, still finds no such image, as
#703 intends. #703's changelog fragment and docs/docker.md say so.

Fixes #787

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012e6DJc1H3748D8sheqhKTV
@schmitch
schmitch enabled auto-merge November 2, 2026 20:51
@schmitch
schmitch added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 9de2238 Nov 2, 2026
19 checks passed
@schmitch
schmitch deleted the claude/2-2-merge-read-review-6u8q5j branch November 2, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docker: an image a selector hides from a caller without credentials answers 404 and no challenge, so containerd and Kubernetes pulls never send theirs

2 participants