Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temporary credentials from EC2 Metadata Service are not reflected in the AWS.config.credentials attribute, breaks S3.getSignedUrl(). #246

Closed
maitreya1975 opened this issue Mar 16, 2014 · 4 comments

Comments

@maitreya1975
Copy link

This is a bug I noticed with the following versions of the AWS SDK for JavaScript:
2.0.0-rc11, 2.0.0-rc9, 1.18.0.
I am testing against node v0.10.26.
The SDK does not automatically configure the AWS.config singleton with credentials from the EC2 Metadata service.
This is related to a documentation bug raised recently: #153
Actual invocation of the services (making REST calls to S3, DynamoDB, SNS, SQS) does work but the AWS.config.credentials attribute is not setup correctly.
This impacts ability to query the AWS.config object to get the credentials that are currently in use. Also, this breaks the S3.getSignedUrl() method.

See an example code here that exhibits this behavior:

var AWS = require('aws-sdk');

console.log("AWS SDK Version=", JSON.stringify(AWS.VERSION));
console.log("Credentials=", JSON.stringify(AWS.config.credentials));
if(AWS.config.credentials != undefined) {
    console.log("SecretKey=", JSON.stringify(AWS.config.credentials.secretAccessKey));
}

var s3 = new AWS.S3();

var signedUrl = s3.getSignedUrl('getObject', {Bucket: 'test-bucket', Key: 'test-key'});
console.log("SignedUrl=", JSON.stringify(signedUrl));

I am running an EC2 instance that has an IAM Role and associated temporary credentials from the EC2 Metadata Service. See output below:

[ec2-user@ip-172-31-35-29 credtest]$ ROLE=$( curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ )
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    13  100    13    0     0   1506      0 --:--:-- --:--:-- --:--:--  1625
[ec2-user@ip-172-31-35-29 credtest]$ echo $ROLE 
ImageProcRole
[ec2-user@ip-172-31-35-29 credtest]$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE
{
  "Code" : "Success",
  "LastUpdated" : "2014-03-16T20:53:26Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "xxx",
  "SecretAccessKey" : "xxx",
  "Token" : "xxx",
  "Expiration" : "2014-03-17T03:27:31Z"
}

The SDK picks up credentials set in the Environment variables correctly and S3.getSignedUrl returns a URL signed with the credentials. For example:

[ec2-user@ip-172-31-35-29 credtest]$ AWS_ACCESS_KEY_ID='ACCESS' AWS_SECRET_ACCESS_KEY='SECRET' AWS_SESSION_TOKEN='SESSION' node testawscreds.js 
AWS SDK Version= "2.0.0-rc11"
Credentials= {"expired":false,"expireTime":null,"accessKeyId":"ACCESS","sessionToken":"SESSION","envPrefix":"AWS"}
SecretKey= "SECRET"
SignedUrl= "https://test-bucket.s3.amazonaws.com/test-key?AWSAccessKeyId=ACCESS&Expires=1395005939&Signature=KSr1iHcK3copgYP0R6zOwSkTk5c%3D&x-amz-security-token=SESSION"

However, it does not pick up credentials from the EC2 metadata service. Also, note that the S3 getSignedUrl method now just returns the S3 hostname and no other parameters:

[ec2-user@ip-172-31-35-29 credtest]$ node testawscreds.js 
AWS SDK Version= "2.0.0-rc11"
Credentials= null
SignedUrl= "https://s3.amazonaws.com/"
@lsegal
Copy link
Contributor

lsegal commented Mar 17, 2014

This is expected behavior of the SDK. Credentials can be loaded asynchronously, something we support in the SDK. Since loading anything from HTTP is an asynchronous call in Node, the only way to load credentials would be to wait until the loading completes. The SDK already waits for this in its internal request lifecycle, so you don't have to do anything yourself.

Note that getSignedUrl() has both a synchronous and asynchronous mode. The synchronous mode is a convenience for certain configurations (static or environment provided credentials), but we explicitly document the caveat for asynchronous credential providers in the documentation for getSignedUrl().

If you do want to ensure that credentials are loaded before you do anything with the SDK to inspect the credentials, you can call:

AWS.config.getCredentials(function(err) {
  // no err means AWS.config.credentials is loaded
});

This is what we transparently do for you before signing each request.

Keep in mind that you should still not try to use getSignedUrl synchronously with async credentials even if you've preloaded them with the above code. If your credentials expire, the SDK will attempt to (asynchronously) refresh credentials from your metadata service, something that getSignedUrl's synchronous mode cannot support.

Hope that clarifies.

@maitreya1975
Copy link
Author

Thanks very much for your prompt response and clarification.
It might be my ignorance, but I can't find the method AWS.config.getCredentials(callback) in the latest documentation:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html
However, I fixed my script based on your answer and it works fine in both cases - environment variables and EC2 metadata:

var AWS = require('aws-sdk');

AWS.config.update({region: 'us-west-2'});
console.log("AWS SDK Version=", JSON.stringify(AWS.VERSION));

AWS.config.getCredentials(useCredentials);

function useCredentials(err) {
    if(err) throw err;
    console.log("Credentials=", JSON.stringify(AWS.config.credentials));
    if(AWS.config.credentials != undefined) {
        console.log("SecretKey=", JSON.stringify(AWS.config.credentials.secretAccessKey));
    }
    var s3 = new AWS.S3();

    s3.getSignedUrl('getObject', {Bucket: 'test-bucket', Key: 'test-key'}, useSignedUrl);

    function useSignedUrl(err, url) {
        if(err) throw err;

        console.log("SignedUrl=", JSON.stringify(url));
    }
}

@lsegal
Copy link
Contributor

lsegal commented Mar 17, 2014

@maitreya1975 thanks, it looks like getCredentials is marked private for now, though it's likely something we can expose as the public API. I will keep this issue open to track if we can update the docs for that.

@lsegal lsegal closed this as completed in c44ebd2 Mar 18, 2014
lsegal added a commit that referenced this issue Mar 26, 2014
lsegal added a commit that referenced this issue Apr 24, 2014
@lock
Copy link

lock bot commented Sep 30, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants