|
2 | 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 | 3 | * SPDX-License-Identifier: Apache-2.0.
|
4 | 4 | */
|
5 |
| - |
6 |
| - |
| 5 | +#include <aws/core/Globals.h> |
7 | 6 | #include <aws/core/auth/STSCredentialsProvider.h>
|
8 |
| -#include <aws/core/config/AWSProfileConfigLoader.h> |
| 7 | +#include <aws/core/client/ClientConfiguration.h> |
9 | 8 | #include <aws/core/platform/Environment.h>
|
10 |
| -#include <aws/core/platform/FileSystem.h> |
11 |
| -#include <aws/core/utils/logging/LogMacros.h> |
12 |
| -#include <aws/core/utils/StringUtils.h> |
13 |
| -#include <aws/core/utils/FileSystemUtils.h> |
14 |
| -#include <aws/core/client/SpecifiedRetryableErrorsRetryStrategy.h> |
15 |
| -#include <aws/core/utils/StringUtils.h> |
16 |
| -#include <aws/core/utils/UUID.h> |
17 |
| -#include <cstdlib> |
18 |
| -#include <fstream> |
19 |
| -#include <string.h> |
20 |
| -#include <climits> |
21 |
| - |
| 9 | +#include <aws/crt/auth/Credentials.h> |
22 | 10 |
|
23 |
| -using namespace Aws::Utils; |
24 |
| -using namespace Aws::Utils::Logging; |
25 | 11 | using namespace Aws::Auth;
|
26 |
| -using namespace Aws::Internal; |
27 |
| -using namespace Aws::FileSystem; |
28 |
| -using namespace Aws::Client; |
29 |
| -using Aws::Utils::Threading::ReaderLockGuard; |
30 |
| -using Aws::Utils::Threading::WriterLockGuard; |
| 12 | +using namespace Aws::Utils; |
31 | 13 |
|
32 |
| -static const char STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG[] = "STSAssumeRoleWithWebIdentityCredentialsProvider"; |
33 |
| -static const int STS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD = 5 * 60 * 1000; // 5 Minutes. |
| 14 | +namespace { |
| 15 | +const char* STS_LOG_TAG = "STSAssumeRoleWebIdentityCredentialsProvider"; |
| 16 | +} |
34 | 17 |
|
35 |
| -STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider(Aws::Client::ClientConfiguration::CredentialProviderConfiguration credentialsConfig): |
36 |
| - m_initialized(false) |
| 18 | +STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider( |
| 19 | + Aws::Client::ClientConfiguration::CredentialProviderConfiguration credentialsConfig) |
| 20 | + : m_credentialsProvider(nullptr), m_providerFuturesTimeoutMs(credentialsConfig.stsCredentialsProviderConfig.retrieveCredentialsFutureTimeout) |
37 | 21 | {
|
38 |
| - m_roleArn = Aws::Environment::GetEnv("AWS_ROLE_ARN"); |
39 |
| - m_tokenFile = Aws::Environment::GetEnv("AWS_WEB_IDENTITY_TOKEN_FILE"); |
40 |
| - m_sessionName = Aws::Environment::GetEnv("AWS_ROLE_SESSION_NAME"); |
41 |
| - |
42 |
| - // check profile_config if either m_roleArn or m_tokenFile is not loaded from environment variable |
43 |
| - // region source is not enforced, but we need it to construct sts endpoint, if we can't find from environment, we should check if it's set in config file. |
44 |
| - if (m_roleArn.empty() || m_tokenFile.empty()) |
45 |
| - { |
46 |
| - auto profile = Aws::Config::GetCachedConfigProfile(credentialsConfig.profile); |
47 |
| - // If either of these two were not found from environment, use whatever found for all three in config file |
48 |
| - if (m_roleArn.empty() || m_tokenFile.empty()) |
49 |
| - { |
50 |
| - m_roleArn = profile.GetRoleArn(); |
51 |
| - m_tokenFile = profile.GetValue("web_identity_token_file"); |
52 |
| - m_sessionName = profile.GetValue("role_session_name"); |
53 |
| - } |
54 |
| - } |
55 |
| - |
56 |
| - if (m_tokenFile.empty()) |
57 |
| - { |
58 |
| - AWS_LOGSTREAM_WARN(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Token file must be specified to use STS AssumeRole web identity creds provider."); |
59 |
| - return; // No need to do further constructing |
| 22 | + Aws::Crt::Auth::CredentialsProviderSTSWebIdentityConfig stsConfig{}; |
| 23 | + stsConfig.Bootstrap = GetDefaultClientBootstrap(); |
| 24 | + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); |
| 25 | + const Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT); |
| 26 | + stsConfig.TlsCtx = tlsContext; |
| 27 | + stsConfig.Region = credentialsConfig.region.c_str(); |
| 28 | + stsConfig.TokenFilePath = credentialsConfig.stsCredentialsProviderConfig.tokenFilePath.c_str(); |
| 29 | + stsConfig.RoleArn = credentialsConfig.stsCredentialsProviderConfig.roleArn.c_str(); |
| 30 | + stsConfig.SessionName = [&credentialsConfig]() -> Aws::String { |
| 31 | + if (!credentialsConfig.stsCredentialsProviderConfig.sessionName.empty()) { |
| 32 | + return credentialsConfig.stsCredentialsProviderConfig.sessionName; |
60 | 33 | }
|
61 |
| - else |
62 |
| - { |
63 |
| - AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved token_file from profile_config or environment variable to be " << m_tokenFile); |
64 |
| - } |
65 |
| - |
66 |
| - if (m_roleArn.empty()) |
67 |
| - { |
68 |
| - AWS_LOGSTREAM_WARN(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "RoleArn must be specified to use STS AssumeRole web identity creds provider."); |
69 |
| - return; // No need to do further constructing |
70 |
| - } |
71 |
| - else |
72 |
| - { |
73 |
| - AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved role_arn from profile_config or environment variable to be " << m_roleArn); |
74 |
| - } |
75 |
| - |
76 |
| - if (m_sessionName.empty()) |
77 |
| - { |
78 |
| - m_sessionName = Aws::Utils::UUID::PseudoRandomUUID(); |
79 |
| - } |
80 |
| - else |
81 |
| - { |
82 |
| - AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved session_name from profile_config or environment variable to be " << m_sessionName); |
83 |
| - } |
84 |
| - |
85 |
| - Aws::Client::ClientConfiguration config; |
86 |
| - config.scheme = Aws::Http::Scheme::HTTPS; |
87 |
| - config.region = credentialsConfig.region; |
88 |
| - Aws::Vector<Aws::String> retryableErrors; |
89 |
| - retryableErrors.push_back("IDPCommunicationError"); |
90 |
| - retryableErrors.push_back("InvalidIdentityToken"); |
91 |
| - |
92 |
| - config.retryStrategy = Aws::MakeShared<SpecifiedRetryableErrorsRetryStrategy>(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, retryableErrors, 3/*maxRetries*/); |
93 |
| - |
94 |
| - m_client = Aws::MakeUnique<Aws::Internal::STSCredentialsClient>(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, config); |
95 |
| - m_initialized = true; |
96 |
| - AWS_LOGSTREAM_INFO(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Creating STS AssumeRole with web identity creds provider."); |
| 34 | + return UUID::RandomUUID(); |
| 35 | + }().c_str(); |
| 36 | + m_credentialsProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderSTSWebIdentity(stsConfig); |
| 37 | + if (m_credentialsProvider && m_credentialsProvider->IsValid()) { |
| 38 | + m_state = STATE::INITIALIZED; |
| 39 | + } else { |
| 40 | + AWS_LOGSTREAM_WARN(STS_LOG_TAG, "Failed to create STS credentials provider"); |
| 41 | + } |
97 | 42 | }
|
98 | 43 |
|
99 |
| -Aws::String LegacyGetRegion() { |
100 |
| - auto region = Aws::Environment::GetEnv("AWS_DEFAULT_REGION"); |
101 |
| - if (region.empty()) { |
| 44 | +Aws::String GetLegacySettingFromEnvOrProfile(const Aws::String& envVar, |
| 45 | + std::function<Aws::String (Aws::Config::Profile)> profileFetchFunction) |
| 46 | +{ |
| 47 | + auto value = Aws::Environment::GetEnv(envVar.c_str()); |
| 48 | + if (value.empty()) { |
102 | 49 | auto profile = Aws::Config::GetCachedConfigProfile(Aws::Auth::GetConfigProfileName());
|
103 |
| - region = profile.GetRegion(); |
| 50 | + value = profileFetchFunction(profile); |
104 | 51 | }
|
105 |
| - return region; |
| 52 | + return value; |
106 | 53 | }
|
107 | 54 |
|
108 | 55 | STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider()
|
109 | 56 | : STSAssumeRoleWebIdentityCredentialsProvider(
|
110 |
| - Aws::Client::ClientConfiguration::CredentialProviderConfiguration{Aws::Auth::GetConfigProfileName(), LegacyGetRegion(), {}}) {} |
111 |
| - |
112 |
| -AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::GetAWSCredentials() |
113 |
| -{ |
114 |
| - // A valid client means required information like role arn and token file were constructed correctly. |
115 |
| - // We can use this provider to load creds, otherwise, we can just return empty creds. |
116 |
| - if (!m_initialized) |
117 |
| - { |
118 |
| - return Aws::Auth::AWSCredentials(); |
119 |
| - } |
120 |
| - RefreshIfExpired(); |
121 |
| - ReaderLockGuard guard(m_reloadLock); |
122 |
| - return m_credentials; |
123 |
| -} |
124 |
| - |
125 |
| -void STSAssumeRoleWebIdentityCredentialsProvider::Reload() |
126 |
| -{ |
127 |
| - AWS_LOGSTREAM_INFO(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Credentials have expired, attempting to renew from STS."); |
128 |
| - |
129 |
| - Aws::IFStream tokenFile(m_tokenFile.c_str()); |
130 |
| - if(tokenFile) |
131 |
| - { |
132 |
| - Aws::String token((std::istreambuf_iterator<char>(tokenFile)), std::istreambuf_iterator<char>()); |
133 |
| - m_token = token; |
134 |
| - } |
135 |
| - else |
136 |
| - { |
137 |
| - AWS_LOGSTREAM_ERROR(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Can't open token file: " << m_tokenFile); |
138 |
| - return; |
139 |
| - } |
140 |
| - STSCredentialsClient::STSAssumeRoleWithWebIdentityRequest request {m_sessionName, m_roleArn, m_token}; |
141 |
| - |
142 |
| - auto result = m_client->GetAssumeRoleWithWebIdentityCredentials(request); |
143 |
| - AWS_LOGSTREAM_TRACE(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Successfully retrieved credentials with AWS_ACCESS_KEY: " << result.creds.GetAWSAccessKeyId()); |
144 |
| - m_credentials = result.creds; |
145 |
| -} |
| 57 | + Aws::Client::ClientConfiguration::CredentialProviderConfiguration{ |
| 58 | + Aws::Auth::GetConfigProfileName(), |
| 59 | + GetLegacySettingFromEnvOrProfile("AWS_DEFAULT_REGION", |
| 60 | + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRegion(); }), |
| 61 | + {}, |
| 62 | + { |
| 63 | + GetLegacySettingFromEnvOrProfile("AWS_ROLE_ARN", |
| 64 | + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRoleArn(); }), |
| 65 | + GetLegacySettingFromEnvOrProfile("AWS_ROLE_SESSION_NAME", |
| 66 | + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("role_session_name"); }), |
| 67 | + GetLegacySettingFromEnvOrProfile("AWS_WEB_IDENTITY_TOKEN_FILE", |
| 68 | + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("web_identity_token_file"); }) |
| 69 | + }}) |
| 70 | +{} |
| 71 | + |
| 72 | +STSAssumeRoleWebIdentityCredentialsProvider::~STSAssumeRoleWebIdentityCredentialsProvider() = default; |
| 73 | + |
| 74 | +AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::GetAWSCredentials() { |
| 75 | + if (m_state != STATE::INITIALIZED) { |
| 76 | + AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "STSCredentialsProvider is not initialized, returning empty credentials"); |
| 77 | + return AWSCredentials{}; |
| 78 | + } |
| 79 | + AWSCredentials credentials{}; |
| 80 | + auto refreshDone = false; |
| 81 | + m_credentialsProvider->GetCredentials( |
| 82 | + [this, &credentials, &refreshDone](std::shared_ptr<Aws::Crt::Auth::Credentials> crtCredentials, int errorCode) -> void { |
| 83 | + { |
| 84 | + const std::unique_lock<std::mutex> lock{m_refreshMutex}; |
| 85 | + if (errorCode != AWS_ERROR_SUCCESS) { |
| 86 | + AWS_LOGSTREAM_ERROR(STS_LOG_TAG, "Failed to get credentials from STS: " << errorCode); |
| 87 | + } else { |
| 88 | + const auto accountIdCursor = crtCredentials->GetAccessKeyId(); |
| 89 | + credentials.SetAWSAccessKeyId({reinterpret_cast<char*>(accountIdCursor.ptr), accountIdCursor.len}); |
| 90 | + const auto secretKeuCursor = crtCredentials->GetSecretAccessKey(); |
| 91 | + credentials.SetAWSSecretKey({reinterpret_cast<char*>(secretKeuCursor.ptr), secretKeuCursor.len}); |
| 92 | + const auto expiration = crtCredentials->GetExpirationTimepointInSeconds(); |
| 93 | + credentials.SetExpiration(DateTime{static_cast<double>(expiration)}); |
| 94 | + const auto sessionTokenCursor = crtCredentials->GetSessionToken(); |
| 95 | + credentials.SetSessionToken({reinterpret_cast<char*>(sessionTokenCursor.ptr), sessionTokenCursor.len}); |
| 96 | + } |
| 97 | + refreshDone = true; |
| 98 | + } |
| 99 | + m_refreshSignal.notify_one(); |
| 100 | + }); |
146 | 101 |
|
147 |
| -bool STSAssumeRoleWebIdentityCredentialsProvider::ExpiresSoon() const |
148 |
| -{ |
149 |
| - return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < STS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD); |
| 102 | + std::unique_lock<std::mutex> lock{m_refreshMutex}; |
| 103 | + m_refreshSignal.wait_for(lock, m_providerFuturesTimeoutMs, [&refreshDone]() -> bool { return refreshDone; }); |
| 104 | + return credentials; |
150 | 105 | }
|
151 | 106 |
|
152 |
| -void STSAssumeRoleWebIdentityCredentialsProvider::RefreshIfExpired() |
153 |
| -{ |
154 |
| - ReaderLockGuard guard(m_reloadLock); |
155 |
| - if (!m_credentials.IsEmpty() && !ExpiresSoon()) |
156 |
| - { |
157 |
| - return; |
158 |
| - } |
159 |
| - |
160 |
| - guard.UpgradeToWriterLock(); |
161 |
| - if (!m_credentials.IsExpiredOrEmpty() && !ExpiresSoon()) // double-checked lock to avoid refreshing twice |
162 |
| - { |
163 |
| - return; |
164 |
| - } |
165 |
| - |
166 |
| - Reload(); |
| 107 | +void STSAssumeRoleWebIdentityCredentialsProvider::Reload() { |
| 108 | + AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "Calling reload on STSCredentialsProvider is a no-op and no longer in the call path"); |
167 | 109 | }
|
0 commit comments