6

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.

Steffen Opel
  • 63,899
  • 11
  • 192
  • 211
Nik
  • 2,718
  • 23
  • 34
  • I just deleted my answer a second time (for good) and will refrain from adding another one until I can actually reproduce an end to end solution in .NET. Pre-signed URLs for custom domains are known to work in principle (see e.g this ancient AWS team response to [CNAME alias with pre-signed query](https://forums.aws.amazon.com/message.jspa?messageID=71409#71409) and I've used them as well accordingly; the VANITY parameter in the Ruby example is deprecated and not available in the AWS SDK for .NET anyway, where it's supposed to just work like so, AFAIK - Again, sorry for the hassle! – Steffen Opel Mar 07 '12 at 00:31

2 Answers2

3

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.

Community
  • 1
  • 1
Steffen Opel
  • 63,899
  • 11
  • 192
  • 211
  • Hi Steffen. Thanks for this, but I'm failing to see where the custom domain is specified in the above so that it can be implied by the S3 CNAME? All we've set here is a key. Thanks. – Nik Mar 06 '12 at 23:32
  • @Nik: You are absolutely correct of course, I messed up the information gathered by a local test with my initial wrong answer (which I incidentally deleted already after realizing it didn't address the question ;) - sorry for the hassle! The `ServiceURL` must be your CNAME indeed, and removing the already implied bucket name takes care of the duplication. – Steffen Opel Mar 06 '12 at 23:46
  • 1
    When I removed the WithBucketName() I found that the domain is correct, but that the key wasn't used in the url for some reason. e.g.: http://foo.example.com?AWSAccessKeyId=bar&Expires=1331078589&Signature=xxx. Can you confirm this definitely works for you? – Nik Mar 06 '12 at 23:58
  • @Nik: I gave it another shot and have been able to come up with a workaround at least, tested and reproduced end to end this time around ;) – Steffen Opel Mar 16 '12 at 14:57
  • Thank you for your persistence Steffen - it works! Does seem like a bug in the AWS SDK to me. – Nik Mar 16 '12 at 18:46
  • @Nik: I had planned to revisit this anyway, given my sloppy performance with the initial answer(s) - your bounty made it all the more worthwhile now, sorry you had to invest it at all though ;) – Steffen Opel Mar 19 '12 at 12:47
0

the presignedURL returns an URL object after signing the request. I have used the same and >dont have real issues, but there are some things to consider:

  1. Ensure the object URL you are considering does not have a '//' it could easily happen if >you start the storage path starts with a "/", you would have stored the object in a path some >thing like https:///x/y/z/abc.png the key for such a resource is x/y/z/abc.png >and not /x/y/z/abc.png

  2. If the above is ensured, then from the URL object that is returned get the query parameters >from the URL object url.getQuery() will return the query parameters which contains the >signature information, just suffix this with your original awsURL and things should work with >out any encoding issues.

Hope this helps..

Essentially you need to use url.getQuery on the returned url object rather than simply affixing it to the end of your bucket.

https://forums.aws.amazon.com/thread.jspa?threadID=70521

  • 1
    That link is to an issue with encoding. Can you be a little clearer as to how it solves my problem? Thanks! – Nik Mar 16 '12 at 10:09