s3: fix signature mismatch with non-standard ports and capitalized host (#8386)
* s3: fix signature mismatch with non-standard ports and capitalized host - ensure host header extraction is case-insensitive in SignedHeaders - prioritize non-standard ports in X-Forwarded-Host over default ports in X-Forwarded-Port - add regression tests for both scenarios fixes https://github.com/seaweedfs/seaweedfs/issues/8382 * simplify
This commit is contained in:
@@ -769,7 +769,7 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header,
|
||||
extractedSignedHeaders := make(http.Header)
|
||||
for _, header := range signedHeaders {
|
||||
// `host` is not a case-sensitive header, unlike other headers such as `x-amz-date`.
|
||||
if header == "host" {
|
||||
if strings.ToLower(header) == "host" {
|
||||
// Get host value.
|
||||
hostHeaderValue := extractHostHeader(r)
|
||||
extractedSignedHeaders[header] = []string{hostHeaderValue}
|
||||
@@ -815,12 +815,12 @@ func extractHostHeader(r *http.Request) string {
|
||||
} else {
|
||||
host = strings.TrimSpace(forwardedHost)
|
||||
}
|
||||
port = forwardedPort
|
||||
if h, p, err := net.SplitHostPort(host); err == nil {
|
||||
host = h
|
||||
if port == "" {
|
||||
port = p
|
||||
}
|
||||
port = p
|
||||
}
|
||||
if forwardedPort != "" && isDefaultPort(scheme, port) {
|
||||
port = forwardedPort
|
||||
}
|
||||
} else {
|
||||
host = r.Host
|
||||
|
||||
@@ -292,6 +292,47 @@ func TestExtractHostHeader(t *testing.T) {
|
||||
forwardedProto: "http",
|
||||
expected: "[::ffff:127.0.0.1]:8080",
|
||||
},
|
||||
{
|
||||
name: "Simple port 442",
|
||||
hostHeader: "bucket.domain.com:442",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
{
|
||||
name: "Port 442 with X-Forwarded-Host",
|
||||
hostHeader: "backend:8333",
|
||||
forwardedHost: "bucket.domain.com:442",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
{
|
||||
name: "Port 442 with X-Forwarded-Port",
|
||||
hostHeader: "backend:8333",
|
||||
forwardedHost: "bucket.domain.com",
|
||||
forwardedPort: "442",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
{
|
||||
name: "HTTPS with port 442 (should NOT strip)",
|
||||
hostHeader: "bucket.domain.com:442",
|
||||
forwardedProto: "https",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
{
|
||||
name: "X-Forwarded-Host with multiple hosts (including port)",
|
||||
forwardedHost: "bucket.domain.com:442, internal.proxy",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
{
|
||||
name: "IPv6 with port",
|
||||
hostHeader: "[2001:db8::1]:442",
|
||||
expected: "[2001:db8::1]:442",
|
||||
},
|
||||
{
|
||||
name: "X-Forwarded-Host with port 442, but X-Forwarded-Port is 80 (should PREFER 442)",
|
||||
forwardedHost: "bucket.domain.com:442",
|
||||
forwardedPort: "80",
|
||||
forwardedProto: "http",
|
||||
expected: "bucket.domain.com:442",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -322,3 +363,40 @@ func TestExtractHostHeader(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractSignedHeadersCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
host string
|
||||
signedHeads []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "lowercase host",
|
||||
host: "bucket.domain.com:442",
|
||||
signedHeads: []string{"host"},
|
||||
expected: "host:bucket.domain.com:442\n",
|
||||
},
|
||||
{
|
||||
name: "uppercase Host",
|
||||
host: "bucket.domain.com:442",
|
||||
signedHeads: []string{"Host"},
|
||||
expected: "host:bucket.domain.com:442\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r, _ := http.NewRequest("GET", "http://"+tt.host+"/", nil)
|
||||
r.Host = tt.host
|
||||
extracted, errCode := extractSignedHeaders(tt.signedHeads, r)
|
||||
if errCode != s3err.ErrNone {
|
||||
t.Fatalf("extractSignedHeaders failed: %v", errCode)
|
||||
}
|
||||
actual := getCanonicalHeaders(extracted)
|
||||
if actual != tt.expected {
|
||||
t.Errorf("%s: expected %q, got %q", tt.name, tt.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user