-
-
Save antaflos/f4cb8844efa27d60d5179c77ad5b83ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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