Skip to content

Commit

Permalink
Updates for General HTTP Credentials provider (aka TaskRole credentia…
Browse files Browse the repository at this point in the history
…ls provider)
  • Loading branch information
SergeyRyabinin committed Dec 8, 2023
1 parent 1a005b0 commit 7e2ba3a
Show file tree
Hide file tree
Showing 8 changed files with 1,078 additions and 281 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@


#pragma once
#if !defined(AWS_CREDENTIALS_PROVIDER)
#define AWS_CREDENTIALS_PROVIDER

#include <aws/core/Core_EXPORTS.h>
#include <aws/core/utils/UnreferencedParam.h>
Expand All @@ -24,6 +26,8 @@ namespace Aws
{
constexpr int REFRESH_THRESHOLD = 1000 * 60 * 5;

constexpr int AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD = 5 * 1000;

/**
* Returns the full path of the config file.
*/
Expand Down Expand Up @@ -224,54 +228,6 @@ namespace Aws
long m_loadFrequencyMs;
};

/**
* ECS credentials provider implementation that loads credentials from the Amazon
* ECS metadata service or an arbitrary endpoint.
*/
class AWS_CORE_API TaskRoleCredentialsProvider : public AWSCredentialsProvider
{
public:
/**
* Initializes the provider to retrieve credentials from the ECS metadata service every 5 minutes,
* or before it expires.
* @param resourcePath A path appended to the metadata service endpoint.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
TaskRoleCredentialsProvider(const char* resourcePath, long refreshRateMs = REFRESH_THRESHOLD);

/**
* Initializes the provider to retrieve credentials from a provided endpoint every 5 minutes or before it
* expires.
* @param endpoint The full URI to resolve to get credentials.
* @param token An optional authorization token passed to the URI via the 'Authorization' HTTP header.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
TaskRoleCredentialsProvider(const char* endpoint, const char* token, long refreshRateMs = REFRESH_THRESHOLD);

/**
* Initializes the provider to retrieve credentials using the provided client.
* @param client The ECSCredentialsClient instance to use when retrieving credentials.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
TaskRoleCredentialsProvider(const std::shared_ptr<Aws::Internal::ECSCredentialsClient>& client,
long refreshRateMs = REFRESH_THRESHOLD);
/**
* Retrieves the credentials if found, otherwise returns empty credential set.
*/
AWSCredentials GetAWSCredentials() override;

protected:
void Reload() override;
private:
bool ExpiresSoon() const;
void RefreshIfExpired();

private:
std::shared_ptr<Aws::Internal::ECSCredentialsClient> m_ecsCredentialsClient;
long m_loadFrequencyMs;
Aws::Auth::AWSCredentials m_credentials;
};

/**
* Process credentials provider that loads credentials by running another command (or program) configured in config file
* The configuration format is as following:
Expand Down Expand Up @@ -315,3 +271,7 @@ namespace Aws
};
} // namespace Auth
} // namespace Aws

// TODO: remove on a next minor API bump from 1.11.x
#endif // !defined(AWS_CLIENT_H)
#include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/


#pragma once

#include <aws/core/auth/AWSCredentialsProvider.h>

namespace Aws
{
namespace Auth
{
/**
* General HTTP Credentials Provider (previously known as ECS credentials provider)
* implementation that loads credentials from an arbitrary HTTP(S) endpoint specified by the environment
* or loop back / Amazon ECS / Amazon EKS container host metadata services by default.
*/
class AWS_CORE_API GeneralHTTPCredentialsProvider : public AWSCredentialsProvider
{
private:
static bool ShouldCreateGeneralHTTPProvider(const Aws::String& relativeUri, const Aws::String& absoluteUri, const Aws::String authToken);
public:
using ShouldCreateFunc = std::function<bool(const Aws::String& relativeUri, const Aws::String& absoluteUri, const Aws::String authToken)>;

/**
* Initializes the provider to retrieve credentials from a general http provided endpoint every 5 minutes or before it
* expires.
* @param relativeUri A path appended to the metadata service endpoint. OR
* @param absoluteUri The full URI to resolve to get credentials.
* @param authTokenFilePath A path to a file with optional authorization token passed to the URI via the 'Authorization' HTTP header.
* @param authToken An optional authorization token passed to the URI via the 'Authorization' HTTP header.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
* @param ShouldCreateFunc
*/
GeneralHTTPCredentialsProvider(const Aws::String& relativeUri,
const Aws::String& absoluteUri,
const Aws::String& authTokenFilePath = "",
const Aws::String& authToken = "",
long refreshRateMs = REFRESH_THRESHOLD,
ShouldCreateFunc shouldCreateFunc = ShouldCreateGeneralHTTPProvider);

/**
* Check if GeneralHTTPCredentialsProvider was initialized with allowed configuration
* @return true if provider configuration is valid
*/
bool IsValid() const
{
if (!m_ecsCredentialsClient)
return false;
if (!m_authTokenFilePath.empty())
return !LoadTokenFromFile().empty();
return true;
}

/**
* Initializes the provider to retrieve credentials from the ECS metadata service every 5 minutes,
* or before it expires.
* @param resourcePath A path appended to the metadata service endpoint.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
// TODO: 1.12: AWS_DEPRECATED("This c-tor is deprecated, please use one above.")
GeneralHTTPCredentialsProvider(const char* resourcePath, long refreshRateMs = REFRESH_THRESHOLD)
: GeneralHTTPCredentialsProvider(resourcePath, "", "", "", refreshRateMs)
{}

/**
* Initializes the provider to retrieve credentials from a provided endpoint every 5 minutes or before it
* expires.
* @param endpoint The full URI to resolve to get credentials.
* @param token An optional authorization token passed to the URI via the 'Authorization' HTTP header.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
// TODO: 1.12: AWS_DEPRECATED("This c-tor is deprecated, please use one above.")
GeneralHTTPCredentialsProvider(const char* endpoint, const char* token, long refreshRateMs = REFRESH_THRESHOLD)
: GeneralHTTPCredentialsProvider("", endpoint, token, "", refreshRateMs)
{}

/**
* Initializes the provider to retrieve credentials using the provided client.
* @param client The ECSCredentialsClient instance to use when retrieving credentials.
* @param refreshRateMs The number of milliseconds after which the credentials will be fetched again.
*/
GeneralHTTPCredentialsProvider(const std::shared_ptr<Aws::Internal::ECSCredentialsClient>& client,
long refreshRateMs = REFRESH_THRESHOLD);
/**
* Retrieves the credentials if found, otherwise returns empty credential set.
*/
AWSCredentials GetAWSCredentials() override;

static const char AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE[];
static const char AWS_CONTAINER_CREDENTIALS_RELATIVE_URI[];
static const char AWS_CONTAINER_CREDENTIALS_FULL_URI[];
static const char AWS_CONTAINER_AUTHORIZATION_TOKEN[];

static const char AWS_ECS_CONTAINER_HOST[];
static const char AWS_EKS_CONTAINER_HOST[];
static const char AWS_EKS_CONTAINER_HOST_IPV6[];

protected:
void Reload() override;

private:
bool ExpiresSoon() const;
void RefreshIfExpired();

Aws::String LoadTokenFromFile() const;

std::shared_ptr<Aws::Internal::ECSCredentialsClient> m_ecsCredentialsClient;
Aws::String m_authTokenFilePath;

long m_loadFrequencyMs = REFRESH_THRESHOLD;
Aws::Auth::AWSCredentials m_credentials;
};

// GeneralHTTPCredentialsProvider was previously known as TaskRoleCredentialsProvider or "ECS credentials provider"
using TaskRoleCredentialsProvider = GeneralHTTPCredentialsProvider;
} // namespace Auth
} // namespace Aws
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,14 @@ namespace Aws
*/
virtual Aws::String GetECSCredentials() const
{
return GetResource(m_endpoint.c_str(), m_resourcePath.c_str(), m_token.c_str());
return GetResource(m_endpoint.c_str(),
m_resourcePath.c_str(),
m_token.empty() ? nullptr : m_token.c_str());
}

inline void SetToken(Aws::String token)
{
m_token = std::move(token);
}

private:
Expand Down
90 changes: 0 additions & 90 deletions src/aws-cpp-sdk-core/source/auth/AWSCredentialsProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ static const char DEFAULT_CREDENTIALS_FILE[] = "credentials";
extern const char DEFAULT_CONFIG_FILE[] = "config";


static const int AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD = 5 * 1000;

void AWSCredentialsProvider::Reload()
{
m_lastLoadedMs = DateTime::Now().Millis();
Expand Down Expand Up @@ -307,94 +305,6 @@ void InstanceProfileCredentialsProvider::RefreshIfExpired()
Reload();
}

static const char TASK_ROLE_LOG_TAG[] = "TaskRoleCredentialsProvider";

TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* URI, long refreshRateMs) :
m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, URI)),
m_loadFrequencyMs(refreshRateMs)
{
AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
}

TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* endpoint, const char* token, long refreshRateMs) :
m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, ""/*resourcePath*/, endpoint, token)),
m_loadFrequencyMs(refreshRateMs)
{
AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
}

TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(
const std::shared_ptr<Aws::Internal::ECSCredentialsClient>& client, long refreshRateMs) :
m_ecsCredentialsClient(client),
m_loadFrequencyMs(refreshRateMs)
{
AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
}

AWSCredentials TaskRoleCredentialsProvider::GetAWSCredentials()
{
RefreshIfExpired();
ReaderLockGuard guard(m_reloadLock);
return m_credentials;
}

bool TaskRoleCredentialsProvider::ExpiresSoon() const
{
return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD);
}

void TaskRoleCredentialsProvider::Reload()
{
AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Credentials have expired or will expire, attempting to re-pull from ECS IAM Service.");
if (!m_ecsCredentialsClient)
{
AWS_LOGSTREAM_ERROR(INSTANCE_LOG_TAG, "ECS Credentials client is a nullptr");
return;
}

auto credentialsStr = m_ecsCredentialsClient->GetECSCredentials();
if (credentialsStr.empty()) return;

Json::JsonValue credentialsDoc(credentialsStr);
if (!credentialsDoc.WasParseSuccessful())
{
AWS_LOGSTREAM_ERROR(TASK_ROLE_LOG_TAG, "Failed to parse output from ECSCredentialService.");
return;
}

Aws::String accessKey, secretKey, token;
Utils::Json::JsonView credentialsView(credentialsDoc);
accessKey = credentialsView.GetString("AccessKeyId");
secretKey = credentialsView.GetString("SecretAccessKey");
token = credentialsView.GetString("Token");
AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Successfully pulled credentials from metadata service with access key " << accessKey);

m_credentials.SetAWSAccessKeyId(accessKey);
m_credentials.SetAWSSecretKey(secretKey);
m_credentials.SetSessionToken(token);
m_credentials.SetExpiration(Aws::Utils::DateTime(credentialsView.GetString("Expiration"), DateFormat::ISO_8601));
AWSCredentialsProvider::Reload();
}

void TaskRoleCredentialsProvider::RefreshIfExpired()
{
AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Checking if latest credential pull has expired.");
ReaderLockGuard guard(m_reloadLock);
if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon())
{
return;
}

guard.UpgradeToWriterLock();

if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon())
{
return;
}

Reload();
}

static const char PROCESS_LOG_TAG[] = "ProcessCredentialsProvider";
ProcessCredentialsProvider::ProcessCredentialsProvider() :
m_profileToUse(Aws::Auth::GetConfigProfileName())
Expand Down
39 changes: 18 additions & 21 deletions src/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
using namespace Aws::Auth;
using namespace Aws::Utils::Threading;

static const char AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI[] = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
static const char AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI[] = "AWS_CONTAINER_CREDENTIALS_FULL_URI";
static const char AWS_ECS_CONTAINER_AUTHORIZATION_TOKEN[] = "AWS_CONTAINER_AUTHORIZATION_TOKEN";
static const char AWS_EC2_METADATA_DISABLED[] = "AWS_EC2_METADATA_DISABLED";
static const char DefaultCredentialsProviderChainTag[] = "DefaultAWSCredentialsProviderChain";

Expand Down Expand Up @@ -51,35 +48,35 @@ DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain() : AWSCr
AddProvider(Aws::MakeShared<STSAssumeRoleWebIdentityCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<SSOCredentialsProvider>(DefaultCredentialsProviderChainTag));

//ECS TaskRole Credentials only available when ENVIRONMENT VARIABLE is set
const auto relativeUri = Aws::Environment::GetEnv(AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI);
AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag, "The environment variable value " << AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI
// General HTTP Credentials (prev. known as ECS TaskRole credentials) only available when ENVIRONMENT VARIABLE is set
const auto relativeUri = Aws::Environment::GetEnv(GeneralHTTPCredentialsProvider::AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag, "The environment variable value " << GeneralHTTPCredentialsProvider::AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
<< " is " << relativeUri);

const auto absoluteUri = Aws::Environment::GetEnv(AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI);
AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag, "The environment variable value " << AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI
const auto absoluteUri = Aws::Environment::GetEnv(GeneralHTTPCredentialsProvider::AWS_CONTAINER_CREDENTIALS_FULL_URI);
AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag, "The environment variable value " << GeneralHTTPCredentialsProvider::AWS_CONTAINER_CREDENTIALS_FULL_URI
<< " is " << absoluteUri);

const auto ec2MetadataDisabled = Aws::Environment::GetEnv(AWS_EC2_METADATA_DISABLED);
AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag, "The environment variable value " << AWS_EC2_METADATA_DISABLED
<< " is " << ec2MetadataDisabled);

if (!relativeUri.empty())
if (!relativeUri.empty() || !absoluteUri.empty())
{
AddProvider(Aws::MakeShared<TaskRoleCredentialsProvider>(DefaultCredentialsProviderChainTag, relativeUri.c_str()));
AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag, "Added ECS metadata service credentials provider with relative path: ["
<< relativeUri << "] to the provider chain.");
}
else if (!absoluteUri.empty())
{
const auto token = Aws::Environment::GetEnv(AWS_ECS_CONTAINER_AUTHORIZATION_TOKEN);
AddProvider(Aws::MakeShared<TaskRoleCredentialsProvider>(DefaultCredentialsProviderChainTag,
absoluteUri.c_str(), token.c_str()));
const Aws::String token = Aws::Environment::GetEnv(GeneralHTTPCredentialsProvider::AWS_CONTAINER_AUTHORIZATION_TOKEN);
const Aws::String tokenPath = Aws::Environment::GetEnv(GeneralHTTPCredentialsProvider::AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE);

//DO NOT log the value of the authorization token for security purposes.
AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag, "Added ECS credentials provider with URI: ["
<< absoluteUri << "] to the provider chain with a" << (token.empty() ? "n empty " : " non-empty ")
auto genProvider = Aws::MakeShared<GeneralHTTPCredentialsProvider>(DefaultCredentialsProviderChainTag,
relativeUri, absoluteUri, token, tokenPath);
if (genProvider && genProvider->IsValid()) {
AddProvider(std::move(genProvider));
auto& uri = !relativeUri.empty() ? relativeUri : absoluteUri;
AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag, "Added General HTTP / ECS credentials provider with ur: ["
<< uri << "] to the provider chain with a" << ((token.empty() && tokenPath.empty()) ? "n empty " : " non-empty ")
<< "authorization token.");
} else {
AWS_LOGSTREAM_ERROR(DefaultCredentialsProviderChainTag, "Unable to create GeneralHTTPCredentialsProvider");
}
}
else if (Aws::Utils::StringUtils::ToLower(ec2MetadataDisabled.c_str()) != "true")
{
Expand Down
Loading

0 comments on commit 7e2ba3a

Please sign in to comment.