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

Assuming a role from a profile with MFA is not possible #1480

Closed
nhinds opened this issue Apr 7, 2017 · 3 comments
Closed

Assuming a role from a profile with MFA is not possible #1480

nhinds opened this issue Apr 7, 2017 · 3 comments
Labels
feature-request A feature should be added or improved.

Comments

@nhinds
Copy link

nhinds commented Apr 7, 2017

The CredentialProviderChain supports assuming a role from a profile if the profile section in ~/.aws/config has the role_arn and source_profile settings.

However, if the profile contains mfa_serial (because the IAM role requires MFA to assume) then it appears to not be possible to provide the token_code parameter for the MFA call.

Simply trying to use the profile fails with an error "MultiFactorAuthentication failed, must provide both MFA serial number and one time pass code."

2.0.0-p643 :001 > require 'aws-sdk'
 => true 
2.0.0-p643 :002 > Aws.config[:profile] = 'my-profile'
 => "my-profile" 
2.0.0-p643 :003 > Aws.config[:region] = 'us-west-2'
 => "us-west-2" 
2.0.0-p643 :004 > Aws::STS::Client.new.get_caller_identity
Aws::STS::Errors::AccessDenied: MultiFactorAuthentication failed, must provide both MFA serial number and one time pass code.
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/plugins/raise_response_errors.rb:15:in `call'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/plugins/jsonvalue_converter.rb:20:in `call'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/plugins/idempotency_token.rb:18:in `call'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/plugins/param_converter.rb:20:in `call'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/plugins/response_target.rb:21:in `call'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/request.rb:70:in `send_request'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:207:in `block (2 levels) in define_operation_methods'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/assume_role_credentials.rb:49:in `refresh'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/refreshing_credentials.rb:20:in `initialize'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/assume_role_credentials.rb:40:in `initialize'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/shared_config.rb:148:in `new'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/shared_config.rb:148:in `assume_role_from_profile'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/shared_config.rb:111:in `assume_role_credentials_from_config'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/credential_provider_chain.rb:94:in `assume_role_with_profile'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/credential_provider_chain.rb:77:in `assume_role_credentials'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/aws-sdk-core/credential_provider_chain.rb:12:in `block in resolve'
... 5 levels...
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:205:in `block in resolve_defaults'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:57:in `each'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:57:in `each'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:204:in `resolve_defaults'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:200:in `value_at'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:189:in `block in resolve'
	from /path/to/my/ruby/lib/ruby/2.0.0/set.rb:232:in `each_key'
	from /path/to/my/ruby/lib/ruby/2.0.0/set.rb:232:in `each'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:189:in `resolve'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:177:in `apply_defaults'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:150:in `build!'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:68:in `build_config'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:19:in `initialize'
	from /path/to/my/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:105:in `new'
	from (irb):4
	from /path/to/my/ruby/bin/irb:12:in `<main>'

Attempting to pass the token_code parameter to Aws.config results in an error "invalid configuration option `:token_code'" when making an API call:

2.0.0-p643 :008 > Aws.config[:token_code] = '123456'
 => "123456" 
2.0.0-p643 :009 > Aws::STS::Client.new.get_caller_identity
ArgumentError: invalid configuration option `:token_code'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:166:in `rescue in block in apply_options'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:162:in `block in apply_options'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:161:in `each'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:161:in `apply_options'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/configuration.rb:149:in `build!'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:68:in `build_config'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:19:in `initialize'
	from /path/to/my/gems/ruby-2.0.0-p643/gems/aws-sdk-core-2.9.5/lib/seahorse/client/base.rb:105:in `new'
	from (irb):9
	from /path/to/my/ruby/bin/irb:12:in `<main>'

This appears to be because CredentialProviderChain has no way to provide the token_code parameter to SharedConfig.assume_role_credentials_from_config, and SharedConfig.assume_role_from_profile doesn't default token_code to anything from Aws.config so the token code is left completely unset.

Using SharedConfig.assume_role_credentials_from_config works perfectly, but the SharedConfig is marked as private so I presume this is discouraged.

2.0.0-p643 :001 > require 'aws-sdk'
 => true 
2.0.0-p643 :002 > Aws.config[:region] = 'us-west-2'
 => "us-west-2" 
2.0.0-p643 :003 > Aws.config[:credentials] = Aws.shared_config.assume_role_credentials_from_config(profile: 'my-profile', token_code: '123456')
 => #<Aws::AssumeRoleCredentials:0x00000000d15dd0 @assume_role_params={:token_code=>"123456", :role_session_name=>"session-name-from-profile", :role_arn=>"arn:aws:iam::123456789012:role/blah", :external_id=>nil, :serial_number=>"arn:aws:iam::987654321098:mfa/me"}, @client=#<Aws::STS::Client>, @mutex=#<Mutex:0x000000019399e0>, @credentials=#<Aws::Credentials access_key_id="ABCDEFG">, @expiration=2017-04-07 20:55:11 UTC> 
2.0.0-p643 :004 > Aws::STS::Client.new.get_caller_identity => #<struct Aws::STS::Types::GetCallerIdentityResponse user_id="ABCDEFG:session-name-from-profile", account="123456789012", arn="arn:aws:sts::123456789012:assumed-role/blah/session-name-from-profile">
@awood45 awood45 added feature-request A feature should be added or improved. Version 2 labels Apr 10, 2017
@awood45
Copy link
Member

awood45 commented Apr 10, 2017

There are some discussions within the team as to how we can integrate the default credential provider chain with MFA - it's a bit of a difficult issue since it requires an interrupt or extra handler to consume the MFA token value.

In the meantime, Aws::AssumeRoleCredentials should work for MFA roles.

@nhinds
Copy link
Author

nhinds commented Apr 12, 2017

AssumeRoleCredentials doesn't have a way to read the role and source profile from an AWS profile, so it does not help for the case of assuming a role from a profile with MFA. It does work when you know the role, MFA serial, and source credentials in advance though.

I don't particularly mind whether the default credential provider supports prompting for MFA, as long as it's possible to get the AssumeRoleCredentials somehow. It would be sufficient for my use case to just mark Aws.shared_config.assume_role_credentials_from_config as public API, or provide another mechanism for doing the same thing (assuming a role from a profile with MFA, without needing to know the role ARN, MFA serial, or source profile in advance)

@awood45
Copy link
Member

awood45 commented Apr 21, 2017

Adding to feature request backlog. I'm definitely open to a PR which adds a method that does this (and takes the MFA token as a param).

@awood45 awood45 closed this as completed Apr 21, 2017
awood45 added a commit that referenced this issue Apr 21, 2017
awood45 added a commit that referenced this issue Jun 29, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A feature should be added or improved.
Projects
None yet
Development

No branches or pull requests

2 participants