fix(auth): add workaround for Docker client auth with mixed anonymous policies (#3868)

* fix(auth): add workaround for Docker client auth with mixed anonymous policies

Docker client fails to authenticate to protected repositories when basic auth
(htpasswd/LDAP) is used with mixed access policies (some repos anonymous,
some requiring auth). This happens because Docker determines whether to send
credentials based on the /v2/ response - if it returns 200, Docker assumes
no auth is needed anywhere.

Add `forceDockerClientAuth` config option that, when enabled, forces 401 on
/v2/ for Docker clients, triggering Docker's authentication flow.

This workaround only affects Docker clients (detected via User-Agent).
Podman and other OCI-compliant clients are unaffected.

Refs: https://github.com/opencontainers/wg-auth/blob/main/docs/implementations/moby.md

Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>

* feat: remove ForceDockerClientAuth flag and use only authz policies to determine the docker specific behavior

Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>

---------

Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
This commit is contained in:
Andrei Aaron
2026-04-17 09:10:02 +03:00
committed by GitHub
parent d443346196
commit 7ceb01dcff
3 changed files with 137 additions and 1 deletions
+12 -1
View File
@@ -415,6 +415,8 @@ 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")
// Get auth config safely
authConfig := ctlr.Config.CopyAuthConfig()
@@ -466,7 +468,16 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
// If no credentials provided - check for anonymous / mgmt requests
case allowAnonymous || isMgmtRequested:
authenticated = true
// Docker workaround: force 401 on /v2/ when anonymous policies coexist with
// authenticated-only policies. Otherwise Docker treats 200 on /v2/ as "no auth"
// and will not send stored credentials for protected repositories.
// See: https://github.com/opencontainers/wg-auth/blob/main/docs/implementations/moby.md
hasMixedPolicy := accessControlConfig.HasMixedAnonymousAndAuthenticatedPolicies()
if isDockerClient && isV2Requested && hasMixedPolicy && authConfig.CanAuthenticateWithBasicCredentials() {
authenticated = false
} else {
authenticated = true
}
}
// If error occurred during authn process - return 500 error