fix: return IllegalLocationConstraintException for a mismatched LocationConstraint

A region specific endpoint requires CreateBucket to name that same region
in `LocationConstraint`. The gateway reported a mismatch as
`InvalidLocationConstraint`, which AWS reserves for a constraint that is
not a valid location at all, and accepted a request that omitted the
constraint entirely.

Keep `InvalidLocationConstraint` for the global `us-east-1` endpoint,
which takes no constraint, and return
`IllegalLocationConstraintException` when a region specific endpoint gets
a constraint naming another region, or none.

Fixes #2246
This commit is contained in:
dualfroz committed 2026-09-13 15:08:58 +02:00
1 parent 2a5705ab02
commit 2742fc2216
3 files changed
+110 -11

No files matched your search

+38 -11
View File
@@ -546,6 +546,32 @@ func (c S3ApiController) PutBucketAcl(ctx fiber.Ctx) (*Response, error) {
}, err }, err
} }
// validateLocationConstraint checks a CreateBucket location constraint. The
// global endpoint serves defaultRegion and takes no constraint; any other
// region is a region specific endpoint and requires the constraint to name it.
func validateLocationConstraint(constraint *string, region string) error {
if region == defaultRegion {
if constraint != nil {
debuglogger.Logf("invalid location constraint: %s", *constraint)
return s3err.GetInvalidLocationConstraintErr(*constraint)
}
return nil
}
if constraint == nil {
debuglogger.Logf("missing location constraint for region %s", region)
return s3err.GetIllegalLocationConstraintErr("")
}
if *constraint != region {
debuglogger.Logf("illegal location constraint %s for region %s", *constraint, region)
return s3err.GetIllegalLocationConstraintErr(*constraint)
}
return nil
}
func (c S3ApiController) CreateBucket(ctx fiber.Ctx) (*Response, error) { func (c S3ApiController) CreateBucket(ctx fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket") bucket := ctx.Params("bucket")
acl := types.BucketCannedACL(c.getAclHeaderValue(ctx, "X-Amz-Acl")) acl := types.BucketCannedACL(c.getAclHeaderValue(ctx, "X-Amz-Acl"))
@@ -658,18 +684,19 @@ func (c S3ApiController) CreateBucket(ctx fiber.Ctx) (*Response, error) {
}, },
}, s3err.GetAPIError(s3err.ErrMalformedXML) }, s3err.GetAPIError(s3err.ErrMalformedXML)
} }
}
if body.LocationConstraint != nil { region, ok := utils.ContextKeyRegion.Get(ctx).(string)
region := utils.ContextKeyRegion.Get(ctx).(string) if !ok {
if *body.LocationConstraint != region || *body.LocationConstraint == "us-east-1" { region = defaultRegion
debuglogger.Logf("invalid location constraint: %s", *body.LocationConstraint) }
return &Response{
MetaOpts: &MetaOptions{ if err := validateLocationConstraint(body.LocationConstraint, region); err != nil {
BucketOwner: bucketOwner.Access, return &Response{
}, MetaOpts: &MetaOptions{
}, s3err.GetInvalidLocationConstraintErr(*body.LocationConstraint) BucketOwner: bucketOwner.Access,
} },
} }, err
} }
defACL := auth.ACL{ defACL := auth.ACL{
+58
View File
@@ -726,6 +726,11 @@ func TestS3ApiController_CreateBucket(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
euLocConstBody, err := xml.Marshal(s3response.CreateBucketConfiguration{
LocationConstraint: utils.GetStringPtr("eu-central-1"),
})
assert.NoError(t, err)
tests := []struct { tests := []struct {
name string name string
input testInput input testInput
@@ -812,6 +817,59 @@ func TestS3ApiController_CreateBucket(t *testing.T) {
err: s3err.GetInvalidLocationConstraintErr("us-west-1"), err: s3err.GetInvalidLocationConstraintErr("us-west-1"),
}, },
}, },
{
name: "illegal location constraint",
input: testInput{
locals: map[utils.ContextKey]any{
utils.ContextKeyAccount: adminAcc,
utils.ContextKeyRegion: "us-west-1",
},
body: euLocConstBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: adminAcc.Access},
},
err: s3err.GetIllegalLocationConstraintErr("eu-central-1"),
},
},
{
name: "missing location constraint",
input: testInput{
locals: map[utils.ContextKey]any{
utils.ContextKeyAccount: adminAcc,
utils.ContextKeyRegion: "us-west-1",
},
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: adminAcc.Access},
},
err: s3err.GetIllegalLocationConstraintErr(""),
},
},
{
name: "matching location constraint",
input: testInput{
locals: map[utils.ContextKey]any{
utils.ContextKeyAccount: adminAcc,
utils.ContextKeyRegion: "us-west-1",
},
bucket: "my-bucket",
body: invLocConstBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: adminAcc.Access,
},
Headers: map[string]*string{
"Location": utils.GetStringPtr("/my-bucket"),
"x-amz-bucket-arn": utils.GetStringPtr("arn:aws:s3:::my-bucket"),
},
},
},
},
{ {
name: "invalid ownership", name: "invalid ownership",
input: testInput{ input: testInput{
+14
View File
@@ -984,6 +984,20 @@ func GetCopySourceObjectTooLargeErr(limit int64) APIError {
} }
} }
// Returns illegal location constraint APIError. An empty constraint stands for
// a CreateBucket request that carried no LocationConstraint at all.
func GetIllegalLocationConstraintErr(constraint string) APIError {
if constraint == "" {
constraint = "unspecified"
}
return APIError{
Code: "IllegalLocationConstraintException",
Description: fmt.Sprintf("The %s location constraint is incompatible for the region specific endpoint this request was sent to.", constraint),
HTTPStatusCode: http.StatusBadRequest,
}
}
func GetInvalidRedirectCodeErr(input int) APIError { func GetInvalidRedirectCodeErr(input int) APIError {
return APIError{ return APIError{
Code: "InvalidRequest", Code: "InvalidRequest",