-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Extensible Credential Providers #1820
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
Extensible Credential Providers #1820
Conversation
awood45
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the approach here. It would seem to make more sense for this to actually be its own credential provider. While Shared Config would be a place for the credential_process flag, it should be its own provider in its own class with its own tests.
|
There are other benefits to it being a standalone credential provider - for example, it would allow users to invoke it directly, rather than as a shared configuration side effect. |
| else | ||
| ProcessCredentials.new(profile_name: ENV['AWS_PROFILE'].nil? ? 'default' : ENV['AWS_PROFILE']) | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make the "else nil" explicit here.
| include CredentialProvider | ||
| include RefreshingCredentials | ||
|
|
||
| def initialize(options = {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is the way to go about this either - I think when this credential provider is created, you should be able to specify the process identifier directly. You can fetch that value in the credential provider chain approach and pass it in to this constructor.
Essentially, we shouldn't strictly require that a shared config file even exists.
|
|
||
| @expiration = creds_json['Expiration'] ? Time.iso8601(creds_json['Expiration']) : nil | ||
| return creds if creds.set? | ||
| raise ArgumentError.new("Invalid payload for JSON credentials version 1") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use a SDK typed exception here, as above. Similar to https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Errors/MissingCredentialsError.html but not that exception. Perhaps Aws::Errors::ProcessCredentialsError?
| end | ||
|
|
||
| def refresh | ||
| proc = Aws.shared_config.credentials_process(@profile_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, we need to not source from shared config within this class. It's also static, so the place to set this is in the constructor.
proc is also a reserved word, best not to be used as a variable name.
| def process_credentials(options) | ||
| if Aws.shared_config.config_enabled? | ||
| if options[:config] | ||
| ProcessCredentials.new(profile_name: options[:config].profile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make the "is process credentials enabled by shared config" decision fully within this logic section. If so, we create a ProcessCredentials object and expect it to work. If not, we return nil and move on.
| include CredentialProvider | ||
| include RefreshingCredentials | ||
|
|
||
| def initialize(process) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add more documentation for #initialize like what kind of process variable it's expecting?
| @credentials = credentials_from_process(@process) | ||
| end | ||
|
|
||
| # @return [Credentials] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aws::Credentials?
|
|
||
| include CredentialProvider | ||
| include RefreshingCredentials | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also add some doc here like an example of how to use this?
e.g https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/AssumeRoleCredentials.html
| @@ -0,0 +1,43 @@ | |||
| require_relative '../spec_helper' | |||
|
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test suites looks good, just checking does these covered all cli test suits for the process credentials as well?
| end | ||
| end | ||
|
|
||
| # Raised when a credentials provider process returns a JSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
surprise! ;) you need to update the region line here: https://github.com/aws/aws-sdk-ruby/blob/master/build_tools/spec/region_spec.rb#L12
That spec is for testing we don't hard code region names, since the error file has been modified, the line number need to be modified as well :D That' why you are seeing the travis error
awood45
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks much better - I'd address Jingyi's comments on documentation in AssumeRoleCredentials (in fact, double bonus for explicitly testing the use case of plugging in a process credential provider there, since that's by far the most common use case for this), and for double-checking against the SEP definition.
| private | ||
| def credentials_from_process(proc_invocation) | ||
| begin | ||
| raw_out = `#{proc_invocation}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My last point is for us to take another pass on the process invocation choice. Backticks run this way are going to be unescaped, and while we're taking in the data from the credentials file, we may want to be cautious. Take a look at Open3 and let's see if Open3.capture2 works equally well.
| # provided in the credentials payload | ||
| # | ||
| # credentials = Aws::ProcessCredentials.new( | ||
| # 'echo \'{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use a different example, closer to what we would want to do. A hard-coded echo with hard-coded credentials would never be recommended. It's just meant to be an illustration, but some may take it literally.
| # @param [String] process Invocation string for process | ||
| # credentials provider. | ||
| def initialize(process) | ||
| @process = process |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're close now, at this point we just need to make sure we're splitting and escaping the string in the same way Python/CLI does: https://github.com/boto/botocore/blob/25a6a4c7c8a8b1797fc78c6a34dc7bca3f0bfba1/botocore/credentials.py#L793-L796
|
So the last thing we need before merging is to add credential provider chain tests to cover what should and should not happen (making sure this is the end of the credential stack, ensuring it's a terminal state). |
| expect(client.config.credentials.access_key_id).to eq("ACCESS_KEY_SC1") | ||
| end | ||
|
|
||
| it 'prefers process credentials over metadata credentials' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last bit here, I would also add some tests with this same profile where we ensure that static profile credentials, for example, are pulled in first. It's a bit thorough, but important for regression.
awood45
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll do the merge work on this one
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.