Skip to content

Commit 6242fa9

Browse files
sai-sunder-scodyoss
authored andcommitted
google/internal: Add AWS Session Token to Metadata Requests
AWS released a new instance metadata service (IMDSv2). IMDSv2 brought a requirement that a session token header is now required on every call to metadata endpoint. Modify the AWS credential retrieval flow to fetch the session token and send it along with the calls to metadata endpoints Change-Id: I539912ab38f5e591658b29a1e7a99d2b828a1128 GitHub-Last-Rev: 29e1f4a GitHub-Pull-Request: #554 Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/390794 Reviewed-by: Cody Oss <[email protected]> Trust: Cody Oss <[email protected]> Run-TryBot: Cody Oss <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Leo Siracusa <[email protected]> Trust: Tyler Bui-Palsulich <[email protected]>
1 parent ee48083 commit 6242fa9

File tree

3 files changed

+205
-53
lines changed

3 files changed

+205
-53
lines changed

google/internal/externalaccount/aws.go

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ const (
5252
// The AWS authorization header name for the security session token if available.
5353
awsSecurityTokenHeader = "x-amz-security-token"
5454

55+
// The name of the header containing the session token for metadata endpoint calls
56+
awsIMDSv2SessionTokenHeader = "X-aws-ec2-metadata-token"
57+
58+
awsIMDSv2SessionTtlHeader = "X-aws-ec2-metadata-token-ttl-seconds"
59+
60+
awsIMDSv2SessionTtl = "300"
61+
5562
// The AWS authorization header name for the auto-generated date.
5663
awsDateHeader = "x-amz-date"
5764

@@ -241,6 +248,7 @@ type awsCredentialSource struct {
241248
RegionURL string
242249
RegionalCredVerificationURL string
243250
CredVerificationURL string
251+
IMDSv2SessionTokenURL string
244252
TargetResource string
245253
requestSigner *awsRequestSigner
246254
region string
@@ -268,12 +276,22 @@ func (cs awsCredentialSource) doRequest(req *http.Request) (*http.Response, erro
268276

269277
func (cs awsCredentialSource) subjectToken() (string, error) {
270278
if cs.requestSigner == nil {
271-
awsSecurityCredentials, err := cs.getSecurityCredentials()
279+
awsSessionToken, err := cs.getAWSSessionToken()
280+
if err != nil {
281+
return "", err
282+
}
283+
284+
headers := make(map[string]string)
285+
if awsSessionToken != "" {
286+
headers[awsIMDSv2SessionTokenHeader] = awsSessionToken
287+
}
288+
289+
awsSecurityCredentials, err := cs.getSecurityCredentials(headers)
272290
if err != nil {
273291
return "", err
274292
}
275293

276-
if cs.region, err = cs.getRegion(); err != nil {
294+
if cs.region, err = cs.getRegion(headers); err != nil {
277295
return "", err
278296
}
279297

@@ -340,7 +358,37 @@ func (cs awsCredentialSource) subjectToken() (string, error) {
340358
return url.QueryEscape(string(result)), nil
341359
}
342360

343-
func (cs *awsCredentialSource) getRegion() (string, error) {
361+
func (cs *awsCredentialSource) getAWSSessionToken() (string, error) {
362+
if cs.IMDSv2SessionTokenURL == "" {
363+
return "", nil
364+
}
365+
366+
req, err := http.NewRequest("PUT", cs.IMDSv2SessionTokenURL, nil)
367+
if err != nil {
368+
return "", err
369+
}
370+
371+
req.Header.Add(awsIMDSv2SessionTtlHeader, awsIMDSv2SessionTtl)
372+
373+
resp, err := cs.doRequest(req)
374+
if err != nil {
375+
return "", err
376+
}
377+
defer resp.Body.Close()
378+
379+
respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
380+
if err != nil {
381+
return "", err
382+
}
383+
384+
if resp.StatusCode != 200 {
385+
return "", fmt.Errorf("oauth2/google: unable to retrieve AWS session token - %s", string(respBody))
386+
}
387+
388+
return string(respBody), nil
389+
}
390+
391+
func (cs *awsCredentialSource) getRegion(headers map[string]string) (string, error) {
344392
if envAwsRegion := getenv("AWS_REGION"); envAwsRegion != "" {
345393
return envAwsRegion, nil
346394
}
@@ -357,6 +405,10 @@ func (cs *awsCredentialSource) getRegion() (string, error) {
357405
return "", err
358406
}
359407

408+
for name, value := range headers {
409+
req.Header.Add(name, value)
410+
}
411+
360412
resp, err := cs.doRequest(req)
361413
if err != nil {
362414
return "", err
@@ -381,7 +433,7 @@ func (cs *awsCredentialSource) getRegion() (string, error) {
381433
return string(respBody[:respBodyEnd]), nil
382434
}
383435

384-
func (cs *awsCredentialSource) getSecurityCredentials() (result awsSecurityCredentials, err error) {
436+
func (cs *awsCredentialSource) getSecurityCredentials(headers map[string]string) (result awsSecurityCredentials, err error) {
385437
if accessKeyID := getenv("AWS_ACCESS_KEY_ID"); accessKeyID != "" {
386438
if secretAccessKey := getenv("AWS_SECRET_ACCESS_KEY"); secretAccessKey != "" {
387439
return awsSecurityCredentials{
@@ -392,12 +444,12 @@ func (cs *awsCredentialSource) getSecurityCredentials() (result awsSecurityCrede
392444
}
393445
}
394446

395-
roleName, err := cs.getMetadataRoleName()
447+
roleName, err := cs.getMetadataRoleName(headers)
396448
if err != nil {
397449
return
398450
}
399451

400-
credentials, err := cs.getMetadataSecurityCredentials(roleName)
452+
credentials, err := cs.getMetadataSecurityCredentials(roleName, headers)
401453
if err != nil {
402454
return
403455
}
@@ -413,7 +465,7 @@ func (cs *awsCredentialSource) getSecurityCredentials() (result awsSecurityCrede
413465
return credentials, nil
414466
}
415467

416-
func (cs *awsCredentialSource) getMetadataSecurityCredentials(roleName string) (awsSecurityCredentials, error) {
468+
func (cs *awsCredentialSource) getMetadataSecurityCredentials(roleName string, headers map[string]string) (awsSecurityCredentials, error) {
417469
var result awsSecurityCredentials
418470

419471
req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", cs.CredVerificationURL, roleName), nil)
@@ -422,6 +474,10 @@ func (cs *awsCredentialSource) getMetadataSecurityCredentials(roleName string) (
422474
}
423475
req.Header.Add("Content-Type", "application/json")
424476

477+
for name, value := range headers {
478+
req.Header.Add(name, value)
479+
}
480+
425481
resp, err := cs.doRequest(req)
426482
if err != nil {
427483
return result, err
@@ -441,7 +497,7 @@ func (cs *awsCredentialSource) getMetadataSecurityCredentials(roleName string) (
441497
return result, err
442498
}
443499

444-
func (cs *awsCredentialSource) getMetadataRoleName() (string, error) {
500+
func (cs *awsCredentialSource) getMetadataRoleName(headers map[string]string) (string, error) {
445501
if cs.CredVerificationURL == "" {
446502
return "", errors.New("oauth2/google: unable to determine the AWS metadata server security credentials endpoint")
447503
}
@@ -451,6 +507,10 @@ func (cs *awsCredentialSource) getMetadataRoleName() (string, error) {
451507
return "", err
452508
}
453509

510+
for name, value := range headers {
511+
req.Header.Add(name, value)
512+
}
513+
454514
resp, err := cs.doRequest(req)
455515
if err != nil {
456516
return "", err

0 commit comments

Comments
 (0)