class Aws::ECSCredentials
An auto-refreshing credential provider that loads credentials from instances running in containers.
ecs_credentials = Aws::ECSCredentials.new(retries: 3) ec2 = Aws::EC2::Client.new(credentials: ecs_credentials)
Constants
- NETWORK_ERRORS
These are the errors we trap when attempting to talk to the instance metadata service. Any of these imply the service is not present, no responding or some other non-recoverable error. @api private
Attributes
@return [Integer] The number of times to retry failed attempts to
fetch credentials from the instance metadata service. Defaults to 0.
Public Class Methods
@param [Hash] options @option options [Integer] :retries (5) Number of times to retry
when retrieving credentials.
@option options [String] :ip_address (‘169.254.170.2’) This value is
ignored if `endpoint` is set and `credential_path` is not set.
@option options [Integer] :port (80) This value is ignored if ‘endpoint`
is set and `credential_path` is not set.
@option options [String] :credential_path By default, the value of the
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variable.
@option options [String] :endpoint The container credential endpoint.
By default, this is the value of the AWS_CONTAINER_CREDENTIALS_FULL_URI environment variable. This value is ignored if `credential_path` or ENV['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'] is set.
@option options [Float] :http_open_timeout (5) @option options [Float] :http_read_timeout (5) @option options [Numeric, Proc] :delay By default, failures are retried
with exponential back-off, i.e. `sleep(1.2 ** num_failures)`. You can pass a number of seconds to sleep between failed attempts, or a Proc that accepts the number of failures.
@option options [IO] :http_debug_output (nil) HTTP wire
traces are sent to this object. You can specify something like $stdout.
@option options [Callable] before_refresh Proc called before
credentials are refreshed. `before_refresh` is called with an instance of this object when AWS credentials are required and need to be refreshed.
Aws::RefreshingCredentials::new
# File lib/aws-sdk-core/ecs_credentials.rb, line 67 def initialize(options = {}) credential_path = options[:credential_path] || ENV['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'] endpoint = options[:endpoint] || ENV['AWS_CONTAINER_CREDENTIALS_FULL_URI'] initialize_uri(options, credential_path, endpoint) @retries = options[:retries] || 5 @http_open_timeout = options[:http_open_timeout] || 5 @http_read_timeout = options[:http_read_timeout] || 5 @http_debug_output = options[:http_debug_output] @backoff = backoff(options[:backoff]) @async_refresh = false super end
Private Instance Methods
# File lib/aws-sdk-core/ecs_credentials.rb, line 173 def backoff(backoff) case backoff when Proc then backoff when Numeric then ->(_) { sleep(backoff) } else ->(num_failures) { Kernel.sleep(1.2**num_failures) } end end
Verify that the IP address is a link-local address from ECS or EKS. ECS container host (IPv4 ‘169.254.170.2`) EKS container host (IPv4 `169.254.170.23`, IPv6 `fd00:ec2::23`)
# File lib/aws-sdk-core/ecs_credentials.rb, line 162 def ecs_or_eks_ip?(ip_address) case ip_address.family when Socket::AF_INET [0xa9feaa02, 0xa9feaa17].include?(ip_address) when Socket::AF_INET6 ip_address == 0xfd00_0ec2_0000_0000_0000_0000_0000_0023 else false end end
# File lib/aws-sdk-core/ecs_credentials.rb, line 199 def get_credentials # Retry loading credentials a configurable number of times if # the instance metadata service is not responding. retry_errors(NETWORK_ERRORS, max_retries: @retries) do open_connection do |conn| http_get(conn, @credential_path) end end rescue TokenFileReadError, InvalidTokenError raise rescue StandardError => e warn("Error retrieving ECS Credentials: #{e.message}") '{}' end
# File lib/aws-sdk-core/ecs_credentials.rb, line 249 def http_get(connection, path) request = Net::HTTP::Get.new(path) set_authorization_token(request) response = connection.request(request) raise Non200Response unless response.code.to_i == 200 response.body end
# File lib/aws-sdk-core/ecs_credentials.rb, line 109 def initialize_full_uri(endpoint) uri = URI.parse(endpoint) validate_full_uri_scheme!(uri) validate_full_uri!(uri) @host = uri.hostname @port = uri.port @scheme = uri.scheme @credential_path = uri.request_uri end
# File lib/aws-sdk-core/ecs_credentials.rb, line 102 def initialize_relative_uri(options, path) @host = options[:ip_address] || '169.254.170.2' @port = options[:port] || 80 @scheme = 'http' @credential_path = path end
# File lib/aws-sdk-core/ecs_credentials.rb, line 89 def initialize_uri(options, credential_path, endpoint) if credential_path initialize_relative_uri(options, credential_path) # Use FULL_URI/endpoint only if RELATIVE_URI/path is not set elsif endpoint initialize_full_uri(endpoint) else raise ArgumentError, 'Cannot instantiate an ECS Credential Provider '\ 'without a credential path or endpoint.' end end
loopback? method is available in Ruby 2.5+ Replicate the logic here. loopback (IPv4 127.0.0.0/8, IPv6 ::1/128)
# File lib/aws-sdk-core/ecs_credentials.rb, line 148 def ip_loopback?(ip_address) case ip_address.family when Socket::AF_INET ip_address & 0xff000000 == 0x7f000000 when Socket::AF_INET6 ip_address == 1 else false end end
# File lib/aws-sdk-core/ecs_credentials.rb, line 239 def open_connection http = Net::HTTP.new(@host, @port, nil) http.open_timeout = @http_open_timeout http.read_timeout = @http_read_timeout http.set_debug_output(@http_debug_output) if @http_debug_output http.use_ssl = @scheme == 'https' http.start yield(http).tap { http.finish } end
# File lib/aws-sdk-core/ecs_credentials.rb, line 181 def refresh # Retry loading credentials up to 3 times is the instance metadata # service is responding but is returning invalid JSON documents # in response to the GET profile credentials call. retry_errors([Aws::Json::ParseError, StandardError], max_retries: 3) do c = Aws::Json.load(get_credentials.to_s) @credentials = Credentials.new( c['AccessKeyId'], c['SecretAccessKey'], c['Token'] ) @expiration = c['Expiration'] ? Time.iso8601(c['Expiration']) : nil end rescue Aws::Json::ParseError raise Aws::Errors::MetadataParserError end
# File lib/aws-sdk-core/ecs_credentials.rb, line 265 def retry_errors(error_classes, options = {}) max_retries = options[:max_retries] retries = 0 begin yield rescue TokenFileReadError, InvalidTokenError raise rescue *error_classes => _e raise unless retries < max_retries @backoff.call(retries) retries += 1 retry end end
# File lib/aws-sdk-core/ecs_credentials.rb, line 141 def valid_ip_address?(ip_address) ip_loopback?(ip_address) || ecs_or_eks_ip?(ip_address) end
Validate that the full URI is using a loopback address if scheme is http.
# File lib/aws-sdk-core/ecs_credentials.rb, line 126 def validate_full_uri!(full_uri) return unless full_uri.scheme == 'http' begin return if valid_ip_address?(IPAddr.new(full_uri.host)) rescue IPAddr::InvalidAddressError addresses = Resolv.getaddresses(full_uri.host) return if addresses.all? { |addr| valid_ip_address?(IPAddr.new(addr)) } end raise ArgumentError, 'AWS_CONTAINER_CREDENTIALS_FULL_URI must use a local loopback '\ 'or an ECS or EKS link-local address when using the http scheme.' end
# File lib/aws-sdk-core/ecs_credentials.rb, line 119 def validate_full_uri_scheme!(full_uri) return if full_uri.is_a?(URI::HTTP) || full_uri.is_a?(URI::HTTPS) raise ArgumentError, "'#{full_uri}' must be a valid HTTP or HTTPS URI" end