From 7c6fcadf434999ad35bae2c6d9c12930754e27c6 Mon Sep 17 00:00:00 2001
From: Tung Lam <53996158+tunglambk@users.noreply.github.com>
Date: Sun, 11 Oct 2026 04:23:31 +0000
Subject: [PATCH] fix: set the CreateBucket location constraint from the S3
backend region
The api layer validates a client's LocationConstraint against the gateway
region, but the bucket is created in the s3 backend's own region
(--region / VGW_S3_REGION). They are separate settings, so passing the
validated client constraint through to the backend only worked when the two
regions matched. With the gateway on the default us-east-1 and the backend
on eu-west-1 the client sends no constraint at all, and the backend still
answered
IllegalLocationConstraintException: The unspecified location constraint is
incompatible for the region specific endpoint this request was sent to.
which is the error in #2490.
Set the constraint in S3Proxy.CreateBucket from the backend client's region
instead, next to the existing empty-configuration drop. A backend on
us-east-1 still gets no constraint, and the api layer's rejection of a
client constraint that disagrees with the gateway region is unchanged.
Fixes #2490
---
backend/s3proxy/s3.go | 10 ++++++++
backend/s3proxy/s3_test.go | 49 +++++++++++++++++++++++++++++++++++++-
2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/backend/s3proxy/s3.go b/backend/s3proxy/s3.go
index a6d9fdb3..3b8ec26b 100644
--- a/backend/s3proxy/s3.go
+++ b/backend/s3proxy/s3.go
@@ -204,6 +204,16 @@ func (s *S3Proxy) CreateBucket(ctx context.Context, input *s3.CreateBucketInput,
}
}
+ // the bucket is created in the backend region, which is independent of the
+ // gateway region the client's constraint was validated against; a regional
+ // S3 endpoint outside us-east-1 rejects a CreateBucket without a constraint
+ if region := s.client.Options().Region; region != "" && region != "us-east-1" {
+ if input.CreateBucketConfiguration == nil {
+ input.CreateBucketConfiguration = &types.CreateBucketConfiguration{}
+ }
+ input.CreateBucketConfiguration.LocationConstraint = types.BucketLocationConstraint(region)
+ }
+
// drop an empty CreateBucketConfiguration: strict backends (Ceph RGW) reject the empty element
if input.CreateBucketConfiguration != nil &&
len(input.CreateBucketConfiguration.Tags) == 0 &&
diff --git a/backend/s3proxy/s3_test.go b/backend/s3proxy/s3_test.go
index 5b26e9e1..31f74e28 100644
--- a/backend/s3proxy/s3_test.go
+++ b/backend/s3proxy/s3_test.go
@@ -54,10 +54,15 @@ func (c *captureHTTPClient) Do(req *http.Request) (*http.Response, error) {
}
func newCaptureProxy(t *testing.T) (*S3Proxy, *captureHTTPClient) {
+ t.Helper()
+ return newCaptureProxyWithRegion(t, "us-east-1")
+}
+
+func newCaptureProxyWithRegion(t *testing.T, region string) (*S3Proxy, *captureHTTPClient) {
t.Helper()
capture := &captureHTTPClient{}
client := s3.New(s3.Options{
- Region: "us-east-1",
+ Region: region,
BaseEndpoint: aws.String("http://backend.test"),
UsePathStyle: true,
Credentials: credentials.NewStaticCredentialsProvider("access", "secret", ""),
@@ -169,3 +174,45 @@ func TestCreateBucketKeepsBucketInfo(t *testing.T) {
t.Errorf("CreateBucket with bucket info must forward the configuration, got %q", capture.body)
}
}
+
+// The bucket is created in the backend region, not the gateway region the
+// api layer validated the client's constraint against. A regional endpoint
+// outside us-east-1 rejects a CreateBucket that carries no constraint, so the
+// backend has to add its own even when the api layer sent none.
+func TestCreateBucketSetsBackendRegionConstraint(t *testing.T) {
+ p, capture := newCaptureProxyWithRegion(t, "eu-west-1")
+
+ err := p.CreateBucket(context.Background(), &s3.CreateBucketInput{
+ Bucket: aws.String("test-bucket"),
+ CreateBucketConfiguration: &types.CreateBucketConfiguration{},
+ }, []byte{})
+ if err != nil {
+ t.Fatalf("CreateBucket: %v", err)
+ }
+ if !strings.Contains(string(capture.body), "eu-west-1") {
+ t.Errorf("CreateBucket against a regional backend must carry the backend region constraint, got %q", capture.body)
+ }
+}
+
+func TestCreateBucketKeepsTagsAndSetsBackendRegionConstraint(t *testing.T) {
+ p, capture := newCaptureProxyWithRegion(t, "eu-west-1")
+
+ err := p.CreateBucket(context.Background(), &s3.CreateBucketInput{
+ Bucket: aws.String("test-bucket"),
+ CreateBucketConfiguration: &types.CreateBucketConfiguration{
+ Tags: []types.Tag{
+ {Key: aws.String("env"), Value: aws.String("test")},
+ },
+ },
+ }, []byte{})
+ if err != nil {
+ t.Fatalf("CreateBucket: %v", err)
+ }
+ body := string(capture.body)
+ if !strings.Contains(body, "eu-west-1") {
+ t.Errorf("CreateBucket against a regional backend must carry the backend region constraint, got %q", body)
+ }
+ if !strings.Contains(body, "env") || !strings.Contains(body, "test") {
+ t.Errorf("CreateBucket must keep the requested tags, got %q", body)
+ }
+}