Skip to content

Commit 6357a86

Browse files
committed
fixes sts creds provider env var resolution
1 parent 2768d01 commit 6357a86

File tree

3 files changed

+118
-44
lines changed

3 files changed

+118
-44
lines changed

src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
#pragma once
77

88
#include <aws/core/Core_EXPORTS.h>
9-
#include <aws/core/http/Scheme.h>
10-
#include <aws/core/http/Version.h>
119
#include <aws/core/Region.h>
12-
#include <aws/core/utils/memory/stl/AWSString.h>
1310
#include <aws/core/http/HttpTypes.h>
11+
#include <aws/core/http/Scheme.h>
12+
#include <aws/core/http/Version.h>
1413
#include <aws/core/utils/Array.h>
14+
#include <aws/core/utils/StringUtils.h>
15+
#include <aws/core/utils/memory/stl/AWSString.h>
1516
#include <aws/crt/Optional.h>
1617
#include <smithy/tracing/TelemetryProvider.h>
18+
1719
#include <memory>
1820

1921
namespace Aws
@@ -447,6 +449,16 @@ namespace Aws
447449
static Aws::String LoadConfigFromEnvOrProfile(const Aws::String& envKey, const Aws::String& profile,
448450
const Aws::String& profileProperty, const Aws::Vector<Aws::String>& allowedValues,
449451
const Aws::String& defaultValue);
452+
/**
453+
* A helper function to read config value from env variable or aws profile config. Addresses a problem in
454+
* LoadConfigFromEnvOrProfile where env variables values are always mapped to their lower case equivalent.
455+
* This fails for cases where ENV vars need to be case sensitive in instances like AWS_ROLE_ARN can have
456+
* camel case values.
457+
*/
458+
static Aws::String LoadConfigFromEnvOrProfileCaseSensitive(
459+
const Aws::String& envKey, const Aws::String& profile, const Aws::String& profileProperty,
460+
const Aws::Vector<Aws::String>& allowedValues, const Aws::String& defaultValue,
461+
const std::function<Aws::String(const char*)>& envValueMapping = Utils::StringUtils::ToLower);
450462

451463
/**
452464
* A wrapper for interfacing with telemetry functionality. Defaults to Noop provider.

src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@ static const char* AWS_METADATA_SERVICE_TIMEOUT_ENV_VAR = "AWS_METADATA_SERVICE_
4545
static const char* AWS_METADATA_SERVICE_TIMEOUT_CONFIG_VAR = "metadata_service_timeout";
4646
static const char* AWS_METADATA_SERVICE_NUM_ATTEMPTS_ENV_VAR = "AWS_METADATA_SERVICE_NUM_ATTEMPTS";
4747
static const char* AWS_METADATA_SERVICE_NUM_ATTEMPTS_CONFIG_VAR = "metadata_service_num_attempts";
48-
static const char* AWS_IAM_ROLE_ARN_ENV_VAR = "AWS_IAM_ROLE_ARN";
48+
static const char* AWS_IAM_ROLE_ARN_ENV_VAR = "AWS_ROLE_ARN";
49+
static const char* AWS_IAM_ROLE_ARN_ENV_VAR_COMPAT = "AWS_IAM_ROLE_ARN";
4950
static const char* AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION = "role_arn";
50-
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR = "AWS_IAM_ROLE_SESSION_NAME";
51+
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR = "AWS_ROLE_SESSION_NAME";
52+
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT = "AWS_IAM_ROLE_SESSION_NAME";
5153
static const char* AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION = "role_session_name";
5254
static const char* AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR = "AWS_WEB_IDENTITY_TOKEN_FILE";
5355
static const char* AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION = "web_identity_token_file";
@@ -327,23 +329,34 @@ void setConfigFromEnvOrProfile(ClientConfiguration &config)
327329
// Uses default retry mode with the specified max attempts from metadata_service_num_attempts
328330
config.credentialProviderConfig.imdsConfig.imdsRetryStrategy = InitRetryStrategy(attempts, "");
329331

330-
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_IAM_ROLE_ARN_ENV_VAR,
331-
config.profileName,
332-
AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION,
333-
{}, /* allowed values */
334-
"" /* default value */);
335-
336-
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_IAM_ROLE_SESSION_NAME_ENV_VAR,
337-
config.profileName,
338-
AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
339-
{}, /* allowed values */
340-
"" /* default value */);
341-
342-
config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR,
343-
config.profileName,
344-
AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION,
345-
{}, /* allowed values */
346-
"" /* default value */);
332+
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
333+
AWS_IAM_ROLE_ARN_ENV_VAR_COMPAT, config.profileName, AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION, {}, /* allowed values */
334+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
335+
336+
// there was a typo in the original environment variable, this exists for backwards compatibility
337+
if (config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn.empty()) {
338+
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
339+
AWS_IAM_ROLE_ARN_ENV_VAR, config.profileName, AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION, {}, /* allowed values */
340+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
341+
}
342+
343+
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
344+
AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT, config.profileName, AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION, {}, /* allowed values */
345+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
346+
347+
// there was a typo in the original environment variable, this exists for backwards compatibility
348+
if (config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName.empty()) {
349+
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName =
350+
ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
351+
AWS_IAM_ROLE_SESSION_NAME_ENV_VAR, config.profileName, AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION, {}, /* allowed values */
352+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
353+
}
354+
355+
config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath =
356+
ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
357+
AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR, config.profileName, AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION,
358+
{}, /* allowed values */
359+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
347360
}
348361

349362
ClientConfiguration::ClientConfiguration()
@@ -558,29 +571,35 @@ Aws::String ClientConfiguration::LoadConfigFromEnvOrProfile(const Aws::String& e
558571
const Aws::Vector<Aws::String>& allowedValues,
559572
const Aws::String& defaultValue)
560573
{
561-
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
562-
if (option.empty()) {
563-
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
564-
}
565-
option = Aws::Utils::StringUtils::ToLower(option.c_str());
566-
if (option.empty()) {
567-
return defaultValue;
568-
}
569-
570-
if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
571-
Aws::OStringStream expectedStr;
572-
expectedStr << "[";
573-
for(const auto& allowed : allowedValues) {
574-
expectedStr << allowed << ";";
575-
}
576-
expectedStr << "]";
574+
return LoadConfigFromEnvOrProfileCaseSensitive(envKey, profile, profileProperty, allowedValues, defaultValue);
575+
}
576+
Aws::String ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(const Aws::String& envKey, const Aws::String& profile,
577+
const Aws::String& profileProperty,
578+
const Aws::Vector<Aws::String>& allowedValues,
579+
const Aws::String& defaultValue,
580+
const std::function<Aws::String(const char*)>& envValueMapping) {
581+
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
582+
if (option.empty()) {
583+
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
584+
}
585+
option = envValueMapping(option.c_str());
586+
if (option.empty()) {
587+
return defaultValue;
588+
}
577589

578-
AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option <<
579-
". Using default instead: " << defaultValue <<
580-
". Expected empty or one of: " << expectedStr.str());
581-
option = defaultValue;
590+
if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
591+
Aws::OStringStream expectedStr;
592+
expectedStr << "[";
593+
for (const auto& allowed : allowedValues) {
594+
expectedStr << allowed << ";";
582595
}
583-
return option;
596+
expectedStr << "]";
597+
598+
AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option << ". Using default instead: "
599+
<< defaultValue << ". Expected empty or one of: " << expectedStr.str());
600+
option = defaultValue;
601+
}
602+
return option;
584603
}
585604

586605
} // namespace Client

tests/aws-cpp-sdk-core-integration-tests/STSWebIdentityProviderIntegrationTest.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
#include <aws/cognito-identity/model/SetIdentityPoolRolesRequest.h>
77
#include <aws/core/Aws.h>
88
#include <aws/core/auth/STSCredentialsProvider.h>
9-
#include <aws/core/utils/FileSystemUtils.h>
9+
#include <aws/core/client/ClientConfiguration.h>
1010
#include <aws/core/platform/Environment.h>
11+
#include <aws/core/utils/FileSystemUtils.h>
1112
#include <aws/iam/IAMClient.h>
1213
#include <aws/iam/model/CreateRoleRequest.h>
1314
#include <aws/iam/model/DeleteRolePolicyRequest.h>
@@ -16,10 +17,12 @@
1617
#include <aws/sts/STSClient.h>
1718
#include <aws/sts/model/AssumeRoleRequest.h>
1819
#include <aws/testing/AwsTestHelpers.h>
20+
#include <aws/testing/platform/PlatformTesting.h>
1921
#include <gtest/gtest.h>
2022

2123
using namespace Aws;
2224
using namespace Aws::Client;
25+
using namespace Aws::Environment;
2326
using namespace Aws::Auth;
2427
using namespace Aws::Utils;
2528
using namespace Aws::IAM;
@@ -170,3 +173,43 @@ TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWork) {
170173
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
171174
EXPECT_FALSE(credentials.IsEmpty());
172175
}
176+
177+
TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVar) {
178+
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
179+
const EnvironmentRAII environmentRAII{
180+
{{"AWS_ROLE_ARN", testResourcesRAII.GetRoleArn()}, {"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}}};
181+
const ClientConfiguration config{};
182+
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
183+
AWSCredentials credentials{};
184+
size_t attempts = 0;
185+
bool shouldSleep = false;
186+
do {
187+
if (shouldSleep) {
188+
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
189+
}
190+
credentials = provider.GetAWSCredentials();
191+
shouldSleep = true;
192+
attempts++;
193+
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
194+
EXPECT_FALSE(credentials.IsEmpty());
195+
}
196+
197+
TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVarBackwardsCompat) {
198+
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
199+
const EnvironmentRAII environmentRAII{
200+
{{"AWS_IAM_ROLE_ARN", testResourcesRAII.GetRoleArn()}, {"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}}};
201+
const ClientConfiguration config{};
202+
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
203+
AWSCredentials credentials{};
204+
size_t attempts = 0;
205+
bool shouldSleep = false;
206+
do {
207+
if (shouldSleep) {
208+
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
209+
}
210+
credentials = provider.GetAWSCredentials();
211+
shouldSleep = true;
212+
attempts++;
213+
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
214+
EXPECT_FALSE(credentials.IsEmpty());
215+
}

0 commit comments

Comments
 (0)