1 | /* |
2 | * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"). |
5 | * You may not use this file except in compliance with the License. |
6 | * A copy of the License is located at |
7 | * |
8 | * http://aws.amazon.com/apache2.0 |
9 | * |
10 | * or in the "license" file accompanying this file. This file is distributed |
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
12 | * express or implied. See the License for the specific language governing |
13 | * permissions and limitations under the License. |
14 | */ |
15 | |
16 | |
17 | #include <aws/core/auth/AWSCredentialsProvider.h> |
18 | |
19 | #include <aws/core/config/AWSProfileConfigLoader.h> |
20 | #include <aws/core/platform/Environment.h> |
21 | #include <aws/core/platform/FileSystem.h> |
22 | #include <aws/core/platform/OSVersionInfo.h> |
23 | #include <aws/core/utils/logging/LogMacros.h> |
24 | #include <aws/core/utils/StringUtils.h> |
25 | #include <aws/core/utils/json/JsonSerializer.h> |
26 | #include <aws/core/utils/FileSystemUtils.h> |
27 | #include <aws/core/client/AWSError.h> |
28 | #include <aws/core/utils/StringUtils.h> |
29 | #include <aws/core/utils/xml/XmlSerializer.h> |
30 | #include <cstdlib> |
31 | #include <fstream> |
32 | #include <string.h> |
33 | #include <climits> |
34 | |
35 | |
36 | using namespace Aws::Utils; |
37 | using namespace Aws::Utils::Logging; |
38 | using namespace Aws::Auth; |
39 | using namespace Aws::Internal; |
40 | using namespace Aws::FileSystem; |
41 | using namespace Aws::Utils::Xml; |
42 | using namespace Aws::Client; |
43 | using Aws::Utils::Threading::ReaderLockGuard; |
44 | using Aws::Utils::Threading::WriterLockGuard; |
45 | |
46 | static const char ACCESS_KEY_ENV_VAR[] = "AWS_ACCESS_KEY_ID" ; |
47 | static const char SECRET_KEY_ENV_VAR[] = "AWS_SECRET_ACCESS_KEY" ; |
48 | static const char SESSION_TOKEN_ENV_VAR[] = "AWS_SESSION_TOKEN" ; |
49 | static const char DEFAULT_PROFILE[] = "default" ; |
50 | static const char AWS_PROFILE_ENV_VAR[] = "AWS_PROFILE" ; |
51 | static const char AWS_PROFILE_DEFAULT_ENV_VAR[] = "AWS_DEFAULT_PROFILE" ; |
52 | |
53 | static const char AWS_CREDENTIALS_FILE[] = "AWS_SHARED_CREDENTIALS_FILE" ; |
54 | extern const char AWS_CONFIG_FILE[] = "AWS_CONFIG_FILE" ; |
55 | |
56 | extern const char PROFILE_DIRECTORY[] = ".aws" ; |
57 | static const char DEFAULT_CREDENTIALS_FILE[] = "credentials" ; |
58 | extern const char DEFAULT_CONFIG_FILE[] = "config" ; |
59 | |
60 | |
61 | static const int EXPIRATION_GRACE_PERIOD = 5 * 1000; |
62 | |
63 | void AWSCredentialsProvider::Reload() |
64 | { |
65 | m_lastLoadedMs = DateTime::Now().Millis(); |
66 | } |
67 | |
68 | bool AWSCredentialsProvider::IsTimeToRefresh(long reloadFrequency) |
69 | { |
70 | if (DateTime::Now().Millis() - m_lastLoadedMs > reloadFrequency) |
71 | { |
72 | return true; |
73 | } |
74 | return false; |
75 | } |
76 | |
77 | |
78 | static const char* ENVIRONMENT_LOG_TAG = "EnvironmentAWSCredentialsProvider" ; |
79 | |
80 | |
81 | AWSCredentials EnvironmentAWSCredentialsProvider::GetAWSCredentials() |
82 | { |
83 | auto accessKey = Aws::Environment::GetEnv(ACCESS_KEY_ENV_VAR); |
84 | AWSCredentials credentials; |
85 | |
86 | if (!accessKey.empty()) |
87 | { |
88 | credentials.SetAWSAccessKeyId(accessKey); |
89 | |
90 | AWS_LOGSTREAM_DEBUG(ENVIRONMENT_LOG_TAG, "Found credential in environment with access key id " << accessKey); |
91 | auto secretKey = Aws::Environment::GetEnv(SECRET_KEY_ENV_VAR); |
92 | |
93 | if (!secretKey.empty()) |
94 | { |
95 | credentials.SetAWSSecretKey(secretKey); |
96 | AWS_LOGSTREAM_INFO(ENVIRONMENT_LOG_TAG, "Found secret key" ); |
97 | } |
98 | |
99 | auto sessionToken = Aws::Environment::GetEnv(SESSION_TOKEN_ENV_VAR); |
100 | |
101 | if(!sessionToken.empty()) |
102 | { |
103 | credentials.SetSessionToken(sessionToken); |
104 | AWS_LOGSTREAM_INFO(ENVIRONMENT_LOG_TAG, "Found sessionToken" ); |
105 | } |
106 | } |
107 | |
108 | return credentials; |
109 | } |
110 | |
111 | Aws::String Aws::Auth::GetConfigProfileFilename() |
112 | { |
113 | auto configFileNameFromVar = Aws::Environment::GetEnv(AWS_CONFIG_FILE); |
114 | if (!configFileNameFromVar.empty()) |
115 | { |
116 | return configFileNameFromVar; |
117 | } |
118 | else |
119 | { |
120 | return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CONFIG_FILE; |
121 | } |
122 | } |
123 | |
124 | Aws::String Aws::Auth::GetConfigProfileName() |
125 | { |
126 | auto profileFromVar = Aws::Environment::GetEnv(AWS_PROFILE_DEFAULT_ENV_VAR); |
127 | if (profileFromVar.empty()) |
128 | { |
129 | profileFromVar = Aws::Environment::GetEnv(AWS_PROFILE_ENV_VAR); |
130 | } |
131 | |
132 | if (profileFromVar.empty()) |
133 | { |
134 | return Aws::String(DEFAULT_PROFILE); |
135 | } |
136 | else |
137 | { |
138 | return profileFromVar; |
139 | } |
140 | } |
141 | |
142 | static const char* PROFILE_LOG_TAG = "ProfileConfigFileAWSCredentialsProvider" ; |
143 | |
144 | Aws::String ProfileConfigFileAWSCredentialsProvider::GetCredentialsProfileFilename() |
145 | { |
146 | auto credentialsFileNameFromVar = Aws::Environment::GetEnv(AWS_CREDENTIALS_FILE); |
147 | |
148 | if (credentialsFileNameFromVar.empty()) |
149 | { |
150 | return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CREDENTIALS_FILE; |
151 | } |
152 | else |
153 | { |
154 | return credentialsFileNameFromVar; |
155 | } |
156 | } |
157 | |
158 | Aws::String ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory() |
159 | { |
160 | Aws::String credentialsFileName = GetCredentialsProfileFilename(); |
161 | auto lastSeparator = credentialsFileName.find_last_of(PATH_DELIM); |
162 | if (lastSeparator != std::string::npos) |
163 | { |
164 | return credentialsFileName.substr(0, lastSeparator); |
165 | } |
166 | else |
167 | { |
168 | return {}; |
169 | } |
170 | } |
171 | |
172 | ProfileConfigFileAWSCredentialsProvider::ProfileConfigFileAWSCredentialsProvider(long refreshRateMs) : |
173 | m_profileToUse(Aws::Auth::GetConfigProfileName()), |
174 | m_credentialsFileLoader(GetCredentialsProfileFilename()), |
175 | m_loadFrequencyMs(refreshRateMs) |
176 | { |
177 | AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file" |
178 | << " and " << GetConfigProfileFilename() << " for the config file " |
179 | << ", for use with profile " << m_profileToUse); |
180 | } |
181 | |
182 | ProfileConfigFileAWSCredentialsProvider::ProfileConfigFileAWSCredentialsProvider(const char* profile, long refreshRateMs) : |
183 | m_profileToUse(profile), |
184 | m_credentialsFileLoader(GetCredentialsProfileFilename()), |
185 | m_loadFrequencyMs(refreshRateMs) |
186 | { |
187 | AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file" |
188 | << " and " << GetConfigProfileFilename() << " for the config file " |
189 | << ", for use with profile " << m_profileToUse); |
190 | } |
191 | |
192 | AWSCredentials ProfileConfigFileAWSCredentialsProvider::GetAWSCredentials() |
193 | { |
194 | RefreshIfExpired(); |
195 | ReaderLockGuard guard(m_reloadLock); |
196 | auto credsFileProfileIter = m_credentialsFileLoader.GetProfiles().find(m_profileToUse); |
197 | |
198 | if(credsFileProfileIter != m_credentialsFileLoader.GetProfiles().end()) |
199 | { |
200 | return credsFileProfileIter->second.GetCredentials(); |
201 | } |
202 | |
203 | return AWSCredentials(); |
204 | } |
205 | |
206 | |
207 | void ProfileConfigFileAWSCredentialsProvider::Reload() |
208 | { |
209 | m_credentialsFileLoader.Load(); |
210 | AWSCredentialsProvider::Reload(); |
211 | } |
212 | |
213 | void ProfileConfigFileAWSCredentialsProvider::RefreshIfExpired() |
214 | { |
215 | ReaderLockGuard guard(m_reloadLock); |
216 | if (!IsTimeToRefresh(m_loadFrequencyMs)) |
217 | { |
218 | return; |
219 | } |
220 | |
221 | guard.UpgradeToWriterLock(); |
222 | if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice |
223 | { |
224 | return; |
225 | } |
226 | |
227 | Reload(); |
228 | } |
229 | |
230 | static const char* INSTANCE_LOG_TAG = "InstanceProfileCredentialsProvider" ; |
231 | |
232 | InstanceProfileCredentialsProvider::InstanceProfileCredentialsProvider(long refreshRateMs) : |
233 | m_ec2MetadataConfigLoader(Aws::MakeShared<Aws::Config::EC2InstanceProfileConfigLoader>(INSTANCE_LOG_TAG)), |
234 | m_loadFrequencyMs(refreshRateMs) |
235 | { |
236 | AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Creating Instance with default EC2MetadataClient and refresh rate " << refreshRateMs); |
237 | } |
238 | |
239 | |
240 | InstanceProfileCredentialsProvider::InstanceProfileCredentialsProvider(const std::shared_ptr<Aws::Config::EC2InstanceProfileConfigLoader>& loader, long refreshRateMs) : |
241 | m_ec2MetadataConfigLoader(loader), |
242 | m_loadFrequencyMs(refreshRateMs) |
243 | { |
244 | AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Creating Instance with injected EC2MetadataClient and refresh rate " << refreshRateMs); |
245 | } |
246 | |
247 | |
248 | AWSCredentials InstanceProfileCredentialsProvider::GetAWSCredentials() |
249 | { |
250 | RefreshIfExpired(); |
251 | ReaderLockGuard guard(m_reloadLock); |
252 | auto profileIter = m_ec2MetadataConfigLoader->GetProfiles().find(Aws::Config::INSTANCE_PROFILE_KEY); |
253 | |
254 | if(profileIter != m_ec2MetadataConfigLoader->GetProfiles().end()) |
255 | { |
256 | return profileIter->second.GetCredentials(); |
257 | } |
258 | |
259 | return AWSCredentials(); |
260 | } |
261 | |
262 | void InstanceProfileCredentialsProvider::Reload() |
263 | { |
264 | AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Credentials have expired attempting to repull from EC2 Metadata Service." ); |
265 | m_ec2MetadataConfigLoader->Load(); |
266 | AWSCredentialsProvider::Reload(); |
267 | } |
268 | |
269 | void InstanceProfileCredentialsProvider::RefreshIfExpired() |
270 | { |
271 | AWS_LOGSTREAM_DEBUG(INSTANCE_LOG_TAG, "Checking if latest credential pull has expired." ); |
272 | ReaderLockGuard guard(m_reloadLock); |
273 | if (!IsTimeToRefresh(m_loadFrequencyMs)) |
274 | { |
275 | return; |
276 | } |
277 | |
278 | guard.UpgradeToWriterLock(); |
279 | if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice |
280 | { |
281 | return; |
282 | } |
283 | Reload(); |
284 | } |
285 | |
286 | static const char TASK_ROLE_LOG_TAG[] = "TaskRoleCredentialsProvider" ; |
287 | |
288 | TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* URI, long refreshRateMs) : |
289 | m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, URI)), |
290 | m_loadFrequencyMs(refreshRateMs) |
291 | { |
292 | AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs); |
293 | } |
294 | |
295 | TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* endpoint, const char* token, long refreshRateMs) : |
296 | m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, "" /*resourcePath*/, endpoint, token)), |
297 | m_loadFrequencyMs(refreshRateMs) |
298 | { |
299 | AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs); |
300 | } |
301 | |
302 | TaskRoleCredentialsProvider::TaskRoleCredentialsProvider( |
303 | const std::shared_ptr<Aws::Internal::ECSCredentialsClient>& client, long refreshRateMs) : |
304 | m_ecsCredentialsClient(client), |
305 | m_loadFrequencyMs(refreshRateMs) |
306 | { |
307 | AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs); |
308 | } |
309 | |
310 | AWSCredentials TaskRoleCredentialsProvider::GetAWSCredentials() |
311 | { |
312 | RefreshIfExpired(); |
313 | ReaderLockGuard guard(m_reloadLock); |
314 | return m_credentials; |
315 | } |
316 | |
317 | bool TaskRoleCredentialsProvider::ExpiresSoon() const |
318 | { |
319 | return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < EXPIRATION_GRACE_PERIOD); |
320 | } |
321 | |
322 | void TaskRoleCredentialsProvider::Reload() |
323 | { |
324 | AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Credentials have expired or will expire, attempting to repull from ECS IAM Service." ); |
325 | |
326 | auto credentialsStr = m_ecsCredentialsClient->GetECSCredentials(); |
327 | if (credentialsStr.empty()) return; |
328 | |
329 | Json::JsonValue credentialsDoc(credentialsStr); |
330 | if (!credentialsDoc.WasParseSuccessful()) |
331 | { |
332 | AWS_LOGSTREAM_ERROR(TASK_ROLE_LOG_TAG, "Failed to parse output from ECSCredentialService." ); |
333 | return; |
334 | } |
335 | |
336 | Aws::String accessKey, secretKey, token; |
337 | Utils::Json::JsonView credentialsView(credentialsDoc); |
338 | accessKey = credentialsView.GetString("AccessKeyId" ); |
339 | secretKey = credentialsView.GetString("SecretAccessKey" ); |
340 | token = credentialsView.GetString("Token" ); |
341 | AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Successfully pulled credentials from metadata service with access key " << accessKey); |
342 | |
343 | m_credentials.SetAWSAccessKeyId(accessKey); |
344 | m_credentials.SetAWSSecretKey(secretKey); |
345 | m_credentials.SetSessionToken(token); |
346 | m_credentials.SetExpiration(Aws::Utils::DateTime(credentialsView.GetString("Expiration" ), DateFormat::ISO_8601)); |
347 | AWSCredentialsProvider::Reload(); |
348 | } |
349 | |
350 | void TaskRoleCredentialsProvider::RefreshIfExpired() |
351 | { |
352 | AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Checking if latest credential pull has expired." ); |
353 | ReaderLockGuard guard(m_reloadLock); |
354 | if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon()) |
355 | { |
356 | return; |
357 | } |
358 | |
359 | guard.UpgradeToWriterLock(); |
360 | |
361 | if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon()) |
362 | { |
363 | return; |
364 | } |
365 | |
366 | Reload(); |
367 | } |
368 | |
369 | static const char PROCESS_LOG_TAG[] = "ProcessCredentialsProvider" ; |
370 | ProcessCredentialsProvider::ProcessCredentialsProvider() : |
371 | m_profileToUse(Aws::Auth::GetConfigProfileName()) |
372 | { |
373 | AWS_LOGSTREAM_INFO(PROCESS_LOG_TAG, "Setting process credentials provider to read config from " << m_profileToUse); |
374 | } |
375 | |
376 | ProcessCredentialsProvider::ProcessCredentialsProvider(const Aws::String& profile) : |
377 | m_profileToUse(profile) |
378 | { |
379 | AWS_LOGSTREAM_INFO(PROCESS_LOG_TAG, "Setting process credentials provider to read config from " << m_profileToUse); |
380 | } |
381 | |
382 | AWSCredentials ProcessCredentialsProvider::GetAWSCredentials() |
383 | { |
384 | RefreshIfExpired(); |
385 | ReaderLockGuard guard(m_reloadLock); |
386 | return m_credentials; |
387 | } |
388 | |
389 | |
390 | void ProcessCredentialsProvider::Reload() |
391 | { |
392 | auto profile = Aws::Config::GetCachedConfigProfile(m_profileToUse); |
393 | const Aws::String &command = profile.GetCredentialProcess(); |
394 | if (command.empty()) |
395 | { |
396 | AWS_LOGSTREAM_ERROR(PROCESS_LOG_TAG, "Failed to find credential process's profile: " << m_profileToUse); |
397 | return; |
398 | } |
399 | m_credentials = GetCredentialsFromProcess(command); |
400 | } |
401 | |
402 | void ProcessCredentialsProvider::RefreshIfExpired() |
403 | { |
404 | ReaderLockGuard guard(m_reloadLock); |
405 | if (!m_credentials.IsExpiredOrEmpty()) |
406 | { |
407 | return; |
408 | } |
409 | |
410 | guard.UpgradeToWriterLock(); |
411 | if (!m_credentials.IsExpiredOrEmpty()) // double-checked lock to avoid refreshing twice |
412 | { |
413 | return; |
414 | } |
415 | |
416 | Reload(); |
417 | } |
418 | |
419 | AWSCredentials Aws::Auth::GetCredentialsFromProcess(const Aws::String& process) |
420 | { |
421 | Aws::String command = process; |
422 | command.append(" 2>&1" ); // redirect stderr to stdout |
423 | Aws::String result = Aws::Utils::StringUtils::Trim(Aws::OSVersionInfo::GetSysCommandOutput(command.c_str()).c_str()); |
424 | Json::JsonValue credentialsDoc(result); |
425 | if (!credentialsDoc.WasParseSuccessful()) |
426 | { |
427 | AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Failed to load credential from running: " << command << " Error: " << result); |
428 | return {}; |
429 | } |
430 | |
431 | Aws::Utils::Json::JsonView credentialsView(credentialsDoc); |
432 | if (!credentialsView.KeyExists("Version" ) || credentialsView.GetInteger("Version" ) != 1) |
433 | { |
434 | AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Encountered an unsupported process credentials payload version:" << credentialsView.GetInteger("Version" )); |
435 | return {}; |
436 | } |
437 | |
438 | AWSCredentials credentials; |
439 | Aws::String accessKey, secretKey, token, expire; |
440 | if (credentialsView.KeyExists("AccessKeyId" )) |
441 | { |
442 | credentials.SetAWSAccessKeyId(credentialsView.GetString("AccessKeyId" )); |
443 | } |
444 | |
445 | if (credentialsView.KeyExists("SecretAccessKey" )) |
446 | { |
447 | credentials.SetAWSSecretKey(credentialsView.GetString("SecretAccessKey" )); |
448 | } |
449 | |
450 | if (credentialsView.KeyExists("SessionToken" )) |
451 | { |
452 | credentials.SetSessionToken(credentialsView.GetString("SessionToken" )); |
453 | } |
454 | |
455 | if (credentialsView.KeyExists("Expiration" )) |
456 | { |
457 | const auto expiration = Aws::Utils::DateTime(credentialsView.GetString("Expiration" ), DateFormat::ISO_8601); |
458 | if (expiration.WasParseSuccessful()) |
459 | { |
460 | credentials.SetExpiration(expiration); |
461 | } |
462 | else |
463 | { |
464 | AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Failed to parse credential's expiration value as an ISO 8601 Date. Credentials will be marked expired." ); |
465 | credentials.SetExpiration(Aws::Utils::DateTime::Now()); |
466 | } |
467 | } |
468 | else |
469 | { |
470 | credentials.SetExpiration(std::chrono::time_point<std::chrono::system_clock>::max()); |
471 | } |
472 | |
473 | AWS_LOGSTREAM_DEBUG(PROFILE_LOG_TAG, "Successfully pulled credentials from process credential with AccessKey: " << accessKey << ", Expiration:" << credentialsView.GetString("Expiration" )); |
474 | return credentials; |
475 | } |
476 | |
477 | |