From 781243b47ee336cbe722c780f6b7f38bc11e257f Mon Sep 17 00:00:00 2001 From: Tanna McClure Date: Tue, 19 Nov 2024 11:28:55 -0600 Subject: [PATCH 1/3] add access key decode back for aws account id --- lib/new_relic/agent/aws.rb | 54 ++++++++++++++++++++++++++++++-- test/new_relic/agent/aws_test.rb | 25 +++++++++++---- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/lib/new_relic/agent/aws.rb b/lib/new_relic/agent/aws.rb index cb1d4f53a9..573b172abf 100644 --- a/lib/new_relic/agent/aws.rb +++ b/lib/new_relic/agent/aws.rb @@ -5,13 +5,61 @@ module NewRelic module Agent module Aws - def self.create_arn(service, resource, region) - return unless NewRelic::Agent.config[:'cloud.aws.account_id'] + CHARACTERS = %w[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7].freeze + HEX_MASK = '7fffffffff80' - "arn:aws:#{service}:#{region}:#{NewRelic::Agent.config[:'cloud.aws.account_id']}:#{resource}" + def self.create_arn(service, resource, region, account_id) + "arn:aws:#{service}:#{region}:#{account_id}:#{resource}" rescue => e NewRelic::Agent.logger.warn("Failed to create ARN: #{e}") end + + def self.get_account_id(config) + # if it is set in the agent config, use that first + return NewRelic::Agent.config[:'cloud.aws.account_id'] if NewRelic::Agent.config[:'cloud.aws.account_id'] + + access_key_id = config.credentials.credentials.access_key_id if config&.credentials&.credentials&.respond_to?(:access_key_id) + return unless access_key_id + + NewRelic::Agent::Aws.convert_access_key_to_account_id(access_key_id) + rescue => e + NewRelic::Agent.logger.debug("Failed to create account id: #{e}") + end + + def self.convert_access_key_to_account_id(access_key) + decoded_key = Integer(decode_to_hex(access_key[4..-1]), 16) + mask = Integer(HEX_MASK, 16) + (decoded_key & mask) >> 7 + end + + def self.decode_to_hex(access_key) + bytes = access_key.delete('=').each_char.map { |c| CHARACTERS.index(c) } + + bytes.each_slice(8).map do |section| + convert_section(section) + end.flatten[0...6].join + end + + def self.convert_section(section) + buffer = 0 + section.each do |chunk| + buffer = (buffer << 5) + chunk + end + + chunk_count = (section.length * 5.0 / 8.0).floor + + if section.length < 8 + buffer >>= (5 - (chunk_count * 8)) % 5 + end + + decoded = [] + chunk_count.times do |i| + shift = 8 * (chunk_count - 1 - i) + decoded << ((buffer >> shift) & 255).to_s(16) + end + + decoded + end end end end diff --git a/test/new_relic/agent/aws_test.rb b/test/new_relic/agent/aws_test.rb index 7af8b8247f..b61de1a024 100644 --- a/test/new_relic/agent/aws_test.rb +++ b/test/new_relic/agent/aws_test.rb @@ -11,15 +11,28 @@ def test_create_arn account_id = '123456789' resource = 'test/test-resource' expected = 'arn:aws:test-service:us-test-region-1:123456789:test/test-resource' + arn = NewRelic::Agent::Aws.create_arn(service, resource, region, account_id) - with_config('cloud.aws.account_id': account_id) do - arn = NewRelic::Agent::Aws.create_arn(service, resource, region) + assert_equal expected, arn + end - assert_equal expected, arn - end + def test_get_account_id_decodes_access_key + config = mock + mock_credentials = mock + mock_credentials.stubs(:credentials).returns(mock_credentials) + mock_credentials.stubs(:access_key_id).returns('AKIAIOSFODNN7EXAMPLE') # this is a fake access key id from aws docs + config.stubs(:credentials).returns(mock_credentials) + + account_id = NewRelic::Agent::Aws.get_account_id(config) + + assert_equal 36315003739, account_id end - def test_doesnt_create_arn_no_account_id - assert_nil NewRelic::Agent::Aws.create_arn('service', 'resource', 'region') + def test_get_account_id_uses_config + config = mock + + with_config(:'cloud.aws.account_id' => '123456789') do + assert_equal '123456789', NewRelic::Agent::Aws.get_account_id(config) + end end end From 8ba43d493aa6818efc376c83bb7e9376fbe564a7 Mon Sep 17 00:00:00 2001 From: Tanna McClure Date: Tue, 19 Nov 2024 11:31:47 -0600 Subject: [PATCH 2/3] move nil check to arn create method --- lib/new_relic/agent/aws.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/new_relic/agent/aws.rb b/lib/new_relic/agent/aws.rb index 573b172abf..df24c4e696 100644 --- a/lib/new_relic/agent/aws.rb +++ b/lib/new_relic/agent/aws.rb @@ -9,6 +9,9 @@ module Aws HEX_MASK = '7fffffffff80' def self.create_arn(service, resource, region, account_id) + # if any of the values are nil, we can't create an ARN + return unless service && resource && region && account_id + "arn:aws:#{service}:#{region}:#{account_id}:#{resource}" rescue => e NewRelic::Agent.logger.warn("Failed to create ARN: #{e}") From a33550cc1c858aa08e42032c0680208241447d96 Mon Sep 17 00:00:00 2001 From: Tanna McClure Date: Tue, 19 Nov 2024 11:38:18 -0600 Subject: [PATCH 3/3] update aws instrumentation to use updated access key method --- .../aws_sdk_lambda/instrumentation.rb | 17 ++++++++--------- .../instrumentation/dynamodb/instrumentation.rb | 8 +++++++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb b/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb index 39c9682fc9..21ffc0beb2 100644 --- a/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +++ b/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb @@ -63,10 +63,9 @@ def process_function_error(response) def generate_segment(action, options = {}) function = function_name(options) region = aws_region - account_id = aws_account_id arn = aws_arn(function, region) segment = NewRelic::Agent::Tracer.start_segment(name: "Lambda/#{action}/#{function}") - segment.add_agent_attribute('cloud.account.id', account_id) + segment.add_agent_attribute('cloud.account.id', nr_account_id) segment.add_agent_attribute('cloud.platform', CLOUD_PLATFORM) segment.add_agent_attribute('cloud.region', region) segment.add_agent_attribute('cloud.resource_id', arn) if arn @@ -77,18 +76,18 @@ def function_name(options = {}) (options.fetch(:function_name, nil) if options.respond_to?(:fetch)) || NewRelic::UNKNOWN end - def aws_account_id - return unless self.respond_to?(:config) - - config&.account_id || NewRelic::Agent.config[:'cloud.aws.account_id'] - end - def aws_region config&.region if self.respond_to?(:config) end def aws_arn(function, region) - NewRelic::Agent::Aws.create_arn(AWS_SERVICE, "function:#{function}", region) + NewRelic::Agent::Aws.create_arn(AWS_SERVICE, "function:#{function}", region, nr_account_id) + end + + def nr_account_id + return @nr_account_id if defined?(@nr_account_id) + + @nr_account_id = NewRelic::Agent::Aws.get_account_id(config) end end end diff --git a/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb b/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb index 721c41e582..cdc2e2b192 100644 --- a/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +++ b/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb @@ -49,10 +49,16 @@ def build_request_with_new_relic(*args) @nr_captured_request = yield end + def nr_account_id + return @nr_account_id if defined?(@nr_account_id) + + @nr_account_id = NewRelic::Agent::Aws.get_account_id(config) + end + def get_arn(params) return unless params[:table_name] - NewRelic::Agent::Aws.create_arn(PRODUCT.downcase, "table/#{params[:table_name]}", config&.region) + NewRelic::Agent::Aws.create_arn(PRODUCT.downcase, "table/#{params[:table_name]}", config&.region, nr_account_id) end end end