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

Kv2 support with a specified secret key #54

Merged
merged 8 commits into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 14 additions & 4 deletions lib/puppet/functions/vault_lookup/lookup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
optional_param 'Optional[String]', :vault_cert_path_segment
optional_param 'String', :vault_cert_role
optional_param 'String', :vault_namespace
optional_param 'String', :vault_key
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO we need to allow empty strings, or could we also use:

Suggested change
optional_param 'String', :vault_key
optional_param 'String[1]', :vault_key

(which enforces a minimal string length of 1, or no string at all)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should... because of how this ends up being called sometime you need to pass in the empty string.
and yes while it's the final argument in the list it doesn't matter.. but if we ever need another argument it's problems.

A better way to handle this that works well with deferred would be nice but isn't obvious to me.

Deferred doesn't take named arguments (for better or mostly worse) so for example when not using namespaces you need to pass the empty string to access the key parameter

return_type 'Sensitive'
end

Expand All @@ -14,7 +15,8 @@ def lookup(path,
vault_url = nil,
vault_cert_path_segment = nil,
vault_cert_role = nil,
vault_namespace = nil)
vault_namespace = nil,
vault_key = nil)
if vault_url.nil?
Puppet.debug 'No Vault address was set on function, defaulting to value from VAULT_ADDR env value'
vault_url = ENV['VAULT_ADDR']
Expand Down Expand Up @@ -44,7 +46,11 @@ def lookup(path,
vault_namespace)

secret_uri = vault_base_uri + "/v1/#{path.delete_prefix('/')}"
data = get_secret(client, secret_uri, token, vault_namespace)
data = get_secret(client,
secret_uri,
token,
vault_namespace,
vault_key)
Puppet::Pops::Types::PSensitiveType::Sensitive.new(data)
end

Expand All @@ -58,7 +64,7 @@ def auth_login_body(vault_cert_role)
end
end

def get_secret(client, uri, token, namespace)
def get_secret(client, uri, token, namespace, key)
headers = { 'X-Vault-Token' => token, 'X-Vault-Namespace' => namespace }.delete_if { |_key, value| value.nil? }
secret_response = client.get(uri,
headers: headers,
Expand All @@ -68,7 +74,11 @@ def get_secret(client, uri, token, namespace)
raise Puppet::Error, append_api_errors(message, secret_response)
end
begin
JSON.parse(secret_response.body)['data']
if key.nil?
JSON.parse(secret_response.body)['data']
else
JSON.parse(secret_response.body)['data']['data'][key]
end
rescue StandardError
raise Puppet::Error, 'Error parsing json secret data from vault response'
end
Expand Down
14 changes: 13 additions & 1 deletion spec/functions/lookup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
end
end

it 'logs on, requests a secret using a token, and returns the data wrapped in the Sensitive type' do
it 'logs on, requests a kv1 secret using a token, and returns the data wrapped in the Sensitive type' do
vault_server = MockVault.new
vault_server.mount('/v1/auth/cert/login', AuthSuccess)
vault_server.mount('/v1/kv/test', SecretLookupSuccess)
Expand All @@ -60,6 +60,18 @@
end
end

it 'logs on, requests a kv2 secret using a token, and returns the data wrapped in the Sensitive type' do
custom_auth_segment = 'v1/custom/auth/segment'
vault_server = MockVault.new
vault_server.mount("/#{custom_auth_segment}/login", AuthSuccess)
vault_server.mount('/v1/kv/test', SecretLookupSuccessKV2)
vault_server.start_vault do |port|
result = function.execute('kv/test', "http://127.0.0.1:#{port}", custom_auth_segment, '', '', 'bar')
expect(result).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)
expect(result.unwrap).to eq('baz')
end
end

it 'is successful when providing a cert_role while authenticating' do
vault_server = MockVault.new
vault_server.mount('/v1/auth/cert/login', AuthSuccessWithRole)
Expand Down
31 changes: 31 additions & 0 deletions spec/mock_vault_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ module PuppetVaultLookupHelpers
JSON
.freeze

SECRET_SUCCESS_KV2_DATA = <<~JSON
{
"request_id": "36b0eadc-890b-1da7-b06d-36bda6a9d94d",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"data": {
"bar": "baz"
},
"metadata": {
"created_time": "2022-07-21T12:38:39.082211175Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
}
},
"warnings": null
}
JSON
.freeze

AUTH_SUCCESS_DATA = <<~JSON
{
"request_id": "03d11bd4-b994-c432-150f-5703a75641d1",
Expand Down Expand Up @@ -115,6 +138,14 @@ def do_GET(_request, response)
end
end

class SecretLookupSuccessKV2 < WEBrick::HTTPServlet::AbstractServlet
def do_GET(_request, response)
response.body = SECRET_SUCCESS_KV2_DATA
response.status = 200
response.content_type = 'application/json'
end
end

class SecretLookupWarning < WEBrick::HTTPServlet::AbstractServlet
def do_GET(_request, response)
response.body = SECRETS_WARNING_DATA
Expand Down