Skip to content

Commit

Permalink
Align with other SDKs on IMDSv1 config
Browse files Browse the repository at this point in the history
  • Loading branch information
sbiscigl committed Nov 15, 2023
1 parent f6d2e14 commit 9b1c0e4
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ namespace Aws
*/
Aws::Http::Version version = Http::Version::HTTP_VERSION_2TLS;

/**
* Disable all internal IMDSV1 Calls
*/
bool disableImdsV1 = false;

/**
* A helper function to read config value from env variable or aws profile config
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ namespace Aws
mutable Aws::String m_token;
mutable bool m_tokenRequired;
mutable Aws::String m_region;
bool m_disableIMDSV1 = false;
};

void AWS_CORE_API InitEC2MetadataClient();
Expand Down
18 changes: 18 additions & 0 deletions src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ static const char* USE_REQUEST_COMPRESSION_CONFIG_VAR = "use_request_compression
static const char* REQUEST_MIN_COMPRESSION_SIZE_BYTES_ENV_VAR = "REQUEST_MIN_COMPRESSION_SIZE_BYTES";
static const char* REQUEST_MIN_COMPRESSION_SIZE_BYTES_CONFIG_VAR = "request_min_compression_size_bytes";
static const char* AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV";
static const char* DISABLE_IMDSV1_CONFIG_VAR = "AWS_EC2_METADATA_V1_DISABLED";
static const char* DISABLE_IMDSV1_ENV_VAR = "ec2_metadata_v1_disabled";

Aws::String FilterUserAgentToken(char const * const source)
{
Expand Down Expand Up @@ -205,6 +207,18 @@ void setLegacyClientConfigurationParameters(ClientConfiguration& clientConfig)
}
}

void setConfigFromEnvOrProfile(ClientConfiguration &config)
{
Aws::String disableIMDSv1 = ClientConfiguration::LoadConfigFromEnvOrProfile(DISABLE_IMDSV1_ENV_VAR,
config.profileName,
DISABLE_IMDSV1_CONFIG_VAR,
{"true", "false"},
"false");
if (disableIMDSv1 == "true") {
config.disableImdsV1 = true;
}
}

ClientConfiguration::ClientConfiguration()
{
this->disableIMDS = false;
Expand All @@ -226,6 +240,7 @@ ClientConfiguration::ClientConfiguration()
return;
}
region = Aws::String(Aws::Region::US_EAST_1);
setConfigFromEnvOrProfile(*this);
}

ClientConfiguration::ClientConfiguration(const ClientConfigurationInitValues &configuration)
Expand All @@ -249,6 +264,7 @@ ClientConfiguration::ClientConfiguration(const ClientConfigurationInitValues &co
return;
}
region = Aws::String(Aws::Region::US_EAST_1);
setConfigFromEnvOrProfile(*this);
}

ClientConfiguration::ClientConfiguration(const char* profile, bool shouldDisableIMDS)
Expand Down Expand Up @@ -295,6 +311,7 @@ ClientConfiguration::ClientConfiguration(const char* profile, bool shouldDisable
}

AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "User specified profile: [" << profile << "] is not found, will use the SDK resolved one.");
setConfigFromEnvOrProfile(*this);
}

ClientConfiguration::ClientConfiguration(bool /*useSmartDefaults*/, const char* defaultMode, bool shouldDisableIMDS)
Expand Down Expand Up @@ -323,6 +340,7 @@ ClientConfiguration::ClientConfiguration(bool /*useSmartDefaults*/, const char*
}

Aws::Config::Defaults::SetSmartDefaultsConfigurationParameters(*this, defaultMode, hasEc2MetadataRegion, ec2MetadataRegion);
setConfigFromEnvOrProfile(*this);
}

std::shared_ptr<RetryStrategy> InitRetryStrategy(Aws::String retryMode)
Expand Down
11 changes: 8 additions & 3 deletions src/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ namespace Aws
AWSHttpResourceClient(clientConfiguration, EC2_METADATA_CLIENT_LOG_TAG),
m_endpoint(endpoint),
m_disableIMDS(clientConfiguration.disableIMDS),
m_tokenRequired(true)
m_tokenRequired(true),
m_disableIMDSV1(clientConfiguration.disableImdsV1)
{

}
Expand All @@ -209,6 +210,10 @@ namespace Aws
AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Skipping call to IMDS Service");
return {};
}
if (m_disableIMDSV1) {
AWS_LOGSTREAM_INFO(m_logtag.c_str(), "Attempting to call IMDSv1 Service while disabled");
return {};
}
std::unique_lock<std::recursive_mutex> locker(m_tokenMutex);
if (m_tokenRequired)
{
Expand Down Expand Up @@ -259,7 +264,7 @@ namespace Aws
}
std::unique_lock<std::recursive_mutex> locker(m_tokenMutex);
#if !defined(DISABLE_IMDSV1)
if (!m_tokenRequired) {
if (!m_disableIMDSV1 && !m_tokenRequired) {
return GetDefaultCredentials();
}
#endif
Expand All @@ -280,7 +285,7 @@ namespace Aws
return {};
}
#if !defined(DISABLE_IMDSV1)
else if (result.GetResponseCode() != HttpResponseCode::OK || trimmedTokenString.empty())
if (!m_disableIMDSV1 && (result.GetResponseCode() != HttpResponseCode::OK || trimmedTokenString.empty()))
{
m_tokenRequired = false;
AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Calling EC2MetadataService to get token failed, falling back to less secure way.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,40 @@ namespace
ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[3].GetURIString());
ASSERT_EQ("", cred);
}

TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientSkipsV1CallForConfiguration) {
clientConfig.disableImdsV1 = true;
auto ec2MetadataClient = Aws::MakeShared<Aws::Internal::EC2MetadataClient>(ALLOCATION_TAG, clientConfig);

// This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out.
std::shared_ptr<HttpRequest> tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"),
HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
std::shared_ptr<StandardHttpResponse> tokenResponse = Aws::MakeShared<StandardHttpResponse>(ALLOCATION_TAG, tokenRequest);
tokenResponse->SetResponseCode(HttpResponseCode::NOT_FOUND);
tokenResponse->GetResponseBody() << "error information"; // empty body will be treated as NETWORK_ERROR, hence retryable error.
mockHttpClient->AddResponseToReturn(tokenResponse);

std::shared_ptr<HttpRequest> profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"),
HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
std::shared_ptr<StandardHttpResponse> profileResponse = Aws::MakeShared<StandardHttpResponse>(ALLOCATION_TAG, profileRequest);
std::shared_ptr<StandardHttpResponse> profileResponseRetry = Aws::MakeShared<StandardHttpResponse>(ALLOCATION_TAG, profileRequest);
profileResponseRetry->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE);
profileResponseRetry->GetResponseBody() << "Throttling";
mockHttpClient->AddResponseToReturn(profileResponseRetry);
profileResponse->SetResponseCode(HttpResponseCode::OK);
profileResponse->GetResponseBody() << "credentials";
mockHttpClient->AddResponseToReturn(profileResponse);

auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely();
auto mockRequests = mockHttpClient->GetAllRequestsMade();
ASSERT_EQ(6u, mockRequests.size());
ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString());
ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod());
ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER));
for (size_t i = 1; i < mockRequests.size(); ++i) {
ASSERT_TRUE(mockRequests[i].HasHeader(EC2_IMDS_TOKEN_HEADER));
}
}
#endif

TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithNullResponse)
Expand Down

0 comments on commit 9b1c0e4

Please sign in to comment.