1542 Commits
Author SHA1 Message Date
jeongho-kim 355b036bf9 proxy: add opt-in Range pass-through
Relay cache-miss blob Range requests to the upstream when
proxy.rangepassthrough is enabled, without populating the blob cache.
Preserve upstream 200, 206 and 416 responses while keeping ordinary GET
cache fills and cache-hit handling unchanged.

Reuse the authenticated client, abort interrupted streams, and exclude
416 responses from blob transfer metrics and pull notifications.
Document the option and cover the HTTP and cache behavior in tests.

Signed-off-by: jeongho-kim <jeongho.kim@toss.im>
2026-10-22 17:18:21 +09:00
YoungjuKim 54058ad724 registry/storage: do not write hash state when closing a cancelled upload
Cancel removes the upload directory, but the handler then closes the
writer, and Close stores the hash state again. That recreates the
upload directory without a startedat file, so the upload purger can
never remove it, and Close reports "already closed". Skip storing the
hash state once the upload has been cancelled.

Signed-off-by: YoungjuKim <fjvbn2003@gmail.com>
2026-10-17 20:10:49 +09:00
Milos Gajdos f7d69d28c2 driver/filesystem: fix uploads on Windows (#5012) 2026-10-14 13:00:17 -07:00
Milos Gajdos 84baa4692d registry/storage: avoid duplicate SHA-256 in canonical full-fallback validation (#4921) 2026-10-14 10:56:17 -07:00
CrazyMax cf25a54ab3 ci: run unit tests on Linux and Windows
Run make test across Ubuntu and Windows with the latest Go 1.26 and 1.27
patch releases. This includes filesystem driver tests, which the existing
coverage target excludes, and catches Windows regressions before release.

Keep coverage and the Docker-backed S3 and Azure tests in a Linux-only job,
and require both test jobs before building artifacts.

Make the file health checks portable by using temporary directories and
closing the marker file before removing it. Check removal errors so a
failed deletion is reported directly.

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2026-10-14 12:02:35 +02:00
CrazyMax 65804bfb78 driver/filesystem: fix uploads on Windows
Since v3.1.0, the filesystem driver has tried to rename an open temporary
file when creating an upload session, causing every blob upload on Windows
to fail with HTTP 500. Closing the file exposes a second failure when the
driver tries to sync the parent directory.

Close the temporary file before renaming it and make directory syncing a
no-op on Windows. Preserve file syncing, replacement semantics, and the
existing writer lifecycle contract. Return slash-separated storage keys
from List so callers can use them on every platform.

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2026-10-14 10:48:57 +02:00
Milos Gajdos be78ef9417 Include filesystem rootdirectory in PathNotFound errors (#5007) 2026-10-11 17:18:39 -07:00
Milos Gajdos 036e830f80 fix(handlers): return 400 NAME_INVALID for over-long repository names (#4995) 2026-10-11 16:36:42 -07:00
yusheng.wang 89b2357009 Include filesystem rootdirectory in PathNotFound errors
PathNotFoundError previously printed a storage-driver-relative path that callers treated as an absolute OS path. Include the filesystem driver's rootdirectory in the error message so the configured storage root is visible. Fixes #1793.

Signed-off-by: yusheng.wang <yusheng.wang@chaitin.com>
2026-10-10 04:25:53 +08:00
Milos Gajdos fd9609dd58 Fix inmemory driver find traversing too deep into directory nodes (#4996) 2026-10-07 07:48:39 -07:00
Tianon Gravi 2ff9628d81 Add a regression test for inmemory race
Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
2026-10-06 16:48:20 -07:00
ningmingxiao f3556cdabf build: reduce binary size via improved dead‑code elimination
Signed-off-by: ningmingxiao <ning.mingxiao@zte.com.cn>
2026-10-05 08:48:30 +08:00
Tianon Gravi f9e83ae0b7 Fix inmemory driver find traversing too deep into directory nodes
When the final path component resolves to a directory, `q = q[i+1:]` with `i == -1` leaves `q` unchanged and recurses into the child -- going one level deeper than intended.  That is normally harmless, because the repeated lookup finds nothing and returns the directory it is already in, but it returns the wrong node when that directory has a child with the same name.

Guard with `i >= 0` so the found directory is returned directly.

Fixes an intermittent flake (~3-7% of runs) in `TestDeleteOnlyDeletesSubpaths`, which constructs `dirname/dirname` to deliberately trigger the ambiguous case.

Assisted-By: "claude my eyes right out"
Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
2026-10-02 14:54:29 -07:00
somaz b1b87d9550 fix(handlers): return 400 NAME_INVALID for over-long repository names
A repository name whose path is longer than 255 characters fails
reference.WithName in the dispatcher. That branch appended a bare
distribution.ErrRepositoryNameInvalid to context.Errors, and since it
is not an errcode.ErrorCoder, errcode.ServeJSON answered 500 UNKNOWN.
Wrap it in errcode.ErrorCodeNameInvalid, as the Repository() branch
right below already does for the same error type.

Since a811c1bb moved the automatic error response into a deferred call
at the top of the dispatcher, the early returns for an invalid name, a
failed Repository() call and a failed repository middleware have each
written the error envelope twice: net/http drops the second WriteHeader
as superfluous and the body ends up with two JSON documents. Remove the
explicit ServeJSON calls and leave the response to the deferred handler.
The Repository() switch gains a default case, so an error of any other
type still gets a 500, now with an UNKNOWN entry instead of an empty
envelope.

Fixes #4699

Assisted-by: Claude Opus 5.5 (via Claude Code)
Signed-off-by: somaz <genius5711@gmail.com>
2026-10-01 18:08:15 +09:00
Sebastiaan van Stijn 9356fc8cfa registry: remove logrus-logstash-hook dependency
The logstash formatter was originally added in a75f0f2 using the
formatter that was provided by logrus at the time.

Commit 1d6c1a6 switched to logrus-logstash-hook after the formatter
was removed from logrus. However, it instantiated LogstashFormatter
directly instead of using DefaultFormatter, so the Logstash field
mappings and default fields provided by that package are not used.

The only additional formatting currently provided by the wrapper is
copying caller information to "function" and "file". Logrus'
JSONFormatter supports configuring these field names directly and
already formats the file as "<file>:<line>".

Replace the external formatter with JSONFormatter and map the caller
function field to preserve the current output.

This removes the logrus-logstash-hook dependency without intentionally
changing the logstash formatter output.

Refs: a75f0f26f7
Refs: 1d6c1a6468

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-09-22 18:40:28 +02:00
Milos Gajdos 5d7daa1aa1 Fix lint
Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-09-21 21:01:32 -07:00
Tyagiquamar e0eb0fcf23 fix(handlers): defer context cancellation on Shutdown to stop upload purger
Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com>
2026-09-21 21:55:09 +05:30
Pujitha Paladugu 885e775cfe fix(s3): use path.Dir instead of filepath.Dir in directoryDiff
Motivation:
directoryDiff walks up S3 object-key path components while Walk infers
directories from a sorted key listing (used by, among others, the
background PurgeUploads upload-cleanup job). It used filepath.Dir from
path/filepath, which is OS-dependent. S3 keys always use "/" as a
separator regardless of host OS, but on Windows filepath.Dir/Clean
normalizes to "\" as the separator. Since the loop's only unconditional
exit check is `parent == "/"`, once repeated filepath.Dir calls converge
to the fixed point "\", the check never fires, none of the other break
conditions match either (they compare against prev, which still uses
"/"), and the loop runs forever, endlessly appending to paths. This
matches a reported Windows crash where a heap profile attributed
essentially all of a ~28 GB allocation spike to this append call during
a PurgeUploads walk of a ~2 GB bucket.

Approach:
Swap the path/filepath import for path and use path.Dir, which is pure
string manipulation with no OS-specific behavior, matching how S3 keys
actually behave everywhere. This is a no-op change on Unix-like
platforms, where filepath.Dir already behaves identically to path.Dir
for forward-slash-only input. A pagination test added earlier
(registry/storage/catalog_pagination_test.go's directoryDiffLocal, a
hand-written mirror of this function) already independently used
path.Dir, corroborating that this is the correct choice.

Validation:
Ran on macOS/arm64:
  go build ./registry/storage/driver/s3-aws/...
  go test ./registry/storage/driver/s3-aws/... -run 'TestDirectoryDiff|TestIsSubpath' -v
  golangci-lint run ./registry/storage/driver/s3-aws/...
All pass, including a new TestDirectoryDiff covering the function's
documented examples. Note: macOS and Linux cannot reproduce the
Windows-specific divergence that causes the reported crash, because
their path separator is already "/", so filepath.Dir and path.Dir are
behaviorally identical there and the added/existing tests pass
identically before and after this change on this platform. Correctness
for Windows rests on documented, deterministic Go stdlib semantics
(path/filepath is OS-dependent; path is not) rather than an executed
repro of the crash, and was independently re-derived by reading the Go
stdlib source (internal/filepathlite and path_windows.go) rather than
taken on faith. The live S3 integration tests in this package
(TestWalk, TestEmptyRootList, etc.) require AWS/S3 credentials and were
not run.

Report: https://github.com/distribution/distribution/issues/4949
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: claude-sonnet-5 (via Claude Code)
2026-09-21 07:52:04 -07:00
Milos Gajdos 5b354e6fda Fix 500s from malformed manifest digests and out-of-order final chunk PUTs (#4945) 2026-08-31 12:51:49 -07:00
Tianon GraviandTianon Gravi 5241fa3bbb Fix 500s from malformed manifest digests and out-of-order final chunk PUTs
`registry/handlers/blobupload.go`: `PutBlobUploadComplete` never validated `Content-Range` contiguity on the final chunked-upload PUT, so an out-of-order last chunk fell through to digest validation and returned 400 instead of the spec-mandated 416.  d7a2b14489 ("add content range handling in patch blob") added this contiguity check to `PatchBlobData` but never extended it to the PUT completion path, so this reuses the same check there.

`registry/handlers/manifests.go`: `manifestDispatcher` silently treated any reference containing `:` that failed `digest.Parse` as a tag name, so a malformed digest reference (such as `sha256:baddigeststring`) reached the tag-based storage path and produced a 500 from an invalid filesystem path instead of a clean 400.  Reject it as `ErrorCodeDigestInvalid` before the tag fallback instead.

Verified against the OCI distribution-spec conformance suite -- the three previously-failing tests (chunked out-of-order + put chunk, invalid-digest-format manifest-put, invalid-digest-format manifest-get) now pass; 779 pass / 6 skip / 4 disabled / 0 fail out of 789 total.

Assisted-By: "claude my eyes right out"
Co-Authored-By: Tianon Gravi <admwiggin@gmail.com>
Signed-off-by: Tianon Gravi <tianon.gravi@docker.com>
2026-08-28 17:47:57 -07:00
Dean Chen 21e0c31653 test: use ephemeral ports for registry listen tests
Hardcoded :5000/:5001 collide with a local dev registry and make
TestGracefulShutdown / cipher-suite tests talk to the wrong process.
Also fix unbuffered nil errchan in the TLS tests.

Fixes: #4928

Signed-off-by: Dean Chen <862469039@qq.com>
2026-08-28 02:38:19 +05:00
Sebastiaan van Stijn ea86c567e7 configuration: migrate to go.yaml.in/yaml/v2
use the currently maintained version of the YAML module to reduce
indirect dependencies.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-26 17:20:07 +02:00
Joonas Bergius c36fab07fc fix(tags): return empty array after final pagination marker
Signed-off-by: Joonas Bergius <joonas@defenseunicorns.com>
2026-08-10 11:32:01 -05:00
Milos Gajdos 5337dd7eba fix(storage): don't drop catalog repos that prefix another name (#4904) 2026-07-29 08:19:00 -07:00
darshanime 5077b0741a rename errcodeErrorsFor -> toErrcodeErrors
Signed-off-by: darshanime <darshan@argonaut.dev>
2026-07-20 22:28:43 +05:30
darshanime c6903beb8e pass thru upstream error code
Signed-off-by: darshanime <darshan@argonaut.dev>
2026-07-20 17:05:58 +05:30
Milos Gajdos 39d5bf7777 fix(security): fix CVE-2026-46595 by bumping golang.org/x/crypto (#4912) 2026-07-18 09:35:55 -07:00
Milos Gajdos 0ae90d59eb fix: check backend directly in blobStore.Put to avoid stale cache (#4868) 2026-07-18 09:33:09 -07:00
Milos Gajdos 1c159f24f2 fix(security): fix CVE-2026-46595 by bumping golang.org/x/crypto
We also address deprecated package that was breaking lint

Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-07-18 09:22:22 -07:00
Perfloop Agent 5a9d2d17e9 registry/storage: avoid duplicate SHA-256 in canonical full-fallback validation
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
2026-07-13 23:17:54 +00:00
Dustin Kaiser 77e0351964 fix(storage): don't drop catalog repos that prefix another name
Reported in #4903: with the S3 driver, paging /v2/_catalog silently skips a
repository whose name is a prefix of another's.

The S3 Walk lists keys in byte order (where '/' is 0x2F), but the catalog
filter in handleRepository compared the page cursor with lessPath, which
sorts '/' first. For a prefix pair like ".../math" / ".../math-voila" (next
byte '-' is 0x2D, below '/') the orders disagree, so a page ending on the
longer name drops the shorter one on every later page.

The driver already positions the walk after `last` via the start-after hint,
so the extra lessPath check is what breaks pagination. Drop it and only
de-duplicate the boundary repo. Each driver then stays self-consistent: byte
order for S3, component-wise for the filesystem/inmemory WalkFallback.

S3-only (WalkFallback trims the hint per level). Regressed in 3.0.0
(cf81f67a, e22f7cbc); 2.8.x is fine. Adds a regression test replaying the S3
byte-order Walk that fails before and passes after.

Fixes #4903

Signed-off-by: Dustin Kaiser <8209087+mrnicegyu11@users.noreply.github.com>
2026-06-29 17:01:30 +02:00
Milos Gajdos d1dbbb196c fix(s3): tags/list drops tags that are a lexical prefix of another tag
The S3 driver's Walk used a bare strings.HasPrefix to decide whether a
walkInfo was under the last ErrSkipDir directory. Without a trailing
"/" on the parent, a sibling whose name starts with the skipped
directory's name (e.g. "0.1.20" after "0.1.2") falsely matched and was
skipped, so its tag dir was never emitted to handleTag and the tag was
omitted from /v2/<name>/tags/list.

Replace the prefix check with isSubpath, which appends "/" to the
parent so only true descendants match. Add a unit test pinning the
sibling-with-lexical-prefix case.

The bug is S3-only because the filesystem and inmemory drivers list
directories recursively via WalkFallback and don't use this skip
mechanism. It surfaced in 3.1.0 when the tags handler switched from
tagService.All() (driver List with delimiter) to tagService.List()
(driver Walk) for pagination support.

Fixes #4891

Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-06-14 17:22:48 -07:00
luochenhui1andClaude Opus 4.6 2bdf7895e9 fix: check backend directly in blobStore.Put to avoid stale cache
Co-Authored-By: Claude Opus 4.6 <noreply@anthropicxxxxxx.com>
Signed-off-by: luochenhui1 <luochenhui1@kingsoft.com>
2026-06-10 20:54:38 +08:00
Baptiste Girard-Carrabin 927250bcaf azure: fix race condition in PutContent causing duplicate append writes
Write PutContent objects as block blobs so Azure retries overwrite small objects instead of appending duplicate link contents. Delete legacy append-blob objects before uploading because Azure cannot replace an AppendBlob with a BlockBlob in place. Preserve zero-byte append compatibility and add Azure regression coverage for link-style content, legacy migration, and concurrent same-path writes.

Signed-off-by: Baptiste Girard-Carrabin <baptiste.girardcarrabin@datadoghq.com>
2026-06-05 18:12:03 +02:00
Milos Gajdos 915084f4bc feat(s3): parse and strictly validate redirectendpoint on driver initialization (#4870) 2026-05-31 12:31:37 -07:00
darsh 2af40858be fix(s3): add redirectendpoint validation and fix doc formatting
Signed-off-by: darsh <darshanravichandran07@gmail.com>
2026-05-31 12:08:57 +05:30
Milos Gajdos 22c97cc513 fix: report error in DeleteManifest when Delete returns ErrDigestUnsupported (#4802) 2026-05-28 07:29:39 -07:00
Milos Gajdos 25b2d1dc7f fix(storage): Fix blob type migration in the azure driver (#4871) 2026-05-28 07:29:34 -07:00
Milos Gajdos 0d5789588f chore: smol go fix changes
This PR changes the code based on the `go fix` suggestions
to modernize the codebase and keep up with the latest Go features.

Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-05-24 15:56:02 -07:00
David Marby 5c4717ced7 Fix blob type migration in the azure driver
Currently, the Azure driver always creates blobs as an AppendBlob, but in previous versions, it used to create them as BlockBlobs.
There is migration logic to handle this, but it's currently inversed, so if the blob exists as a BlockBlob, we don't delete it, and get an error back from Azure:
```
RESPONSE 409: 409 The blob type is invalid for this operation.
ERROR CODE: InvalidBlobType
```

Since the check is inversed, this also means that any operation against AppendBlobs does an extra delete -> create for no reason.

Signed-off-by: David Marby <david@dmarby.se>
2026-05-20 23:48:23 +02:00
Milos Gajdos e09ff10155 Merge commit from fork
fix: prevent tag deletion when `storage.delete.enabled` is false
2026-05-01 07:27:34 -07:00
Joonas Bergius 8baf3e0826 fix: prevent tag deletion when storage.delete.enabled is false
Signed-off-by: Joonas Bergius <joonas@defenseunicorns.com>
2026-04-25 17:42:36 -05:00
SAY-5 72c88bc960 fix(storage): bounds-check the file basename in PurgeUploads Walk callback
PurgeUploads' Walk callback split the visited path with path.Split and
indexed file[0] immediately. path.Split returns an empty basename for
paths that end in a trailing slash - in practice this happens when an
S3 driver surfaces a bare directory (common prefix) with an empty
Key. Indexing a zero-length string then panics with
'index out of range [0] with length 0' and takes down the whole
PurgeUploads goroutine (#4713).

Guard the length before touching file[0] so a trailing-slash /
empty-basename entry is simply skipped as 'not a reserved directory',
which matches what the branch was trying to do anyway. Runtime
behaviour for every non-empty entry is unchanged.

Closes #4713

Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
2026-04-21 10:56:45 -07:00
Milos Gajdos 1b5e226b6f feat(s3): add express zone one support to S3 driver (#4858) 2026-04-20 07:47:28 -07:00
Milos Gajdos 835c1c581f feat(s3): add express zone one support to S3 driver
Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-04-18 17:36:52 -07:00
Milos Gajdos c6f552e5db fix(proxy): clamp oversized n query param instead of returning 400
PR #4353 made MaxTags (default 1000) a hard ceiling on the `n` query
parameter — anything larger and the handler returns 400
PAGINATION_NUMBER_INVALID before the request ever reaches storage or
the proxy tag service. That broke clients like Renovate which use
n=10000 against pull-through caches. #4846 fixed a related 500 in
proxy mode but not this 400, so users reported the regression still
persisted.

The OCI distribution-spec describes pagination differently: a server
MAY return fewer than `n` results "when the total number of tags
attached to the repository is less than <int> or a Link header is
provided" — otherwise it MUST include `<int>` results. In other
words, the right answer for "client asked for more than we'll serve"
is `maxtags` results plus a Link header, not a rejection.
PAGINATION_NUMBER_INVALID isn't among the 14 error codes the spec
defines, either.

Drop the oversized-n rejection and clamp to MaxTags instead; the
existing Link-header path already handles continuation correctly.
Malformed (non-integer) and negative `n` values keep returning 400,
since the spec defines `n` as a non-negative integer and those
requests are genuinely invalid.

Verified end-to-end against registry-1.docker.io in proxy mode:
n=10000 now returns the tag list (or a clamped page with Link)
instead of 400. Also restores pre-3.1.0 behavior for Renovate-style
clients without needing proxy-specific logic.

Spec reference:
https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-tags

Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
2026-04-18 16:53:53 -07:00
Milos Gajdos ca7d4de0ee fix(proxy): fix tag list endpoint in proxy mode (#4846) 2026-04-16 14:17:56 -07:00
chuanshanjida 0679fc13c5 refactor: use slices.Backward to simplify the code
Signed-off-by: chuanshanjida <chuanshanjida@outlook.com>
2026-04-12 16:01:45 +08:00
njucjc 60de6e3443 fix(proxy): fix tag list endpoint in proxy mode
Signed-off-by: njucjc <njucjc@alibaba-inc.com>
Signed-off-by: chenjinci.cjc <chenjinci.cjc@alibaba-inc.com>
2026-04-09 23:30:51 +08:00
Milos Gajdos 078b0783f2 Merge commit from fork
fix redis repo-scoped blob descriptor revocation
2026-04-05 17:08:50 -07:00