-
Notifications
You must be signed in to change notification settings - Fork 137
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 IAM EC2 auth #161
Add IAM EC2 auth #161
Changes from 12 commits
4432283
b70de61
78c05e9
60f70e2
7602166
75fd1b0
cc1365b
128fdef
e598ffc
98ddc19
11a5cde
6217a9f
370d7e0
d23372a
d8357d4
1321c11
1a57422
9f4ff2f
ca17bac
43e211d
63a54f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,6 +186,66 @@ def aws_ec2(role, pkcs7, nonce = nil) | |
return secret | ||
end | ||
|
||
# Authenticate via IAM EC2 method by providing a AWS CredentialProvider (either ECS, AssumeRole, etc.) | ||
# If authentication is successful, the resulting token will be stored on the client and used | ||
# for future requests. | ||
# | ||
# @example | ||
# Vault.auth.aws_ecs_iam("dev-role-iam", Aws::AssumeRoleCredentials.new, "vault.example.com", "https://sts.us-east-2.amazonaws.com") #=> #<Vault::Secret lease_id=""> | ||
# | ||
# @param [String] role | ||
# @param [CredentialProvider] credentials_provider | ||
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CredentialProvider.html | ||
# @param [String] iam_auth_header_value optional | ||
# As of Jan 2018, Vault will accept ANY or NO header, but this is subject to change and should not be relied upon | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be better to read: "Vault will accept ANY or NO header if none is configured by the Vault server admin" |
||
# @param [String] sts_endpoint optional | ||
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html | ||
# @return [Secret] | ||
def aws_iam(role, credentials_provider, iam_auth_header_value = nil, sts_endpoint = 'https://sts.amazonaws.com') | ||
require "aws-sigv4" | ||
require "base64" | ||
|
||
# STS in the China (Beijing) region (cn-north-1) is sts.cn-north-1.amazonaws.com.cn | ||
# Take care changing below regex with that edge case in mind | ||
valid_sts_endpoint = %r{https:\/\/sts.?(.*).amazonaws.com}.match(sts_endpoint) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, the STS endpoint for STS in the China (Beijing) region (cn-north-1) is The endpoint for GovCloud is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you need to escape the . to be correct: |
||
raise "Unable to parse STS endpoint #{sts_endpoint}" unless valid_sts_endpoint | ||
region = valid_sts_endpoint[1].empty? ? 'us-east-1' : valid_sts_endpoint[1] | ||
|
||
request_body = 'Action=GetCallerIdentity&Version=2011-06-15' | ||
request_method = 'POST' | ||
|
||
vault_headers = { | ||
'User-Agent' => Vault::Client::USER_AGENT, | ||
'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' | ||
} | ||
|
||
vault_headers['X-Vault-AWS-IAM-Server-ID'] = iam_auth_header_value if iam_auth_header_value | ||
|
||
sig4_headers = Aws::Sigv4::Signer.new( | ||
service: 'sts', | ||
region: region, | ||
credentials_provider: credentials_provider | ||
).sign_request( | ||
http_method: request_method, | ||
url: sts_endpoint, | ||
headers: vault_headers, | ||
body: request_body | ||
).headers | ||
|
||
payload = { | ||
role: role, | ||
iam_http_request_method: request_method, | ||
iam_request_url: Base64.strict_encode64(sts_endpoint), | ||
iam_request_headers: Base64.strict_encode64(vault_headers.merge(sig4_headers).to_json), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably OK, but just want to call out that this format for encoding the headers is only compatible with Vault starting with v0.8.0. |
||
iam_request_body: Base64.strict_encode64(request_body) | ||
} | ||
|
||
json = client.post('/v1/auth/aws/login', JSON.fast_generate(payload)) | ||
secret = Secret.decode(json) | ||
client.token = secret.auth.client_token | ||
return secret | ||
end | ||
|
||
# Authenticate via a TLS authentication method. If authentication is | ||
# successful, the resulting token will be stored on the client and used | ||
# for future requests. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
require "spec_helper" | ||
require "aws-sigv4" | ||
|
||
module Vault | ||
describe Auth do | ||
|
@@ -211,5 +212,39 @@ module Vault | |
}.to_not change { subject.token } | ||
end | ||
end | ||
|
||
describe "#aws_iam" do | ||
before(:context) do | ||
vault_test_client.sys.enable_auth("aws", "aws", nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be good to also configure the header value here and then insert it, just to ensure it gets processed properly. |
||
end | ||
|
||
after(:context) do | ||
vault_test_client.sys.disable_auth("aws") | ||
end | ||
|
||
let!(:old_token) { subject.token } | ||
let(:credentials_provider) do | ||
double( | ||
credentials: | ||
double(access_key_id: 'very', secret_access_key: 'secure', session_token: 'thing') | ||
) | ||
end | ||
let(:secret) { double(auth: double(client_token: 'a great token')) } | ||
|
||
after do | ||
subject.token = old_token | ||
end | ||
|
||
it "authenticates and saves the token on the client", vault: "> 0.7.3" do | ||
expect(subject).to receive(:post).and_return 'huzzah!' | ||
expect(Secret).to receive(:decode).and_return secret | ||
expect(::Aws::Sigv4::Signer).to( | ||
receive(:new).with( | ||
service: 'sts', region: 'cn-north-1', credentials_provider: credentials_provider | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this expectation could obviate the need for the comment in e598ffc |
||
).and_call_original | ||
) | ||
subject.auth.aws_iam('yabba', credentials_provider, 'canary_header', 'https://sts.cn-north-1.amazonaws.com.cn') | ||
end | ||
end | ||
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.
It would be better to say something like "Authenticate via AWS IAM auth method" as this doesn't use the ec2 auth method at all.