Module: Fog::AWS::CredentialFetcher::ServiceMethods

Instance Method Summary collapse

Instance Method Details

#fetch_credentials(options) ⇒ Object

[View source]

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/fog/aws/credential_fetcher.rb', line 17

def fetch_credentials(options)
  if options[:use_iam_profile] && Fog.mocking?
    return Fog::AWS::Compute::Mock.data[:iam_role_based_creds]
  end
  if options[:use_iam_profile]
    begin
      role_data = nil
      region = options[:region] || ENV["AWS_REGION"] || ENV["AWS_DEFAULT_REGION"]

      if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
        connection = options[:connection] || Excon.new(CONTAINER_CREDENTIALS_HOST)
        credential_path = options[:credential_path] || ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
        role_data = connection.get(:path => credential_path, :idempotent => true, :expects => 200).body
        session = Fog::JSON.decode(role_data)

        if region.nil?
          connection = options[:metadata_connection] || Excon.new(INSTANCE_METADATA_HOST)
          token_header = fetch_credentials_token_header(connection, options[:disable_imds_v2])
          region = connection.get(:path => INSTANCE_METADATA_AZ, :idempotent => true, :expects => 200, :headers => token_header).body[0..-2]
        end
      elsif ENV["AWS_CONTAINER_CREDENTIALS_FULL_URI"]
        connection = options[:connection] || Excon.new(ENV['AWS_CONTAINER_CREDENTIALS_FULL_URI'])
        container_authorization_token = File.read(options[:aws_container_authorization_token_file] || ENV['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'])
        role_data = connection.get(:idempotent => true, :expects => 200, :headers => {'Authorization' => container_authorization_token}).body
        session = Fog::JSON.decode(role_data)

        if region.nil?
          connection = options[:metadata_connection] || Excon.new(INSTANCE_METADATA_HOST)
          token_header = fetch_credentials_token_header(connection, options[:disable_imds_v2])
          region = connection.get(:path => INSTANCE_METADATA_AZ, :idempotent => true, :expects => 200, :headers => token_header).body[0..-2]
        end
      elsif ENV["AWS_WEB_IDENTITY_TOKEN_FILE"]
        params = {
          :Action => "AssumeRoleWithWebIdentity",
          :RoleArn => options[:role_arn] || ENV.fetch("AWS_ROLE_ARN"),
          :RoleSessionName => options[:role_session_name] || ENV["AWS_ROLE_SESSION_NAME"] || "fog-aws-#{SecureRandom.hex}",
          :WebIdentityToken => File.read(options[:aws_web_identity_token_file] || ENV.fetch("AWS_WEB_IDENTITY_TOKEN_FILE")),
          :DurationSeconds => options[:duration] || 3600,
          :Version => "2011-06-15",
        }

        sts_endpoint =
          if ENV["AWS_STS_REGIONAL_ENDPOINTS"] == "regional" && region
            "https://sts.#{region}.amazonaws.com"
          else
            "https://sts.amazonaws.com"
          end

        connection = options[:connection] || Excon.new(sts_endpoint, :query => params)
        document = Nokogiri::XML(connection.get(:idempotent => true, :expects => 200).body)

        session = {
          "AccessKeyId" => document.css("AccessKeyId").children.text,
          "SecretAccessKey" => document.css("SecretAccessKey").children.text,
          "Token" => document.css("SessionToken").children.text,
          "Expiration" => document.css("Expiration").children.text,
        }

        if region.nil?
          connection = options[:metadata_connection] || Excon.new(INSTANCE_METADATA_HOST)
          token_header = fetch_credentials_token_header(connection, options[:disable_imds_v2])
          region = connection.get(:path => INSTANCE_METADATA_AZ, :idempotent => true, :expects => 200, :headers => token_header).body[0..-2]
        end
      else
        connection = options[:connection] || Excon.new(INSTANCE_METADATA_HOST)
        token_header = fetch_credentials_token_header(connection, options[:disable_imds_v2])
        role_name = connection.get(:path => INSTANCE_METADATA_PATH, :idempotent => true, :expects => 200, :headers => token_header).body
        role_data = connection.get(:path => INSTANCE_METADATA_PATH+role_name, :idempotent => true, :expects => 200, :headers => token_header).body
        session = Fog::JSON.decode(role_data)

        region ||= connection.get(:path => INSTANCE_METADATA_AZ, :idempotent => true, :expects => 200, :headers => token_header).body[0..-2]
      end

      credentials = {}
      credentials[:aws_access_key_id] = session['AccessKeyId']
      credentials[:aws_secret_access_key] = session['SecretAccessKey']
      credentials[:aws_session_token] = session['Token']
      credentials[:aws_credentials_expire_at] = Time.xmlschema session['Expiration']

      # set region by default to the one the instance is in.
      credentials[:region] = region
      credentials[:sts_endpoint] = sts_endpoint if sts_endpoint
      #these indicate the metadata service is unavailable or has no profile setup
      credentials
    rescue Excon::Error => e
      Fog::Logger.warning("Unable to fetch credentials: #{e.message}")
      super
    end
  else
    super
  end
end

#fetch_credentials_token_header(connection, disable_imds_v2) ⇒ Object

[View source]

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/fog/aws/credential_fetcher.rb', line 110

def fetch_credentials_token_header(connection, disable_imds_v2)
  return nil if disable_imds_v2

  token = connection.put(
    :path => INSTANCE_METADATA_TOKEN,
    :idempotent => true,
    :expects => 200,
    :retry_interval => 1,
    :retry_limit => 3,
    :read_timeout => 1,
    :write_timeout => 1,
    :connect_timeout => 1,
    :headers => { "X-aws-ec2-metadata-token-ttl-seconds" => "300" }
  ).body

  { "X-aws-ec2-metadata-token" => token }
rescue Excon::Error
  nil
end