Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Credential scope support #2932

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ def built_in_client_context_param_value(param_data)
else
'context.config.use_dualstack_endpoint'
end
when 'AWS::Auth::CredentialScope'
mullermp marked this conversation as resolved.
Show resolved Hide resolved
'context.config.credentials.credential_scope'
when 'AWS::STS::UseGlobalEndpoint'
"context.config.sts_regional_endpoints == 'legacy'"
when 'AWS::S3::UseGlobalEndpoint'
Expand Down
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Support Credential scoping using `ENV['AWS_CREDENTIAL_SCOPE']`, `aws_credential_scope` shared config, or the `credential_scope` Client configuration option.

3.185.1 (2023-10-05)
------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def static_credentials(options)
Credentials.new(
options[:config].access_key_id,
options[:config].secret_access_key,
options[:config].session_token
options[:config].session_token,
options[:config].credential_scope
)
end
end
Expand Down Expand Up @@ -94,7 +95,8 @@ def env_credentials(_options)
key = %w[AWS_ACCESS_KEY_ID AMAZON_ACCESS_KEY_ID AWS_ACCESS_KEY]
secret = %w[AWS_SECRET_ACCESS_KEY AMAZON_SECRET_ACCESS_KEY AWS_SECRET_KEY]
token = %w[AWS_SESSION_TOKEN AMAZON_SESSION_TOKEN]
Credentials.new(envar(key), envar(secret), envar(token))
scope = %w[AWS_CREDENTIAL_SCOPE]
Credentials.new(envar(key), envar(secret), envar(token), envar(scope))
end

def envar(keys)
Expand Down
18 changes: 12 additions & 6 deletions gems/aws-sdk-core/lib/aws-sdk-core/credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ class Credentials
# @param [String] access_key_id
# @param [String] secret_access_key
# @param [String] session_token (nil)
def initialize(access_key_id, secret_access_key, session_token = nil)
# @param [String] credential_scope (nil)
def initialize(access_key_id, secret_access_key, session_token = nil,
credential_scope = nil)
@access_key_id = access_key_id
@secret_access_key = secret_access_key
@session_token = session_token
@credential_scope = credential_scope
end

# @return [String, nil]
# @return [String]
attr_reader :access_key_id

# @return [String, nil]
# @return [String]
attr_reader :secret_access_key

# @return [String, nil]
attr_reader :session_token

# @return [String, nil]
attr_reader :credential_scope

# @return [Credentials]
def credentials
self
Expand All @@ -30,9 +36,9 @@ def credentials
# access key are both set.
def set?
!access_key_id.nil? &&
!access_key_id.empty? &&
!secret_access_key.nil? &&
!secret_access_key.empty?
!access_key_id.empty? &&
!secret_access_key.nil? &&
!secret_access_key.empty?
end

# Removing the secret access key from the default inspect string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class CredentialsConfiguration < Seahorse::Client::Plugin

option(:session_token, doc_type: String, docstring: '')

option(:credential_scope, doc_type: String, docstring: '')

option(:profile,
doc_default: 'default',
doc_type: String,
Expand Down Expand Up @@ -57,13 +59,15 @@ class CredentialsConfiguration < Seahorse::Client::Plugin
locations will be searched for credentials:

* `Aws.config[:credentials]`
* The `:access_key_id`, `:secret_access_key`, and `:session_token` options.
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']
* The `:access_key_id`, `:secret_access_key`, `:session_token`, and
`credential_scope` options.
mullermp marked this conversation as resolved.
Show resolved Hide resolved
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'],
ENV['AWS_SESSION_TOKEN'], and ENV['AWS_CREDENTIAL_SCOPE']
* `~/.aws/credentials`
* `~/.aws/config`
* EC2/ECS IMDS instance profile - When used by default, the timeouts
are very aggressive. Construct and pass an instance of
`Aws::InstanceProfileCredentails` or `Aws::ECSCredentials` to
`Aws::InstanceProfileCredentials` or `Aws::ECSCredentials` to
enable retries and extended timeouts. Instance profile credential
fetching can be disabled by setting ENV['AWS_EC2_METADATA_DISABLED']
to true.
Expand Down
3 changes: 2 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/shared_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ def credentials_from_profile(prof_config)
creds = Credentials.new(
prof_config['aws_access_key_id'],
prof_config['aws_secret_access_key'],
prof_config['aws_session_token']
prof_config['aws_session_token'],
prof_config['aws_credential_scope']
)
creds if creds.set?
end
Expand Down
49 changes: 28 additions & 21 deletions gems/aws-sdk-core/spec/aws/credential_provider_chain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
module Aws
describe CredentialProviderChain do
def random_creds
{ access_key_id: SecureRandom.hex,
secret_access_key: SecureRandom.hex, session_token: SecureRandom.hex }
{
access_key_id: SecureRandom.hex,
secret_access_key: SecureRandom.hex,
session_token: SecureRandom.hex,
credential_scope: SecureRandom.hex
}
end

def with_shared_credentials(profile_name = SecureRandom.hex, credentials_file = nil)
Expand All @@ -19,6 +23,7 @@ def with_shared_credentials(profile_name = SecureRandom.hex, credentials_file =
aws_access_key_id = #{creds[:access_key_id]}
aws_secret_access_key = #{creds[:secret_access_key]}
aws_session_token = #{creds[:session_token]}
aws_credential_scope = #{creds[:credential_scope]}
CREDS
allow(Dir).to receive(:home).and_return('HOME')
allow(File).to receive(:exist?).with(path).and_return(true)
Expand All @@ -29,9 +34,10 @@ def with_shared_credentials(profile_name = SecureRandom.hex, credentials_file =

def with_env_credentials
creds = random_creds
env['AWS_ACCESS_KEY_ID'] = creds[:access_key_id]
env['AWS_SECRET_ACCESS_KEY'] = creds[:secret_access_key]
env['AWS_SESSION_TOKEN'] = creds[:session_token]
ENV['AWS_ACCESS_KEY_ID'] = creds[:access_key_id]
ENV['AWS_SECRET_ACCESS_KEY'] = creds[:secret_access_key]
ENV['AWS_SESSION_TOKEN'] = creds[:session_token]
ENV['AWS_CREDENTIAL_SCOPE'] = creds[:credential_scope]
creds
end

Expand All @@ -40,6 +46,7 @@ def with_config_credentials
allow(config).to receive(:access_key_id).and_return(creds[:access_key_id])
allow(config).to receive(:secret_access_key).and_return(creds[:secret_access_key])
allow(config).to receive(:session_token).and_return(creds[:session_token])
allow(config).to receive(:credential_scope).and_return(creds[:credential_scope])
creds
end

Expand All @@ -49,15 +56,15 @@ def validate_credentials(expected_creds)
expect(creds.access_key_id).to eq(expected_creds[:access_key_id])
expect(creds.secret_access_key).to eq(expected_creds[:secret_access_key])
expect(creds.session_token).to eq(expected_creds[:session_token])
expect(creds.credential_scope).to eq(expected_creds[:credential_scope])
end

let(:env) { {} }

let(:config) do
double('config',
access_key_id: nil,
secret_access_key: nil,
session_token: nil,
credential_scope: nil,
profile: nil,
region: nil,
instance_profile_credentials_timeout: 1,
Expand All @@ -71,7 +78,6 @@ def validate_credentials(expected_creds)
let(:credentials) { chain.resolve }

before(:each) do
stub_const('ENV', env)
allow(InstanceProfileCredentials).to receive(:new).and_return(mock_instance_creds)
end

Expand All @@ -82,31 +88,32 @@ def validate_credentials(expected_creds)

it 'hydrates credentials from ENV with prefix AWS_' do
expected_creds = random_creds
env['AWS_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
env['AWS_SECRET_ACCESS_KEY'] = expected_creds[:secret_access_key]
env['AWS_SESSION_TOKEN'] = expected_creds[:session_token]
ENV['AWS_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
ENV['AWS_SECRET_ACCESS_KEY'] = expected_creds[:secret_access_key]
ENV['AWS_SESSION_TOKEN'] = expected_creds[:session_token]
ENV['AWS_CREDENTIAL_SCOPE'] = expected_creds[:credential_scope]
validate_credentials(expected_creds)
end

it 'hydrates credentials from ENV with prefix AMAZON_' do
expected_creds = random_creds
env['AMAZON_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
env['AMAZON_SECRET_ACCESS_KEY'] = expected_creds[:secret_access_key]
env['AMAZON_SESSION_TOKEN'] = expected_creds[:session_token]
expected_creds = random_creds.merge(credential_scope: nil)
ENV['AMAZON_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
ENV['AMAZON_SECRET_ACCESS_KEY'] = expected_creds[:secret_access_key]
ENV['AMAZON_SESSION_TOKEN'] = expected_creds[:session_token]
validate_credentials(expected_creds)
end

it 'hydrates credentials from ENV at AWS_ACCESS_KEY & AWS_SECRET_KEY' do
expected_creds = random_creds.merge(session_token: nil)
env['AWS_ACCESS_KEY'] = expected_creds[:access_key_id]
env['AWS_SECRET_KEY'] = expected_creds[:secret_access_key]
expected_creds = random_creds.merge(session_token: nil, credential_scope: nil)
ENV['AWS_ACCESS_KEY'] = expected_creds[:access_key_id]
ENV['AWS_SECRET_KEY'] = expected_creds[:secret_access_key]
validate_credentials(expected_creds)
end

it 'hydrates credentials from ENV at AWS_ACCESS_KEY_ID & AWS_SECRET_KEY' do
expected_creds = random_creds.merge(session_token: nil)
env['AWS_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
env['AWS_SECRET_KEY'] = expected_creds[:secret_access_key]
expected_creds = random_creds.merge(session_token: nil, credential_scope: nil)
ENV['AWS_ACCESS_KEY_ID'] = expected_creds[:access_key_id]
ENV['AWS_SECRET_KEY'] = expected_creds[:secret_access_key]
validate_credentials(expected_creds)
end

Expand Down
11 changes: 10 additions & 1 deletion gems/aws-sdk-core/spec/aws/credentials_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Aws
expect(Credentials.new('akid', 'secret').secret_access_key).to eq('secret')
end

it 'provides access to the session tokey' do
it 'provides access to the session token' do
creds = Credentials.new('akid', 'secret', 'token')
expect(creds.session_token).to eq('token')
end
Expand All @@ -22,6 +22,15 @@ module Aws
expect(Credentials.new('akid', 'secret').session_token).to be(nil)
end

it 'provides access to the credential scope' do
creds = Credentials.new('akid', 'secret', nil, 'scope')
expect(creds.credential_scope).to eq('scope')
end

it 'defaults the credential scope to nil' do
expect(Credentials.new('akid', 'secret').credential_scope).to be(nil)
end

describe '#set?' do

it 'returns true when the key and secret are both non nil values' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ region = us-east-1
aws_access_key_id = ACCESS_KEY_SHARED
aws_secret_access_key = SECRET_KEY_SHARED
aws_session_token = TOKEN_SHARED
aws_credential_scope = SCOPE_SHARED

[profile assumerole_prof]
role_arn = arn:aws:iam:123456789012:role/foo
Expand Down
Loading