Skip to content

Instantly share code, notes, and snippets.

@antaflos
Created March 9, 2017 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antaflos/f4cb8844efa27d60d5179c77ad5b83ce to your computer and use it in GitHub Desktop.
Save antaflos/f4cb8844efa27d60d5179c77ad5b83ce to your computer and use it in GitHub Desktop.
require 'hiera/config'
require 'hiera/backend/eyaml'
require 'hiera/backend/eyaml/options'
require 'hiera/backend/eyaml/encryptor'
require 'hiera/backend/eyaml/encryptors/gpg'
require 'hiera/backend/eyaml/encryptors/pkcs7'
class Hiera
module Backend
class File_eyaml_backend
def initialize
Hiera.debug("Hiera File eyaml backend starting")
if Hiera::Config.include?(:file_eyaml) and Hiera::Config[:file_eyaml].has_key? :interpolate
@interpolate = Hiera::Config[:file_eyaml][:interpolate]
else
@interpolate = true
end
end
def lookup_with_segments(segments, scope, order_override, resolution_type, context={:recurse_guard => nil, :order_override => nil})
Hiera.debug("lookup_with_segments() was called in file_eyaml_backend, with segments = #{segments}")
value = lookup(segments.join('.'), scope, order_override, resolution_type, context)
throw (:no_such_key) if value.nil?
return value
end
def lookup(key, scope, order_override, resolution_type, context={:recurse_guard => nil, :order_override => nil})
Hiera.debug("lookup() was called in file_eyaml_backend, with key = #{key}")
answer = nil
Hiera.debug("Looking up #{key} in File eyaml backend")
Backend.datasources(scope, order_override) do |source|
Hiera.debug("Hiera File_eyaml_backend: looking for data source '#{source}'")
datadir = Backend.datafile(:file_eyaml, scope, source, "d") or next
validate_key_lookup!(datadir, key)
path = File.join(datadir, key)
epath = path+'.enc'
next unless (File.exist?(path) or File.exist?(epath))
if File.exist?(epath)
data = File.read(epath)
md = data.match(/^ENC\[(\w+,)?([a-zA-Z0-9\+\/=]+?)\]$/)
raise Exception, "Hiera File eyaml backend: enc file does not match format '#{epath}'" if md.nil?
Config[:file_eyaml].each do |key, value|
parsed_value = parse_string(value, scope, {}, context)
Eyaml::Options[key] = parsed_value
Hiera.debug("Set option: #{key} = #{parsed_value}")
end
Eyaml::Options[:source] = 'hiera'
Eyaml::Options[:interpolate] = 'false'
begin
hfe = Hiera::Backend::Eyaml::Encryptor.find(md[1].split(",").first)
data = hfe.decrypt(hfe.decode(md[2]))
rescue
Hiera.debug("Hiera File_eyaml_backend failed to decrypt '#{path}'")
end
else
data = File.read(path)
end
case resolution_type
when :array
answer ||= []
answer << parse_answer(data, scope, {}, context)
else
answer = parse_answer(data, scope, {}, context)
break
end
end
if answer.nil? || (resolution_type == :array && answer == [])
if Gem::Version.new(Hiera.version) >= Gem::Version.new('3.0.0')
throw (:no_such_key)
else
return nil
end
else
return answer
end
end
# Ensure that looked up files are within the datadir to prevent directory traversal
#
# @param datadir [String] The directory being used for the lookup
# @param key [String] The key being looked up
#
# @todo Raise a SecurityError instead of an Exception
# @raise [Exception] If the path to the data file is outside of the datadir
def validate_key_lookup!(datadir, key)
# Expand the datadir and path, and ensure that the datadir contains
# the given key. If the expanded key is outside of the datadir then
# this is a directory traversal attack and should be aborted.
abs_datadir = File.expand_path(datadir)
abs_path = File.expand_path(File.join(abs_datadir, key))
unless abs_path.index(abs_datadir) == 0
raise Exception, "Hiera File eyaml backend: key lookup outside of datadir '#{key}'"
end
end
# Parse the answer according to the chosen interpolation mode
#
# @param data [String] The value to parse
# @param scope [Hash] The variable scope to use for interpolation
#
# @return [String] The interpolated data
def parse_answer(data, scope, extra_data={}, context={:recurse_guard => nil, :order_override => nil})
if @interpolate
Backend.parse_answer(data, scope, extra_data, context)
else
data
end
end
def parse_string(data, scope, extra_data={}, context={:recurse_guard => nil, :order_override => nil})
if Backend.method(:parse_string).arity.abs == 3
return Backend.parse_string(data, scope, extra_data)
else
return Backend.parse_string(data, scope, extra_data, context)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment