-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Use Case
I am in an environment where the secrets management is handled strictly. The Agents use their own credentials to access a Hashicorp Vault directly (using Deferred calls to https://github.com/voxpupuli/puppet-vault_lookup) and the secrets must not be present in reports or logs of the puppet agent.
In this setup, I get an issue when presented with a case as follows:
A class expects a Sensitive[String[1]]
as parameter for a certificate private key and passes this parameter to a file
ressource.
The profile gets the secret from vault using a Deferred
function call ... since puppet-vault_lookup returns a Sensitive[Hash]
, I made a ruby wrapper for PuppetX::VaultLookup::Lookup.lookup
to return Hash[String[1], Sensitive[String[1]]]
and then use a Deferred('get', ...
to obtain the content of the hash.
During typechecking this works correctly. However, it seems that the content
of the file resource ignores the Sensitive
type of the parameter and only looks at the Deferred
it actually receives. This means that the diff does show in the logs of the agent and in the report.
The puppet documentation tells to wrap the Deferred
with Sensitive
but in this case I have a type checking error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Resource Statement, Class[SomeClass]: parameter 'private_key' expects a Sensitive[String] value, got Sensitive[Object[{name => 'Deferred', attributes => {'name' => Pattern[/\A[$]?[a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)*\z/], 'arguments' => {type => Optional[Array], value => undef}}}]]
For completeness, here is the wrapper function I created:
# frozen_string_literal: true
require 'puppet'
require 'puppet_x/vault_lookup/lookup'
# @summary Get secret from Hashicorp Vault, cast the result to a Hash[String[1], Sensitive[String[1]]], where the keys are the fields requested.
Puppet::Functions.create_function(:'base::get_vault_secret', Puppet::Functions::InternalFunction) do
# @param my_data The String to evaluate
# @return Hash[String[1],Sensitive[String[1]]]
dispatch :get_vault_secret do
cache_param
param 'String[1]', :vault_path
param 'Array[String]', :fields
optional_param 'String', :role
optional_param 'Optional[String]', :auth_mount_path
optional_param 'String', :vault_addr
return_type 'Hash[String[1],Sensitive[String[1]]]'
end
def get_vault_secret(cache,
vault_path,
fields,
role = 'agent',
auth_mount_path = '/v1/auth/puppet',
vault_addr = 'https://vault.example.com')
temp = PuppetX::VaultLookup::Lookup.lookup(cache: cache,
path: vault_path,
vault_addr: vault_addr,
cert_path_segment: auth_mount_path,
cert_role: role,
namespace: nil,
field: nil,
auth_method: 'cert',
role_id: role,
secret_id: nil,
approle_path_segment: nil,
agent_sink_file: nil).unwrap
fields.each_with_object({}) do |elem, memo|
raise ArgumentError, _("Vault Secret at path '%{path}' does not contain field '%{field}'") % {field: elem, path: vault_path} unless temp.key?(elem)
memo[elem] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(temp[elem])
end
end
end
And a quick coding example of what I describe:
class someclass(
Sensitive[String[1]] $private_key,
) {
file { '/etc/ssl/private.key':
content => $private_key,
}
}
class profile {
$vault_result = Deferred(
'base::get_vault_secret',
[
'secret/vault/path/for/private/key',
['private_key']
],
)
$private_key = Deferred('get', [$vault_result, 'private_key'])
class { 'someclass':
private_key => $private_key,
}
}
Describe the solution you would like
I would prefer if the Sensitive
type was correctly handled by all native resource types even if wrapped by a Deferred
, this would simplify the understanding of how Sensitive
actually work and meet the expectation of what the type checking actually agrees with.
Describe alternatives you've considered
My guess is that I should wrap the variable usage in the file resource content parameter... but this is conceptually weird since the typechecking agrees this is already a Sensitive
. This would also mean every module requesting a Sensitive[String]
to wrap the parameter into a Sensitive
.
Additional context
No response