AWSCredentialsProvider.cpp 17 KB

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