Description
Describe the feature
AWS S3 allows additional request parameters to be set when a request is issued. In my case, I want to add the expected bucket owner
to a GetObject request to S3. These parameters can either be added via a header or a query parameter.
When using presigned URLs, their main intended use case is to encapsulate the entire request in the URL for later usage by a thrid-party who should not need to know additional information to add to the request (except executing the presigned URL).[1]
When creating a presigned URL in the aws-go-sdk-v3 with the following code (a GetObject request with an expected bucket owner parameter set):
package main
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"os"
"strings"
)
func main() {
if len(os.Args) != 4 {
println("Usage: go run test_bucket_owner.go ACCOUNT_ID BUCKET_NAME OBJECT_KEY")
os.Exit(1)
}
account := os.Args[1]
bucket := os.Args[2]
key := os.Args[3]
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("eu-west-1"))
if err != nil {
panic(err)
}
c := s3.NewFromConfig(cfg)
p := s3.NewPresignClient(c)
i := s3.GetObjectInput{
ExpectedBucketOwner: aws.String(account),
Bucket: aws.String(bucket),
Key: aws.String(key),
}
u, err := p.PresignGetObject(ctx, &i)
if err != nil {
panic(err)
}
println(u.URL)
for k, vs := range u.SignedHeader {
fmt.Printf("%s: %s\n", k, strings.Join(vs, ","))
}
println(u.Method)
}
the URL will look something like this:
https://<bucket-name>.s3.eu-west-1.amazonaws.com/<object-key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<access-key-id>%2F20240209%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240209T095920Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3Jp...&X-Amz-SignedHeaders=host%3Bx-amz-expected-bucket-owner&x-id=GetObject&X-Amz-Signature=e856...
A caller of this URL is required to add the X-Amz-Expected-Bucket-Owner
header with the same value as provided by the creator of the pre-signed URL. This adds a burden and requires additional information that needs to be passed from the creator of the pre-signed URL to the executor of the URL. It also seems to be in contrast to the intended behaviour of pre-signed URLs, as they are now not entirely expressed as a URL anymore.
When executing the above code to create a pre-signed URL, the URL should then look something like that, all parameters should be included as query parameters. This is supported by the S3 API:
https://<bucket-name>.s3.eu-west-1.amazonaws.com/<object-key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<access-key-id>%2F20240209%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240209T100408Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3Jp...&X-Amz-SignedHeaders=host&x-amz-expected-bucket-owner=<aws-account-id>&x-id=GetObject&X-Amz-Signature=7ef0789...
Executing this URL will work without needing to add any additional header values.
[1] Refer to "Using query parameters to authenticate requests is useful when you want to express a request entirely in a URL." (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)
Use Case
I have two components, which work with objects in a S3 bucket, one of them having actual aws credentials to access objects from buckets (the buckets are owned by different accounts). The service that has credentials for these buckets creates pre-signed URLs for the other component to use and work with these objects. As the buckets are configured by customers of these both services, I want to use the expected bucket owner as an additional safety net to ensure that both services are talking to buckets that are owned by the customer we intend them to be owned by. This should protect against a customer deleting a bucket and it being re-created by a malicious actor.
The service that creates pre-signed URLs is expected to pass a URL only to the other service. No other information is communicated at this step. This does, as of now, not work with presigned URLs and the aws-sdk-go-v2.
Proposed Solution
No response
Other Information
No response
Acknowledgements
- I may be able to implement this feature request
- This feature might incur a breaking change
AWS Go SDK V2 Module Versions Used
v1.24.1
Go version used
go version go1.21.6 darwin/arm64
Activity