PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Credentials / EcsCredentialProvider.php
transferito / vendor / aws / aws-sdk-php / src / Credentials Last commit date
AssumeRoleCredentialProvider.php 11 months ago AssumeRoleWithWebIdentityCredentialProvider.php 11 months ago CredentialProvider.php 11 months ago Credentials.php 11 months ago CredentialsInterface.php 11 months ago EcsCredentialProvider.php 11 months ago InstanceProfileProvider.php 11 months ago
EcsCredentialProvider.php
233 lines
1 <?php
2 namespace Aws\Credentials;
3
4 use Aws\Exception\CredentialsException;
5 use GuzzleHttp\Exception\GuzzleException;
6 use GuzzleHttp\Psr7\Request;
7 use GuzzleHttp\Promise\PromiseInterface;
8 use Psr\Http\Message\ResponseInterface;
9
10 /**
11 * Credential provider that fetches container credentials with GET request.
12 * container environment variables are used in constructing request URI.
13 */
14 class EcsCredentialProvider
15 {
16 const SERVER_URI = 'http://169.254.170.2';
17 const ENV_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
18 const ENV_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI";
19 const ENV_AUTH_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN";
20 const ENV_AUTH_TOKEN_FILE = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE";
21 const ENV_TIMEOUT = 'AWS_METADATA_SERVICE_TIMEOUT';
22 const EKS_SERVER_HOST_IPV4 = '169.254.170.23';
23 const EKS_SERVER_HOST_IPV6 = 'fd00:ec2::23';
24
25 /** @var callable */
26 private $client;
27
28 /** @var float|mixed */
29 private $timeout;
30
31 /**
32 * The constructor accepts following options:
33 * - timeout: (optional) Connection timeout, in seconds, default 1.0
34 * - client: An EcsClient to make request from
35 *
36 * @param array $config Configuration options
37 */
38 public function __construct(array $config = [])
39 {
40 $timeout = getenv(self::ENV_TIMEOUT);
41
42 if (!$timeout) {
43 $timeout = $_SERVER[self::ENV_TIMEOUT] ?? ($config['timeout'] ?? 1.0);
44 }
45
46 $this->timeout = (float) $timeout;
47 $this->client = $config['client'] ?? \Aws\default_http_handler();
48 }
49
50 /**
51 * Load container credentials.
52 *
53 * @return PromiseInterface
54 * @throws GuzzleException
55 */
56 public function __invoke()
57 {
58 $client = $this->client;
59 $uri = self::getEcsUri();
60
61 if ($this->isCompatibleUri($uri)) {
62 $request = new Request('GET', $uri);
63
64 $headers = $this->getHeadersForAuthToken();
65 return $client(
66 $request,
67 [
68 'timeout' => $this->timeout,
69 'proxy' => '',
70 'headers' => $headers
71 ]
72 )->then(function (ResponseInterface $response) {
73 $result = $this->decodeResult((string) $response->getBody());
74 return new Credentials(
75 $result['AccessKeyId'],
76 $result['SecretAccessKey'],
77 $result['Token'],
78 strtotime($result['Expiration'])
79 );
80 })->otherwise(function ($reason) {
81 $reason = is_array($reason) ? $reason['exception'] : $reason;
82 $msg = $reason->getMessage();
83 throw new CredentialsException(
84 "Error retrieving credentials from container metadata ($msg)"
85 );
86 });
87 }
88
89 throw new CredentialsException("Uri '{$uri}' contains an unsupported host.");
90 }
91
92 /**
93 * Retrieves authorization token.
94 *
95 * @return array|false|string
96 */
97 private function getEcsAuthToken()
98 {
99 if (!empty($path = getenv(self::ENV_AUTH_TOKEN_FILE))) {
100 if (is_readable($path)) {
101 return file_get_contents($path);
102 }
103
104 throw new CredentialsException(
105 "Failed to read authorization token from '{$path}': no such file or directory."
106 );
107 }
108
109 return getenv(self::ENV_AUTH_TOKEN);
110 }
111
112 /**
113 * Provides headers for credential metadata request.
114 *
115 * @return array|array[]|string[]
116 */
117 private function getHeadersForAuthToken()
118 {
119 $authToken = self::getEcsAuthToken();
120 $headers = [];
121
122 if (!empty($authToken))
123 $headers = ['Authorization' => $authToken];
124
125 return $headers;
126 }
127
128 /** @deprecated */
129 public function setHeaderForAuthToken()
130 {
131 $authToken = self::getEcsAuthToken();
132 $headers = [];
133 if (!empty($authToken))
134 $headers = ['Authorization' => $authToken];
135
136 return $headers;
137 }
138
139 /**
140 * Fetch container metadata URI from container environment variable.
141 *
142 * @return string Returns container metadata URI
143 */
144 private function getEcsUri()
145 {
146 $credsUri = getenv(self::ENV_URI);
147
148 if ($credsUri === false) {
149 $credsUri = $_SERVER[self::ENV_URI] ?? '';
150 }
151
152 if (empty($credsUri)){
153 $credFullUri = getenv(self::ENV_FULL_URI);
154 if ($credFullUri === false){
155 $credFullUri = $_SERVER[self::ENV_FULL_URI] ?? '';
156 }
157
158 if (!empty($credFullUri))
159 return $credFullUri;
160 }
161
162 return self::SERVER_URI . $credsUri;
163 }
164
165 private function decodeResult($response)
166 {
167 $result = json_decode($response, true);
168
169 if (!isset($result['AccessKeyId'])) {
170 throw new CredentialsException('Unexpected container metadata credentials value');
171 }
172 return $result;
173 }
174
175 /**
176 * Determines whether or not a given request URI is a valid
177 * container credential request URI.
178 *
179 * @param $uri
180 *
181 * @return bool
182 */
183 private function isCompatibleUri($uri)
184 {
185 $parsed = parse_url($uri);
186
187 if ($parsed['scheme'] !== 'https') {
188 $host = trim($parsed['host'], '[]');
189 $ecsHost = parse_url(self::SERVER_URI)['host'];
190 $eksHost = self::EKS_SERVER_HOST_IPV4;
191
192 if ($host !== $ecsHost
193 && $host !== $eksHost
194 && $host !== self::EKS_SERVER_HOST_IPV6
195 && !$this->isLoopbackAddress(gethostbyname($host))
196 ) {
197 return false;
198 }
199 }
200
201 return true;
202 }
203
204 /**
205 * Determines whether or not a given host
206 * is a loopback address.
207 *
208 * @param $host
209 *
210 * @return bool
211 */
212 private function isLoopbackAddress($host)
213 {
214 if (!filter_var($host, FILTER_VALIDATE_IP)) {
215 return false;
216 }
217
218 if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
219 if ($host === '::1') {
220 return true;
221 }
222
223 return false;
224 }
225
226 $loopbackStart = ip2long('127.0.0.0');
227 $loopbackEnd = ip2long('127.255.255.255');
228 $ipLong = ip2long($host);
229
230 return ($ipLong >= $loopbackStart && $ipLong <= $loopbackEnd);
231 }
232 }
233