diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) | |
download | ydb-718c552901d703c502ccbefdfc3c9028d608b947.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal')
-rw-r--r-- | contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp | 916 |
1 files changed, 458 insertions, 458 deletions
diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp index 24145e4d92..7dd2bd53ad 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp @@ -1,7 +1,7 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ #include <aws/core/internal/AWSHttpResourceClient.h> #include <aws/core/client/DefaultRetryStrategy.h> @@ -10,31 +10,31 @@ #include <aws/core/http/HttpResponse.h> #include <aws/core/utils/logging/LogMacros.h> #include <aws/core/utils/StringUtils.h> -#include <aws/core/utils/HashingUtils.h> +#include <aws/core/utils/HashingUtils.h> #include <aws/core/platform/Environment.h> #include <aws/core/client/AWSError.h> #include <aws/core/client/CoreErrors.h> -#include <aws/core/utils/xml/XmlSerializer.h> -#include <mutex> +#include <aws/core/utils/xml/XmlSerializer.h> +#include <mutex> #include <sstream> -using namespace Aws; +using namespace Aws; using namespace Aws::Utils; using namespace Aws::Utils::Logging; -using namespace Aws::Utils::Xml; +using namespace Aws::Utils::Xml; using namespace Aws::Http; using namespace Aws::Client; using namespace Aws::Internal; -static const char EC2_SECURITY_CREDENTIALS_RESOURCE[] = "/latest/meta-data/iam/security-credentials"; -static const char EC2_REGION_RESOURCE[] = "/latest/meta-data/placement/availability-zone"; -static const char EC2_IMDS_TOKEN_RESOURCE[] = "/latest/api/token"; -static const char EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE[] = "21600"; -static const char EC2_IMDS_TOKEN_TTL_HEADER[] = "x-aws-ec2-metadata-token-ttl-seconds"; -static const char EC2_IMDS_TOKEN_HEADER[] = "x-aws-ec2-metadata-token"; -static const char RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG[] = "AWSHttpResourceClient"; -static const char EC2_METADATA_CLIENT_LOG_TAG[] = "EC2MetadataClient"; -static const char ECS_CREDENTIALS_CLIENT_LOG_TAG[] = "ECSCredentialsClient"; +static const char EC2_SECURITY_CREDENTIALS_RESOURCE[] = "/latest/meta-data/iam/security-credentials"; +static const char EC2_REGION_RESOURCE[] = "/latest/meta-data/placement/availability-zone"; +static const char EC2_IMDS_TOKEN_RESOURCE[] = "/latest/api/token"; +static const char EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE[] = "21600"; +static const char EC2_IMDS_TOKEN_TTL_HEADER[] = "x-aws-ec2-metadata-token-ttl-seconds"; +static const char EC2_IMDS_TOKEN_HEADER[] = "x-aws-ec2-metadata-token"; +static const char RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG[] = "AWSHttpResourceClient"; +static const char EC2_METADATA_CLIENT_LOG_TAG[] = "EC2MetadataClient"; +static const char ECS_CREDENTIALS_CLIENT_LOG_TAG[] = "ECSCredentialsClient"; namespace Aws { @@ -43,464 +43,464 @@ namespace Aws Aws::String ComputeUserAgentString(); } - namespace Internal - { - static ClientConfiguration MakeDefaultHttpResourceClientConfiguration(const char *logtag) - { - ClientConfiguration res; - - res.maxConnections = 2; - res.scheme = Scheme::HTTP; - - #if defined(WIN32) && defined(BYPASS_DEFAULT_PROXY) - // For security reasons, we must bypass any proxy settings when fetching sensitive information, for example - // user credentials. On Windows, IXMLHttpRequest2 does not support bypassing proxy settings, therefore, - // we force using WinHTTP client. On POSIX systems, CURL is set to bypass proxy settings by default. - res.httpLibOverride = TransferLibType::WIN_HTTP_CLIENT; - AWS_LOGSTREAM_INFO(logtag, "Overriding the current HTTP client to WinHTTP to bypass proxy settings."); - #else - (void) logtag; // To disable warning about unused variable - #endif - // Explicitly set the proxy settings to empty/zero to avoid relying on defaults that could potentially change - // in the future. - res.proxyHost = ""; - res.proxyUserName = ""; - res.proxyPassword = ""; - res.proxyPort = 0; - - // EC2MetadataService throttles by delaying the response so the service client should set a large read timeout. - // EC2MetadataService delay is in order of seconds so it only make sense to retry after a couple of seconds. - res.connectTimeoutMs = 1000; - res.requestTimeoutMs = 1000; - res.retryStrategy = Aws::MakeShared<DefaultRetryStrategy>(RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG, 1, 1000); - - return res; - } - - AWSHttpResourceClient::AWSHttpResourceClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* logtag) - : m_logtag(logtag), m_retryStrategy(clientConfiguration.retryStrategy), m_httpClient(nullptr) - { - AWS_LOGSTREAM_INFO(m_logtag.c_str(), - "Creating AWSHttpResourceClient with max connections " - << clientConfiguration.maxConnections - << " and scheme " - << SchemeMapper::ToString(clientConfiguration.scheme)); - - m_httpClient = CreateHttpClient(clientConfiguration); - } - - AWSHttpResourceClient::AWSHttpResourceClient(const char* logtag) - : AWSHttpResourceClient(MakeDefaultHttpResourceClientConfiguration(logtag), logtag) - { - } - - AWSHttpResourceClient::~AWSHttpResourceClient() - { - } - - Aws::String AWSHttpResourceClient::GetResource(const char* endpoint, const char* resource, const char* authToken) const - { - return GetResourceWithAWSWebServiceResult(endpoint, resource, authToken).GetPayload(); - } - - AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const char *endpoint, const char *resource, const char *authToken) const + namespace Internal + { + static ClientConfiguration MakeDefaultHttpResourceClientConfiguration(const char *logtag) + { + ClientConfiguration res; + + res.maxConnections = 2; + res.scheme = Scheme::HTTP; + + #if defined(WIN32) && defined(BYPASS_DEFAULT_PROXY) + // For security reasons, we must bypass any proxy settings when fetching sensitive information, for example + // user credentials. On Windows, IXMLHttpRequest2 does not support bypassing proxy settings, therefore, + // we force using WinHTTP client. On POSIX systems, CURL is set to bypass proxy settings by default. + res.httpLibOverride = TransferLibType::WIN_HTTP_CLIENT; + AWS_LOGSTREAM_INFO(logtag, "Overriding the current HTTP client to WinHTTP to bypass proxy settings."); + #else + (void) logtag; // To disable warning about unused variable + #endif + // Explicitly set the proxy settings to empty/zero to avoid relying on defaults that could potentially change + // in the future. + res.proxyHost = ""; + res.proxyUserName = ""; + res.proxyPassword = ""; + res.proxyPort = 0; + + // EC2MetadataService throttles by delaying the response so the service client should set a large read timeout. + // EC2MetadataService delay is in order of seconds so it only make sense to retry after a couple of seconds. + res.connectTimeoutMs = 1000; + res.requestTimeoutMs = 1000; + res.retryStrategy = Aws::MakeShared<DefaultRetryStrategy>(RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG, 1, 1000); + + return res; + } + + AWSHttpResourceClient::AWSHttpResourceClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* logtag) + : m_logtag(logtag), m_retryStrategy(clientConfiguration.retryStrategy), m_httpClient(nullptr) + { + AWS_LOGSTREAM_INFO(m_logtag.c_str(), + "Creating AWSHttpResourceClient with max connections " + << clientConfiguration.maxConnections + << " and scheme " + << SchemeMapper::ToString(clientConfiguration.scheme)); + + m_httpClient = CreateHttpClient(clientConfiguration); + } + + AWSHttpResourceClient::AWSHttpResourceClient(const char* logtag) + : AWSHttpResourceClient(MakeDefaultHttpResourceClientConfiguration(logtag), logtag) + { + } + + AWSHttpResourceClient::~AWSHttpResourceClient() + { + } + + Aws::String AWSHttpResourceClient::GetResource(const char* endpoint, const char* resource, const char* authToken) const + { + return GetResourceWithAWSWebServiceResult(endpoint, resource, authToken).GetPayload(); + } + + AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const char *endpoint, const char *resource, const char *authToken) const + { + Aws::StringStream ss; + ss << endpoint; + if (resource) + { + ss << resource; + } + std::shared_ptr<HttpRequest> request(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + + request->SetUserAgent(ComputeUserAgentString()); + + if (authToken) + { + request->SetHeaderValue(Aws::Http::AWS_AUTHORIZATION_HEADER, authToken); + } + + return GetResourceWithAWSWebServiceResult(request); + } + + AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const std::shared_ptr<HttpRequest> &httpRequest) const { - Aws::StringStream ss; - ss << endpoint; - if (resource) - { - ss << resource; - } - std::shared_ptr<HttpRequest> request(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - - request->SetUserAgent(ComputeUserAgentString()); - - if (authToken) - { - request->SetHeaderValue(Aws::Http::AWS_AUTHORIZATION_HEADER, authToken); - } - - return GetResourceWithAWSWebServiceResult(request); - } - - AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const std::shared_ptr<HttpRequest> &httpRequest) const - { - AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Retrieving credentials from " << httpRequest->GetURIString()); - - for (long retries = 0;; retries++) - { - std::shared_ptr<HttpResponse> response(m_httpClient->MakeRequest(httpRequest)); - - if (response->GetResponseCode() == HttpResponseCode::OK) - { - Aws::IStreamBufIterator eos; - return {Aws::String(Aws::IStreamBufIterator(response->GetResponseBody()), eos), response->GetHeaders(), HttpResponseCode::OK}; - } - - const Aws::Client::AWSError<Aws::Client::CoreErrors> error = [this, &response]() { - if (response->HasClientError() || response->GetResponseBody().tellp() < 1) - { - AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed"); - return AWSError<CoreErrors>(CoreErrors::NETWORK_CONNECTION, true); // Retryable - } - else if (m_errorMarshaller) - { - return m_errorMarshaller->Marshall(*response); - } - else - { - const auto responseCode = response->GetResponseCode(); - - AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed with error code " - << static_cast<int>(responseCode)); - return CoreErrorsMapper::GetErrorForHttpResponseCode(responseCode); - } - }(); - - if (!m_retryStrategy->ShouldRetry(error, retries)) - { - AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Can not retrive resource from " << httpRequest->GetURIString()); - return {{}, response->GetHeaders(), error.GetResponseCode()}; - } - auto sleepMillis = m_retryStrategy->CalculateDelayBeforeNextRetry(error, retries); - AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Request failed, now waiting " << sleepMillis << " ms before attempting again."); - m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis)); - } - } - - EC2MetadataClient::EC2MetadataClient(const char* endpoint) - : AWSHttpResourceClient(EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) - { - } - - EC2MetadataClient::EC2MetadataClient(const Aws::Client::ClientConfiguration &clientConfiguration, const char *endpoint) - : AWSHttpResourceClient(clientConfiguration, EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) - { - } - - EC2MetadataClient::~EC2MetadataClient() - { - - } - - Aws::String EC2MetadataClient::GetResource(const char* resourcePath) const - { - return GetResource(m_endpoint.c_str(), resourcePath, nullptr/*authToken*/); - } - - Aws::String EC2MetadataClient::GetDefaultCredentials() const - { - std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); - if (m_tokenRequired) - { - return GetDefaultCredentialsSecurely(); - } - - AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting default credentials for ec2 instance"); - auto result = GetResourceWithAWSWebServiceResult(m_endpoint.c_str(), EC2_SECURITY_CREDENTIALS_RESOURCE, nullptr); - Aws::String credentialsString = result.GetPayload(); - auto httpResponseCode = result.GetResponseCode(); - - // Note, if service is insane, it might return 404 for our initial secure call, - // then when we fall back to insecure call, it might return 401 ask for secure call, - // Then, SDK might get into a recursive loop call situation between secure and insecure call. - if (httpResponseCode == Http::HttpResponseCode::UNAUTHORIZED) - { - m_tokenRequired = true; - return {}; - } - locker.unlock(); - - Aws::String trimmedCredentialsString = StringUtils::Trim(credentialsString.c_str()); - if (trimmedCredentialsString.empty()) return {}; - - Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedCredentialsString, '\n'); - - AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE - << " returned credential string " << trimmedCredentialsString); - - if (securityCredentials.size() == 0) - { - AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Initial call to ec2Metadataservice to get credentials failed"); - return {}; - } - - Aws::StringStream ss; - ss << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; - AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str()); - return GetResource(ss.str().c_str()); + AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Retrieving credentials from " << httpRequest->GetURIString()); + + for (long retries = 0;; retries++) + { + std::shared_ptr<HttpResponse> response(m_httpClient->MakeRequest(httpRequest)); + + if (response->GetResponseCode() == HttpResponseCode::OK) + { + Aws::IStreamBufIterator eos; + return {Aws::String(Aws::IStreamBufIterator(response->GetResponseBody()), eos), response->GetHeaders(), HttpResponseCode::OK}; + } + + const Aws::Client::AWSError<Aws::Client::CoreErrors> error = [this, &response]() { + if (response->HasClientError() || response->GetResponseBody().tellp() < 1) + { + AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed"); + return AWSError<CoreErrors>(CoreErrors::NETWORK_CONNECTION, true); // Retryable + } + else if (m_errorMarshaller) + { + return m_errorMarshaller->Marshall(*response); + } + else + { + const auto responseCode = response->GetResponseCode(); + + AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed with error code " + << static_cast<int>(responseCode)); + return CoreErrorsMapper::GetErrorForHttpResponseCode(responseCode); + } + }(); + + if (!m_retryStrategy->ShouldRetry(error, retries)) + { + AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Can not retrive resource from " << httpRequest->GetURIString()); + return {{}, response->GetHeaders(), error.GetResponseCode()}; + } + auto sleepMillis = m_retryStrategy->CalculateDelayBeforeNextRetry(error, retries); + AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Request failed, now waiting " << sleepMillis << " ms before attempting again."); + m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis)); + } } - Aws::String EC2MetadataClient::GetDefaultCredentialsSecurely() const - { - std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); - if (!m_tokenRequired) - { - return GetDefaultCredentials(); - } + EC2MetadataClient::EC2MetadataClient(const char* endpoint) + : AWSHttpResourceClient(EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) + { + } - Aws::StringStream ss; - ss << m_endpoint << EC2_IMDS_TOKEN_RESOURCE; - std::shared_ptr<HttpRequest> tokenRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_PUT, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - tokenRequest->SetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER, EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE); - auto userAgentString = ComputeUserAgentString(); - tokenRequest->SetUserAgent(userAgentString); - AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Calling EC2MetadataService to get token"); - auto result = GetResourceWithAWSWebServiceResult(tokenRequest); - Aws::String tokenString = result.GetPayload(); - Aws::String trimmedTokenString = StringUtils::Trim(tokenString.c_str()); - - if (result.GetResponseCode() == HttpResponseCode::BAD_REQUEST) - { - return {}; - } - else if (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."); - return GetDefaultCredentials(); - } - m_token = trimmedTokenString; - locker.unlock(); - ss.str(""); - ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE; - std::shared_ptr<HttpRequest> profileRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - profileRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); - profileRequest->SetUserAgent(userAgentString); - Aws::String profileString = GetResourceWithAWSWebServiceResult(profileRequest).GetPayload(); - - Aws::String trimmedProfileString = StringUtils::Trim(profileString.c_str()); - Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedProfileString, '\n'); - - AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE - << " with token returned profile string " << trimmedProfileString); - if (securityCredentials.size() == 0) - { - AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Calling EC2Metadataservice to get profiles failed"); - return {}; - } - - ss.str(""); - ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; - std::shared_ptr<HttpRequest> credentialsRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - credentialsRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); - credentialsRequest->SetUserAgent(userAgentString); - AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str() << " with token."); - return GetResourceWithAWSWebServiceResult(credentialsRequest).GetPayload(); - } - - Aws::String EC2MetadataClient::GetCurrentRegion() const + EC2MetadataClient::EC2MetadataClient(const Aws::Client::ClientConfiguration &clientConfiguration, const char *endpoint) + : AWSHttpResourceClient(clientConfiguration, EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) { - if (!m_region.empty()) - { - return m_region; - } - - AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting current region for ec2 instance"); - - Aws::StringStream ss; - ss << m_endpoint << EC2_REGION_RESOURCE; - std::shared_ptr<HttpRequest> regionRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - { - std::lock_guard<std::recursive_mutex> locker(m_tokenMutex); - if (m_tokenRequired) - { - regionRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, m_token); - } - } - regionRequest->SetUserAgent(ComputeUserAgentString()); - Aws::String azString = GetResourceWithAWSWebServiceResult(regionRequest).GetPayload(); - - if (azString.empty()) - { - AWS_LOGSTREAM_INFO(m_logtag.c_str() , - "Unable to pull region from instance metadata service "); - return {}; - } - - Aws::String trimmedAZString = StringUtils::Trim(azString.c_str()); - AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " - << EC2_REGION_RESOURCE << " , returned credential string " << trimmedAZString); - - Aws::String region; - region.reserve(trimmedAZString.length()); - - bool digitFound = false; - for (auto character : trimmedAZString) - { - if(digitFound && !isdigit(character)) - { - break; - } - if (isdigit(character)) - { - digitFound = true; - } - - region.append(1, character); - } - - AWS_LOGSTREAM_INFO(m_logtag.c_str(), "Detected current region as " << region); - m_region = region; - return region; } - #ifdef _MSC_VER - // VS2015 compiler's bug, warning s_ec2metadataClient: symbol will be dynamically initialized (implementation limitation) - AWS_SUPPRESS_WARNING(4592, - static std::shared_ptr<EC2MetadataClient> s_ec2metadataClient(nullptr); - ) - #else - static std::shared_ptr<EC2MetadataClient> s_ec2metadataClient(nullptr); - #endif - - void InitEC2MetadataClient() - { - if (s_ec2metadataClient) + EC2MetadataClient::~EC2MetadataClient() + { + + } + + Aws::String EC2MetadataClient::GetResource(const char* resourcePath) const + { + return GetResource(m_endpoint.c_str(), resourcePath, nullptr/*authToken*/); + } + + Aws::String EC2MetadataClient::GetDefaultCredentials() const + { + std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); + if (m_tokenRequired) { - return; + return GetDefaultCredentialsSecurely(); } - s_ec2metadataClient = Aws::MakeShared<EC2MetadataClient>(EC2_METADATA_CLIENT_LOG_TAG); - } - - void CleanupEC2MetadataClient() - { - if (!s_ec2metadataClient) + + AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting default credentials for ec2 instance"); + auto result = GetResourceWithAWSWebServiceResult(m_endpoint.c_str(), EC2_SECURITY_CREDENTIALS_RESOURCE, nullptr); + Aws::String credentialsString = result.GetPayload(); + auto httpResponseCode = result.GetResponseCode(); + + // Note, if service is insane, it might return 404 for our initial secure call, + // then when we fall back to insecure call, it might return 401 ask for secure call, + // Then, SDK might get into a recursive loop call situation between secure and insecure call. + if (httpResponseCode == Http::HttpResponseCode::UNAUTHORIZED) { - return; + m_tokenRequired = true; + return {}; + } + locker.unlock(); + + Aws::String trimmedCredentialsString = StringUtils::Trim(credentialsString.c_str()); + if (trimmedCredentialsString.empty()) return {}; + + Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedCredentialsString, '\n'); + + AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE + << " returned credential string " << trimmedCredentialsString); + + if (securityCredentials.size() == 0) + { + AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Initial call to ec2Metadataservice to get credentials failed"); + return {}; } - s_ec2metadataClient = nullptr; - } - std::shared_ptr<EC2MetadataClient> GetEC2MetadataClient() + Aws::StringStream ss; + ss << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; + AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str()); + return GetResource(ss.str().c_str()); + } + + Aws::String EC2MetadataClient::GetDefaultCredentialsSecurely() const { - return s_ec2metadataClient; + std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); + if (!m_tokenRequired) + { + return GetDefaultCredentials(); + } + + Aws::StringStream ss; + ss << m_endpoint << EC2_IMDS_TOKEN_RESOURCE; + std::shared_ptr<HttpRequest> tokenRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_PUT, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + tokenRequest->SetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER, EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE); + auto userAgentString = ComputeUserAgentString(); + tokenRequest->SetUserAgent(userAgentString); + AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Calling EC2MetadataService to get token"); + auto result = GetResourceWithAWSWebServiceResult(tokenRequest); + Aws::String tokenString = result.GetPayload(); + Aws::String trimmedTokenString = StringUtils::Trim(tokenString.c_str()); + + if (result.GetResponseCode() == HttpResponseCode::BAD_REQUEST) + { + return {}; + } + else if (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."); + return GetDefaultCredentials(); + } + m_token = trimmedTokenString; + locker.unlock(); + ss.str(""); + ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE; + std::shared_ptr<HttpRequest> profileRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + profileRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); + profileRequest->SetUserAgent(userAgentString); + Aws::String profileString = GetResourceWithAWSWebServiceResult(profileRequest).GetPayload(); + + Aws::String trimmedProfileString = StringUtils::Trim(profileString.c_str()); + Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedProfileString, '\n'); + + AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE + << " with token returned profile string " << trimmedProfileString); + if (securityCredentials.size() == 0) + { + AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Calling EC2Metadataservice to get profiles failed"); + return {}; + } + + ss.str(""); + ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; + std::shared_ptr<HttpRequest> credentialsRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + credentialsRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); + credentialsRequest->SetUserAgent(userAgentString); + AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str() << " with token."); + return GetResourceWithAWSWebServiceResult(credentialsRequest).GetPayload(); } - - ECSCredentialsClient::ECSCredentialsClient(const char* resourcePath, const char* endpoint, const char* token) - : AWSHttpResourceClient(ECS_CREDENTIALS_CLIENT_LOG_TAG), - m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) + Aws::String EC2MetadataClient::GetCurrentRegion() const + { + if (!m_region.empty()) + { + return m_region; + } + + AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting current region for ec2 instance"); + + Aws::StringStream ss; + ss << m_endpoint << EC2_REGION_RESOURCE; + std::shared_ptr<HttpRequest> regionRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + { + std::lock_guard<std::recursive_mutex> locker(m_tokenMutex); + if (m_tokenRequired) + { + regionRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, m_token); + } + } + regionRequest->SetUserAgent(ComputeUserAgentString()); + Aws::String azString = GetResourceWithAWSWebServiceResult(regionRequest).GetPayload(); + + if (azString.empty()) + { + AWS_LOGSTREAM_INFO(m_logtag.c_str() , + "Unable to pull region from instance metadata service "); + return {}; + } + + Aws::String trimmedAZString = StringUtils::Trim(azString.c_str()); + AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " + << EC2_REGION_RESOURCE << " , returned credential string " << trimmedAZString); + + Aws::String region; + region.reserve(trimmedAZString.length()); + + bool digitFound = false; + for (auto character : trimmedAZString) + { + if(digitFound && !isdigit(character)) + { + break; + } + if (isdigit(character)) + { + digitFound = true; + } + + region.append(1, character); + } + + AWS_LOGSTREAM_INFO(m_logtag.c_str(), "Detected current region as " << region); + m_region = region; + return region; + } + + #ifdef _MSC_VER + // VS2015 compiler's bug, warning s_ec2metadataClient: symbol will be dynamically initialized (implementation limitation) + AWS_SUPPRESS_WARNING(4592, + static std::shared_ptr<EC2MetadataClient> s_ec2metadataClient(nullptr); + ) + #else + static std::shared_ptr<EC2MetadataClient> s_ec2metadataClient(nullptr); + #endif + + void InitEC2MetadataClient() + { + if (s_ec2metadataClient) + { + return; + } + s_ec2metadataClient = Aws::MakeShared<EC2MetadataClient>(EC2_METADATA_CLIENT_LOG_TAG); + } + + void CleanupEC2MetadataClient() + { + if (!s_ec2metadataClient) + { + return; + } + s_ec2metadataClient = nullptr; + } + + std::shared_ptr<EC2MetadataClient> GetEC2MetadataClient() + { + return s_ec2metadataClient; + } + + + ECSCredentialsClient::ECSCredentialsClient(const char* resourcePath, const char* endpoint, const char* token) + : AWSHttpResourceClient(ECS_CREDENTIALS_CLIENT_LOG_TAG), + m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) { } - - ECSCredentialsClient::ECSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* resourcePath, const char* endpoint, const char* token) - : AWSHttpResourceClient(clientConfiguration, ECS_CREDENTIALS_CLIENT_LOG_TAG), - m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) + + ECSCredentialsClient::ECSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* resourcePath, const char* endpoint, const char* token) + : AWSHttpResourceClient(clientConfiguration, ECS_CREDENTIALS_CLIENT_LOG_TAG), + m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) { } - static const char STS_RESOURCE_CLIENT_LOG_TAG[] = "STSResourceClient"; - STSCredentialsClient::STSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration) - : AWSHttpResourceClient(clientConfiguration, STS_RESOURCE_CLIENT_LOG_TAG) - { - SetErrorMarshaller(Aws::MakeUnique<Aws::Client::XmlErrorMarshaller>(STS_RESOURCE_CLIENT_LOG_TAG)); - - Aws::StringStream ss; - if (clientConfiguration.scheme == Aws::Http::Scheme::HTTP) - { - ss << "http://"; - } - else - { - ss << "https://"; - } - - static const int CN_NORTH_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTH_1); - static const int CN_NORTHWEST_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTHWEST_1); - auto hash = Aws::Utils::HashingUtils::HashString(clientConfiguration.region.c_str()); - - ss << "sts." << clientConfiguration.region << ".amazonaws.com"; - if (hash == CN_NORTH_1_HASH || hash == CN_NORTHWEST_1_HASH) - { - ss << ".cn"; - } - m_endpoint = ss.str(); - - AWS_LOGSTREAM_INFO(STS_RESOURCE_CLIENT_LOG_TAG, "Creating STS ResourceClient with endpoint: " << m_endpoint); - } - - STSCredentialsClient::STSAssumeRoleWithWebIdentityResult STSCredentialsClient::GetAssumeRoleWithWebIdentityCredentials(const STSAssumeRoleWithWebIdentityRequest& request) - { - //Calculate query string - Aws::StringStream ss; - ss << "Action=AssumeRoleWithWebIdentity" - << "&Version=2011-06-15" - << "&RoleSessionName=" << Aws::Utils::StringUtils::URLEncode(request.roleSessionName.c_str()) - << "&RoleArn=" << Aws::Utils::StringUtils::URLEncode(request.roleArn.c_str()) - << "&WebIdentityToken=" << Aws::Utils::StringUtils::URLEncode(request.webIdentityToken.c_str()); - - std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(m_endpoint, HttpMethod::HTTP_POST, - Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); - - httpRequest->SetUserAgent(ComputeUserAgentString()); - - std::shared_ptr<Aws::IOStream> body = Aws::MakeShared<Aws::StringStream>("STS_RESOURCE_CLIENT_LOG_TAG"); - *body << ss.str(); - - httpRequest->AddContentBody(body); - body->seekg(0, body->end); - auto streamSize = body->tellg(); - body->seekg(0, body->beg); - Aws::StringStream contentLength; - contentLength << streamSize; - httpRequest->SetContentLength(contentLength.str()); - httpRequest->SetContentType("application/x-www-form-urlencoded"); - - Aws::String credentialsStr = GetResourceWithAWSWebServiceResult(httpRequest).GetPayload(); - - //Parse credentials - STSAssumeRoleWithWebIdentityResult result; - if (credentialsStr.empty()) - { - AWS_LOGSTREAM_WARN(STS_RESOURCE_CLIENT_LOG_TAG, "Get an empty credential from sts"); - return result; - } - - const Utils::Xml::XmlDocument xmlDocument = XmlDocument::CreateFromXmlString(credentialsStr); - XmlNode rootNode = xmlDocument.GetRootElement(); - XmlNode resultNode = rootNode; - if (!rootNode.IsNull() && (rootNode.GetName() != "AssumeRoleWithWebIdentityResult")) - { - resultNode = rootNode.FirstChild("AssumeRoleWithWebIdentityResult"); - } - - if (!resultNode.IsNull()) - { - XmlNode credentialsNode = resultNode.FirstChild("Credentials"); - if (!credentialsNode.IsNull()) - { - XmlNode accessKeyIdNode = credentialsNode.FirstChild("AccessKeyId"); - if (!accessKeyIdNode.IsNull()) - { - result.creds.SetAWSAccessKeyId(accessKeyIdNode.GetText()); - } - - XmlNode secretAccessKeyNode = credentialsNode.FirstChild("SecretAccessKey"); - if (!secretAccessKeyNode.IsNull()) - { - result.creds.SetAWSSecretKey(secretAccessKeyNode.GetText()); - } - - XmlNode sessionTokenNode = credentialsNode.FirstChild("SessionToken"); - if (!sessionTokenNode.IsNull()) - { - result.creds.SetSessionToken(sessionTokenNode.GetText()); - } - - XmlNode expirationNode = credentialsNode.FirstChild("Expiration"); - if (!expirationNode.IsNull()) - { - result.creds.SetExpiration(DateTime(StringUtils::Trim(expirationNode.GetText().c_str()).c_str(), DateFormat::ISO_8601)); - } - } - } - return result; - } - } + static const char STS_RESOURCE_CLIENT_LOG_TAG[] = "STSResourceClient"; + STSCredentialsClient::STSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration) + : AWSHttpResourceClient(clientConfiguration, STS_RESOURCE_CLIENT_LOG_TAG) + { + SetErrorMarshaller(Aws::MakeUnique<Aws::Client::XmlErrorMarshaller>(STS_RESOURCE_CLIENT_LOG_TAG)); + + Aws::StringStream ss; + if (clientConfiguration.scheme == Aws::Http::Scheme::HTTP) + { + ss << "http://"; + } + else + { + ss << "https://"; + } + + static const int CN_NORTH_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTH_1); + static const int CN_NORTHWEST_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTHWEST_1); + auto hash = Aws::Utils::HashingUtils::HashString(clientConfiguration.region.c_str()); + + ss << "sts." << clientConfiguration.region << ".amazonaws.com"; + if (hash == CN_NORTH_1_HASH || hash == CN_NORTHWEST_1_HASH) + { + ss << ".cn"; + } + m_endpoint = ss.str(); + + AWS_LOGSTREAM_INFO(STS_RESOURCE_CLIENT_LOG_TAG, "Creating STS ResourceClient with endpoint: " << m_endpoint); + } + + STSCredentialsClient::STSAssumeRoleWithWebIdentityResult STSCredentialsClient::GetAssumeRoleWithWebIdentityCredentials(const STSAssumeRoleWithWebIdentityRequest& request) + { + //Calculate query string + Aws::StringStream ss; + ss << "Action=AssumeRoleWithWebIdentity" + << "&Version=2011-06-15" + << "&RoleSessionName=" << Aws::Utils::StringUtils::URLEncode(request.roleSessionName.c_str()) + << "&RoleArn=" << Aws::Utils::StringUtils::URLEncode(request.roleArn.c_str()) + << "&WebIdentityToken=" << Aws::Utils::StringUtils::URLEncode(request.webIdentityToken.c_str()); + + std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(m_endpoint, HttpMethod::HTTP_POST, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + + httpRequest->SetUserAgent(ComputeUserAgentString()); + + std::shared_ptr<Aws::IOStream> body = Aws::MakeShared<Aws::StringStream>("STS_RESOURCE_CLIENT_LOG_TAG"); + *body << ss.str(); + + httpRequest->AddContentBody(body); + body->seekg(0, body->end); + auto streamSize = body->tellg(); + body->seekg(0, body->beg); + Aws::StringStream contentLength; + contentLength << streamSize; + httpRequest->SetContentLength(contentLength.str()); + httpRequest->SetContentType("application/x-www-form-urlencoded"); + + Aws::String credentialsStr = GetResourceWithAWSWebServiceResult(httpRequest).GetPayload(); + + //Parse credentials + STSAssumeRoleWithWebIdentityResult result; + if (credentialsStr.empty()) + { + AWS_LOGSTREAM_WARN(STS_RESOURCE_CLIENT_LOG_TAG, "Get an empty credential from sts"); + return result; + } + + const Utils::Xml::XmlDocument xmlDocument = XmlDocument::CreateFromXmlString(credentialsStr); + XmlNode rootNode = xmlDocument.GetRootElement(); + XmlNode resultNode = rootNode; + if (!rootNode.IsNull() && (rootNode.GetName() != "AssumeRoleWithWebIdentityResult")) + { + resultNode = rootNode.FirstChild("AssumeRoleWithWebIdentityResult"); + } + + if (!resultNode.IsNull()) + { + XmlNode credentialsNode = resultNode.FirstChild("Credentials"); + if (!credentialsNode.IsNull()) + { + XmlNode accessKeyIdNode = credentialsNode.FirstChild("AccessKeyId"); + if (!accessKeyIdNode.IsNull()) + { + result.creds.SetAWSAccessKeyId(accessKeyIdNode.GetText()); + } + + XmlNode secretAccessKeyNode = credentialsNode.FirstChild("SecretAccessKey"); + if (!secretAccessKeyNode.IsNull()) + { + result.creds.SetAWSSecretKey(secretAccessKeyNode.GetText()); + } + + XmlNode sessionTokenNode = credentialsNode.FirstChild("SessionToken"); + if (!sessionTokenNode.IsNull()) + { + result.creds.SetSessionToken(sessionTokenNode.GetText()); + } + + XmlNode expirationNode = credentialsNode.FirstChild("Expiration"); + if (!expirationNode.IsNull()) + { + result.creds.SetExpiration(DateTime(StringUtils::Trim(expirationNode.GetText().c_str()).c_str(), DateFormat::ISO_8601)); + } + } + } + return result; + } + } } |