Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align with other SDKs on IMDSv1 config #2755

Merged
merged 1 commit into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
sbiscigl marked this conversation as resolved.
Show resolved Hide resolved
{

}
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
Loading