From e8946e59ca042891162cc07a970f4bda270e83ec Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 27 Feb 2026 13:41:45 -0800 Subject: [PATCH] fix(s3api): correctly extract host header port in extractHostHeader (#8464) * Prevent concurrent maintenance tasks per volume * fix panic * fix(s3api): correctly extract host header port when X-Forwarded-Port is present * test(s3api): add test cases for misreported X-Forwarded-Port --- weed/s3api/auth_signature_v4.go | 25 ++++++++++++++----------- weed/s3api/auth_signature_v4_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index 716a3a757..c52cb2dac 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -828,29 +828,32 @@ func extractHostHeader(r *http.Request, externalHost string) string { } else { host = strings.TrimSpace(forwardedHost) } - // Baseline port from forwarded port if available - if forwardedPort != "" { - port = forwardedPort - } + // If the host itself contains a port, it should take precedence if h, p, err := net.SplitHostPort(host); err == nil { host = h port = p + } else { + // If X-Forwarded-Host has no port, try to get port from r.Host if hostnames match + if rh, rp, err := net.SplitHostPort(r.Host); err == nil && rh == host { + port = rp + } else if forwardedPort != "" { + port = forwardedPort + } } } else { host = r.Host if host == "" { host = r.URL.Host } - // Also apply X-Forwarded-Port in the fallback path - if forwardedPort != "" { - if h, _, err := net.SplitHostPort(host); err == nil { - host = h - } - port = forwardedPort - } else if h, p, err := net.SplitHostPort(host); err == nil { + + // If the host already contains a port, use it. + // Otherwise, if X-Forwarded-Port is set, use it. + if h, p, err := net.SplitHostPort(host); err == nil { host = h port = p + } else if forwardedPort != "" { + port = forwardedPort } } diff --git a/weed/s3api/auth_signature_v4_test.go b/weed/s3api/auth_signature_v4_test.go index 2d8fd0a6e..0bc704faf 100644 --- a/weed/s3api/auth_signature_v4_test.go +++ b/weed/s3api/auth_signature_v4_test.go @@ -364,6 +364,31 @@ func TestExtractHostHeader(t *testing.T) { externalHost: "[::1]:9000", expected: "[::1]:9000", }, + // Bug fix: X-Forwarded-Port should not override more specific ports in other headers + { + name: "User reported case: X-Forwarded-Port misreports 443 but Host has 30007", + hostHeader: "storage-stgops.mt.mtnet:30007", + forwardedHost: "storage-stgops.mt.mtnet", + forwardedPort: "443", + forwardedProto: "https", + expected: "storage-stgops.mt.mtnet:30007", + }, + { + name: "X-Forwarded-Host already contains correct port, ignore misaligned X-Forwarded-Port", + hostHeader: "backend:8333", + forwardedHost: "storage-stgops.mt.mtnet:30007", + forwardedPort: "443", + forwardedProto: "https", + expected: "storage-stgops.mt.mtnet:30007", + }, + { + name: "X-Forwarded-Host has no port, match r.Host hostname and take its port", + hostHeader: "example.com:8080", + forwardedHost: "example.com", + forwardedPort: "80", + forwardedProto: "http", + expected: "example.com:8080", + }, } for _, tt := range tests {