/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Util;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
namespace Amazon.Runtime
{
///
/// When running in an ECS container and AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set,
/// use the given end point to retrieve the credentials.
///
public class ECSTaskCredentials : URIBasedRefreshingCredentialHelper
{
///
/// These constants should not be consumed by client code. They are only relevant
/// in the context of ECS container and, especially, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, AWS_CONTAINER_CREDENTIALS_FULL_URI & AWS_CONTAINER_AUTHORIZATION_TOKEN
/// environment variable should not be overriden by the client code.
///
public const string ContainerCredentialsURIEnvVariable = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
public const string ContainerCredentialsFullURIEnvVariable = "AWS_CONTAINER_CREDENTIALS_FULL_URI";
public const string ContainerCredentialsAuthorizationTokenEnvVariable = "AWS_CONTAINER_AUTHORIZATION_TOKEN";
public const string EndpointAddress = "http://169.254.170.2";
private Uri Uri {
get
{
var relativeUri = Environment.GetEnvironmentVariable(ContainerCredentialsURIEnvVariable);
var fullUri = Environment.GetEnvironmentVariable(ContainerCredentialsFullURIEnvVariable);
// AWS_CONTAINER_CREDENTIALS_FULL_URI variable is not compatible with AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, and will not be used if AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set.
if (!string.IsNullOrEmpty(relativeUri))
{
var uriBuilder = new UriBuilder(Server);
uriBuilder.Path = relativeUri;
return uriBuilder.Uri;
}
else if (!string.IsNullOrEmpty(fullUri))
{
return new Uri(fullUri);
}
else
{
throw new ArgumentException($"Cannot fetch credentials from container - neither {ContainerCredentialsURIEnvVariable} or {ContainerCredentialsFullURIEnvVariable}" +
$" environment variables are set.");
}
}
}
private string Server = null;
private static int MaxRetries = 5;
private IWebProxy Proxy;
public ECSTaskCredentials() : this(null) { PreemptExpiryTime = TimeSpan.FromMinutes(5); }
public ECSTaskCredentials(IWebProxy proxy)
{
Server = EndpointAddress;
Proxy = proxy;
PreemptExpiryTime = TimeSpan.FromMinutes(5);
}
protected override CredentialsRefreshState GenerateNewCredentials()
{
SecurityCredentials credentials = null;
Uri ecsEndpointUri = Uri;
JitteredDelay retry = new JitteredDelay(new TimeSpan(0, 0, 0, 0, 200), new TimeSpan(0, 0, 0, 0, 50));
// Attempt to get the credentials 4 times ignoring null return/exceptions and on the 5th try, escalate the exception if there is one.
for (int i = 1; ; i++)
{
try
{
// AWS_CONTAINER_AUTHORIZATION_TOKEN is optional environment variable
// If this variable is set the SDK will set the Authorization header on the HTTP request with the environment variable's value.
var headers = CreateAuthorizationHeader();
credentials = GetObjectFromResponse(ecsEndpointUri, Proxy, headers);
if (credentials != null)
{
break;
}
}
catch (Exception e)
{
if (i == MaxRetries)
{
throw new AmazonServiceException(string.Format(CultureInfo.InvariantCulture,
"Unable to retrieve credentials. Message = \"{0}\".",
e.Message));
}
};
Util.AWSSDKUtils.Sleep(retry.Next());
}
return new CredentialsRefreshState(new ImmutableCredentials(credentials.AccessKeyId, credentials.SecretAccessKey, credentials.Token), credentials.Expiration);
}
private static Dictionary CreateAuthorizationHeader()
{
Dictionary headers = null;
var authorizationToken = Environment.GetEnvironmentVariable(ContainerCredentialsAuthorizationTokenEnvVariable);
if (!string.IsNullOrEmpty(authorizationToken))
{
headers = new Dictionary
{
{HeaderKeys.AuthorizationHeader, authorizationToken}
};
}
return headers;
}
}
}