From d1105e3a2619c6101ac01c2e7d9664526559b880 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 16 Jul 2024 16:08:00 -0400 Subject: [PATCH] chore(credential-provider-node): emit warning when AWS_PROFILE is set alongside ENV credentials (#6277) --- .../credential-provider-node.integ.spec.ts | 19 ++++++++ .../src/defaultProvider.spec.ts | 6 +++ .../src/defaultProvider.ts | 45 +++++++++++++++---- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts index 3b83e504cc33c..ac6485335e59c 100644 --- a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts +++ b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts @@ -293,6 +293,25 @@ describe("credential-provider-node integration test", () => { credentialScope: "us-env-1", }); }); + + it("should (for now) resolve AWS_PROFILE instead of static credentials from ENV if both are set. However, this is subject to change.", async () => { + process.env.AWS_ACCESS_KEY_ID = "ENV_ACCESS_KEY"; + process.env.AWS_SECRET_ACCESS_KEY = "ENV_SECRET_KEY"; + process.env.AWS_PROFILE = "default"; + + Object.assign(iniProfileData.default, { + aws_access_key_id: "INI_STATIC_ACCESS_KEY", + aws_secret_access_key: "INI_STATIC_SECRET_KEY", + }); + + await sts.getCallerIdentity({}); + const credentials = await sts.config.credentials(); + + expect(credentials).toEqual({ + accessKeyId: "INI_STATIC_ACCESS_KEY", + secretAccessKey: "INI_STATIC_SECRET_KEY", + }); + }); }); describe("fromSSO", () => { diff --git a/packages/credential-provider-node/src/defaultProvider.spec.ts b/packages/credential-provider-node/src/defaultProvider.spec.ts index b02f12d204715..03924600cab8e 100644 --- a/packages/credential-provider-node/src/defaultProvider.spec.ts +++ b/packages/credential-provider-node/src/defaultProvider.spec.ts @@ -34,6 +34,12 @@ describe(defaultProvider.name, () => { const mockInit = { profile: "mockProfile", + logger: { + debug() {}, + info() {}, + warn() {}, + error() {}, + }, }; const mockEnvFn = jest.fn().mockImplementation(() => credentials()); diff --git a/packages/credential-provider-node/src/defaultProvider.ts b/packages/credential-provider-node/src/defaultProvider.ts index 65b8c4b2cf29e..f7213f7ec8d71 100644 --- a/packages/credential-provider-node/src/defaultProvider.ts +++ b/packages/credential-provider-node/src/defaultProvider.ts @@ -1,4 +1,4 @@ -import { fromEnv } from "@aws-sdk/credential-provider-env"; +import { ENV_KEY, ENV_SECRET, fromEnv } from "@aws-sdk/credential-provider-env"; import type { FromHttpOptions } from "@aws-sdk/credential-provider-http"; import type { FromIniInit } from "@aws-sdk/credential-provider-ini"; import type { FromProcessInit } from "@aws-sdk/credential-provider-process"; @@ -21,6 +21,11 @@ export type DefaultProviderInit = FromIniInit & (FromSSOInit & Partial) & FromTokenFileInit; +/** + * @internal + */ +let multipleCredentialSourceWarningEmitted = false; + /** * Creates a credential provider that will attempt to find credentials from the * following sources (listed in order of precedence): @@ -58,14 +63,36 @@ export type DefaultProviderInit = FromIniInit & export const defaultProvider = (init: DefaultProviderInit = {}): MemoizedProvider => memoize( chain( - ...(init.profile || process.env[ENV_PROFILE] - ? [] - : [ - async () => { - init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromEnv"); - return fromEnv(init)(); - }, - ]), + async () => { + const profile = init.profile ?? process.env[ENV_PROFILE]; + if (profile) { + const envStaticCredentialsAreSet = process.env[ENV_KEY] && process.env[ENV_SECRET]; + if (envStaticCredentialsAreSet) { + if (!multipleCredentialSourceWarningEmitted) { + const warnFn = + init.logger?.warn && init.logger?.constructor?.name !== "NoOpLogger" ? init.logger.warn : console.warn; + warnFn( + `@aws-sdk/credential-provider-node - defaultProvider::fromEnv WARNING: + Multiple credential sources detected: + Both AWS_PROFILE and the pair AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY static credentials are set. + This SDK will proceed with the AWS_PROFILE value. + + However, a future version may change this behavior to prefer the ENV static credentials. + Please ensure that your environment only sets either the AWS_PROFILE or the + AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY pair. +` + ); + multipleCredentialSourceWarningEmitted = true; + } + throw new CredentialsProviderError("AWS_PROFILE is set, skipping fromEnv provider.", { + logger: init.logger, + tryNextLink: true, + }); + } + } + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromEnv"); + return fromEnv(init)(); + }, async () => { init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromSSO"); const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoSession } = init;