Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate pre-signed Amazon S3 url for a vanity domain, using amazon sdk?

Tags:

c#

.net

amazon-s3

I have an s3 bucket called foo.example.com, which is all CNAMEd correctly.

I'm switching to the latest AWS .net SDK.

I wish to generate pre signed url like:

http://foo.example.com/myfile.txt?s3_params_here

Note the vanity cname there.

I have:

string bucketName = "foo.example.com";

AmazonS3Client s3Client = new AmazonS3Client("bar", "xxx",
    new AmazonS3Config
    {
        ServiceURL = bucketName, 
        CommunicationProtocol = Protocol.HTTP
    });


string key = "myfile.txt";


GetPreSignedUrlRequest request = new GetPreSignedUrlRequest()
.WithBucketName(bucketName)
.WithKey(key)
.WithExpires(DateTime.Now.AddMinutes(5))
.WithProtocol(Protocol.HTTP);

string url = s3Client.GetPreSignedURL(request);

the url I get is something like:

http://foo.example.com.foo.example.com/myfile.txt?AWSAccessKeyId=bar&Expires=1331069777&Signature=234KoUUvfE1nCcs2vLj9RQUhqF8%3D

Which is clearly wrong.

I've tried a buch of different variations with ServiceURL, bucketname, etc, but nothing seems to work.

I can't find any good documentation - what is the correct way to do this?

Thanks.

like image 360
Nik Avatar asked Nov 05 '22 04:11

Nik


1 Answers

Update [workaround]

I've meanwhile resolved the contradicting test results of mine, which stem from respectively unsystematic testing and URL manipulations. The following workaround does the trick for me (i.e. tested and reproducible), simply starting from your solution:

string bucketName = "foo.example.com";

// [...]

GetPreSignedUrlRequest request = new GetPreSignedUrlRequest()
    .WithBucketName(bucketName)
    .WithKey(key)
    .WithExpires(DateTime.Now.AddMinutes(32))
    .WithProtocol(Protocol.HTTP);

Now this yields the erroneous URL with a duplicate domain name, i.e. http://foo.example.com.foo.example.com/myfile.txt?[...]

The duplicate can simply be removed though, e.g.:

string url = s3Client.GetPreSignedURL(request);

// KLUDGE: remove duplicate domain name.
url = url.Replace(bucketName + "." + bucketName, bucketName);

This yields a proper working pre-signed URL for me (i.e. http://foo.example.com/myfile.txt?[...]) by working around the encountered limitation regarding the desired approach outlined below.

Justification

Manipulating the generated URL like so seems odd, but this not having an effect on the query string authentication is in line with how these signatures are created, see Query String Request Authentication Alternative, where you'll find the pseudo-grammar that illustrates the query string request authentication method:

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;    

That is, the domain name isn't used for the signature creation at all, rather only information regarding the resource itself; section Example Query String Request Authentication right below the referenced pseudo-grammar fragment illustrates this with an actual resource.

Assessment

I don't know whether there is still a misunderstanding on our part or whether this might just be a bug in the AWS SDK for .NET, see e.g. Why is my S3 pre-signed request invalid when I set a response header override that contains a “+”? for a related bug resolved via a similar workaround as well, which has meanwhile been fixed though; accordingly, this should likely be escalated to the AWS forums and/or support channels to get an appropriate answer or solution.

Good luck!


Desired answer [dysfunctional]

The S3 CNAME handling implies the bucket name already, so all you need to do is removing your bucket name from GetPreSignedUrlRequest, i.e. it should look like so:

GetPreSignedUrlRequest request = new GetPreSignedUrlRequest()
    .WithKey(key)
    .WithExpires(DateTime.Now.AddMinutes(5))
    .WithProtocol(Protocol.HTTP);

I've tested this with a bucket of mine and it works as expected like so.

like image 170
Steffen Opel Avatar answered Nov 15 '22 01:11

Steffen Opel