@@ -37,6 +37,11 @@ AWS.MetadataService = inherit({
3737 */
3838 httpOptions : { timeout : 0 } ,
3939
40+ /**
41+ * when enabled, metadata service will not fetch token
42+ */
43+ disableFetchToken : false ,
44+
4045 /**
4146 * Creates a new MetadataService object with a given set of options.
4247 *
@@ -60,21 +65,36 @@ AWS.MetadataService = inherit({
6065 * Sends a request to the instance metadata service for a given resource.
6166 *
6267 * @param path [String] the path of the resource to get
68+ *
69+ * @param options [map] an optional map used to make request
70+ *
71+ * * **method** (String) — HTTP request method
72+ *
73+ * * **headers** (map<String,String>) — a map of response header keys and their respective values
74+ *
6375 * @callback callback function(err, data)
6476 * Called when a response is available from the service.
6577 * @param err [Error, null] if an error occurred, this value will be set
6678 * @param data [String, null] if the request was successful, the body of
6779 * the response
6880 */
69- request : function request ( path , callback ) {
81+ request : function request ( path , options , callback ) {
82+ if ( arguments . length === 2 ) {
83+ callback = options ;
84+ options = { } ;
85+ }
86+
7087 if ( process . env [ AWS . util . imdsDisabledEnv ] ) {
7188 callback ( new Error ( 'EC2 Instance Metadata Service access disabled' ) ) ;
7289 return ;
7390 }
7491
7592 path = path || '/' ;
7693 var httpRequest = new AWS . HttpRequest ( 'http://' + this . host + path ) ;
77- httpRequest . method = 'GET' ;
94+ httpRequest . method = options . method || 'GET' ;
95+ if ( options . headers ) {
96+ httpRequest . headers = options . headers ;
97+ }
7898 AWS . util . handleRequestWithRetries ( httpRequest , this , callback ) ;
7999 } ,
80100
@@ -83,6 +103,72 @@ AWS.MetadataService = inherit({
83103 */
84104 loadCredentialsCallbacks : [ ] ,
85105
106+ /**
107+ * Fetches metadata token used for getting credentials
108+ *
109+ * @api private
110+ * @callback callback function(err, token)
111+ * Called when token is loaded from the resource
112+ */
113+ fetchMetadataToken : function fetchMetadataToken ( callback ) {
114+ var self = this ;
115+ var tokenFetchPath = '/latest/api/token' ;
116+ self . request (
117+ tokenFetchPath ,
118+ {
119+ 'method' : 'PUT' ,
120+ 'headers' : {
121+ 'x-aws-ec2-metadata-token-ttl-seconds' : '21600'
122+ }
123+ } ,
124+ callback
125+ ) ;
126+ } ,
127+
128+ /**
129+ * Fetches credentials
130+ *
131+ * @api private
132+ * @callback cb function(err, creds)
133+ * Called when credentials are loaded from the resource
134+ */
135+ fetchCredentials : function fetchCredentials ( options , cb ) {
136+ var self = this ;
137+ var basePath = '/latest/meta-data/iam/security-credentials/' ;
138+
139+ self . request ( basePath , options , function ( err , roleName ) {
140+ if ( err ) {
141+ self . disableFetchToken = ! ( err . statusCode === 401 ) ;
142+ cb ( AWS . util . error (
143+ err ,
144+ {
145+ message : 'EC2 Metadata roleName request returned error'
146+ }
147+ ) ) ;
148+ return ;
149+ }
150+ roleName = roleName . split ( '\n' ) [ 0 ] ; // grab first (and only) role
151+ self . request ( basePath + roleName , options , function ( credErr , credData ) {
152+ if ( credErr ) {
153+ self . disableFetchToken = ! ( credErr . statusCode === 401 ) ;
154+ cb ( AWS . util . error (
155+ credErr ,
156+ {
157+ message : 'EC2 Metadata creds request returned error'
158+ }
159+ ) ) ;
160+ return ;
161+ }
162+ try {
163+ var credentials = JSON . parse ( credData ) ;
164+ cb ( null , credentials ) ;
165+ } catch ( parseError ) {
166+ cb ( parseError ) ;
167+ }
168+ } ) ;
169+ } ) ;
170+ } ,
171+
86172 /**
87173 * Loads a set of credentials stored in the instance metadata service
88174 *
@@ -95,7 +181,6 @@ AWS.MetadataService = inherit({
95181 */
96182 loadCredentials : function loadCredentials ( callback ) {
97183 var self = this ;
98- var basePath = '/latest/meta-data/iam/security-credentials/' ;
99184 self . loadCredentialsCallbacks . push ( callback ) ;
100185 if ( self . loadCredentialsCallbacks . length > 1 ) { return ; }
101186
@@ -106,23 +191,41 @@ AWS.MetadataService = inherit({
106191 }
107192 }
108193
109- self . request ( basePath , function ( err , roleName ) {
110- if ( err ) callbacks ( err ) ;
111- else {
112- roleName = roleName . split ( '\n' ) [ 0 ] ; // grab first (and only) role
113- self . request ( basePath + roleName , function ( credErr , credData ) {
114- if ( credErr ) callbacks ( credErr ) ;
115- else {
116- try {
117- var credentials = JSON . parse ( credData ) ;
118- callbacks ( null , credentials ) ;
119- } catch ( parseError ) {
120- callbacks ( parseError ) ;
121- }
194+ if ( self . disableFetchToken ) {
195+ self . fetchCredentials ( { } , callbacks ) ;
196+ } else {
197+ self . fetchMetadataToken ( function ( tokenError , token ) {
198+ if ( tokenError ) {
199+ if ( tokenError . code === 'TimeoutError' ) {
200+ self . disableFetchToken = true ;
201+ } else if ( tokenError . retryable === true ) {
202+ callbacks ( AWS . util . error (
203+ tokenError ,
204+ {
205+ message : 'EC2 Metadata token request returned error'
206+ }
207+ ) ) ;
208+ return ;
209+ } else if ( tokenError . statusCode === 400 ) {
210+ callbacks ( AWS . util . error (
211+ tokenError ,
212+ {
213+ message : 'EC2 Metadata token request returned 400'
214+ }
215+ ) ) ;
216+ return ;
122217 }
123- } ) ;
124- }
125- } ) ;
218+ }
219+ var options = { } ;
220+ if ( token ) {
221+ options . headers = {
222+ 'x-aws-ec2-metadata-token' : token
223+ } ;
224+ }
225+ self . fetchCredentials ( options , callbacks ) ;
226+ } ) ;
227+
228+ }
126229 }
127230} ) ;
128231
0 commit comments