fix(auth): use X-Forwarded-Host for token realm URL
- Use X-Forwarded-Host header for Bearer realm URL when behind proxy - Prioritize X-Forwarded-Proto header for scheme detection - Preserve original X-Forwarded-Proto through proxy chain Fixes Docker push failures through external_routes when behind Cloudflare or other reverse proxies that terminate TLS.
- commit
- e0a75b4eb953daa6f259fe3ef96b7c5de7388f37
- author
- brice · · 2026-01-16 04:38 UTC
- parent
- c4ce6e0
- signature
- unsigned
2 files · +10 -4
M internal/adapters/in/http/middleware/auth.go +4 -4
| @@ -387,12 +387,12 @@ func sendUnauthorized(w http.ResponseWriter, authType domain.AuthType, host stri | |||
| 387 | 387 | if realmHost == "" { | |
| 388 | 388 | realmHost = host | |
| 389 | 389 | } | |
| 390 | - | // Detect scheme from request (TLS or X-Forwarded-Proto header) | |
| 390 | + | // Detect scheme: prioritize X-Forwarded-Proto (set by reverse proxies like Cloudflare) | |
| 391 | 391 | scheme := "http" | |
| 392 | - | if r.TLS != nil { | |
| 393 | - | scheme = "https" | |
| 394 | - | } else if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { | |
| 392 | + | if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { | |
| 395 | 393 | scheme = proto | |
| 394 | + | } else if r.TLS != nil { | |
| 395 | + | scheme = "https" | |
| 396 | 396 | } | |
| 397 | 397 | realm := scheme + "://" + realmHost + "/v2/token" | |
| 398 | 398 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | |
| @@ -387,12 +387,12 @@ func sendUnauthorized(w http.ResponseWriter, authType domain.AuthType, host stri | |||||
| 387 | if realmHost == "" { | 387 | if realmHost == "" { | ||
| 388 | realmHost = host | 388 | realmHost = host | ||
| 389 | } | 389 | } | ||
| 390 | - | // Detect scheme from request (TLS or X-Forwarded-Proto header) | 390 | + | // Detect scheme: prioritize X-Forwarded-Proto (set by reverse proxies like Cloudflare) |
| 391 | scheme := "http" | 391 | scheme := "http" | ||
| 392 | - | if r.TLS != nil { | 392 | + | if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { |
| 393 | - | scheme = "https" | |||
| 394 | - | } else if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { | |||
| 395 | scheme = proto | 393 | scheme = proto | ||
| 394 | + | } else if r.TLS != nil { | |||
| 395 | + | scheme = "https" | |||
| 396 | } | 396 | } | ||
| 397 | realm := scheme + "://" + realmHost + "/v2/token" | 397 | realm := scheme + "://" + realmHost + "/v2/token" | ||
| 398 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | 398 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | ||
M internal/usecase/proxy/service.go +6 -0
| @@ -277,7 +277,13 @@ func newReverseProxy(targetURL *url.URL, errorHandler func(http.ResponseWriter, | |||
| 277 | 277 | return &httputil.ReverseProxy{ | |
| 278 | 278 | Rewrite: func(pr *httputil.ProxyRequest) { | |
| 279 | 279 | pr.SetURL(targetURL) | |
| 280 | + | // Preserve original X-Forwarded-Proto from upstream proxy (e.g., Cloudflare) | |
| 281 | + | // before SetXForwarded overwrites it based on local connection | |
| 282 | + | origProto := pr.In.Header.Get("X-Forwarded-Proto") | |
| 280 | 283 | pr.SetXForwarded() | |
| 284 | + | if origProto != "" { | |
| 285 | + | pr.Out.Header.Set("X-Forwarded-Proto", origProto) | |
| 286 | + | } | |
| 281 | 287 | pr.Out.Host = targetURL.Host | |
| 282 | 288 | }, | |
| 283 | 289 | Transport: proxyTransport, | |
| @@ -277,7 +277,13 @@ func newReverseProxy(targetURL *url.URL, errorHandler func(http.ResponseWriter, | |||||
| 277 | return &httputil.ReverseProxy{ | 277 | return &httputil.ReverseProxy{ | ||
| 278 | Rewrite: func(pr *httputil.ProxyRequest) { | 278 | Rewrite: func(pr *httputil.ProxyRequest) { | ||
| 279 | pr.SetURL(targetURL) | 279 | pr.SetURL(targetURL) | ||
| 280 | + | // Preserve original X-Forwarded-Proto from upstream proxy (e.g., Cloudflare) | |||
| 281 | + | // before SetXForwarded overwrites it based on local connection | |||
| 282 | + | origProto := pr.In.Header.Get("X-Forwarded-Proto") | |||
| 280 | pr.SetXForwarded() | 283 | pr.SetXForwarded() | ||
| 284 | + | if origProto != "" { | |||
| 285 | + | pr.Out.Header.Set("X-Forwarded-Proto", origProto) | |||
| 286 | + | } | |||
| 281 | pr.Out.Host = targetURL.Host | 287 | pr.Out.Host = targetURL.Host | ||
| 282 | }, | 288 | }, | ||
| 283 | Transport: proxyTransport, | 289 | Transport: proxyTransport, | ||