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

Workload Identity with AWS & IMDSv2 use expired token #1755

Closed
whs opened this issue Feb 9, 2024 · 0 comments · Fixed by #1765
Closed

Workload Identity with AWS & IMDSv2 use expired token #1755

whs opened this issue Feb 9, 2024 · 0 comments · Fixed by #1765
Assignees
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@whs
Copy link

whs commented Feb 9, 2024

Thanks for stopping by to let us know something could be better!

PLEASE READ: If you have a support contract with Google, please create an issue in the support console instead of filing on GitHub. This will ensure a timely response.

Environment details

  • OS: Linux
  • Node.js version: 16.20.2 (I know it's EOL..)
  • npm version: 8.19.4
  • google-auth-library version: 9.4.0

Steps to reproduce

  1. Setup federation with AWS according to https://cloud.google.com/iam/docs/workload-identity-federation-with-other-clouds . The JSON file should have imdsv2_session_token_url.
  2. Observe that API calls to Google (we're testing with service account signJwt) are working normally
  3. Wait about an hour
  4. Observe that API calls are now failing

The error object we receive is:

GaxiosError
    at Gaxios._request (/app/node_modules/gaxios/build/src/gaxios.js:142:23)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async AwsClient.getAwsRoleName (/app/node_modules/google-auth-library/build/src/auth/awsclient.js:217:26)
    at async AwsRequestSigner.getCredentials (/app/node_modules/google-auth-library/build/src/auth/awsclient.js:110:34)
    at async AwsRequestSigner.getRequestOptions (/app/node_modules/google-auth-library/build/src/auth/awsrequestsigner.js:66:40)
    at async AwsClient.retrieveSubjectToken (/app/node_modules/google-auth-library/build/src/auth/awsclient.js:125:25)
    at async AwsClient.refreshAccessTokenAsync (/app/node_modules/google-auth-library/build/src/auth/baseexternalclient.js:288:30)
    at async AwsClient.getAccessToken (/app/node_modules/google-auth-library/build/src/auth/baseexternalclient.js:165:13)
    [scrubbed] {
  config: {
    url: 'http://169.254.169.254/latest/meta-data/iam/security-credentials',
    method: 'GET',
    responseType: 'text',
    headers: {
      'x-aws-ec2-metadata-token': '<private>',
      'User-Agent': 'google-api-nodejs-client/9.6.1',
      'x-goog-api-client': 'gl-node/16.20.2'
    },
    paramsSerializer: [Function: paramsSerializer],
    validateStatus: [Function: validateStatus],
    errorRedactor: [Function: defaultErrorRedactor]
  },
  response: {
    config: {
      url: 'http://169.254.169.254/latest/meta-data/iam/security-credentials',
      method: 'GET',
      responseType: 'text',
      headers: [Object],
      paramsSerializer: [Function: paramsSerializer],
      validateStatus: [Function: validateStatus],
      errorRedactor: [Function: defaultErrorRedactor]
    },
    data: '',
    headers: {
      connection: 'close',
      'content-length': '0',
      'content-type': 'text/plain',
      date: 'Wed, 07 Feb 2024 05:38:23 GMT',
      server: 'EC2ws'
    },
    status: 401,
    statusText: 'Unauthorized',
    request: {
      responseURL: 'http://169.254.169.254/latest/meta-data/iam/security-credentials'
    }
  },
  error: undefined,
  status: 401,
  [Symbol(gaxios-gaxios-error)]: '6.2.0'
}

I think this is because in

if (!this.awsRequestSigner) {
const metadataHeaders: Headers = {};
// Only retrieve the IMDSv2 session token if both the security credentials and region are
// not retrievable through the environment.
// The credential config contains all the URLs by default but clients may be running this
// where the metadata server is not available and returning the credentials through the environment.
// Removing this check may break them.
if (this.shouldUseMetadataServer() && this.imdsV2SessionTokenUrl) {
metadataHeaders['x-aws-ec2-metadata-token'] =
await this.getImdsV2SessionToken();
}
the IMDSv2 Session Token API is only called once when this.awsRequestSigner is null, then is reused for all subsequent requests.

However in getImdsV2SessionToken the x-aws-ec2-metadata-token-ttl-seconds is only configured to be 300s:

headers: {'x-aws-ec2-metadata-token-ttl-seconds': '300'},

so after the application run for some times the GCP token expires and refreshing of the token fails because it use expired IMDSv2 token.

Semi-related but I checked how the Go SDK has implemented and it seems to be 1:1 match to this behavior and may also have similar problem https://cs.opensource.google/go/x/oauth2/+/refs/tags/v0.17.0:google/internal/externalaccount/aws.go;l=304

@whs whs added priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. labels Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants