Skip to content

Commit 587c113

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

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: 58 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,33 @@ 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, 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_COMPAT, 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, 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_COMPAT, config.profileName, AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
352+
{}, /* allowed values */
353+
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
354+
}
355+
356+
config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath = ClientConfiguration::LoadConfigFromEnvOrProfile(
357+
AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR, config.profileName, AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION, {}, /* allowed values */
358+
"" /* default value */);
347359
}
348360

349361
ClientConfiguration::ClientConfiguration()
@@ -558,29 +570,35 @@ Aws::String ClientConfiguration::LoadConfigFromEnvOrProfile(const Aws::String& e
558570
const Aws::Vector<Aws::String>& allowedValues,
559571
const Aws::String& defaultValue)
560572
{
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 << "]";
573+
return LoadConfigFromEnvOrProfileCaseSensitive(envKey, profile, profileProperty, allowedValues, defaultValue);
574+
}
575+
Aws::String ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(const Aws::String& envKey, const Aws::String& profile,
576+
const Aws::String& profileProperty,
577+
const Aws::Vector<Aws::String>& allowedValues,
578+
const Aws::String& defaultValue,
579+
const std::function<Aws::String(const char*)>& envValueMapping) {
580+
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
581+
if (option.empty()) {
582+
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
583+
}
584+
option = envValueMapping(option.c_str());
585+
if (option.empty()) {
586+
return defaultValue;
587+
}
577588

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;
589+
if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
590+
Aws::OStringStream expectedStr;
591+
expectedStr << "[";
592+
for (const auto& allowed : allowedValues) {
593+
expectedStr << allowed << ";";
582594
}
583-
return option;
595+
expectedStr << "]";
596+
597+
AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option << ". Using default instead: "
598+
<< defaultValue << ". Expected empty or one of: " << expectedStr.str());
599+
option = defaultValue;
600+
}
601+
return option;
584602
}
585603

586604
} // namespace Client

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
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>
@@ -16,10 +16,12 @@
1616
#include <aws/sts/STSClient.h>
1717
#include <aws/sts/model/AssumeRoleRequest.h>
1818
#include <aws/testing/AwsTestHelpers.h>
19+
#include <aws/testing/platform/PlatformTesting.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,45 @@ 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{{{"AWS_ROLE_ARN", testResourcesRAII.GetRoleArn()},
179+
{"AWS_ROLE_SESSION_NAME", UUID::RandomUUID()},
180+
{"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 environment_raii{{{"AWS_IAM_ROLE_ARN", testResourcesRAII.GetRoleArn()},
200+
{"AWS_IAM_ROLE_SESSION_NAME", UUID::RandomUUID()},
201+
{"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}}};
202+
const ClientConfiguration config{};
203+
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
204+
AWSCredentials credentials{};
205+
size_t attempts = 0;
206+
bool shouldSleep = false;
207+
do {
208+
if (shouldSleep) {
209+
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
210+
}
211+
credentials = provider.GetAWSCredentials();
212+
shouldSleep = true;
213+
attempts++;
214+
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
215+
EXPECT_FALSE(credentials.IsEmpty());
216+
}

0 commit comments

Comments
 (0)