/// <summary>
        /// Fetches the api token to use with metadata requests.
        /// </summary>
        /// <param name="tries">The number of tries to fetch the api token before giving up and throwing the web exception</param>
        /// <returns>The API token or null if an API token couldn't be obtained and doesn't need to be used</returns>
        private static string FetchApiToken(int tries)
        {
            for (int retry = 1; retry <= tries; retry++)
            {
                if (!IsIMDSEnabled || useNullToken)
                {
                    return(null);
                }

                try
                {
                    var uriForToken = new Uri(EC2_APITOKEN_URL);

                    var headers = new Dictionary <string, string>();
                    headers.Add(HeaderKeys.XAwsEc2MetadataTokenTtlSeconds, DEFAULT_APITOKEN_TTL.ToString(CultureInfo.InvariantCulture));
                    var content = AWSSDKUtils.ExecuteHttpRequest(uriForToken, "PUT", null, TimeSpan.FromSeconds(5), Proxy, headers);
                    return(content.Trim());
                }
                catch (Exception e)
                {
                    HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                    if (httpStatusCode == HttpStatusCode.NotFound ||
                        httpStatusCode == HttpStatusCode.MethodNotAllowed ||
                        httpStatusCode == HttpStatusCode.Forbidden)
                    {
                        useNullToken = true;
                        return(null);
                    }

                    if (retry >= tries)
                    {
                        if (httpStatusCode == HttpStatusCode.BadRequest)
                        {
                            Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "Unable to contact EC2 Metadata service to obtain a metadata token.");
                            throw;
                        }

                        Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "Unable to contact EC2 Metadata service to obtain a metadata token. Attempting to access IMDS without a token.");

                        //If there isn't a status code, it was a failure to contact the server which would be
                        //a request failure, a network issue, or a timeout. Cache this response and fallback
                        //to IMDS flow without a token. If the non token IMDS flow returns unauthorized, the
                        //useNullToken flag will be cleared and the IMDS flow will attempt to obtain another
                        //token.
                        if (httpStatusCode == null)
                        {
                            useNullToken = true;
                        }

                        //Return null to fallback to the IMDS flow without using a token.
                        return(null);
                    }

                    PauseExponentially(retry - 1);
                }
            }

            return(null);
        }
        protected override CredentialsRefreshState GenerateNewCredentials()
        {
            CredentialsRefreshState newState = null;
            var token = EC2InstanceMetadata.FetchApiToken();

            try
            {
                // Attempt to get early credentials. OK to fail at this point.
                newState = GetRefreshState(token);
            }
            catch (Exception e)
            {
                HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                if (httpStatusCode == HttpStatusCode.Unauthorized)
                {
                    EC2InstanceMetadata.ClearTokenFlag();
                    Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                    throw;
                }

                var logger = Logger.GetLogger(typeof(InstanceProfileAWSCredentials));
                logger.InfoFormat("Error getting credentials from Instance Profile service: {0}", e);
            }

            // If successful, save new credentials
            if (newState != null)
            {
                _currentRefreshState = newState;
            }

            // If still not successful (no credentials available at start), attempt once more to
            // get credentials, but now without swallowing exception
            if (_currentRefreshState == null)
            {
                try
                {
                    _currentRefreshState = GetRefreshState(token);
                }
                catch (Exception e)
                {
                    HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                    if (httpStatusCode == HttpStatusCode.Unauthorized)
                    {
                        EC2InstanceMetadata.ClearTokenFlag();
                        Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                    }

                    throw;
                }
            }

            // Return credentials that will expire in at most one hour
            CredentialsRefreshState state = GetEarlyRefreshState(_currentRefreshState);

            return(state);
        }
        /// <summary>
        /// Retrieves a list of all roles available through current InstanceProfile service
        /// </summary>
        /// <returns></returns>
        public static IEnumerable <string> GetAvailableRoles(IWebProxy proxy)
        {
            var token = EC2InstanceMetadata.FetchApiToken();

            var allAliases = string.Empty;

            try
            {
                allAliases = GetContents(RolesUri, proxy, CreateMetadataTokenHeaders(token));
            }
            catch (Exception e)
            {
                HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                if (httpStatusCode == HttpStatusCode.Unauthorized)
                {
                    EC2InstanceMetadata.ClearTokenFlag();
                    Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                }

                throw;
            }
            if (string.IsNullOrEmpty(allAliases))
            {
                yield break;
            }

            string[] parts = allAliases.Split(AliasSeparators, StringSplitOptions.RemoveEmptyEntries);
            foreach (var part in parts)
            {
                var trim = part.Trim();
                if (!string.IsNullOrEmpty(trim))
                {
                    yield return(trim);
                }
            }
        }
        private static List <string> GetItems(string relativeOrAbsolutePath, int tries, bool slurp, string token)
        {
            var items = new List <string>();
            //For all meta-data queries we need to fetch an api token to use. In the event a
            //token cannot be obtained we will fallback to not using a token.
            Dictionary <string, string> headers = null;

            if (token == null)
            {
                token = FetchApiToken(DEFAULT_RETRIES);
            }

            if (!string.IsNullOrEmpty(token))
            {
                headers = new Dictionary <string, string>();
                headers.Add(HeaderKeys.XAwsEc2MetadataToken, token);
            }

            try
            {
                if (!IsIMDSEnabled)
                {
                    throw new IMDSDisabledException();
                }

                // if we are given a relative path, we assume the data we need exists under the
                // main metadata root
                var uri = relativeOrAbsolutePath.StartsWith(EC2_METADATA_SVC, StringComparison.Ordinal)
                            ? new Uri(relativeOrAbsolutePath)
                            : new Uri(EC2_METADATA_ROOT + relativeOrAbsolutePath);

                var content = AWSSDKUtils.ExecuteHttpRequest(uri, "GET", null, TimeSpan.FromSeconds(5), Proxy, headers);
                using (var stream = new StringReader(content))
                {
                    if (slurp)
                    {
                        items.Add(stream.ReadToEnd());
                    }
                    else
                    {
                        string line;
                        do
                        {
                            line = stream.ReadLine();
                            if (line != null)
                            {
                                items.Add(line.Trim());
                            }
                        }while (line != null);
                    }
                }
            }
            catch (IMDSDisabledException)
            {
                // Keep this behavior identical to when HttpStatusCode.NotFound is returned.
                return(null);
            }
            catch (Exception e)
            {
                HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                if (httpStatusCode == HttpStatusCode.NotFound)
                {
                    return(null);
                }
                else if (httpStatusCode == HttpStatusCode.Unauthorized)
                {
                    ClearTokenFlag();
                    Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                    throw;
                }

                if (tries <= 1)
                {
                    Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "Unable to contact EC2 Metadata service.");
                    return(null);
                }

                PauseExponentially(DEFAULT_RETRIES - tries);
                return(GetItems(relativeOrAbsolutePath, tries - 1, slurp, token));
            }

            return(items);
        }
Example #5
0
        protected override CredentialsRefreshState GenerateNewCredentials()
        {
            CredentialsRefreshState newState = null;
            var token = EC2InstanceMetadata.FetchApiToken();

            try
            {
                // Attempt to get early credentials. OK to fail at this point.
                newState = GetRefreshState(token);
            }
            catch (Exception e)
            {
                HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                if (httpStatusCode == HttpStatusCode.Unauthorized)
                {
                    EC2InstanceMetadata.ClearTokenFlag();
                    Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                    throw;
                }

                var logger = Logger.GetLogger(typeof(InstanceProfileAWSCredentials));
                logger.InfoFormat("Error getting credentials from Instance Profile service: {0}", e);

                // if we already have cached credentials, we'll continue to use those credentials,
                // but try again to refresh them in 2 minutes.
                if (null != _currentRefreshState)
                {
                    #pragma warning disable CS0612 // Type or member is obsolete
                    var newExpiryTime = AWSSDKUtils.CorrectedUtcNow.ToLocalTime() + TimeSpan.FromMinutes(2);
#pragma warning restore CS0612                     // Type or member is obsolete

                    _currentRefreshState = new CredentialsRefreshState(_currentRefreshState.Credentials.Copy(), newExpiryTime);
                    return(_currentRefreshState);
                }
            }

            if (newState?.IsExpiredWithin(TimeSpan.Zero) == true)
            {
                // special case - credentials returned are expired
                _logger.InfoFormat(_receivedExpiredCredentialsFromIMDS);

                // use a custom refresh time

                #pragma warning disable CS0612 // Type or member is obsolete
                var newExpiryTime = AWSSDKUtils.CorrectedUtcNow.ToLocalTime() + TimeSpan.FromMinutes(new Random().Next(5, 16));
                #pragma warning restore CS0612 // Type or member is obsolete

                _currentRefreshState = new CredentialsRefreshState(newState.Credentials.Copy(), newExpiryTime);

                return(_currentRefreshState);
            }

            // If successful, save new credentials
            if (newState != null)
            {
                _currentRefreshState = newState;
            }

            // If still not successful (no credentials available at start), attempt once more to
            // get credentials, but now without swallowing exception
            if (_currentRefreshState == null)
            {
                try
                {
                    _currentRefreshState = GetRefreshState(token);
                }
                catch (Exception e)
                {
                    HttpStatusCode?httpStatusCode = ExceptionUtils.DetermineHttpStatusCode(e);

                    if (httpStatusCode == HttpStatusCode.Unauthorized)
                    {
                        EC2InstanceMetadata.ClearTokenFlag();
                        Logger.GetLogger(typeof(EC2InstanceMetadata)).Error(e, "EC2 Metadata service returned unauthorized for token based secure data flow.");
                    }

                    throw;
                }
            }

            // Return credentials that will expire in at most one hour
            CredentialsRefreshState state = GetEarlyRefreshState(_currentRefreshState);
            return(state);
        }