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 | |
33 | using namespace Aws::Utils; |
34 | using namespace Aws::Utils::Logging; |
35 | using namespace Aws::Auth; |
36 | using namespace Aws::Internal; |
37 | using namespace Aws::FileSystem; |
38 | using namespace Aws::Client; |
39 | using Aws::Utils::Threading::ReaderLockGuard; |
40 | using Aws::Utils::Threading::WriterLockGuard; |
41 | |
42 | static const char STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG[] = "STSAssumeRoleWithWebIdentityCredentialsProvider" ; |
43 | STSAssumeRoleWebIdentityCredentialsProvider::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 | |
123 | AWSCredentials 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 | |
136 | void 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 | |
157 | void 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 | |