@@ -150,12 +150,60 @@ function _credentialsTempFile() {
150150}
151151
152152/**
153- * Read the contents of the credentials file into memory. If it is not
154- * found, then return undefined.
153+ * Write the instance profile credentials to a caching backend.
155154 *
156- * @returns {undefined|object } AWS instance profile credentials or undefined
155+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
156+ * @param credentials {{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)}} AWS instance profile credentials
157157 */
158- function readCredentials ( ) {
158+ function writeCredentials ( r , credentials ) {
159+ // Do not bother writing credentials if we are running in a mode where we
160+ // do not need instance credentials.
161+ if ( process . env [ 'S3_ACCESS_KEY_ID' ] && process . env [ 'S3_SECRET_KEY' ] ) {
162+ return ;
163+ }
164+
165+ if ( ! credentials ) {
166+ throw `Cannot write invalid credentials: ${ JSON . stringify ( credentials ) } ` ;
167+ }
168+
169+ if ( "variables" in r && r . variables . cache_instance_credentials_enabled == 1 ) {
170+ _writeCredentialsToKeyValStore ( r , credentials ) ;
171+ } else {
172+ _writeCredentialsToFile ( credentials ) ;
173+ }
174+ }
175+
176+ /**
177+ * Write the instance profile credentials to the NGINX Keyval store.
178+ *
179+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
180+ * @param credentials {{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)}} AWS instance profile credentials
181+ * @private
182+ */
183+ function _writeCredentialsToKeyValStore ( r , credentials ) {
184+ r . variables . instance_credential_json = JSON . stringify ( credentials ) ;
185+ }
186+
187+ /**
188+ * Write the instance profile credentials to a file on the file system. This
189+ * file will be quite small and should end up in the file cache relatively
190+ * quickly if it is repeatedly read.
191+ *
192+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
193+ * @param credentials {{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)}} AWS instance profile credentials
194+ * @private
195+ */
196+ function _writeCredentialsToFile ( credentials ) {
197+ fs . writeFileSync ( _credentialsTempFile ( ) , JSON . stringify ( credentials ) ) ;
198+ }
199+
200+ /**
201+ * Get the instance profile credentials needed to authenticated against S3 from
202+ * a backend cache. If the credentials cannot be found, then return undefined.
203+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
204+ * @returns {undefined|{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)} } AWS instance profile credentials or undefined
205+ */
206+ function readCredentials ( r ) {
159207 if ( process . env [ 'S3_ACCESS_KEY_ID' ] && process . env [ 'S3_SECRET_KEY' ] ) {
160208 return {
161209 accessKeyId : process . env [ 'S3_ACCESS_KEY_ID' ] ,
@@ -165,6 +213,44 @@ function readCredentials() {
165213 } ;
166214 }
167215
216+ if ( "variables" in r && r . variables . cache_instance_credentials_enabled == 1 ) {
217+ return _readCredentialsFromKeyValStore ( r ) ;
218+ } else {
219+ return _readCredentialsFromFile ( ) ;
220+ }
221+ }
222+
223+ /**
224+ * Read credentials from the NGINX Keyval store. If it is not found, then
225+ * return undefined.
226+ *
227+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
228+ * @returns {undefined|{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)} } AWS instance profile credentials or undefined
229+ * @private
230+ */
231+ function _readCredentialsFromKeyValStore ( r ) {
232+ var cached = r . variables . instance_credential_json ;
233+
234+ if ( ! cached ) {
235+ return undefined ;
236+ }
237+
238+ try {
239+ return JSON . parse ( cached ) ;
240+ } catch ( e ) {
241+ _debug_log ( r , `Error parsing JSON value from r.variables.instance_credential_json: ${ e } ` ) ;
242+ return undefined ;
243+ }
244+ }
245+
246+ /**
247+ * Read the contents of the credentials file into memory. If it is not
248+ * found, then return undefined.
249+ *
250+ * @returns {undefined|{accessKeyId: (string), secretAccessKey: (string), sessionToken: (string), expiration: (string)} } AWS instance profile credentials or undefined
251+ * @private
252+ */
253+ function _readCredentialsFromFile ( ) {
168254 var credsFilePath = _credentialsTempFile ( ) ;
169255
170256 try {
@@ -201,7 +287,7 @@ function s3auth(r) {
201287
202288 var signature ;
203289
204- var credentials = readCredentials ( ) ;
290+ var credentials = readCredentials ( r ) ;
205291 if ( sigver == '2' ) {
206292 signature = signatureV2 ( r , bucket , credentials ) ;
207293 } else {
@@ -211,8 +297,14 @@ function s3auth(r) {
211297 return signature ;
212298}
213299
214- function s3SecurityToken ( ) {
215- var credentials = readCredentials ( ) ;
300+ /**
301+ * Get the current session token from the instance profile credential cache.
302+ *
303+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
304+ * @returns {string } current session token or empty string
305+ */
306+ function s3SecurityToken ( r ) {
307+ var credentials = readCredentials ( r ) ;
216308 if ( credentials . sessionToken ) {
217309 return credentials . sessionToken ;
218310 }
@@ -782,7 +874,21 @@ var maxValidityOffsetMs = 4.5 * 60 * 100;
782874 * @returns {Promise<void> }
783875 */
784876async function fetchCredentials ( r ) {
785- var current = readCredentials ( ) ;
877+ // If we are not using an AWS instance profile to set our credentials we
878+ // exit quickly and don't write a credentials file.
879+ if ( process . env [ 'S3_ACCESS_KEY_ID' ] && process . env [ 'S3_SECRET_KEY' ] ) {
880+ r . return ( 200 ) ;
881+ return ;
882+ }
883+
884+ try {
885+ var current = readCredentials ( r ) ;
886+ } catch ( e ) {
887+ _debug_log ( r , `Could not read credentials: ${ e } ` ) ;
888+ r . return ( 500 ) ;
889+ return ;
890+ }
891+
786892 if ( current ) {
787893 var exp = new Date ( current . expiration ) . getTime ( ) - maxValidityOffsetMs ;
788894 if ( now . getTime ( ) < exp ) {
@@ -793,13 +899,6 @@ async function fetchCredentials(r) {
793899
794900 var credentials ;
795901
796- // If we are not using an AWS instance profile to set our credentials we
797- // exit quickly and don't write a credentials file.
798- if ( process . env [ 'S3_ACCESS_KEY_ID' ] && process . env [ 'S3_SECRET_KEY' ] ) {
799- r . return ( 200 ) ;
800- return ;
801- }
802-
803902 _debug_log ( r , 'Cached credentials are expired or not present, requesting new ones' ) ;
804903
805904 if ( process . env [ 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' ] ) {
@@ -821,9 +920,9 @@ async function fetchCredentials(r) {
821920 }
822921 }
823922 try {
824- fs . writeFileSync ( _credentialsTempFile ( ) , JSON . stringify ( credentials ) ) ;
923+ writeCredentials ( r , credentials ) ;
825924 } catch ( e ) {
826- _debug_log ( r , ' Could not write credentials file: ' + JSON . stringify ( e ) ) ;
925+ _debug_log ( r , ` Could not write credentials: ${ e } ` ) ;
827926 r . return ( 500 ) ;
828927 return ;
829928 }
@@ -899,6 +998,7 @@ export default {
899998 awsHeaderDate,
900999 fetchCredentials,
9011000 readCredentials,
1001+ writeCredentials,
9021002 s3date,
9031003 s3auth,
9041004 s3SecurityToken,
0 commit comments