From 3fba7a9a26bb97438d5132e756e4d58a26fc809e Mon Sep 17 00:00:00 2001 From: Ash Tyndall Date: Mon, 18 May 2020 07:10:29 +0800 Subject: [PATCH] Gracefully handle failure of IMDSv2 and allow fallback to IMDSv1 --- lib/fog/aws/credential_fetcher.rb | 23 +++++++++++++++++++---- tests/credentials_tests.rb | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/fog/aws/credential_fetcher.rb b/lib/fog/aws/credential_fetcher.rb index 2b4008960..b35c51b85 100644 --- a/lib/fog/aws/credential_fetcher.rb +++ b/lib/fog/aws/credential_fetcher.rb @@ -26,14 +26,12 @@ module Fog if region.nil? connection = options[:metadata_connection] || Excon.new(INSTANCE_METADATA_HOST) - token = connection.put(:path => INSTANCE_METADATA_TOKEN, :idempotent => true, :expects => 200, :headers => { "X-aws-ec2-metadata-token-ttl-seconds" => "300" }).body - token_header = { "X-aws-ec2-metadata-token" => token } + 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 = connection.put(:path => INSTANCE_METADATA_TOKEN, :idempotent => true, :expects => 200, :headers => { "X-aws-ec2-metadata-token-ttl-seconds" => "300" }).body - token_header = { "X-aws-ec2-metadata-token" => token } + 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 region ||= connection.get(:path => INSTANCE_METADATA_AZ, :idempotent => true, :expects => 200, :headers => token_header).body[0..-2] @@ -58,6 +56,23 @@ module Fog super end end + + 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, + :headers => { "X-aws-ec2-metadata-token-ttl-seconds" => "300" } + ).body + + { "X-aws-ec2-metadata-token" => token } + rescue Excon::Error + nil + end end module ConnectionMethods diff --git a/tests/credentials_tests.rb b/tests/credentials_tests.rb index a6a387392..4b9353b8b 100644 --- a/tests/credentials_tests.rb +++ b/tests/credentials_tests.rb @@ -30,6 +30,23 @@ Shindo.tests('AWS | credentials', ['aws']) do aws_credentials_expire_at: expires_at) { Fog::AWS::Compute.fetch_credentials(use_iam_profile: true) } end + tests('#fetch_credentials when the v2 token 404s') do + Excon.stub({ method: :put, path: '/latest/api/token' }, { status: 404, body: 'not found' }) + returns(aws_access_key_id: 'dummykey', + aws_secret_access_key: 'dummysecret', + aws_session_token: 'dummytoken', + region: 'us-west-1', + aws_credentials_expire_at: expires_at) { Fog::AWS::Compute.fetch_credentials(use_iam_profile: true) } + end + + tests('#fetch_credentials when the v2 disabled') do + returns(aws_access_key_id: 'dummykey', + aws_secret_access_key: 'dummysecret', + aws_session_token: 'dummytoken', + region: 'us-west-1', + aws_credentials_expire_at: expires_at) { Fog::AWS::Compute.fetch_credentials(use_iam_profile: true, disable_imds_v2: true) } + end + ENV['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'] = '/v1/credentials?id=task_id' Excon.stub({ method: :get, path: '/v1/credentials?id=task_id' }, { status: 200, body: Fog::JSON.encode(credentials) })