Browse Source

Fix IPv6 host header formatting to match AWS SDK behavior

Follow-up to PR #7403

When a default port (80 for HTTP, 443 for HTTPS) is stripped from an
IPv6 address, the square brackets should also be removed to match AWS
SDK behavior for S3 signature calculation.

Reference: https://github.com/aws/aws-sdk-go-v2/blob/main/aws/signer/internal/v4/host.go
The AWS SDK's stripPort function explicitly removes brackets when
returning an IPv6 address without a port.

Changes:
- Update extractHostHeader to strip brackets from IPv6 addresses when
  no port or default port is used
- Update test expectations to match AWS SDK behavior
- Add detailed comments explaining the AWS SDK compatibility requirement

This ensures S3 signature validation works correctly with IPv6 addresses
behind reverse proxies, matching AWS S3 canonical request format.

Fixes the issue raised in PR #7403 comment:
https://github.com/seaweedfs/seaweedfs/pull/7403#issuecomment-3471105438
pull/7414/head
Chris Lu 5 days ago
parent
commit
836ab41d23
  1. 11
      weed/s3api/auth_signature_v4.go
  2. 12
      weed/s3api/auth_signature_v4_test.go
  3. 12
      weed/s3api/auto_signature_v4_test.go

11
weed/s3api/auth_signature_v4.go

@ -648,9 +648,14 @@ func extractHostHeader(r *http.Request) string {
return net.JoinHostPort(host, port)
}
// No port or default port, just ensure host is correctly formatted (IPv6 brackets).
if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
return "[" + host + "]"
// No port or default port was stripped. According to AWS SDK behavior (aws-sdk-go-v2),
// when a default port is removed from an IPv6 address, the brackets should also be removed.
// This matches AWS S3 signature calculation requirements.
// Reference: https://github.com/aws/aws-sdk-go-v2/blob/main/aws/signer/internal/v4/host.go
// The stripPort function returns IPv6 without brackets when port is stripped.
if strings.Contains(host, ":") {
// This is an IPv6 address. Strip brackets to match AWS SDK behavior.
return strings.Trim(host, "[]")
}
return host
}

12
weed/s3api/auth_signature_v4_test.go

@ -192,20 +192,20 @@ func TestExtractHostHeader(t *testing.T) {
expected: "[::1]:8080",
},
{
name: "IPv6 address without brackets and standard port, should return bracketed IPv6",
name: "IPv6 address without brackets and standard port, should strip brackets per AWS SDK",
hostHeader: "backend:8333",
forwardedHost: "::1",
forwardedPort: "80",
forwardedProto: "http",
expected: "[::1]",
expected: "::1",
},
{
name: "IPv6 address without brackets and standard HTTPS port, should return bracketed IPv6",
name: "IPv6 address without brackets and standard HTTPS port, should strip brackets per AWS SDK",
hostHeader: "backend:8333",
forwardedHost: "2001:db8::1",
forwardedPort: "443",
forwardedProto: "https",
expected: "[2001:db8::1]",
expected: "2001:db8::1",
},
{
name: "IPv6 address with brackets but no port, should add port",
@ -216,12 +216,12 @@ func TestExtractHostHeader(t *testing.T) {
expected: "[2001:db8::1]:8080",
},
{
name: "IPv6 full address with brackets and default port (should strip port)",
name: "IPv6 full address with brackets and default port (should strip port and brackets)",
hostHeader: "backend:8333",
forwardedHost: "[2001:db8:85a3::8a2e:370:7334]:443",
forwardedPort: "443",
forwardedProto: "https",
expected: "[2001:db8:85a3::8a2e:370:7334]",
expected: "2001:db8:85a3::8a2e:370:7334",
},
{
name: "IPv4-mapped IPv6 address without brackets, should add brackets with port",

12
weed/s3api/auto_signature_v4_test.go

@ -451,25 +451,25 @@ func TestSignatureV4WithoutProxy(t *testing.T) {
name: "IPv6 HTTP with standard port",
host: "[::1]:80",
proto: "http",
expectedHost: "[::1]",
expectedHost: "::1",
},
{
name: "IPv6 HTTPS with standard port",
host: "[::1]:443",
proto: "https",
expectedHost: "[::1]",
expectedHost: "::1",
},
{
name: "IPv6 HTTP without port",
host: "::1",
proto: "http",
expectedHost: "[::1]",
expectedHost: "::1",
},
{
name: "IPv6 HTTPS without port",
host: "::1",
proto: "https",
expectedHost: "[::1]",
expectedHost: "::1",
},
}
@ -608,7 +608,7 @@ func TestSignatureV4WithForwardedPort(t *testing.T) {
forwardedHost: "[::1]:443",
forwardedPort: "443",
forwardedProto: "https",
expectedHost: "[::1]",
expectedHost: "::1",
},
{
name: "IPv6 X-Forwarded-Host with standard http port already included (Traefik/HAProxy style)",
@ -616,7 +616,7 @@ func TestSignatureV4WithForwardedPort(t *testing.T) {
forwardedHost: "[::1]:80",
forwardedPort: "80",
forwardedProto: "http",
expectedHost: "[::1]",
expectedHost: "::1",
},
{
name: "IPv6 with port in brackets",

Loading…
Cancel
Save