mirror of
https://github.com/project-zot/zot.git
synced 2026-06-15 11:37:56 +08:00
fix(api): recognize Docker Compose/Buildx User-Agent in v2 challenge workaround (#3992)
Docker Compose and Buildx proxy through the Docker daemon, which sends a User-Agent starting with "docker/<version>" rather than the "Docker-Client/<version>" string sent by direct Docker CLI pulls. This caused compose/buildx pulls to skip the 401 challenge on registries with mixed anonymous/authenticated access policies, resulting in 'unauthorized' errors. Add strings.HasPrefix(ua, "docker/") alongside the existing Docker-Client check so daemon-proxied requests from any upstream tool (compose, buildx, etc.) are handled correctly. Fixes #3991
This commit is contained in:
+6
-1
@@ -416,7 +416,12 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
|
||||
|
||||
isMgmtRequested := request.RequestURI == constants.FullMgmt
|
||||
isV2Requested := strings.TrimSuffix(request.URL.Path, "/") == constants.RoutePrefix
|
||||
isDockerClient := strings.Contains(request.Header.Get("User-Agent"), "Docker-Client")
|
||||
// Match Docker daemon-proxied requests regardless of the upstream client tool.
|
||||
// The Docker daemon always prefixes its UA with "docker/<version>" when proxying,
|
||||
// while the upstream tool (docker CLI, compose, buildx, etc.) appears inside
|
||||
// "UpstreamClient(...)". Direct Docker CLI requests use "Docker-Client/...".
|
||||
ua := request.Header.Get("User-Agent")
|
||||
isDockerClient := strings.Contains(ua, "Docker-Client") || strings.HasPrefix(ua, "docker/")
|
||||
|
||||
// Get auth config safely
|
||||
authConfig := ctlr.Config.CopyAuthConfig()
|
||||
|
||||
@@ -14261,6 +14261,36 @@ func TestDockerClientV2ChallengeWorkaround(t *testing.T) {
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
// Docker Compose client without credentials should get 401
|
||||
// (daemon-proxied with compose upstream client, UA starts with "docker/")
|
||||
composeUA := "docker/29.4.0 go/go1.24.2 UpstreamClient(compose/v5.1.2)"
|
||||
resp, err = resty.R().
|
||||
SetHeader("User-Agent", composeUA).
|
||||
Get(baseURL + "/v2/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
||||
So(resp.Header().Get("WWW-Authenticate"), ShouldContainSubstring, "Basic realm=")
|
||||
|
||||
// Docker Compose client with valid credentials should get 200
|
||||
resp, err = resty.R().
|
||||
SetHeader("User-Agent", composeUA).
|
||||
SetBasicAuth(htpasswdUsername, htpasswdPassword).
|
||||
Get(baseURL + "/v2/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
// Docker Buildx client without credentials should get 401
|
||||
// (daemon-proxied with buildx upstream client)
|
||||
resp, err = resty.R().
|
||||
SetHeader("User-Agent", "docker/29.4.0 go/go1.24.2 UpstreamClient(buildx/v0.21.2)").
|
||||
Get(baseURL + "/v2/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
||||
So(resp.Header().Get("WWW-Authenticate"), ShouldContainSubstring, "Basic realm=")
|
||||
|
||||
// Podman client without credentials should get 200 (unaffected by workaround)
|
||||
resp, err = resty.R().
|
||||
SetHeader("User-Agent", "containers/5.33.0 (github.com/containers/image)").
|
||||
@@ -14309,6 +14339,14 @@ func TestDockerClientV2ChallengeWorkaround(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
// Docker Compose client without credentials should get 200 (no mixed policies)
|
||||
resp, err = resty.R().
|
||||
SetHeader("User-Agent", "docker/29.4.0 go/go1.24.2 UpstreamClient(compose/v5.1.2)").
|
||||
Get(baseURL + "/v2/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user