From 2dd8b29aed18fcf42535b1ba52c4dbf86c9bf605 Mon Sep 17 00:00:00 2001 From: John Koch <129888757+outrider-jkoch@users.noreply.github.com> Date: Sat, 23 May 2026 23:39:16 -0600 Subject: [PATCH] fix: prefer client bearer token over injected basic auth Registries like quay.io use a two-phase auth flow: the Docker client first exchanges basic credentials for a bearer token, then uses the bearer token for manifest/blob requests. The old single-map approach unconditionally replaced the client's authorization header with "Basic $dockerAuth" whenever credentials were configured, overwriting the bearer token and causing 401s on content fetches. The new two-map approach computes what we would inject ($injectedAuth) but only uses it when the client has sent no authorization header at all. If the client has already negotiated auth (bearer or basic), that header is passed through unchanged. Addresses the upstream @TODO comment regarding gcr.io and quay.io auth. --- nginx.conf | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/nginx.conf b/nginx.conf index 4bd6f90..b0c740b 100644 --- a/nginx.conf +++ b/nginx.conf @@ -100,15 +100,21 @@ http { default ""; } - # @TODO: actually for auth.docker.io, if we want to support multiple authentications, we'll need to decide - # @TODO: based not only on the hostname, but also URI (/token) and query string (?scope) - # @TODO: I wonder if this would help gcr.io and quay.io with authentication also.... - - map $dockerAuth $finalAuth { - "" "$http_authorization"; # if empty, keep the original passed-in from the docker client. - default "Basic $dockerAuth"; # if not empty, add the Basic preamble to the auth + # First, build what we would inject from configured registry credentials (empty if none configured). + map $dockerAuth $injectedAuth { + "" ""; + default "Basic $dockerAuth"; } + # Then prefer the Docker client's own auth header (bearer or basic it negotiated itself) + # and only fall back to injecting our basic credentials when the client has nothing. + # This fixes registries like quay.io that use bearer tokens for blob/manifest endpoints: + # without this, the proxy would overwrite the client's bearer token with basic credentials, + # causing 401s on the actual content fetch after authentication succeeded. + map $http_authorization $finalAuth { + ~\S $http_authorization; # client has auth (bearer or basic) — pass it through + default $injectedAuth; # client has nothing — inject our basic credentials + } # Map to decide which hosts get directed to the caching portion. # This is automatically generated from the list of cached registries, plus a few fixed hosts