anomalizer / ngx_aws_auth

nginx module to proxy to authenticated AWS services
BSD 2-Clause "Simplified" License
470 stars 144 forks source link

Various fixes I had to make to have this work for real #2

Closed npahucki closed 13 years ago

npahucki commented 13 years ago

Thanks for giving me a head start on a working version of this utility, but I did run into several issues.

I had three major problems that I fixed:

1) Add ability to chop out part of the URL to be signed, as when using X-Accel-Redreict and an proxy_pass s3 url that ends with a slash. The proxy takes off the internal part of the url before appending it to the s3 url. However, the url being signed included the internal part of the URL so the signature was wrong. For an example, if my proxy_pass url is http://blah.s3.amazonaws.com/; and was defined in location /pfiles/, when the X-Accel-Redirect is /pfiles/dir1/dir2/file.txt then the final url is http://blah.s3.amazonaws.com/dir1/dir2/file.txt, but the url being signed was http://blah.s3.amazonaws.com/pfiles/dir1/dir2/file.txt.

2) Random errors with incorrect signatures. This was due to a combination of ngx_palloc and ngx_sprintf. Memory from the pool which is reused is uninitialized AND ngx_sprintf does NOT append a null at the end of the string automatically like the standard sprintf, resulting in signature strings that had random garbage at the end of them. I had to add %Z to all the ngx_sprintf calls to make sure that they were null terminated.

3) Url was not url encoded before it was signed. If using a URL with a space in it, the URL was signed with space characters instead of %20 leading to incorrect signatures. I fixed this by encoding the url before signing.

After these changes, things seem to be pretty stable now, though I have yet to try it under any serious load. Also this was my first crack at hacking around with nginx, so there could be easier/better ways of doing things I simply didn't know about (it's not like the doc for nginx apis is ample or anything ;-) ), feel free to suggest.

nfo commented 13 years ago

Hi. I still have an incorrect signature after the last fixes of @npahucki with chop_prefix. It works fine if the location is "/".

The problem occurs when I try to access the file in the browser, or via an X-Accel-Redirect.

Here is the nginx config:

location /report_files {
  # internal;
  proxy_pass http://testbucket.toto.com.s3.amazonaws.com;

  aws_access_key xxx;
  aws_secret_key yyy;
  s3_bucket testbucket.toto.com;
  chop_prefix /report_files;

  proxy_set_header Authorization $s3_auth_token;
  proxy_set_header x-amz-date $aws_date;
}

Here are the logs:

==> logs/error.log <==
2011/10/27 15:16:41 [debug] 72781#0: *1 chop_prefix '/report_files' chopped from URI
2011/10/27 15:16:41 [debug] 72781#0: *1 String to sign:GET

x-amz-date:Thu, 27 Oct 2011 13:16:41 GMT
/testbucket.toto.com/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE
2011/10/27 15:16:41 [debug] 72781#0: *1 Signature: AWS AKIAIUP7RKWIJJPNMCJQ:HCTiXbo18rE29y8Hp49YS4Gsxac=

Here is the reponse from S3:

<?xml version="1.0"?>
<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
  <StringToSignBytes>47 45 54 0a 0a 0a 0a 78 2d 61 6d 7a 2d 64 61 74 65 3a 54 68 75 2c 20 32 37 20 4f 63 74 20 32 30 31 31 20 31 33 3a 31 36 3a 34 31 20 47 4d 54 0a 2f 74 65 73 74 62 75 63 6b 65 74 2e 63 77 73 64 65 76 2e 63 6c 65 76 65 72 73 63 61 6c 65 2e 63 6f 6d 2f 72 65 70 6f 72 74 5f 66 69 6c 65 73 2f 6c 6f 67 73 2f 32 30 31 31 2d 31 30 2d 32 37 2d 31 31 2d 31 36 2d 33 35 2d 33 38 46 35 38 41 46 42 30 46 37 43 36 42 45 45</StringToSignBytes>
  <RequestId>619C80E5CC5C0D2A</RequestId>
  <HostId>V2lRZu6hlU3BFld5VFcFjNNiJ41kfbvlZi/xeiljCc8NV7h7RggopIdyrx49tTni</HostId>
  <SignatureProvided>HCTiXbo18rE29y8Hp49YS4Gsxac=</SignatureProvided>
  <StringToSign>GET

x-amz-date:Thu, 27 Oct 2011 13:16:41 GMT
/testbucket.toto.com/report_files/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE</StringToSign>
  <AWSAccessKeyId>AKIAIUP7RKWIJJPNMCJQ</AWSAccessKeyId>
</Error>

This response is weird, it shows that /report_files is still in the url to sign. I tried a bucket without dots in its name, and a patch without the underscore. I get the same result.

npahucki commented 13 years ago

Try ending your proxy pass with a '/' like :

proxy_pass http://testbucket.toto.com.s3.amazonaws.com/;

On Oct 27, 2011, at 9:21 PM, Nicolas Fouché wrote:

Hi. I still have an incorrect signature after the last fixes of @npahucki with chop_prefix. It works fine if the location is "/".

The problem occurs when I try to access the file in the browser, or via an X-Accel-Redirect.

Here is the nginx config:

location /report_files {

internal;

 proxy_pass http://testbucket.toto.com.s3.amazonaws.com;
 aws_access_key xxx;
 aws_secret_key yyy;
 s3_bucket testbucket.toto.com;
 chop_prefix /report_files;

 proxy_set_header Authorization $s3_auth_token;
 proxy_set_header x-amz-date $aws_date;

}

Here are the logs:

==> logs/error.log <== 2011/10/27 15:16:41 [debug] 72781#0: 1 chop_prefix '/report_files' chopped from URI 2011/10/27 15:16:41 [debug] 72781#0: 1 String to sign:GET

x-amz-date:Thu, 27 Oct 2011 13:16:41 GMT /testbucket.toto.com/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE 2011/10/27 15:16:41 [debug] 72781#0: *1 Signature: AWS AKIAIUP7RKWIJJPNMCJQ:HCTiXbo18rE29y8Hp49YS4Gsxac=

Here is the reponse from S3:

<?xml version="1.0"?>

SignatureDoesNotMatch The request signature we calculated does not match the signature you provided. Check your key and signing method. 47 45 54 0a 0a 0a 0a 78 2d 61 6d 7a 2d 64 61 74 65 3a 54 68 75 2c 20 32 37 20 4f 63 74 20 32 30 31 31 20 31 33 3a 31 36 3a 34 31 20 47 4d 54 0a 2f 74 65 73 74 62 75 63 6b 65 74 2e 63 77 73 64 65 76 2e 63 6c 65 76 65 72 73 63 61 6c 65 2e 63 6f 6d 2f 72 65 70 6f 72 74 5f 66 69 6c 65 73 2f 6c 6f 67 73 2f 32 30 31 31 2d 31 30 2d 32 37 2d 31 31 2d 31 36 2d 33 35 2d 33 38 46 35 38 41 46 42 30 46 37 43 36 42 45 45 619C80E5CC5C0D2A V2lRZu6hlU3BFld5VFcFjNNiJ41kfbvlZi/xeiljCc8NV7h7RggopIdyrx49tTni HCTiXbo18rE29y8Hp49YS4Gsxac= GET x-amz-date:Thu, 27 Oct 2011 13:16:41 GMT /testbucket.toto.com/report_files/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE AKIAIUP7RKWIJJPNMCJQ

This response is weird, it shows that /report_files is still in the url to sign. I tried a bucket without dots in its name, and a patch without the underscore. I get the same result.

Reply to this email directly or view it on GitHub: https://github.com/anomalizer/ngx_aws_auth/pull/2#issuecomment-2543240

nfo commented 13 years ago

Still no luck. Same error. I tried with a bucket with and without dots in its name.

nfo commented 13 years ago

Ok. I could make it work. Details will arrive in a minute. (cc @npahucki)

nfo commented 13 years ago

This configuration does not work with http://127.0.0.1:8001/report_files/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE:

server {
  listen 8001;
  location /report_files {
    proxy_pass http://abucketwithoutdots.s3.amazonaws.com/;

    aws_access_key xxx;
    aws_secret_key yyy;
    s3_bucket abucketwithoutdots;
    chop_prefix /report_files;

    proxy_set_header Authorization $s3_auth_token;
    proxy_set_header x-amz-date $aws_date;
  }
}

This configuration works with http://127.0.0.1:8002/report_files/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE, but I had to add an ending slash to the S3 proxy_pass AND the http proxy_pass:

server {
  listen 8001;
  location / {
    proxy_pass http://abucketwithoutdots.s3.amazonaws.com/; # <===== Added and ending "/" here

    aws_access_key xxx;
    aws_secret_key yyy;
    s3_bucket abucketwithoutdots;
    chop_prefix /report_files;

    proxy_set_header Authorization $s3_auth_token;
    proxy_set_header x-amz-date $aws_date;
  }
}

server {
  listen 8002;
  location /report_files {
    # internal;
    proxy_pass http://127.0.0.1:8001/; # <===== Added an ending "/" here
  }
}

About X-Accel-Redirect, this configuration does not work, with X-Accel-Redirect: /report_files/logs/2011-10-27-11-16-35-38F58AFB0F7C6BEE

upstream webapp {
  server localhost:3000;
}

server {
  listen       8000;

  location / {
    proxy_pass http://webapp/;
  }

  location /report_files {
    internal;
    proxy_pass http://testbucket.cwsdev.cleverscale.com.s3.amazonaws.com/;

    aws_access_key xxx;
    aws_secret_key yyy;
    s3_bucket testbucket.cwsdev.cleverscale.com;
    chop_prefix /report_files;

    proxy_set_header Authorization $s3_auth_token;
    proxy_set_header x-amz-date $aws_date;
  }
}

I need to add an intermediate HTTP proxy_pass on another server where I can define a location starting with a /:

upstream webapp {
  server localhost:3000;
}

server {
  listen       8000;

  location / {
    proxy_pass http://webapp/;
  }

  # This location is triggered via the X-Accel-Redirect response header
  # Don't proxy_pass to S3 directly, we have to proxy_pass to a location listening on /.
  location /report_files {
    internal;
    proxy_pass http://127.0.0.1:8001/; 
  }
}

server {
  listen 8001;
  location / {
    proxy_pass http://abucketwithoutdots.s3.amazonaws.com/;

    aws_access_key xxx;
    aws_secret_key yyy;
    s3_bucket abucketwithoutdots;
    chop_prefix /report_files;

    proxy_set_header Authorization $s3_auth_token;
    proxy_set_header x-amz-date $aws_date;
  }
}

All in all, it's tricky. I know it was your first big patch, so it's still a big thanks :)

davidbirdsong commented 12 years ago

I also find that only a location / will correctly sign and proxy to the s3 url.