diff --git a/s3api/controllers/bucket-put.go b/s3api/controllers/bucket-put.go index 9c1b101..3e834ad 100644 --- a/s3api/controllers/bucket-put.go +++ b/s3api/controllers/bucket-put.go @@ -546,6 +546,32 @@ func (c S3ApiController) PutBucketAcl(ctx fiber.Ctx) (*Response, error) { }, 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) { bucket := ctx.Params("bucket") 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) } + } - if body.LocationConstraint != nil { - region := utils.ContextKeyRegion.Get(ctx).(string) - if *body.LocationConstraint != region || *body.LocationConstraint == "us-east-1" { - debuglogger.Logf("invalid location constraint: %s", *body.LocationConstraint) - return &Response{ - MetaOpts: &MetaOptions{ - BucketOwner: bucketOwner.Access, - }, - }, s3err.GetInvalidLocationConstraintErr(*body.LocationConstraint) - } - } + region, ok := utils.ContextKeyRegion.Get(ctx).(string) + if !ok { + region = defaultRegion + } + + if err := validateLocationConstraint(body.LocationConstraint, region); err != nil { + return &Response{ + MetaOpts: &MetaOptions{ + BucketOwner: bucketOwner.Access, + }, + }, err } defACL := auth.ACL{ diff --git a/s3api/controllers/bucket-put_test.go b/s3api/controllers/bucket-put_test.go index b404588..1cccf1a 100644 --- a/s3api/controllers/bucket-put_test.go +++ b/s3api/controllers/bucket-put_test.go @@ -726,6 +726,11 @@ func TestS3ApiController_CreateBucket(t *testing.T) { }) assert.NoError(t, err) + euLocConstBody, err := xml.Marshal(s3response.CreateBucketConfiguration{ + LocationConstraint: utils.GetStringPtr("eu-central-1"), + }) + assert.NoError(t, err) + tests := []struct { name string input testInput @@ -812,6 +817,59 @@ func TestS3ApiController_CreateBucket(t *testing.T) { 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", input: testInput{ diff --git a/s3err/s3err.go b/s3err/s3err.go index 5fe1413..5a02d44 100644 --- a/s3err/s3err.go +++ b/s3err/s3err.go @@ -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 { return APIError{ Code: "InvalidRequest",