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

Fix issues with incorrect variable assignment of key, secret, and token obtained from Instance Metadata. #84

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 85 additions & 74 deletions lib/aws.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,130 +38,141 @@ exports.createCFNClient = cfn.init(genericAWSClient);
exports.createEMRClient = emr.init(genericAWSClient);
exports.createMetaDataClient = metadata.init();

// a generic AWS API Client which handles the general parts
function genericAWSClient(obj) {
var securityToken = obj.token;
// A generic AWS API Client which handles the general parts.
function genericAWSClient (obj) {
var signHeader = obj.signHeader;
var host = obj.host;
var accessKeyId = obj.accessKeyId;
var path = obj.path;
var agent = obj.agent;
var secretAccessKey = obj.secretAccessKey;
var secure = obj.secure == null ? true : false;
// Default true.
var secure = obj.secure !== false;
var connection = secure ? https : http;

return {call: call};
return { call : call };

function call(action, query, callback) {
function call (action, query, callback) {
// Wrap the callback to prevent it from being called multiple times.
callback = (function(next) {
callback = (function (next) {
var isCalled = false;
return function() {
if (isCalled) return;
return function () {
if (isCalled) {
return;
}
isCalled = true;
next.apply(null, arguments);
};
})(callback);

// Try to set credentials with metadata API if no credentials provided.
// This will add the credentials to obj.
metadata.readCredentials(obj, function (err) {
if (err) {
return callback(err);
}
})(callback)

// Try to set credentials with metadata API if no credentials provided
metadata.readCredentials(obj, function(err) {
if (err) return callback(err);
var date = new Date();

query = addQueryProperties(query, securityToken, accessKeyId, date);
query = addQueryProperties(query, obj.token, obj.accessKeyId, obj.secretAccessKey, date);
var body = qs.stringify(query);
var headers = createHeaders(host, body.length, date, securityToken, accessKeyId, secretAccessKey);
var headers = createHeaders(host, body.length, date, obj.token, obj.accessKeyId, obj.secretAccessKey);
sendRequest();
return;

function sendRequest() {
function sendRequest () {
var options = {
host: host,
path: path,
agent: agent,
method: 'POST',
headers: headers
host : host,
path : path,
agent : agent,
method : 'POST',
headers : headers
};
var req = connection.request(options, function (res) {
var data = '';
//the listener that handles the response chunks
// The listener that handles the response chunks.
res.addListener('data', function (chunk) {
data += chunk.toString()
})
res.addListener('end', function() {
data += chunk.toString();
});
res.addListener('end', function () {
var parser = new xml2js.Parser();
parser.addListener('end', function(result) {
if (typeof result != "undefined") {
var err = result.Error || (result.Errors ? result.Errors.Error : null)
parser.addListener('end', function (result) {
if (result !== undefined) {
var err = result.Error || (result.Errors ? result.Errors.Error : null);
if (err) {
callback(new Error(err.Message), result)
} else {
callback(null, result)
callback(new Error(err.Message), result);
}
} else {
callback(new Error('Unable to parse XML from AWS.'))
else {
callback(null, result);
}
}
else {
callback(new Error('Unable to parse XML from AWS.'));
}
});
parser.parseString(data);
})
res.addListener('error', callback)
});
res.addListener('error', callback);
});
req.write(body)
req.addListener('error', callback)
req.end()
req.write(body);
req.addListener('error', callback);
req.end();
}
});
}

function addQueryProperties(query, securityToken, accessKeyId, date) {
function addQueryProperties (query, securityToken, accessKeyId, secretAccessKey, date) {
var extendedQuery = _.clone(query);
if (securityToken) extendedQuery["SecurityToken"] = securityToken;
extendedQuery["Timestamp"] = date.toISOString();
extendedQuery["AWSAccessKeyId"] = accessKeyId;
extendedQuery["Signature"] = signQuery(extendedQuery);
if (securityToken) {
extendedQuery.SecurityToken = securityToken;
}
extendedQuery.Timestamp = date.toISOString();
extendedQuery.AWSAccessKeyId = accessKeyId;
extendedQuery.Signature = signQuery(extendedQuery, secretAccessKey);
return extendedQuery;
}

function createHeaders(host, bodyLength, date, securityToken, accessKeyId, secretAccessKey) {
function createHeaders (host, bodyLength, date, securityToken, accessKeyId, secretAccessKey) {
var headers = {
"Host": host,
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Content-Length": bodyLength
'Host' : host,
'Content-Type' : 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Length' : bodyLength
};

if (signHeader) {
headers["Date"] = date.toUTCString();
if (securityToken !== undefined) headers["x-amz-security-token"] = securityToken;
headers["x-amzn-authorization"] =
"AWS3-HTTPS " +
"AWSAccessKeyId=" + accessKeyId + ", " +
"Algorithm=HmacSHA256, " +
"Signature=" + utils.hmacSha256(secretAccessKey, date.toUTCString());
headers.Date = date.toUTCString();
if (securityToken !== undefined) {
headers['x-amz-security-token'] = securityToken;
}
headers['x-amzn-authorization'] =
'AWS3-HTTPS ' +
'AWSAccessKeyId=' + accessKeyId + ', ' +
'Algorithm=HmacSHA256, ' +
'Signature=' + utils.hmacSha256(secretAccessKey, date.toUTCString());
}
return headers;
}

function signQuery(query) {
var keys = []
var sorted = {}
function signQuery (query, secretAccessKey) {
var key;
var keys = [];
var sorted = {};

for(var key in query)
keys.push(key)
for (key in query) {
keys.push(key);
}

keys = keys.sort()
keys = keys.sort();

for(var n in keys) {
var key = keys[n]
sorted[key] = query[key]
for (var index = 0, length = keys.length; index < length; index++) {
key = keys[index];
sorted[key] = query[key];
}
var stringToSign = ["POST", host, path, qs.stringify(sorted)].join("\n");

// Amazon signature algorithm seems to require this
stringToSign = stringToSign.replace(/!/g,"%21");
stringToSign = stringToSign.replace(/'/g,"%27");
stringToSign = stringToSign.replace(/\*/g,"%2A");
stringToSign = stringToSign.replace(/\(/g,"%28");
stringToSign = stringToSign.replace(/\)/g,"%29");
var stringToSign = ['POST', host, path, qs.stringify(sorted)].join('\n');

// Amazon signature algorithm seems to require this.
stringToSign = stringToSign.replace(/!/g,'%21');
stringToSign = stringToSign.replace(/'/g,'%27');
stringToSign = stringToSign.replace(/\*/g,'%2A');
stringToSign = stringToSign.replace(/\(/g,'%28');
stringToSign = stringToSign.replace(/\)/g,'%29');

return utils.hmacSha256(secretAccessKey, stringToSign);
}
Expand Down