metadata_service.js
6.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
var AWS = require('./core');
require('./http');
var inherit = AWS.util.inherit;
/**
* Represents a metadata service available on EC2 instances. Using the
* {request} method, you can receieve metadata about any available resource
* on the metadata service.
*
* You can disable the use of the IMDS by setting the AWS_EC2_METADATA_DISABLED
* environment variable to a truthy value.
*
* @!attribute [r] httpOptions
* @return [map] a map of options to pass to the underlying HTTP request:
*
* * **timeout** (Number) — a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
*
* @!macro nobrowser
*/
AWS.MetadataService = inherit({
/**
* @return [String] the hostname of the instance metadata service
*/
host: '169.254.169.254',
/**
* @!ignore
*/
/**
* Default HTTP options. By default, the metadata service is set to not
* timeout on long requests. This means that on non-EC2 machines, this
* request will never return. If you are calling this operation from an
* environment that may not always run on EC2, set a `timeout` value so
* the SDK will abort the request after a given number of milliseconds.
*/
httpOptions: { timeout: 0 },
/**
* when enabled, metadata service will not fetch token
*/
disableFetchToken: false,
/**
* Creates a new MetadataService object with a given set of options.
*
* @option options host [String] the hostname of the instance metadata
* service
* @option options httpOptions [map] a map of options to pass to the
* underlying HTTP request:
*
* * **timeout** (Number) — a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
* @option options maxRetries [Integer] the maximum number of retries to
* perform for timeout errors
* @option options retryDelayOptions [map] A set of options to configure the
* retry delay on retryable errors. See AWS.Config for details.
*/
constructor: function MetadataService(options) {
AWS.util.update(this, options);
},
/**
* Sends a request to the instance metadata service for a given resource.
*
* @param path [String] the path of the resource to get
*
* @param options [map] an optional map used to make request
*
* * **method** (String) — HTTP request method
*
* * **headers** (map<String,String>) — a map of response header keys and their respective values
*
* @callback callback function(err, data)
* Called when a response is available from the service.
* @param err [Error, null] if an error occurred, this value will be set
* @param data [String, null] if the request was successful, the body of
* the response
*/
request: function request(path, options, callback) {
if (arguments.length === 2) {
callback = options;
options = {};
}
if (process.env[AWS.util.imdsDisabledEnv]) {
callback(new Error('EC2 Instance Metadata Service access disabled'));
return;
}
path = path || '/';
var httpRequest = new AWS.HttpRequest('http://' + this.host + path);
httpRequest.method = options.method || 'GET';
if (options.headers) {
httpRequest.headers = options.headers;
}
AWS.util.handleRequestWithRetries(httpRequest, this, callback);
},
/**
* @api private
*/
loadCredentialsCallbacks: [],
/**
* Fetches metadata token used for getting credentials
*
* @api private
* @callback callback function(err, token)
* Called when token is loaded from the resource
*/
fetchMetadataToken: function fetchMetadataToken(callback) {
var self = this;
var tokenFetchPath = '/latest/api/token';
self.request(
tokenFetchPath,
{
'method': 'PUT',
'headers': {
'x-aws-ec2-metadata-token-ttl-seconds': '21600'
}
},
callback
);
},
/**
* Fetches credentials
*
* @api private
* @callback cb function(err, creds)
* Called when credentials are loaded from the resource
*/
fetchCredentials: function fetchCredentials(options, cb) {
var self = this;
var basePath = '/latest/meta-data/iam/security-credentials/';
self.request(basePath, options, function (err, roleName) {
if (err) {
self.disableFetchToken = !(err.statusCode === 401);
cb(AWS.util.error(
err,
{
message: 'EC2 Metadata roleName request returned error'
}
));
return;
}
roleName = roleName.split('\n')[0]; // grab first (and only) role
self.request(basePath + roleName, options, function (credErr, credData) {
if (credErr) {
self.disableFetchToken = !(credErr.statusCode === 401);
cb(AWS.util.error(
credErr,
{
message: 'EC2 Metadata creds request returned error'
}
));
return;
}
try {
var credentials = JSON.parse(credData);
cb(null, credentials);
} catch (parseError) {
cb(parseError);
}
});
});
},
/**
* Loads a set of credentials stored in the instance metadata service
*
* @api private
* @callback callback function(err, credentials)
* Called when credentials are loaded from the resource
* @param err [Error] if an error occurred, this value will be set
* @param credentials [Object] the raw JSON object containing all
* metadata from the credentials resource
*/
loadCredentials: function loadCredentials(callback) {
var self = this;
self.loadCredentialsCallbacks.push(callback);
if (self.loadCredentialsCallbacks.length > 1) { return; }
function callbacks(err, creds) {
var cb;
while ((cb = self.loadCredentialsCallbacks.shift()) !== undefined) {
cb(err, creds);
}
}
if (self.disableFetchToken) {
self.fetchCredentials({}, callbacks);
} else {
self.fetchMetadataToken(function(tokenError, token) {
if (tokenError) {
if (tokenError.code === 'TimeoutError') {
self.disableFetchToken = true;
} else if (tokenError.retryable === true) {
callbacks(AWS.util.error(
tokenError,
{
message: 'EC2 Metadata token request returned error'
}
));
return;
} else if (tokenError.statusCode === 400) {
callbacks(AWS.util.error(
tokenError,
{
message: 'EC2 Metadata token request returned 400'
}
));
return;
}
}
var options = {};
if (token) {
options.headers = {
'x-aws-ec2-metadata-token': token
};
}
self.fetchCredentials(options, callbacks);
});
}
}
});
/**
* @api private
*/
module.exports = AWS.MetadataService;