diff --git a/docs/credentials.md b/docs/credentials.md index 27c0886..32d2255 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -14,6 +14,23 @@ AWS access key id. AWS secret key. +### aws_profile + +Specify a named profile from your shared AWS configuration files (`~/.aws/credentials` or `~/.aws/config`). +This is useful for environments using IAM Roles or short-lived credentials. + +### aws_credential_process + +Specify a `credential_process` command directly in the Fluentd configuration. +This enables integration with external credential helpers like IAM Roles Anywhere. + + + @type s3 + # Example for IAM Roles Anywhere + aws_credential_process "/path/to/aws_signing_helper credential-process --certificate /path/to/cert.pem --private-key /path/to/key.pem --trust-anchor-arn --profile-arn --role-arn " + # ... other settings + + ## \ section Typically, you use AssumeRole for cross-account access or federation. diff --git a/lib/fluent/plugin/in_s3.rb b/lib/fluent/plugin/in_s3.rb index 8cf873b..49ec268 100644 --- a/lib/fluent/plugin/in_s3.rb +++ b/lib/fluent/plugin/in_s3.rb @@ -8,6 +8,7 @@ require 'zlib' require 'time' require 'tempfile' +require 'shellwords' module Fluent::Plugin class S3Input < Input @@ -30,6 +31,10 @@ def initialize config_param :aws_key_id, :string, default: nil, secret: true desc "AWS secret key." config_param :aws_sec_key, :string, default: nil, secret: true + desc "AWS profile name." + config_param :aws_profile, :string, default: nil + desc "The command to run to generate credentials." + config_param :aws_credential_process, :string, default: nil config_section :assume_role_credentials, multi: false do desc "The Amazon Resource Name (ARN) of the role to assume" config_param :role_arn, :string @@ -211,7 +216,7 @@ def run if @match_regexp raw_key = get_raw_key(body) key = CGI.unescape(raw_key) - next unless @match_regexp.match?(key) + next unless @match_regexp.match?(key) end process(body) rescue => e @@ -231,7 +236,7 @@ def is_valid_queue(body) if @sqs.event_bridge_mode log.debug("checking for eventbridge property") !!body["detail"] - else + else log.debug("checking for Records property") !!body["Records"] end @@ -242,7 +247,7 @@ def get_raw_key(body) body["detail"]["object"]["key"] else body["Records"].first["s3"]["object"]["key"] - end + end end def setup_credentials @@ -252,6 +257,10 @@ def setup_credentials when @aws_key_id && @aws_sec_key options[:access_key_id] = @aws_key_id options[:secret_access_key] = @aws_sec_key + when @aws_profile + options[:profile] = @aws_profile + when @aws_credential_process + options[:credentials] = Aws::ProcessCredentials.new(Shellwords.split(@aws_credential_process)) when @assume_role_credentials c = @assume_role_credentials credentials_options[:role_arn] = c.role_arn diff --git a/lib/fluent/plugin/out_s3.rb b/lib/fluent/plugin/out_s3.rb index 4bb90fb..dacff44 100644 --- a/lib/fluent/plugin/out_s3.rb +++ b/lib/fluent/plugin/out_s3.rb @@ -6,6 +6,7 @@ require 'time' require 'tempfile' require 'securerandom' +require 'shellwords' module Fluent::Plugin class S3Output < Output @@ -29,6 +30,10 @@ def initialize config_param :aws_key_id, :string, default: nil, secret: true desc "AWS secret key." config_param :aws_sec_key, :string, default: nil, secret: true + desc "AWS profile name." + config_param :aws_profile, :string, default: nil + desc "The command to run to generate credentials." + config_param :aws_credential_process, :string, default: nil config_section :assume_role_credentials, multi: false do desc "The Amazon Resource Name (ARN) of the role to assume" config_param :role_arn, :string, secret: true @@ -542,6 +547,10 @@ def setup_credentials when @aws_key_id && @aws_sec_key options[:access_key_id] = @aws_key_id options[:secret_access_key] = @aws_sec_key + when @aws_profile + options[:profile] = @aws_profile + when @aws_credential_process + options[:credentials] = Aws::ProcessCredentials.new(Shellwords.split(@aws_credential_process)) when @web_identity_credentials c = @web_identity_credentials region = c.sts_region || @s3_region diff --git a/test/test_in_s3.rb b/test/test_in_s3.rb index 2bd2d9d..5aadeb8 100644 --- a/test/test_in_s3.rb +++ b/test/test_in_s3.rb @@ -71,6 +71,32 @@ def test_empty end end + def test_aws_profile + d = create_driver(%[ + aws_profile test_profile + s3_bucket test_bucket + buffer_type memory + + queue_name test_queue + queue_owner_aws_account_id 123456789123 + + ]) + assert_equal 'test_profile', d.instance.aws_profile + end + + def test_aws_credential_process + d = create_driver(%[ + aws_credential_process "/path/to/aws_signing_helper credential-process" + s3_bucket test_bucket + buffer_type memory + + queue_name test_queue + queue_owner_aws_account_id 123456789123 + + ]) + assert_equal '/path/to/aws_signing_helper credential-process', d.instance.aws_credential_process + end + def test_without_sqs_section conf = %[ aws_key_id test_key_id @@ -708,7 +734,7 @@ def test_event_bridge_mode } } } - + message = Struct::StubMessage.new(1, 1, Yajl.dump(body)) @sqs_poller.get_messages(anything, anything) do |config, stats| config.before_request.call(stats) if config.before_request diff --git a/test/test_out_s3.rb b/test/test_out_s3.rb index 6d9bf9b..86ae4ec 100644 --- a/test/test_out_s3.rb +++ b/test/test_out_s3.rb @@ -69,6 +69,30 @@ def test_configure assert_equal true, d.instance.check_object end + def test_aws_profile + d = create_driver(%[ + aws_profile test_profile + s3_bucket test_bucket + path log + utc + buffer_type memory + time_slice_format %Y%m%d-%H + ]) + assert_equal 'test_profile', d.instance.aws_profile + end + + def test_aws_credential_process + d = create_driver(%[ + aws_credential_process "/path/to/aws_signing_helper credential-process" + s3_bucket test_bucket + path log + utc + buffer_type memory + time_slice_format %Y%m%d-%H + ]) + assert_equal '/path/to/aws_signing_helper credential-process', d.instance.aws_credential_process + end + def test_s3_endpoint_with_valid_endpoint d = create_driver(CONFIG + 's3_endpoint riak-cs.example.com') assert_equal 'riak-cs.example.com', d.instance.s3_endpoint