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/STSCredentialsProvider.h>
18#include <aws/core/config/AWSProfileConfigLoader.h>
19#include <aws/core/platform/Environment.h>
20#include <aws/core/platform/FileSystem.h>
21#include <aws/core/utils/logging/LogMacros.h>
22#include <aws/core/utils/StringUtils.h>
23#include <aws/core/utils/FileSystemUtils.h>
24#include <aws/core/client/SpecifiedRetryableErrorsRetryStrategy.h>
25#include <aws/core/utils/StringUtils.h>
26#include <aws/core/utils/UUID.h>
27#include <cstdlib>
28#include <fstream>
29#include <string.h>
30#include <climits>
31
32
33using namespace Aws::Utils;
34using namespace Aws::Utils::Logging;
35using namespace Aws::Auth;
36using namespace Aws::Internal;
37using namespace Aws::FileSystem;
38using namespace Aws::Client;
39using Aws::Utils::Threading::ReaderLockGuard;
40using Aws::Utils::Threading::WriterLockGuard;
41
42static const char STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG[] = "STSAssumeRoleWithWebIdentityCredentialsProvider";
43STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider() :
44 m_initialized(false)
45{
46 // check environment variables
47 Aws::String tmpRegion = Aws::Environment::GetEnv("AWS_DEFAULT_REGION");
48 m_roleArn = Aws::Environment::GetEnv("AWS_ROLE_ARN");
49 m_tokenFile = Aws::Environment::GetEnv("AWS_WEB_IDENTITY_TOKEN_FILE");
50 m_sessionName = Aws::Environment::GetEnv("AWS_ROLE_SESSION_NAME");
51
52 // check profile_config if either m_roleArn or m_tokenFile is not loaded from environment variable
53 // 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.
54 if (m_roleArn.empty() || m_tokenFile.empty() || tmpRegion.empty())
55 {
56 auto profile = Aws::Config::GetCachedConfigProfile(Aws::Auth::GetConfigProfileName());
57 if (tmpRegion.empty())
58 {
59 tmpRegion = profile.GetRegion();
60 }
61 // If either of these two were not found from environment, use whatever found for all three in config file
62 if (m_roleArn.empty() || m_tokenFile.empty())
63 {
64 m_roleArn = profile.GetRoleArn();
65 m_tokenFile = profile.GetValue("web_identity_token_file");
66 m_sessionName = profile.GetValue("role_session_name");
67 }
68 }
69
70 if (m_tokenFile.empty())
71 {
72 AWS_LOGSTREAM_WARN(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Token file must be specified to use STS AssumeRole web identity creds provider.");
73 return; // No need to do further constructing
74 }
75 else
76 {
77 AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved token_file from profile_config or environment variable to be " << m_tokenFile);
78 }
79
80 if (m_roleArn.empty())
81 {
82 AWS_LOGSTREAM_WARN(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "RoleArn must be specified to use STS AssumeRole web identity creds provider.");
83 return; // No need to do further constructing
84 }
85 else
86 {
87 AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved role_arn from profile_config or environment variable to be " << m_roleArn);
88 }
89
90 if (tmpRegion.empty())
91 {
92 tmpRegion = Aws::Region::US_EAST_1;
93 }
94 else
95 {
96 AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved region from profile_config or environment variable to be " << tmpRegion);
97 }
98
99 if (m_sessionName.empty())
100 {
101 m_sessionName = Aws::Utils::UUID::RandomUUID();
102 }
103 else
104 {
105 AWS_LOGSTREAM_DEBUG(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Resolved session_name from profile_config or environment variable to be " << m_sessionName);
106 }
107
108 Aws::Client::ClientConfiguration config;
109 config.scheme = Aws::Http::Scheme::HTTPS;
110 config.region = tmpRegion;
111
112 Aws::Vector<Aws::String> retryableErrors;
113 retryableErrors.push_back("IDPCommunicationError");
114 retryableErrors.push_back("InvalidIdentityToken");
115
116 config.retryStrategy = Aws::MakeShared<SpecifiedRetryableErrorsRetryStrategy>(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, retryableErrors, 3/*maxRetries*/);
117
118 m_client = Aws::MakeUnique<Aws::Internal::STSCredentialsClient>(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, config);
119 m_initialized = true;
120 AWS_LOGSTREAM_INFO(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Creating STS AssumeRole with web identity creds provider.");
121}
122
123AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::GetAWSCredentials()
124{
125 // A valid client means required information like role arn and token file were constructed correctly.
126 // We can use this provider to load creds, otherwise, we can just return empty creds.
127 if (!m_initialized)
128 {
129 return Aws::Auth::AWSCredentials();
130 }
131 RefreshIfExpired();
132 ReaderLockGuard guard(m_reloadLock);
133 return m_credentials;
134}
135
136void STSAssumeRoleWebIdentityCredentialsProvider::Reload()
137{
138 AWS_LOGSTREAM_INFO(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Credentials have expired, attempting to renew from STS.");
139
140 Aws::IFStream tokenFile(m_tokenFile.c_str());
141 if(tokenFile)
142 {
143 Aws::String token((std::istreambuf_iterator<char>(tokenFile)), std::istreambuf_iterator<char>());
144 m_token = token;
145 }
146 else
147 {
148 AWS_LOGSTREAM_ERROR(STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG, "Can't open token file: " << m_tokenFile);
149 return;
150 }
151 STSCredentialsClient::STSAssumeRoleWithWebIdentityRequest request {m_sessionName, m_roleArn, m_token};
152
153 auto result = m_client->GetAssumeRoleWithWebIdentityCredentials(request);
154 m_credentials = result.creds;
155}
156
157void STSAssumeRoleWebIdentityCredentialsProvider::RefreshIfExpired()
158{
159 ReaderLockGuard guard(m_reloadLock);
160 if (!m_credentials.IsExpiredOrEmpty())
161 {
162 return;
163 }
164
165 guard.UpgradeToWriterLock();
166 if (!m_credentials.IsExpiredOrEmpty()) // double-checked lock to avoid refreshing twice
167 {
168 return;
169 }
170
171 Reload();
172}
173