Skip to content

Commit e5e5fd2

Browse files
committed
fixes sts creds provider env var resolution
1 parent 1122413 commit e5e5fd2

File tree

3 files changed

+126
-35
lines changed

3 files changed

+126
-35
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <aws/core/http/Version.h>
1111
#include <aws/core/Region.h>
1212
#include <aws/core/utils/memory/stl/AWSString.h>
13+
#include <aws/core/utils/StringUtils.h>
1314
#include <aws/core/http/HttpTypes.h>
1415
#include <aws/core/utils/Array.h>
1516
#include <aws/crt/Optional.h>
@@ -447,7 +448,18 @@ namespace Aws
447448
static Aws::String LoadConfigFromEnvOrProfile(const Aws::String& envKey, const Aws::String& profile,
448449
const Aws::String& profileProperty, const Aws::Vector<Aws::String>& allowedValues,
449450
const Aws::String& defaultValue);
450-
451+
/**
452+
* A helper function to read config value from env variable or aws profile config. Addresses a problem in
453+
* LoadConfigFromEnvOrProfile where env variables values are always mapped to their lower case equivalent.
454+
* This fails for cases where ENV vars need to be case sensitive in instances like AWS_ROLE_ARN can have
455+
* camel case values.
456+
*/
457+
static Aws::String LoadConfigFromEnvOrProfileCaseSensitive(const Aws::String& envKey,
458+
const Aws::String& profile,
459+
const Aws::String& profileProperty,
460+
const Aws::Vector<Aws::String>& allowedValues,
461+
const Aws::String& defaultValue,
462+
const std::function<Aws::String(const char*)>& envValueMapping = Utils::StringUtils::ToLower);
451463
/**
452464
* A wrapper for interfacing with telemetry functionality. Defaults to Noop provider.
453465
* Provide TelemetryProvider here or via a factory method.

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

Lines changed: 64 additions & 33 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,45 @@ 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,
332+
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(AWS_IAM_ROLE_ARN_ENV_VAR,
331333
config.profileName,
332334
AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION,
333335
{}, /* allowed values */
334-
"" /* default value */);
336+
"" /* default value */,
337+
[](const Aws::String& envValue) -> Aws::String { return envValue; });
335338

336-
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_IAM_ROLE_SESSION_NAME_ENV_VAR,
339+
// there was a typo in the original environment variable, this exists for backwards compatibility
340+
if (config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn.empty()) {
341+
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(AWS_IAM_ROLE_ARN_ENV_VAR_COMPAT,
337342
config.profileName,
338-
AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
343+
AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION,
339344
{}, /* allowed values */
340-
"" /* default value */);
345+
"" /* default value */,
346+
[](const Aws::String& envValue) -> Aws::String { return envValue; });
347+
}
341348

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 */);
349+
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(AWS_IAM_ROLE_SESSION_NAME_ENV_VAR,
350+
config.profileName,
351+
AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
352+
{}, /* allowed values */
353+
"" /* default value */,
354+
[](const Aws::String& envValue) -> Aws::String { return envValue; });
355+
356+
// there was a typo in the original environment variable, this exists for backwards compatibility
357+
if (config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName.empty()) {
358+
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT,
359+
config.profileName,
360+
AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
361+
{}, /* allowed values */
362+
"" /* default value */,
363+
[](const Aws::String& envValue) -> Aws::String { return envValue; });
364+
}
365+
366+
config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR,
367+
config.profileName,
368+
AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION,
369+
{}, /* allowed values */
370+
"" /* default value */);
347371
}
348372

349373
ClientConfiguration::ClientConfiguration()
@@ -558,29 +582,36 @@ Aws::String ClientConfiguration::LoadConfigFromEnvOrProfile(const Aws::String& e
558582
const Aws::Vector<Aws::String>& allowedValues,
559583
const Aws::String& defaultValue)
560584
{
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 << "]";
585+
return LoadConfigFromEnvOrProfileCaseSensitive(envKey, profile, profileProperty, allowedValues, defaultValue);
586+
}
587+
Aws::String ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(const Aws::String& envKey, const Aws::String& profile,
588+
const Aws::String& profileProperty,
589+
const Aws::Vector<Aws::String>& allowedValues,
590+
const Aws::String& defaultValue,
591+
const std::function<Aws::String(const char*)>& envValueMapping) {
592+
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
593+
if (option.empty()) {
594+
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
595+
}
596+
option = envValueMapping(option.c_str());
597+
if (option.empty()) {
598+
return defaultValue;
599+
}
577600

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;
601+
if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
602+
Aws::OStringStream expectedStr;
603+
expectedStr << "[";
604+
for(const auto& allowed : allowedValues) {
605+
expectedStr << allowed << ";";
582606
}
583-
return option;
607+
expectedStr << "]";
608+
609+
AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option <<
610+
". Using default instead: " << defaultValue <<
611+
". Expected empty or one of: " << expectedStr.str());
612+
option = defaultValue;
613+
}
614+
return option;
584615
}
585616

586617
} // namespace Client

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,22 @@
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>
109
#include <aws/core/platform/Environment.h>
10+
#include <aws/core/utils/FileSystemUtils.h>
1111
#include <aws/iam/IAMClient.h>
1212
#include <aws/iam/model/CreateRoleRequest.h>
1313
#include <aws/iam/model/DeleteRolePolicyRequest.h>
1414
#include <aws/iam/model/DeleteRoleRequest.h>
1515
#include <aws/iam/model/PutRolePolicyRequest.h>
1616
#include <aws/sts/STSClient.h>
1717
#include <aws/sts/model/AssumeRoleRequest.h>
18+
#include <aws/testing/platform/PlatformTesting.h>
1819
#include <aws/testing/AwsTestHelpers.h>
1920
#include <gtest/gtest.h>
2021

2122
using namespace Aws;
2223
using namespace Aws::Client;
24+
using namespace Aws::Environment;
2325
using namespace Aws::Auth;
2426
using namespace Aws::Utils;
2527
using namespace Aws::IAM;
@@ -170,3 +172,49 @@ TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWork) {
170172
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
171173
EXPECT_FALSE(credentials.IsEmpty());
172174
}
175+
176+
TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVar) {
177+
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
178+
const EnvironmentRAII environment_raii{{
179+
{"AWS_ROLE_ARN", testResourcesRAII.GetRoleArn()},
180+
{"AWS_ROLE_SESSION_NAME", UUID::RandomUUID()},
181+
{"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}
182+
}};
183+
const ClientConfiguration config{};
184+
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
185+
AWSCredentials credentials{};
186+
size_t attempts = 0;
187+
bool shouldSleep = false;
188+
do {
189+
if (shouldSleep) {
190+
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
191+
}
192+
credentials = provider.GetAWSCredentials();
193+
shouldSleep = true;
194+
attempts++;
195+
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
196+
EXPECT_FALSE(credentials.IsEmpty());
197+
}
198+
199+
TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVarBackwardsCompat) {
200+
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
201+
const EnvironmentRAII environment_raii{{
202+
{"AWS_IAM_ROLE_ARN", testResourcesRAII.GetRoleArn()},
203+
{"AWS_IAM_ROLE_SESSION_NAME", UUID::RandomUUID()},
204+
{"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}
205+
}};
206+
const ClientConfiguration config{};
207+
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
208+
AWSCredentials credentials{};
209+
size_t attempts = 0;
210+
bool shouldSleep = false;
211+
do {
212+
if (shouldSleep) {
213+
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
214+
}
215+
credentials = provider.GetAWSCredentials();
216+
shouldSleep = true;
217+
attempts++;
218+
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
219+
EXPECT_FALSE(credentials.IsEmpty());
220+
}

0 commit comments

Comments
 (0)