-
Couldn't load subscription status.
- Fork 25
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