Is there a way to get the ipaddress from a >>puppet custom function<< ?
I'm trying use Puppet::FileServing::Content.indirection.find
on a custom endpoint like this:
[secret]
path /var/lib/puppet/secret/%H
allow *
when using Puppet::FileServing::Content.indirection.find('puppet:///secret/file')
, the %H portion of the path is replaced with the puppetmaster hostname instead of the client. After hacking the code, I found that you have to pass the node hostname, the ipaddress and the environment like this.
content = Puppet::FileServing::Content.indirection.find(
'puppet:///secret/file',
:node => lookupvar('fqdn'),
:ip => lookupvar('ipaddress'),
:environment => environment
)
return content.full_path() # this will be /var/lib/puppet/secret/clientnode
However, I found that using lookupvar('fqdn')
and lookupvar('ipaddress')
is not reliable because a node can easily overwrite these values with export FACTER_fqdn=whatever
.
Using lookupvar('clientcert')
seems to be the way to go for the hostname because this is inserted by puppet itself.
EDIT:
The function I'm building is to translate puppet: url that point to an encrypted file to a another puppet: url that point at the decrypted file.
Use case:
file{ '/tmp/test'
source => eyaml_decrypt('puppet:///modules/profile/some_file')
}
In the puppet files, there could be some encrypted file at modules/profile/files/some_file.enc
which represent the encrypted version of the given file. This way, we can share our puppet files with everyone without risking a leak of sensitive information. The function will then return a private puppet url like puppet:///private/some_file
which will resolve to /var/lib/puppet/private/FQDN/some_file
.
This is my decrypt function :
module Puppet::Parser::Functions
newfunction(:eyaml_decrypt, :type => :rvalue, :doc => <<-EOS
Decrypt the given file with `eyaml decrypt -f filepath.enc > filepath`\n
If the target file already exist, the filepath is directly returned. No decryption occurs.\n
[0] - filepath\n
returns filepath\n
EOS
) do |args|
require 'puppet/file_serving'
require 'puppet/file_serving/content'
require 'puppet/file_serving/configuration'
require 'fileutils'
require 'digest'
# Check if file is a puppet url.
if not args[0].start_with?('puppet:///')
# This does not mean that there is an error. It could be because in dev, we use a direct path from a node instead of a puppetmaster path.
Puppet::notice("eyaml_decrypt: The file '#{args[0]}' does not look like a puppet url therefore, no decrypt occurs.")
return args[0]
end
# Check if we already have the unencrypted form of the file in the fileserver
unencrypted_content = Puppet::FileServing::Content.indirection.find(args[0], :node => lookupvar('clientcert'), :ip => lookupvar('ipaddress'), :environment => environment)
if unencrypted_content
return args[0]
end
# Check if we have the encrypted form of the file in the fileserver
encrypted_content = Puppet::FileServing::Content.indirection.find(args[0] + '.enc', :node => lookupvar('clientcert'), :ip => lookupvar('ipaddress'), :environment => environment)
if not encrypted_content
# This happens when the unencrypted file is not present AND the encrypted file is not present aswell.
# This is most likely a mistake from a fully qualified puppetlord and will probably result in a puppet error because the file args[0] won't exist.
return args[0]
end
encrypted_filepath = encrypted_content.full_path()
# If the hiera specify the box-is-vagrant, no decryption can occurs because the private key is not accessible.
# If a vagrant box (most likely dev or intg) tries to decrypt a file, it is most propbably a mistake because encrypted data are restricted to prod and accp
use_hiera_eyaml = function_hiera(['use_hiera_eyaml', false])
if not use_hiera_eyaml
Puppet::notice("eyaml_decrypt: no decryption occured for '#{args[0]}.enc' because use_hiera_eyaml is set to false")
Puppet::notice("eyaml_decrypt: this most likely mean that there is a mistake in the puppet logic. A vagrant box should not have to access private encrypted file")
return args[0] + '.enc'
end
# Check that the private mount does indeed exist.
private_mount = Puppet::FileServing::Configuration.configuration.find_mount('private', environment)
if not private_mount
Puppet::err("eyaml_decrypt: The mount point 'private' is unavailable or does not exist. Check your fileserver.conf")
return args[0]
end
# Prepare the private unencrypted file
private_filename = Digest::MD5.hexdigest(args[0]) + '-' + File::basename(args[0])
private_filepath = private_mount.path(lookupvar('clientcert')) + '/' + private_filename
private_directory_path = File.dirname(private_filepath)
if not File.directory?(private_directory_path)
FileUtils.mkdir_p(private_directory_path)
end
# use eyaml to decrypt the encrypted file to the private file
`eyaml decrypt -f #{encrypted_filepath} > #{private_filepath}`
Puppet::notice("eyaml_decrypt: file '#{encrypted_filepath}' decrypted to '#{private_filepath}'")
# return the private url for the unencrypted file
return 'puppet:///private/' + private_filename
end
end
I wish puppet had an API to send me reliable information about the node that is connecting like the ssl certificate and ip address.