-
Notifications
You must be signed in to change notification settings - Fork 202
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
Add explicit option for using iam profile for authentication #107
Conversation
* Add 'use_iam_profile' config option, default to false * Have #iam_creds return an empty hash unless use_iam_profile is true * Call iam_creds as driver.iam_creds in `default_config` blocks * Rescue fron NoMethodError in iam_creds `Fog::AWS::CredentialFetcher::ServiceMethods::fetch_credentials` will call super when it fails. Because there is no superclass method this will throw a NoMethodError * Write unit tests for #iam_creds
@tyler-ball Finally got these tests to pass. Sorry for all the chatter. |
@JamesAwesome there is already a 'iam_profile_name' param that assigns the named role to the new node. I think 'use_iam_profile' may be little misleading next to it. Would be nice to do the detection automaticaly. |
@Igorshp we could change the name to something like :use_iam_role however if we try to autodetect while on an ec2 node we will always receive some form of value for |
I mean the autodetection should check if key_id and/or secret are manualy set and return session_token accordingly. |
Might be able to whip that up, I'll futz with this a bit more and update |
fetch_credentials(use_iam_profile:true) | ||
rescue RuntimeError => e | ||
config[:use_iam_profile] ? fetch_credentials(use_iam_profile: true) : {} | ||
rescue RuntimeError, NoMethodError => e |
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.
Where do you expect the NoMethodError
to come from?
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.
fetch_credentials calls super when it fails, so since we don't have a super for it to fail over to we need to catch NoMethodError, I think I made a note in the commit message.
Thanks for adding a test and turning I'm not familiar enough with AWS - is there a scenario where you would ever set access_key_id/secret_access_key AND session_token? Or are they mutually exclusive forms of authentication? If you can't use both of them in 1 auth call I agree with @Igorshp. We should detect the presence of access_key_id/secret_access_key and only provide those if available. If those are not available, use the session_token. In this sense, the access_key (if available) overrides the session_token. I think this makes sense because you can simply not provide those and rely on I would love to get @fnichol's opinion on this. |
@tyler-ball the core of the problem is the fact that you can only use one set of credentials that comes from one source. i.e. either all are from iam_creds or all are user defined (session token can be user provided, but i've never seen it used like that. default is nil), because they are not compatible with each other. so if even one piece of credentials is manually set, the rest should not be pulled from iam_role. |
* Remove :use_iam_profile config setting * Update tests to reflect autodetection vs use of :use_iam_profile * Add `#default_aws_session_token` method to handle logic for setting default value of config[:aws_session_token] Since the :use_iam_profile parameter is confusing instead autodetect if `iam_creds` should be used for setting config[:aws_session_token] by checking if other config settings match the values returned from `iam_creds`. If the config settings match then it's a safe bet that iam_creds should be used for setting config[:aws_session_token].
@tyler-ball whoops I regressed the Auto detection should work fine now. |
👍 sweet, thanks! |
Ready for review. No more commits for now. |
|
end | ||
default_config :aws_secret_access_key do |driver| | ||
ENV['AWS_SECRET_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'] || iam_creds[:aws_secret_access_key] | ||
ENV['AWS_SECRET_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'] \ |
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.
If you want to break this across multiple lines you don't need the \
- just leave the ||
at the end of the line
if config[:aws_secret_access_key] == iam_creds[:aws_secret_access_key] \ | ||
&& config[:aws_access_key_id] == iam_creds[:aws_access_key_id] | ||
iam_creds[:aws_session_token] | ||
else |
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.
Why is the user required to specify the same access key / secret as returned by the iam_creds query? If they have to do that, why not just use the provided access key / secret for authentication?
I thought the primary point of the iam_creds
was they allowed the EC2 node acting as a provisioner to provision nodes using the local 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.
Actually I'm just checking that config[:aws_secret_access_key] et all had fallen back to the iam_creds statement so that we know it would be logical to return the iam_creds[:aws_session_token] since our other config values are from iam.
When running on a non-ec2 node it takes a really long time to figure out the metadata service isn't available. With that in mind I created the following patch: diff --git a/lib/kitchen/driver/ec2.rb b/lib/kitchen/driver/ec2.rb
index e8df7af..de5eaf9 100644
--- a/lib/kitchen/driver/ec2.rb
+++ b/lib/kitchen/driver/ec2.rb
@@ -41,14 +41,16 @@ module Kitchen
default_config :iam_profile_name, nil
default_config :price, nil
default_config :aws_access_key_id do |driver|
- ENV['AWS_ACCESS_KEY'] || ENV['AWS_ACCESS_KEY_ID'] || driver.iam_creds[:aws_access_key_id]
+ ENV['AWS_ACCESS_KEY'] || ENV['AWS_ACCESS_KEY_ID'] ||
+ driver.iam_creds[:aws_access_key_id]
end
default_config :aws_secret_access_key do |driver|
ENV['AWS_SECRET_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'] ||
driver.iam_creds[:aws_secret_access_key]
end
default_config :aws_session_token do |driver|
- driver.default_aws_session_token
+ ENV['AWS_SESSION_TOKEN'] || ENV['AWS_TOKEN'] ||
+ driver.iam_creds[:aws_session_token]
end
default_config :aws_ssh_key_id do |driver|
ENV['AWS_SSH_KEY_ID']
@@ -98,10 +100,18 @@ module Kitchen
end
end
+ # First we check the existence of the metadata host. Only fetch_credentials
+ # if we can find the host. Ping logic taken from
+ # http://stackoverflow.com/questions/7519159/ruby-ping-pingecho-missing
def iam_creds
+ require 'net/http'
+ require 'timeout'
@iam_creds ||= begin
+ timeout(5) do
+ Net::HTTP.get(URI.parse('http://169.254.169.254'))
+ end
fetch_credentials(use_iam_profile: true)
- rescue RuntimeError, NoMethodError => e
+ rescue Errno::EHOSTUNREACH, Timeout::Error, StandardError => e
debug("fetch_credentials failed with exception #{e.message}:#{e.backtrace.join("\n")}")
{}
end
@@ -167,15 +177,6 @@ module Kitchen
!!config[:subnet_id]
end
- def default_aws_session_token
- if config[:aws_secret_access_key] == iam_creds[:aws_secret_access_key] \
- && config[:aws_access_key_id] == iam_creds[:aws_access_key_id]
- iam_creds[:aws_session_token]
- else
- ENV['AWS_SESSION_TOKEN'] || ENV['AWS_TOKEN']
- end
- end
-
private
def connection I think thats a better solution for this case. If we can access the metadata service, we do. |
My only concern is that We could work around this by letting |
@JamesAwesome Ah, I see your concern - drat that people are starting to use Test Kitchen in their CI workflow from multiple cloud providers! 😄 But even if we can connect to My concern is that I don't want to wait a full minute for all ec2 creates like the person in #104 is experiencing. |
Yeah I'm perfectly happy with that then if you are. 👍 I'll merge in the patch and get the tests adjusted. |
So problem: Because this patch will call We can deal with this by comparing the configured :aws_access_key_id against the value returned by iam_creds like I do in the current commit, however I'd like to hear @tyler-ball 's opinion on that before I continue so that we can solve this a bit quicker. I'll think on the topic a bit and comment if I come up with anything a bit more clever. |
Updated (failing) tests illustrate the problem |
* only set :aws_session_token via iam_creds in the event that :aws_access_key_id and :aws_secret_access_key are BOTH already set via iam_creds * prefer environment over iam_creds when setting :aws_session_token I think this is the best I can do. All unprevented combinations of autodetectable credentials via iam_creds and user defined settings should simply fail authentication.
I am now 100% happy with this. I had to remove the top level @tyler-ball I promise no more commits unless you comment. 😄 |
@JamesAwesome I did a little bit of test refactoring / cleanup when I merged, but thank you for tackling this issue and doing all the work! |
👍 😄 |
👍 Thanks |
PR #104 broke the ability to use kitchen-ec2 with explicit aws credentials set via .kitchen.yml while on an ec2 host.
When on an ec2 host the
#fetch_credentials
method which is included fromFog::AWS::CredentialFetcher::ServiceMethods
will always return a hash with a value set for the:aws_session_token
key. Because of thisconfig[:aws_session_token]
will always default toiam_creds[:aws_session_token]
. Sinceconfig[:aws_session_token]
is passed to#connection
,Fog::Compute
fails to authenticate using any credentials set in .kitchen.yml since the value of:aws_session_token
is incorrect.By adding an option to explicitly use a server's iam profile we can allow hosts which live in ec2 to use .kitchen.yml as well as their iam profile for driver configuration.