hiera-eyaml-4.2.0/0000755000004100000410000000000014716447232013754 5ustar www-datawww-datahiera-eyaml-4.2.0/hiera-eyaml.gemspec0000644000004100000410000000212714716447232017520 0ustar www-datawww-datalib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'hiera/backend/eyaml' Gem::Specification.new do |gem| gem.name = 'hiera-eyaml' gem.version = Hiera::Backend::Eyaml::VERSION gem.description = 'Hiera backend for decrypting encrypted yaml properties' gem.summary = 'OpenSSL Encryption backend for Hiera' gem.author = 'Vox Pupuli' gem.email = 'voxpupuli@groups.io' gem.license = 'MIT' gem.homepage = 'https://github.com/voxpupuli/hiera-eyaml/' gem.files = `git ls-files`.split($/).reject { |file| file =~ /^features.*$/ } gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) } gem.require_paths = ['lib'] gem.add_runtime_dependency 'highline', '>= 2.1', '< 4' gem.add_runtime_dependency 'optimist', '~> 3.1' gem.add_development_dependency 'rake', '~> 13.2', '>= 13.2.1' gem.add_development_dependency 'rspec-expectations', '~> 3.13' gem.add_development_dependency 'voxpupuli-rubocop', '~> 2.8.0' gem.required_ruby_version = '>= 2.7', ' < 4' end hiera-eyaml-4.2.0/bin/0000755000004100000410000000000014716447232014524 5ustar www-datawww-datahiera-eyaml-4.2.0/bin/eyaml0000755000004100000410000000124514716447232015563 0ustar www-datawww-data#!/usr/bin/env ruby require 'rubygems' require 'hiera/backend/eyaml/CLI' require 'hiera/backend/eyaml/plugins' require 'hiera/backend/eyaml/encryptors/pkcs7' # Register all plugins Hiera::Backend::Eyaml::Encryptors::Pkcs7.register Hiera::Backend::Eyaml::Plugins.find begin Hiera::Backend::Eyaml::CLI.parse rescue StandardError => e Hiera::Backend::Eyaml::LoggingHelper.warn e.message Hiera::Backend::Eyaml::LoggingHelper.debug e.backtrace.join("\n") exit 1 end begin Hiera::Backend::Eyaml::CLI.execute rescue StandardError => e Hiera::Backend::Eyaml::LoggingHelper.warn e.message Hiera::Backend::Eyaml::LoggingHelper.debug e.backtrace.join("\n") exit 1 end hiera-eyaml-4.2.0/.gitignore0000644000004100000410000000043014716447232015741 0ustar www-datawww-data.idea *.iml *.gradle keys/*.pem pkg/ tmp/ .DS_Store .rvmrc .ruby-version .ruby-gemset Gemfile.lock .*.sw? vendor/ .bundle/ features/sandbox/puppet-hiera-merge/reports features/sandbox/puppet-hiera-merge/state features/sandbox/puppet/reports features/sandbox/puppet/state .vendor/ hiera-eyaml-4.2.0/PLUGINS.md0000644000004100000410000000026514716447232015422 0ustar www-datawww-dataPLUGINS ======= Take a look at the skeleton project hiera-eyaml-plaintext, for a bare-bones demo plugin that you can copy and make into your own encryption plugin for hiera-eyaml. hiera-eyaml-4.2.0/sublime_text/0000755000004100000410000000000014716447232016460 5ustar www-datawww-datahiera-eyaml-4.2.0/sublime_text/eyaml.sublime-package0000644000004100000410000000315714716447232022550 0ustar www-datawww-dataPKC;$ӓ(#eyaml.syntax_definitions.tmLanguageUT  R Rux Z{OH;|x&^!`QJJT^¯פQ}ABk3;vSn;B=ñ*5iR&}}P8(VÅ?/Nʮix|vq"뚤|Xg,|+7 YzXГϨU$ J[?2LrwהybJ1o!clʃPCcr32LgLo }`5x,U*U\Ԗaݗ֐ ]`4 [ [3}ȩKvǸS8tɵa'pO"Ai" +[@jfg|]쏋6FCcIƴs}[c>f:I'WmMڗbp*?BEw%>,1|G֬y3J@'}&gMl= QbGjMK ,gR!"7aD&K Xx&J3~YAM K P@dDqŝ 3&aMSj6Lº= =,^UxJJT)  Z "epSAi`o~p| W9ǰ$۷5 9J$#vD28~GsG `S;~eAVcydXb\". B:v|qX*Vy}ZR[j/T"' ,s2G^*T8PI,l̼.B;=;vC U'UZnDշ."nRb" NO+05#¥%P(t\H}"Mn_eQU :1IQ@"&M=ZNL~dAoGsex-8Ũ7㬝Ư "Sz06l@8P@+/ pD5E5so!d?(C%^ER/L\KԔ\PKeF9(j6l (KF$0aLx.^~O1cY9_TeQڹaCew*zur)t^fba z}̴ _wK:*k~]MW"]o>ADI$u$&3^J-"{N sC", "begin": "<%+(?!>)=?", "patterns": [ { "match": "(#).*?(?=%>)", "captures": { "1": { "name": "punctuation.definition.comment.ruby" } }, "name": "comment.line.number-sign.ruby" }, { "include": "source.ruby.rails" } ], "captures": { "0": { "name": "punctuation.section.embedded.ruby" } }, "name": "source.ruby.rails.embedded.html" }, "escaped_char": { "match": "\\\\.", "name": "constant.character.escape.yaml" } }, "patterns": [ { "include": "#erb" }, { "end": "^(?!^\\1)|^(?=\\1(-|\\w+\\s*:)|#)", "begin": "^(\\s*)(?:(-)|(?:(-\\s*)?(\\w+\\s*(:))))\\s*(\\||>)", "beginCaptures": { "3": { "name": "punctuation.definition.entry.yaml" }, "4": { "name": "entity.name.tag.yaml" }, "5": { "name": "punctuation.separator.key-value.yaml" }, "2": { "name": "punctuation.definition.entry.yaml" } }, "patterns": [ { "include": "#erb" } ], "name": "string.unquoted.block.yaml" }, { "match": "(?:(?:(-\\s*)?(\\w+\\s*(:)))|(-))\\s*((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\s*$", "captures": { "3": { "name": "punctuation.separator.key-value.yaml" }, "4": { "name": "punctuation.definition.entry.yaml" }, "1": { "name": "punctuation.definition.entry.yaml" }, "2": { "name": "entity.name.tag.yaml" } }, "name": "constant.numeric.yaml" }, { "match": "(?:(?:(-\\s*)?(\\w+\\s*(:)))|(-))\\s*(?:((\")[^\"]*(\"))|((')[^']*('))|([^,{}&#\\[\\]]+))\\s*", "captures": { "7": { "name": "punctuation.definition.string.end.yaml" }, "3": { "name": "punctuation.separator.key-value.yaml" }, "11": { "name": "string.unquoted.yaml" }, "4": { "name": "punctuation.definition.entry.yaml" }, "8": { "name": "string.quoted.single.yaml" }, "9": { "name": "punctuation.definition.string.begin.yaml" }, "5": { "name": "string.quoted.double.yaml" }, "1": { "name": "punctuation.definition.entry.yaml" }, "6": { "name": "punctuation.definition.string.begin.yaml" }, "10": { "name": "punctuation.definition.string.end.yaml" }, "2": { "name": "entity.name.tag.yaml" } }, "name": "string.unquoted.yaml" }, { "match": "(?:(?:(-\\s*)?(\\w+\\s*(:)))|(-))\\s*([0-9]{4}-[0-9]{2}-[0-9]{2})\\s*$", "captures": { "3": { "name": "punctuation.separator.key-value.yaml" }, "4": { "name": "punctuation.definition.entry.yaml" }, "1": { "name": "punctuation.definition.entry.yaml" }, "2": { "name": "entity.name.tag.yaml" } }, "name": "constant.other.date.yaml" }, { "match": "(\\w.*?)(:)\\s*((\\!\\!)omap)?", "captures": { "3": { "name": "keyword.other.omap.yaml" }, "4": { "name": "punctuation.definition.keyword.yaml" }, "1": { "name": "entity.name.tag.yaml" }, "2": { "name": "punctuation.separator.key-value.yaml" } }, "name": "meta.tag.yaml" }, { "match": "(\\&|\\*)\\w.*?$", "captures": { "1": { "name": "punctuation.definition.variable.yaml" } }, "name": "variable.other.yaml" }, { "end": "\"", "begin": "\"", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.yaml" } }, "patterns": [ { "include": "#escaped_char" }, { "include": "#erb" } ], "endCaptures": { "0": { "name": "punctuation.definition.string.end.yaml" } }, "name": "string.quoted.double.yaml" }, { "end": "'", "begin": "'", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.yaml" } }, "patterns": [ { "include": "#escaped_char" }, { "include": "#erb" } ], "endCaptures": { "0": { "name": "punctuation.definition.string.end.yaml" } }, "name": "string.quoted.single.yaml" }, { "end": "`", "begin": "`", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.yaml" } }, "patterns": [ { "include": "#escaped_char" }, { "include": "#erb" } ], "endCaptures": { "0": { "name": "punctuation.definition.string.end.yaml" } }, "name": "string.interpolated.yaml" }, { "match": "(\\<\\<): ((\\*).*)$", "captures": { "3": { "name": "punctuation.definition.keyword.yaml" }, "1": { "name": "entity.name.tag.yaml" }, "2": { "name": "keyword.operator.merge-key.yaml" } }, "name": "keyword.operator.merge-key.yaml" }, { "match": "( |\t)+$", "disabled": "1", "name": "invalid.deprecated.trailing-whitespace.yaml" }, { "match": "(?> ~/.gem/credentials chmod 0600 ~/.gem/credentials - name: Publish gem to GitHub packages run: gem push --key github --host https://rubygems.pkg.github.com/voxpupuli *.gem hiera-eyaml-4.2.0/.github/workflows/test.yml0000644000004100000410000000366014716447232021060 0ustar www-datawww-dataname: Test on: pull_request: {} push: branches: - master env: BUNDLE_WITHOUT: release jobs: rubocop: env: BUNDLE_WITHOUT: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: "3.3" bundler-cache: true - name: Run Rubocop run: bundle exec rake rubocop test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: - "2.7" - "3.0" - "3.1" - "3.2" - jruby-9.4 puppet: - "~> 8.0" - "~> 7.24" - "https://github.com/puppetlabs/puppet.git#main" exclude: - ruby: "3.0" puppet: "~> 8.0" - ruby: "2.7" puppet: "~> 8.0" - ruby: "3.0" puppet: "https://github.com/puppetlabs/puppet.git#main" - ruby: "2.7" puppet: "https://github.com/puppetlabs/puppet.git#main" env: PUPPET_VERSION: ${{ matrix.puppet }} COVERAGE: ${{ matrix.coverage }} name: "Ruby ${{ matrix.ruby }} - Puppet ${{ matrix.puppet }}" steps: - name: Enable coverage reporting on Ruby 3.1 if: matrix.puppet == '~> 7.24' && matrix.ruby == '3.1' run: echo 'COVERAGE=yes' >> $GITHUB_ENV - uses: actions/checkout@v4 - name: Install expect run: sudo apt-get install expect - name: Install Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: spec tests run: bundle exec rake features - name: Verify gem builds run: gem build --strict --verbose *.gemspec tests: needs: - rubocop - test runs-on: ubuntu-latest name: Test suite steps: - run: echo Test suite completed hiera-eyaml-4.2.0/lib/0000755000004100000410000000000014716447232014522 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/0000755000004100000410000000000014716447232015612 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/0000755000004100000410000000000014716447232017201 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/eyaml_backend.rb0000644000004100000410000001160014716447232022302 0ustar www-datawww-datarequire 'hiera/backend/eyaml/encryptor' require 'hiera/backend/eyaml/utils' require 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/parser/parser' require 'hiera/filecache' require 'yaml' class Hiera module Backend class Eyaml_backend attr_reader :extension def initialize(cache = nil) debug('Hiera eYAML backend starting') @decrypted_cache = {} @cache = cache || Filecache.new @extension = Config[:eyaml][:extension] || 'eyaml' end def lookup(key, scope, order_override, resolution_type) answer = nil parse_options(scope) debug("Looking up #{key} in eYAML backend") Backend.datasources(scope, order_override) do |source| debug("Looking for data source #{source}") eyaml_file = Backend.datafile(:eyaml, scope, source, extension) || next next unless File.exist?(eyaml_file) data = @cache.read(eyaml_file, Hash) do |data| YAML.load(data) || {} end next if data.empty? next unless data.include?(key) # Extra logging that we found the key. This can be outputted # multiple times if the resolution type is array or hash but that # should be expected as the logging will then tell the user ALL the # places where the key is found. debug("Found #{key} in #{source}") # for array resolution we just append to the array whatever # we find, we then goes onto the next file and keep adding to # the array # # for priority searches we break after the first found data item new_answer = parse_answer(data[key], scope) case resolution_type when :array unless new_answer.is_a? Array or new_answer.is_a? String raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" end answer ||= [] answer << new_answer when :hash unless new_answer.is_a? Hash raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" end answer ||= {} answer = Backend.merge_answer(new_answer, answer) else answer = new_answer break end end answer end private def debug(message) Hiera.debug("[eyaml_backend]: #{message}") end def decrypt(data) if encrypted?(data) debug('Attempting to decrypt') begin parser = Eyaml::Parser::ParserFactory.hiera_backend_parser tokens = parser.parse(data) decrypted = tokens.map { |token| token.to_plain_text } plaintext = decrypted.join rescue OpenSSL::PKCS7::PKCS7Error => e debug("Caught exception: #{e.class}, #{e.message}\n" \ "#{e.backtrace.join("\n")}") raise 'Hiera-eyaml decryption failed, check the ' \ "encrypted data matches the key you are using.\n" \ "Raw message from system: #{e.message}" end plaintext.chomp else data end end def encrypted?(data) /.*ENC\[.*\]/.match?(data) ? true : false end def parse_answer(data, scope, extra_data = {}) if data.is_a?(Numeric) or data.is_a?(TrueClass) or data.is_a?(FalseClass) data elsif data.is_a?(String) parse_string(data, scope, extra_data) elsif data.is_a?(Hash) answer = {} data.each_pair do |key, val| interpolated_key = Backend.parse_string(key, scope, extra_data) answer[interpolated_key] = parse_answer(val, scope, extra_data) end answer elsif data.is_a?(Array) answer = [] data.each do |item| answer << parse_answer(item, scope, extra_data) end answer end end def parse_options(scope) Config[:eyaml].each do |key, value| parsed_value = Backend.parse_string(value, scope) Eyaml::Options[key] = parsed_value debug("Set option: #{key} = #{parsed_value}") end Eyaml::Options[:source] = 'hiera' end def parse_string(data, scope, extra_data = {}) if Eyaml::Options[:cache_decrypted] if @decrypted_cache.include?(data) debug('Retrieving data from decrypted cache') decrypted_data = @decrypted_cache[data] else decrypted_data = decrypt(data) debug('Adding data to decrypted cache') @decrypted_cache[data] = decrypted_data end else decrypted_data = decrypt(data) end Backend.parse_string(decrypted_data, scope, extra_data) end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/0000755000004100000410000000000014716447232020310 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/eyaml/encrypthelper.rb0000644000004100000410000000207714716447232023527 0ustar www-datawww-datarequire 'tempfile' require 'fileutils' class Hiera module Backend module Eyaml class EncryptHelper def self.write_important_file(args) require 'hiera/backend/eyaml/highlinehelper' filename = args[:filename] content = args[:content] mode = args[:mode] if File.file?("#{filename}") && !(HighlineHelper.confirm? "Are you sure you want to overwrite \"#{filename}\"?") raise StandardError, 'User aborted' end open("#{filename}", 'w') do |io| io.write(content) end File.chmod(mode, filename) unless mode.nil? end def self.ensure_key_dir_exists(key_file) key_dir = File.dirname key_file return if File.directory? key_dir begin FileUtils.mkdir_p key_dir LoggingHelper.info "Created key directory: #{key_dir}" rescue StandardError raise StandardError, "Cannot create key directory: #{key_dir}" end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/parser/0000755000004100000410000000000014716447232021604 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/eyaml/parser/token.rb0000644000004100000410000000210014716447232023242 0ustar www-datawww-dataclass Hiera module Backend module Eyaml module Parser class TokenType attr_reader :regex @regex def create_token(_string) raise 'Abstract method called' end end class Token attr_reader :match def initialize(match) @match = match end def to_encrypted(_args = {}) raise 'Abstract method called' end def to_decrypted(_args = {}) raise 'Abstract method called' end def to_plain_text raise 'Abstract method called' end def to_s "#{self.class.name}:#{@match}" end end class NonMatchToken < Token def initialize(non_match) super end def to_encrypted(_args = {}) @match end def to_decrypted(_args = {}) @match end def to_plain_text @match end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/parser/encrypted_tokens.rb0000644000004100000410000001440414716447232025514 0ustar www-datawww-datarequire 'hiera/backend/eyaml/parser/token' require 'hiera/backend/eyaml/utils' require 'hiera/backend/eyaml/encryptor' require 'hiera/backend/eyaml' require 'base64' class Hiera module Backend module Eyaml module Parser class EncToken < Token @@tokens_map = {} @@encrypt_unchanged = true attr_reader :format, :cipher, :encryptor, :indentation, :plain_text, :id def self.encrypted_value(format, encryption_scheme, cipher, match, indentation = '') decryptor = Encryptor.find encryption_scheme plain_text = decryptor.decrypt(decryptor.decode(cipher)) EncToken.new(format, plain_text, decryptor, cipher, match, indentation) end def self.decrypted_value(format, plain_text, encryption_scheme, match, id, indentation = '') encryptor = Encryptor.find encryption_scheme cipher = encryptor.encode(encryptor.encrypt(plain_text)) id_number = id.nil? ? nil : id.gsub(/\(|\)/, '').to_i EncToken.new(format, plain_text, encryptor, cipher, match, indentation, id_number) end def self.plain_text_value(format, plain_text, encryption_scheme, match, id, indentation = '') encryptor = Encryptor.find encryption_scheme id_number = id.gsub(/\(|\)/, '').to_i unless id.nil? EncToken.new(format, plain_text, encryptor, '', match, indentation, id_number) end def self.tokens_map @@tokens_map end def self.set_encrypt_unchanged(encrypt_unchanged) @@encrypt_unchanged = encrypt_unchanged end def self.encrypt_unchanged @@encrypt_unchanged end def initialize(format, plain_text, encryptor, cipher, match = '', indentation = '', id = nil) @format = format @plain_text = Utils.convert_to_utf_8(plain_text) @encryptor = encryptor @cipher = cipher @indentation = indentation @id = id super(match) end def to_encrypted(args = {}) label = args[:label] label_string = label.nil? ? '' : "#{label}: " format = args[:format].nil? ? @format : args[:format] encryption_method = args[:change_encryption] unless encryption_method.nil? @encryptor = Encryptor.find encryption_method @cipher = Base64.strict_encode64(@encryptor.encrypt(@plain_text)) end case format when :block @cipher = @cipher.gsub(/\s/, '') chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : '' "#{label_string}#{chevron}" + @indentation + "ENC[#{@encryptor.tag},#{@cipher}]".scan(/.{1,60}/).join("\n" + @indentation) when :string ciphertext = @cipher.gsub(/[\n\r]/, '') "#{label_string}ENC[#{@encryptor.tag},#{ciphertext}]" else raise "#{@format} is not a valid format" end end def to_decrypted(args = {}) label = args[:label] label_string = label.nil? ? '' : "#{label}: " format = args[:format].nil? ? @format : args[:format] index = args[:index].nil? ? '' : "(#{args[:index]})" EncToken.tokens_map[index] = @plain_text if @@encrypt_unchanged == false case format when :block chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : '' "#{label_string}#{chevron}" + indentation + "DEC#{index}::#{@encryptor.tag}[" + @plain_text + ']!' when :string "#{label_string}DEC#{index}::#{@encryptor.tag}[" + @plain_text + ']!' else raise "#{@format} is not a valid format" end end def to_plain_text @plain_text end end class EncTokenType < TokenType def create_enc_token(match, type, enc_comma, cipher, indentation = '') encryption_scheme = enc_comma.nil? ? Eyaml.default_encryption_scheme : enc_comma.split(',').first EncToken.encrypted_value(type, encryption_scheme, cipher, match, indentation) end end class EncHieraTokenType < EncTokenType def initialize @regex = %r{ENC\[(\w+,)?([a-zA-Z0-9+/ =\n]+?)\]} @string_token_type = EncStringTokenType.new end def create_token(string) @string_token_type.create_token(string.gsub(/\s/, '')) end end class EncStringTokenType < EncTokenType def initialize @regex = %r{ENC\[(\w+,)?([a-zA-Z0-9+/=]+?)\]} end def create_token(string) md = @regex.match(string) create_enc_token(string, :string, md[1], md[2]) end end class EncBlockTokenType < EncTokenType def initialize @regex = %r{>\n(\s*)ENC\[(\w+,)?([a-zA-Z0-9+/=\s]+?)\]} end def create_token(string) md = @regex.match(string) create_enc_token(string, :block, md[2], md[3], md[1]) end end class DecStringTokenType < TokenType def initialize @regex = /DEC(\(\d+\))?::(\w+)\[(.+?)\]!/m end def create_token(string) md = @regex.match(string) if EncToken.encrypt_unchanged == false && !md[1].nil? && (md[3] == EncToken.tokens_map[md[1]]) return EncToken.plain_text_value(:string, md[3], md[2], string, md[1]) end EncToken.decrypted_value(:string, md[3], md[2], string, md[1]) end end class DecBlockTokenType < TokenType def initialize @regex = />\n(\s*)DEC(\(\d+\))?::(\w+)\[(.+?)\]!/m end def create_token(string) md = @regex.match(string) if EncToken.encrypt_unchanged == false && !md[2].nil? && (md[4] == EncToken.tokens_map[md[2]]) return EncToken.plain_text_value(:string, md[4], md[3], string, md[2]) end EncToken.decrypted_value(:block, md[4], md[3], string, md[2], md[1]) end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/parser/parser.rb0000644000004100000410000000466314716447232023436 0ustar www-datawww-datarequire 'strscan' require 'hiera/backend/eyaml/parser/token' require 'hiera/backend/eyaml/parser/encrypted_tokens' class Hiera module Backend module Eyaml module Parser class ParserFactory def self.encrypted_parser enc_string = EncStringTokenType.new enc_block = EncBlockTokenType.new Parser.new([enc_string, enc_block]) end def self.decrypted_parser dec_string = DecStringTokenType.new dec_block = DecBlockTokenType.new Parser.new([dec_string, dec_block]) end def self.hiera_backend_parser enc_hiera = EncHieraTokenType.new Parser.new([enc_hiera]) end end class Parser attr_reader :token_types def initialize(token_types) @token_types = token_types end def parse(text) parse_scanner(StringScanner.new(text)).reverse end def parse_scanner(s) if s.eos? [] else # Check if the scanner currently matches a regex current_match = @token_types.find do |token_type| s.match?(token_type.regex) end token = if current_match.nil? # No regex matches here. Find the earliest match. next_match_indexes = @token_types.map do |token_type| next_match = s.check_until(token_type.regex) if next_match.nil? nil else next_match.length - s.matched.length end end.reject { |i| i.nil? } non_match_size = if next_match_indexes.length == 0 s.rest_size else next_match_indexes.min end non_match = s.peek(non_match_size) # advance scanner s.pos = s.pos + non_match_size NonMatchToken.new(non_match) else # A regex matches so create a token and do a recursive call with the advanced scanner current_match.create_token s.scan(current_match.regex) end parse_scanner(s) << token end end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/CLI.rb0000644000004100000410000000267214716447232021253 0ustar www-datawww-datarequire 'optimist' require 'hiera/backend/eyaml' require 'hiera/backend/eyaml/logginghelper' require 'hiera/backend/eyaml/utils' require 'hiera/backend/eyaml/plugins' require 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/subcommand' class Hiera module Backend module Eyaml class CLI def self.parse Utils.require_dir 'hiera/backend/eyaml/subcommands' Eyaml.subcommands = Utils.find_all_subclasses_of({ parent_class: Hiera::Backend::Eyaml::Subcommands }).collect do |classname| Utils.snakecase classname end Eyaml.subcommand = ARGV.shift subcommand = case Eyaml.subcommand when nil ARGV.delete_if { true } 'unknown_command' when /^-/ ARGV.delete_if { true } 'help' else Eyaml.subcommand end command_class = Subcommand.find subcommand options = command_class.parse options[:executor] = command_class options = command_class.validate options Eyaml::Options.set options Eyaml::Options.trace end def self.execute executor = Eyaml::Options[:executor] result = executor.execute puts result unless result.nil? end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/0000755000004100000410000000000014716447232022623 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/decrypt.rb0000644000004100000410000000575114716447232024632 0ustar www-datawww-datarequire 'hiera/backend/eyaml' require 'hiera/backend/eyaml/utils' require 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/parser/parser' require 'hiera/backend/eyaml/subcommand' class Hiera module Backend module Eyaml module Subcommands class Decrypt < Subcommand def self.options [{ name: :string, description: 'Source input is a string provided as an argument', short: 's', type: :string, }, { name: :file, description: 'Source input is a regular file', short: 'f', type: :string, }, { name: :eyaml, description: 'Source input is an eyaml file', short: 'e', type: :string, }, { name: :stdin, description: 'Source input is taken from stdin', short: :none, },] end def self.description 'decrypt some data' end def self.validate(options) sources = %i[eyaml password string file stdin].collect { |x| x if options[x] }.compact Optimist.die 'You must specify a source' if sources.count.zero? Optimist.die "You can only specify one of (#{sources.join(', ')})" if sources.count > 1 options[:source] = sources.first options[:input_data] = case options[:source] when :stdin STDIN.read when :string options[:string] when :file File.read options[:file] when :eyaml File.read options[:eyaml] end options end def self.execute parser = Parser::ParserFactory.encrypted_parser tokens = parser.parse(Eyaml::Options[:input_data]) case Eyaml::Options[:source] when :eyaml decrypted = tokens.map { |token| token.to_decrypted } decrypted.join else yamled = false decrypted = tokens.map do |token| case token.class.name when /::EncToken$/ if yamled yamled = false if /[\r\n]/.match?(token.to_plain_text) "|\n " + token.to_plain_text.gsub(/([\r\n]+)/, '\1 ') else token.to_plain_text end else token.to_plain_text end else yamled = true token.match end end decrypted.join end end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/createkeys.rb0000644000004100000410000000102414716447232025304 0ustar www-datawww-datarequire 'hiera/backend/eyaml/subcommand' class Hiera module Backend module Eyaml module Subcommands class Createkeys < Subcommand def self.options [] end def self.description 'create a set of keys with which to encrypt/decrypt eyaml data' end def self.execute encryptor = Encryptor.find Eyaml.default_encryption_scheme encryptor.create_keys nil end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/encrypt.rb0000644000004100000410000000760514716447232024644 0ustar www-datawww-datarequire 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/parser/parser' require 'hiera/backend/eyaml/parser/encrypted_tokens' require 'hiera/backend/eyaml/subcommand' class Hiera module Backend module Eyaml module Subcommands class Encrypt < Subcommand def self.options [{ name: :password, description: 'Source input is a password entered on the terminal', short: 'p', }, { name: :string, description: 'Source input is a string provided as an argument', short: 's', type: :string, }, { name: :file, description: 'Source input is a regular file', short: 'f', type: :string, }, { name: :stdin, description: 'Source input is taken from stdin', short: :none, }, { name: :eyaml, description: 'Source input is an eyaml file', short: 'e', type: :string, }, { name: :output, description: 'Output format of final result (examples, block, string)', type: :string, short: 'o', default: 'examples', }, { name: :label, description: 'Apply a label to the encrypted result', short: 'l', type: :string, },] end def self.description 'encrypt some data' end def self.validate(options) sources = %i[password string file stdin eyaml].collect { |x| x if options[x] }.compact Optimist.die 'You must specify a source' if sources.count.zero? Optimist.die "You can only specify one of (#{sources.join(', ')})" if sources.count > 1 options[:source] = sources.first options[:input_data] = case options[:source] when :password require 'hiera/backend/eyaml/highlinehelper' HighlineHelper.read_password when :string options[:string] when :file File.read options[:file] when :stdin STDIN.read when :eyaml File.read options[:eyaml] end options end def self.execute case Eyaml::Options[:source] when :eyaml parser = Parser::ParserFactory.decrypted_parser tokens = parser.parse(Eyaml::Options[:input_data]) encrypted = tokens.map { |token| token.to_encrypted } encrypted.join else encryptor = Encryptor.find ciphertext = encryptor.encode(encryptor.encrypt(Eyaml::Options[:input_data])) token = Parser::EncToken.new(:block, Eyaml::Options[:input_data], encryptor, ciphertext, nil, ' ') case Eyaml::Options[:output] when 'block' token.to_encrypted label: Eyaml::Options[:label], use_chevron: !Eyaml::Options[:label].nil?, format: :block when 'string' token.to_encrypted label: Eyaml::Options[:label], format: :string when 'examples' string = token.to_encrypted label: Eyaml::Options[:label] || 'string', format: :string block = token.to_encrypted label: Eyaml::Options[:label] || 'block', format: :block "#{string}\n\nOR\n\n#{block}" else token.to_encrypted format: :string end end end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/unknown_command.rb0000644000004100000410000000213314716447232026344 0ustar www-datawww-datarequire 'hiera/backend/eyaml/subcommand' class Hiera module Backend module Eyaml module Subcommands class UnknownCommand < Eyaml::Subcommand class << self attr_accessor :original_command end @@original_command = 'unknown' def self.options [] end def self.description "Unknown command (#{@@original_command})" end def self.execute subcommands = Eyaml.subcommands puts <<~EOS Unknown subcommand#{': ' + Eyaml.subcommand if Eyaml.subcommand} Usage: eyaml Please use one of the following subcommands or help for more help: #{Eyaml.subcommands.sort.collect do |command| command_class = Subcommands.const_get(Utils.camelcase(command)) command unless command_class.hidden? end.compact.join(', ')} EOS end def self.hidden? true end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/help.rb0000644000004100000410000000227414716447232024105 0ustar www-datawww-datarequire 'hiera/backend/eyaml/subcommand' require 'hiera/backend/eyaml' class Hiera module Backend module Eyaml module Subcommands class Help < Subcommand def self.options [] end def self.description 'this page' end def self.execute puts <<~EOS Welcome to eyaml #{Eyaml::VERSION}#{' '} Usage: eyaml subcommand [global-opts] [subcommand-opts] Available subcommands: #{Eyaml.subcommands.collect do |command| command_class = Subcommands.const_get(Utils.camelcase(command)) format '%15s: %-65s', command.downcase, command_class.description unless command_class.hidden? end.compact.join("\n")} For more help on an individual command, use --help on that command Installed Plugins: #{Plugins.plugins.collect do |plugin| # {' '} "\t" + plugin.name.split('hiera-eyaml-').last end.join("\n")} EOS end def self.hidden? true end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/version.rb0000644000004100000410000000172714716447232024644 0ustar www-datawww-datarequire 'hiera/backend/eyaml/subcommand' require 'hiera/backend/eyaml' class Hiera module Backend module Eyaml module Subcommands class Version < Subcommand def self.options [] end def self.description 'show version information' end def self.execute plugin_versions = {} Eyaml::LoggingHelper.info "hiera-eyaml (core): #{Eyaml::VERSION}" Plugins.plugins.each do |plugin| plugin_shortname = plugin.name.split('hiera-eyaml-').last plugin_version = begin Encryptor.find(plugin_shortname)::VERSION.to_s rescue StandardError 'unknown (is plugin compatible with eyaml 2.0+ ?)' end Eyaml::LoggingHelper.info "hiera-eyaml-#{plugin_shortname} (gem): #{plugin_version}" end nil end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/edit.rb0000644000004100000410000001470314716447232024102 0ustar www-datawww-datarequire 'hiera/backend/eyaml/edithelper' require 'hiera/backend/eyaml/highlinehelper' require 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/parser/parser' require 'hiera/backend/eyaml/subcommand' require 'hiera/backend/eyaml/parser/encrypted_tokens' class Hiera module Backend module Eyaml module Subcommands class Edit < Subcommand def self.options [{ name: :no_preamble, description: "Don't prefix edit sessions with the informative preamble", }, { name: :no_decrypt, short: '-d', description: 'Do not decrypt existing encrypted content. New content marked properly will be encrypted.', },] end def self.description 'edit an eyaml file' end def self.helptext 'Usage: eyaml edit [options] ' end def self.prefix '# |' end def self.preamble tags = (['pkcs7'] + Plugins.plugins.collect do |plugin| plugin.name.split('hiera-eyaml-').last end).collect { |name| Encryptor.find(name).tag } preamble = <<~EOS This is eyaml edit mode. This text (lines starting with #{prefix} at the top of the file) will be removed when you save and exit. - To edit encrypted values, change the content of the DEC()::PKCS7[]! block#{(tags.size > 1) ? " (or #{tags.drop(1).collect { |tag| "DEC()::#{tag}[]!" }.join(' or ')})." : '.'} WARNING: DO NOT change the number in the parentheses. - To add a new encrypted value copy and paste a new block from the appropriate example below. Note that: * the text to encrypt goes in the square brackets * ensure you include the exclamation mark when you copy and paste * you must not include a number when adding a new block e.g. #{tags.collect { |tag| "DEC::#{tag}[]!" }.join(' -or- ')} EOS preamble.gsub(/^/, "#{prefix} ") end def self.validate(options) Optimist.die 'You must specify an eyaml file' if ARGV.empty? options[:source] = :eyaml options[:eyaml] = ARGV.shift if File.exist? options[:eyaml] begin options[:input_data] = File.read options[:eyaml] rescue StandardError raise StandardError, "Could not open file for reading: #{options[:eyaml]}" end else LoggingHelper.info "#{options[:eyaml]} doesn't exist, editing new file" options[:input_data] = '---' end options end def self.execute editor = EditHelper.find_editor Parser::EncToken.set_encrypt_unchanged(false) # The 'no_' option has special handling - bypass that and just check if a flag was set. if Eyaml::Options[:no_decrypt_given] decrypted_input = Eyaml::Options[:input_data] decrypted_file_content = Eyaml::Options[:no_preamble] ? decrypted_input : (preamble + decrypted_input) else encrypted_parser = Parser::ParserFactory.encrypted_parser tokens = encrypted_parser.parse Eyaml::Options[:input_data] decrypted_input = tokens.each_with_index.to_a.map { |(t, index)| t.to_decrypted index: index }.join decrypted_file_content = Eyaml::Options[:no_preamble] ? decrypted_input : (preamble + decrypted_input) end begin decrypted_file ||= EditHelper.write_tempfile decrypted_file_content system "#{editor} \"#{decrypted_file}\"" status = $? raise StandardError, 'File was moved by editor' unless File.file? decrypted_file raw_edited_file = File.read decrypted_file # strip comments at start of file edited_file = raw_edited_file.split($/, -1).drop_while { |line| line.start_with?(prefix) }.join($/) raise StandardError, "Editor #{editor} has not exited?" unless status.exited? unless status.exitstatus == 0 raise StandardError, "Editor did not exit successfully (exit code #{status.exitstatus}), aborting" end raise StandardError, 'Edited file is blank' if edited_file.empty? if edited_file == decrypted_input LoggingHelper.info 'No changes detected, exiting' else decrypted_parser = Parser::ParserFactory.decrypted_parser edited_tokens = decrypted_parser.parse(edited_file) # check that the tokens haven't been copy / pasted used_ids = edited_tokens.find_all { |t| t.class.name =~ /::EncToken$/ and !t.id.nil? }.map { |t| t.id } if used_ids.length != used_ids.uniq.length raise RecoverableError, "A duplicate DEC(ID) was found so I don't know how to proceed. This is probably because you copy and pasted a value - if you do this please delete the ID and parentheses" end # replace untouched values with the source values edited_denoised_tokens = edited_tokens.map do |token| if token.class.name =~ /::EncToken$/ && !token.id.nil? old_token = tokens[token.id] if old_token.plain_text.eql? token.plain_text old_token else token end else token end end encrypted_output = edited_denoised_tokens.map { |t| t.to_encrypted }.join filename = Eyaml::Options[:eyaml] File.write("#{filename}", encrypted_output) end rescue RecoverableError => e LoggingHelper.info e raise e unless agree 'Return to the editor to try again?' retry ensure EditHelper.secure_file_delete file: decrypted_file, num_bytes: [edited_file.length, decrypted_input.length,].max end nil end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommands/recrypt.rb0000644000004100000410000000333314716447232024642 0ustar www-datawww-datarequire 'hiera/backend/eyaml/subcommand' require 'hiera/backend/eyaml/options' require 'hiera/backend/eyaml/parser/parser' class Hiera module Backend module Eyaml module Subcommands class Recrypt < Subcommand def self.options [ { name: :change_encryption, description: 'Specify the new encryption method that should be used for the file', short: 'd', default: 'pkcs7', }, ] end def self.description 'recrypt an eyaml file' end def self.helptext 'Usage: eyaml recrypt [options] ' end def self.validate(options) Optimist.die 'You must specify an eyaml file' if ARGV.empty? options[:source] = :eyaml options[:eyaml] = ARGV.shift options[:input_data] = File.read options[:eyaml] @change_encryption = options[:change_encryption] options end def self.execute encrypted_parser = Parser::ParserFactory.encrypted_parser tokens = encrypted_parser.parse Eyaml::Options[:input_data] decrypted_input = tokens.each_with_index.to_a.map { |(t, index)| t.to_decrypted index: index }.join decrypted_parser = Parser::ParserFactory.decrypted_parser edited_tokens = decrypted_parser.parse(decrypted_input) encrypted_output = edited_tokens.map { |t| t.to_encrypted({ change_encryption: @change_encryption }) }.join filename = Eyaml::Options[:eyaml] File.write("#{filename}", encrypted_output) nil end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/highlinehelper.rb0000644000004100000410000000067714716447232023636 0ustar www-datawww-datarequire 'highline/import' class Hiera module Backend module Eyaml class HighlineHelper def self.read_password ask('Enter password: ') { |q| q.echo = '*' } end def self.confirm?(message) result = ask("#{message} (y/N): ") if result.downcase == 'y' or result.downcase == 'yes' true else false end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/encryptors/0000755000004100000410000000000014716447232022520 5ustar www-datawww-datahiera-eyaml-4.2.0/lib/hiera/backend/eyaml/encryptors/pkcs7.rb0000644000004100000410000001201114716447232024067 0ustar www-datawww-datarequire 'openssl' require 'hiera/backend/eyaml/encryptor' require 'hiera/backend/eyaml/encrypthelper' require 'hiera/backend/eyaml/logginghelper' require 'hiera/backend/eyaml/options' class Hiera module Backend module Eyaml module Encryptors class Pkcs7 < Encryptor self.options = { private_key: { desc: 'Path to private key', type: :string, default: './keys/private_key.pkcs7.pem', }, public_key: { desc: 'Path to public key', type: :string, default: './keys/public_key.pkcs7.pem', }, private_key_env_var: { desc: 'Name of environment variable to read private key from', type: :string, }, public_key_env_var: { desc: 'Name of environment variable to read public key from', type: :string, }, keysize: { desc: 'Key size used for encryption', type: :integer, default: 2048, }, } self.tag = 'PKCS7' def self.encrypt(plaintext) LoggingHelper.trace 'PKCS7 encrypt' public_key_pem = load_public_key_pem if public_key_pem.include? 'BEGIN CERTIFICATE' public_key_x509 = OpenSSL::X509::Certificate.new(public_key_pem) elsif public_key_pem.include? 'BEGIN PUBLIC KEY' public_key_rsa = OpenSSL::PKey::RSA.new(public_key_pem) public_key_x509 = OpenSSL::X509::Certificate.new public_key_x509.public_key = public_key_rsa.public_key else raise StandardError, "file #{public_key_pem} cannot be used to encrypt - invalid public key format" end cipher = OpenSSL::Cipher.new('aes-256-cbc') OpenSSL::PKCS7.encrypt([public_key_x509], plaintext, cipher, OpenSSL::PKCS7::BINARY).to_der end def self.decrypt(ciphertext) LoggingHelper.trace 'PKCS7 decrypt' private_key_pem = load_private_key_pem private_key_rsa = OpenSSL::PKey::RSA.new(private_key_pem) pkcs7 = OpenSSL::PKCS7.new(ciphertext) public_key_x509 = OpenSSL::X509::Certificate.new public_key_x509.serial = pkcs7.recipients[0].serial public_key_x509.issuer = pkcs7.recipients[0].issuer public_key_x509.public_key = private_key_rsa.public_key pkcs7.decrypt(private_key_rsa, public_key_x509) end def self.create_keys # Do equivalent of: # openssl req -x509 -nodes -newkey rsa:2048 -keyout privatekey.pem -out publickey.pem -batch public_key = option :public_key private_key = option :private_key keysize = option :keysize key = OpenSSL::PKey::RSA.new(keysize) EncryptHelper.ensure_key_dir_exists private_key EncryptHelper.write_important_file filename: private_key, content: key.to_pem, mode: 0o600 cert = OpenSSL::X509::Certificate.new # In JRuby implementation of openssl, not_before and not_after # are required to sign cert with key and digest. Signing the # certificate is only required for Ruby 2.7 to call cert.to_pem. cert.not_before = Time.now cert.not_after = if 1.size == 8 # 64bit Time.now + (50 * 365 * 24 * 60 * 60) else # 32bit Time.at(0x7fffffff) end cert.public_key = key.public_key cert.sign key, OpenSSL::Digest.new('SHA256') EncryptHelper.ensure_key_dir_exists public_key EncryptHelper.write_important_file filename: public_key, content: cert.to_pem LoggingHelper.info 'Keys created OK' end def self.load_ANY_key_pem(optname_key, optname_env_var) opt_key = option(optname_key.to_sym) opt_key_env_var = option(optname_env_var.to_sym) if opt_key and opt_key_env_var warn "both #{optname_key} and #{optname_env_var} specified, using #{optname_env_var}" end if opt_key_env_var raise StandardError, "env #{opt_key_env_var} is not set" unless ENV[opt_key_env_var] opt_key_pem = ENV.fetch(opt_key_env_var, nil) elsif opt_key raise StandardError, "file #{opt_key} does not exist" unless File.exist? opt_key opt_key_pem = File.read opt_key else raise StandardError, "pkcs7_#{optname_key} is not defined" unless opt_key or opt_key_env_var end opt_key_pem end def self.load_public_key_pem load_ANY_key_pem('public_key', 'public_key_env_var') end def self.load_private_key_pem load_ANY_key_pem('private_key', 'private_key_env_var') end end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/utils.rb0000644000004100000410000000430514716447232021777 0ustar www-datawww-datarequire 'tempfile' require 'fileutils' require 'hiera/backend/eyaml/logginghelper' class Hiera module Backend module Eyaml class Utils def self.camelcase(string) return string if string !~ /_/ && string =~ /[A-Z]+.*/ string.split('_').map { |e| e.capitalize }.join end def self.snakecase(string) return string unless /[A-Z]/.match?(string) string.split(/(?=[A-Z])/).collect { |x| x.downcase }.join('_') end def self.find_closest_class(args) parent_class = args[:parent_class] class_name = args[:class_name] constants = parent_class.constants candidates = [] constants.each do |candidate| candidates << candidate.to_s if candidate.to_s.downcase == class_name.downcase end return unless candidates.count > 0 parent_class.const_get candidates.first end def self.require_dir(classdir) num_class_hierarchy_levels = to_s.split('::').count - 1 root_folder = File.dirname(__FILE__) + '/' + Array.new(num_class_hierarchy_levels).fill('..').join('/') class_folder = root_folder + '/' + classdir Dir[File.expand_path("#{class_folder}/*.rb")].uniq.each do |file| LoggingHelper.trace "Requiring file: #{file}" require file end end def self.find_all_subclasses_of(args) parent_class = args[:parent_class] constants = parent_class.constants candidates = [] constants.each do |candidate| candidates << candidate.to_s.split('::').last if parent_class.const_get(candidate).instance_of?(::Class) end candidates end def self.hiera? 'hiera'.eql? Eyaml::Options[:source] end def self.convert_to_utf_8(string) orig_encoding = string.encoding return string if orig_encoding == Encoding::UTF_8 string.dup.force_encoding(Encoding::UTF_8) rescue EncodingError => e warn "Unable to encode to \"Encoding::UTF_8\" using the original \"#{orig_encoding}\"" string end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/edithelper.rb0000644000004100000410000000574614716447232022776 0ustar www-datawww-datarequire 'hiera/backend/eyaml/logginghelper' class Hiera module Backend module Eyaml class EditHelper def self.find_editor editor = ENV.fetch('EDITOR', nil) editor ||= %w[/usr/bin/sensible-editor /usr/bin/editor /usr/bin/vim /usr/bin/vi].collect do |e| e if FileTest.executable? e end.compact.first raise StandardError, 'Editor not found. Please set your EDITOR env variable' if editor.nil? if editor.index(' ') editor = editor.dup if editor.frozen? # values from ENV are frozen editor.gsub!(/([^\\]|^)~/, '\1' + ENV.fetch('HOME', nil)) # replace ~ with home unless escaped editor.gsub!(/(^|[^\\])"/, '\1') # remove unescaped quotes during processing editor.gsub!('\\ ', ' ') # unescape spaces since we quote paths pieces = editor.split(' ') paths = # get possible paths, starting with longest pieces.each_with_index.map do |_, x| pieces[0..x].join(' ') end.reverse extensions = (ENV['PATHEXT'] || '').split(';') # handle Windows executables pathdirs = ENV['PATH'].split(File::PATH_SEPARATOR) paths += pathdirs.collect { |dir| paths.collect { |path| File.expand_path(path, dir) } }.flatten editorfile = paths.select do |path| FileTest.file?(path) || !extensions.select { |ext| FileTest.file?(path + ext) }.empty? end.first raise StandardError, 'Editor not found. Please set your EDITOR env variable' if editorfile.nil? raw_command = paths[(paths.index editorfile) % pieces.size] editor = "\"#{editorfile}\"#{editor[raw_command.size..-1]}" end editor end def self.secure_file_delete(args) file = File.open(args[:file], 'r+') num_bytes = args[:num_bytes] [0xff, 0x55, 0xaa, 0x00].each do |byte| file.seek(0, IO::SEEK_SET) num_bytes.times { file.print(byte.chr) } file.fsync end file.close File.delete args[:file] end def self.write_tempfile(data_to_write) file = Tempfile.open(['eyaml_edit', '.yaml']) path = file.path file.close! file = File.open(path, 'w') file.chmod(0o600) if ENV['OS'] == 'Windows_NT' # Windows doesn't support chmod icacls = 'C:\Windows\system32\icacls.exe' if File.executable? icacls current_user = `C:\\Windows\\system32\\whoami.exe`.chomp # Use ACLs to restrict access to the current user only command = %(#{icacls} "#{file.path}" /grant:r "#{current_user}":f /inheritance:r) system "#{command} >NUL 2>&1" end end file.puts data_to_write file.close LoggingHelper.debug "Wrote temporary file: #{path}" path end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/plugins.rb0000644000004100000410000000347214716447232022324 0ustar www-datawww-datarequire 'rubygems' class Hiera module Backend module Eyaml class Plugins @@plugins = [] @@commands = [] @@options = [] def self.register_options(args) options = args[:options] plugin = args[:plugin] options.each do |name, option_hash| new_option = { name: "#{plugin}_#{name}" } new_option.merge! option_hash @@options << new_option end end def self.options @@options end def self.find gem_version = Gem::Version.new(Gem::VERSION) this_version = Gem::Version.create(Hiera::Backend::Eyaml::VERSION) index = (gem_version >= Gem::Version.new('1.8.0')) ? Gem::Specification : Gem.source_index [index].flatten.each do |source| specs = (gem_version >= Gem::Version.new('1.6.0')) ? source.latest_specs(true) : source.latest_specs specs.each do |spec| spec = spec.to_spec if spec.respond_to?(:to_spec) next if @@plugins.include? spec dependency = spec.dependencies.find { |d| d.name == 'hiera-eyaml' } next if dependency && !dependency.requirement.satisfied_by?(this_version) file = nil file = if gem_version >= Gem::Version.new('1.8.0') spec.matches_for_glob('**/eyaml_init.rb').first else Gem.searcher.matching_files(spec, '**/eyaml_init.rb').first end next unless file @@plugins << spec load file end end @@plugins end def self.plugins @@plugins end def self.commands @@commands end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/encryptor.rb0000644000004100000410000000443314716447232022666 0ustar www-datawww-datarequire 'base64' require 'hiera/backend/eyaml/encrypthelper' class Hiera module Backend module Eyaml class Encryptor class << self attr_accessor :options, :tag end def self.find(encryption_scheme = nil) encryption_scheme = Eyaml.default_encryption_scheme if encryption_scheme.nil? require "hiera/backend/eyaml/encryptors/#{File.basename encryption_scheme.downcase}" encryptor_module = Module.const_get(:Hiera).const_get(:Backend).const_get(:Eyaml).const_get(:Encryptors) encryptor_class = Utils.find_closest_class parent_class: encryptor_module, class_name: encryption_scheme if encryptor_class.nil? raise StandardError, "Could not find hiera-eyaml encryptor: #{encryption_scheme}. Try gem install hiera-eyaml-#{encryption_scheme.downcase} ?" end encryptor_class end def self.encode(binary_string) Base64.strict_encode64(binary_string) end def self.decode(string) Base64.decode64(string) end def self.encrypt *_args raise StandardError, "encrypt() not defined for encryptor plugin: #{self}" end def self.decrypt *_args raise StandardError, "decrypt() not defined for decryptor plugin: #{self}" end def self.plugin_classname to_s.split('::').last.downcase end def self.register Hiera::Backend::Eyaml::Plugins.register_options options: options, plugin: plugin_classname end def self.option(name) Eyaml::Options["#{plugin_classname}_#{name}"] || options["#{plugin_classname}_#{name}"] end def self.hiera? Utils.hiera? end def self.format_message(msg) "[eyaml_#{plugin_classname}]: #{msg}" end def self.trace(msg) LoggingHelper.trace from: plugin_classname, msg: msg end def self.debug(msg) LoggingHelper.debug from: plugin_classname, msg: msg end def self.info(msg) LoggingHelper.info from: plugin_classname, msg: msg end def self.warn(msg) LoggingHelper.warn from: plugin_classname, msg: msg end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/options.rb0000644000004100000410000000206614716447232022334 0ustar www-datawww-dataclass Hiera module Backend module Eyaml class Options def self.[]=(key, value) @@options ||= {} @@options[key.to_sym] = value end def self.[](key) @@options ||= {} @@options[key.to_sym] end def self.set(hash) @@options = {} hash.each do |k, v| @@options[k.to_sym] = v end end def self.trace LoggingHelper.trace 'Dump of eyaml tool options dict:' LoggingHelper.trace '--------------------------------' @@options.each do |k, v| LoggingHelper.trace format '%18s %-18s = %18s %-18s', "(#{k.class.name})", k.to_s, "(#{v.class.name})", v.to_s rescue StandardError LoggingHelper.trace format '%18s %-18s = %18s %-18s', "(#{k.class.name})", k.to_s, "(#{v.class.name})", '' # case where v is binary end LoggingHelper.trace '--------------------------------' end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/logginghelper.rb0000644000004100000410000000475614716447232023477 0ustar www-datawww-datarequire 'tempfile' require 'fileutils' class Hiera module Backend module Eyaml class LoggingHelper def self.structure_message(messageinfo) message = { from: 'hiera-eyaml-core' } case messageinfo.class.to_s when 'Hash' message.merge!(messageinfo) else message.merge!({ msg: messageinfo.to_s }) end message[:prefix] = "[#{message[:from]}]" message[:spacer] = " #{' ' * message[:from].length} " formatted_output = message[:msg].split("\n").each_with_index.map do |line, index| if index == 0 "#{message[:prefix]} #{line}" else "#{message[:spacer]} #{line}" end end formatted_output.join "\n" end def self.warn(messageinfo) print_message({ message: structure_message(messageinfo), hiera_loglevel: :warn, cli_color: :red }) end def self.info(messageinfo) print_message({ message: structure_message(messageinfo), hiera_loglevel: :debug, cli_color: :white, threshold: 0 }) end def self.debug(messageinfo) print_message({ message: structure_message(messageinfo), hiera_loglevel: :debug, cli_color: :green, threshold: 1 }) end def self.trace(messageinfo) print_message({ message: structure_message(messageinfo), hiera_loglevel: :debug, cli_color: :blue, threshold: 2 }) end def self.print_message(args) message = args[:message] ||= '' hiera_loglevel = args[:hiera_loglevel] ||= :debug cli_color = args[:cli_color] ||= :blue threshold = args[:threshold] if hiera? Hiera.send(hiera_loglevel, message) if threshold.nil? or Eyaml.verbosity_level > threshold elsif threshold.nil? or Eyaml.verbosity_level > threshold STDERR.puts self.colorize( message, cli_color ) end end def self.colorize(message, color) suffix = "\e[0m" prefix = case color when :red "\e[31m" when :green "\e[32m" when :blue "\e[34m" else # :white "\e[0m" end "#{prefix}#{message}#{suffix}" end def self.hiera? 'hiera'.eql? Eyaml::Options[:source] end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/commands.rb0000644000004100000410000000034714716447232022442 0ustar www-datawww-datarequire 'rubygems' class Hiera module Backend module Eyaml class Commands @@commands = [] def self.register; end def self.commands @@commands end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml/subcommand.rb0000644000004100000410000001223514716447232022770 0ustar www-datawww-datarequire 'base64' require 'yaml' # require 'hiera/backend/eyaml/subcommands/unknown_command' class Hiera module Backend module Eyaml class Subcommand class << self attr_accessor :global_options, :options, :helptext end @@global_options = [ { name: :encrypt_method, description: 'Override default encryption and decryption method (default is PKCS7)', short: 'n', default: 'pkcs7', }, { name: :version, description: 'Show version information', }, { name: :verbose, description: 'Be more verbose', short: 'v', }, { name: :trace, description: 'Enable trace debug', short: 't', }, { name: :quiet, description: 'Be less verbose', short: 'q', }, { name: :help, description: 'Information on how to use this command', short: 'h', }, ] def self.load_config_file config = { options: {}, sources: [] } config_paths = [] # Global config_paths += ['/etc/eyaml/config.yaml'] # Home directory env_home = ENV.fetch('HOME', nil) config_paths += ["#{env_home}/.eyaml/config.yaml"] if env_home # Relative to current directory config_paths += ['.eyaml/config.yaml'] # Explicit ENV variable. env_eyaml_config = ENV.fetch('EYAML_CONFIG', nil) config_paths += [env_eyaml_config] if env_eyaml_config # Load each path and stack configs. config_paths.each do |config_file| next unless config_file and File.file? config_file begin yaml_contents = YAML.load_file(config_file) config[:options].merge! yaml_contents config[:sources].push(config_file) rescue StandardError raise StandardError, "Could not open config file \"#{config_file}\" for reading" end end config end def self.all_options options = @@global_options.dup options += self.options if self.options options += Plugins.options # merge in defaults from configuration files config_file = load_config_file options.map! do |opt| key_name = "#{opt[:name]}" if config_file[:options].has_key? key_name opt[:default] = config_file[:options][key_name] opt else opt end end { options: options, sources: config_file[:sources] || [] } end def self.attach_option(opt) self.suboptions += opt end def self.find(commandname = 'unknown_command') begin require "hiera/backend/eyaml/subcommands/#{commandname.downcase}" rescue Exception => e require 'hiera/backend/eyaml/subcommands/unknown_command' return Hiera::Backend::Eyaml::Subcommands::UnknownCommand end command_module = Module.const_get(:Hiera).const_get(:Backend).const_get(:Eyaml).const_get(:Subcommands) command_class = Utils.find_closest_class parent_class: command_module, class_name: commandname command_class || Hiera::Backend::Eyaml::Subcommands::UnknownCommand end def self.parse me = self all = all_options options = Optimist.options do version 'Hiera-eyaml version ' + Hiera::Backend::Eyaml::VERSION.to_s banner ["eyaml #{me.prettyname}: #{me.description}", me.helptext, 'Options:'].compact.join("\n\n") all[:options].each do |available_option| skeleton = { description: '', short: :none, } skeleton.merge! available_option opt skeleton[:name], skeleton[:desc] || skeleton[:description], # legacy plugins short: skeleton[:short], default: skeleton[:default], type: skeleton[:type] end stop_on Eyaml.subcommands end Hiera::Backend::Eyaml.verbosity_level += 1 if options[:verbose] Hiera::Backend::Eyaml.verbosity_level += 2 if options[:trace] Hiera::Backend::Eyaml.verbosity_level = 0 if options[:quiet] Hiera::Backend::Eyaml.default_encryption_scheme = options[:encrypt_method] if options[:encrypt_method] if all[:sources] all[:sources].each do |source| LoggingHelper.debug "Loaded config from #{source}" end end options end def self.validate(args) args end def self.description 'no description' end def self.helptext "Usage: eyaml #{prettyname} [options]" end def self.execute raise StandardError, "This command is not implemented yet (#{to_s.split('::').last})" end def self.prettyname Utils.snakecase to_s.split('::').last end def self.hidden? false end end end end end hiera-eyaml-4.2.0/lib/hiera/backend/eyaml.rb0000644000004100000410000000175314716447232020643 0ustar www-datawww-dataclass Hiera module Backend module Eyaml VERSION = '4.2.0' DESCRIPTION = 'Hiera-eyaml is a backend for Hiera which provides OpenSSL encryption/decryption for Hiera properties' class RecoverableError < StandardError end def self.subcommand=(command) @@subcommand = command end def self.subcommand @@subcommand end def self.default_encryption_scheme=(new_encryption) @@default_encryption_scheme = new_encryption end def self.default_encryption_scheme @@default_encryption_scheme ||= 'PKCS7' @@default_encryption_scheme end def self.verbosity_level=(new_verbosity_level) @@debug_level = new_verbosity_level end def self.verbosity_level @@debug_level ||= 1 @@debug_level end def self.subcommands=(commands) @@subcommands = commands end def self.subcommands @@subcommands end end end end hiera-eyaml-4.2.0/LICENSE.txt0000644000004100000410000000206714716447232015604 0ustar www-datawww-data The MIT License (MIT) Copyright (c) 2013 Tom Poulton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hiera-eyaml-4.2.0/Rakefile0000644000004100000410000000256514716447232015431 0ustar www-datawww-databegin require 'simplecov' require 'simplecov-console' require 'codecov' rescue LoadError else SimpleCov.start do track_files 'lib/**/*.rb' add_filter '/spec' enable_coverage :branch # do not track vendored files add_filter '/vendor' add_filter '/.vendor' end SimpleCov.formatters = [ SimpleCov::Formatter::Console, SimpleCov::Formatter::Codecov, ] end # https://cucumber.io/docs/tools/ruby/ # https://stackoverflow.com/questions/6473419/using-simplecov-to-display-cucumber-code-coverage require 'cucumber/rake/task' Cucumber::Rake::Task.new(:features) do |t| t.cucumber_opts = %w[--format progress] # Any valid command line option can go here. end begin require 'github_changelog_generator/task' rescue LoadError # Do nothing if no required gem installed else GitHubChangelogGenerator::RakeTask.new :changelog do |config| version = Hiera::Backend::Eyaml::VERSION config.future_release = "v#{version}" if /^\d+\.\d+.\d+$/.match?(version) config.header = "# Changelog\n\nAll notable changes to this project will be documented in this file." config.exclude_labels = %w[duplicate question invalid wontfix wont-fix skip-changelog github_actions] config.user = 'voxpupuli' config.project = 'hiera-eyaml' end end begin require 'voxpupuli/rubocop/rake' rescue LoadError # the voxpupuli-rubocop gem is optional end hiera-eyaml-4.2.0/HISTORY.md0000644000004100000410000000300714716447232015437 0ustar www-datawww-data## v2.1.0 (2016-03-02) - (#187) - Change the way third party highline library is imported to avoid memory leak when running under puppet server (@petems) - (#181) - Improve test suite to run against a variety of puppet versions (@peculater) ## v2.0.8 (2015-04-15) - (#149) - Fix to tempfile permissions and invalid editor scenario (@elyscape) ## v2.0.7 (2015-03-04) - (#142) - Fixed highline dependency to exclude newer versions that are not compatible with ruby 1.8.7 (@elyscape) - (#136) - \t and \r characters are now supported in encrypted blocks (@elyscape) - (#138) - Added missing tags and new tagging tool (@elyscape) ## v2.0.6 (2014-12-13) - (#131) - Fix another EDITOR bug (#130) that could erase command line flags to the specified editor (@elyscape) ## v2.0.5 (2014-12-11) - (#128) - Fix a bug (#127) that caused `eyaml edit` to break when `$EDITOR` was a command on PATH rather than a path to a command (@elyscape) ## v2.0.4 (2014-11-24) - Add change log - (#118) - Some initial support for spaces in filenames (primarily targeted at windows platforms) (@elyscape) - (#114) - Add new config file resolution so that a system wide /etc/eyaml/config.yaml is processed first (@gtmtech) - (#112) - Improve debugging options and colorise output (@gtmtech) - (#102) - Extension of temp files should be yaml to help editors provide syntax highlighting (@ColinHebert) - (#90), #121, #122 - Add preamble in edit mode to make it easier to remember how to edit (@sihil) - (#96), #111, #116 - Various updates to docs hiera-eyaml-4.2.0/.rubocop_todo.yml0000644000004100000410000003271114716447232017257 0ustar www-datawww-data# This configuration was generated by # `rubocop --auto-gen-config` # on 2024-10-20 14:24:21 UTC using RuboCop version 1.64.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment. Layout/CommentIndentation: Exclude: - 'lib/hiera/backend/eyaml/subcommands/help.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, compact, no_space Layout/SpaceInsideParens: Exclude: - 'lib/hiera/backend/eyaml/logginghelper.rb' # Offense count: 1 Lint/DuplicateMethods: Exclude: - 'lib/hiera/backend/eyaml/subcommand.rb' # Offense count: 5 # Configuration parameters: AllowedParentClasses. Lint/MissingSuper: Exclude: - 'lib/hiera/backend/eyaml/parser/encrypted_tokens.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedImplicitNamespaces. # AllowedImplicitNamespaces: Gem Lint/RaiseException: Exclude: - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 Lint/RescueException: Exclude: - 'lib/hiera/backend/eyaml/subcommand.rb' # Offense count: 1 Lint/ShadowingOuterLocalVariable: Exclude: - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: Exclude: - 'features/support/env.rb' - 'lib/hiera/backend/eyaml/plugins.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml/subcommands/unknown_command.rb' - 'lib/hiera/backend/eyaml/subcommands/version.rb' - 'lib/hiera/backend/eyaml/utils.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect. Lint/UselessMethodDefinition: Exclude: - 'lib/hiera/backend/eyaml/parser/token.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects. Lint/Void: Exclude: - 'lib/hiera/backend/eyaml/parser/token.rb' # Offense count: 1 Naming/AccessorMethodName: Exclude: - 'lib/hiera/backend/eyaml/parser/encrypted_tokens.rb' # Offense count: 1 # Configuration parameters: AllowedNames. # AllowedNames: module_parent Naming/ClassAndModuleCamelCase: Exclude: - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms. # CheckDefinitionPathHierarchyRoots: lib, spec, test, src # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS Naming/FileName: Exclude: - 'Rakefile.rb' - 'lib/hiera/backend/eyaml/CLI.rb' # Offense count: 3 # Configuration parameters: ForbiddenDelimiters. # ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - 'lib/hiera/backend/eyaml/subcommands/edit.rb' - 'lib/hiera/backend/eyaml/subcommands/help.rb' - 'lib/hiera/backend/eyaml/subcommands/unknown_command.rb' # Offense count: 1 # Configuration parameters: EnforcedStyle, AllowedPatterns. # SupportedStyles: snake_case, camelCase Naming/MethodName: Exclude: - 'lib/hiera/backend/eyaml/encryptors/pkcs7.rb' # Offense count: 1 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - 'lib/hiera/backend/eyaml/parser/parser.rb' # Offense count: 1 # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, normalcase, non_integer # AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 Naming/VariableNumber: Exclude: - 'lib/hiera/backend/eyaml/utils.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Performance/Detect: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' # Offense count: 5 # This cop supports unsafe autocorrection (--autocorrect-all). Performance/MapCompact: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' - 'lib/hiera/backend/eyaml/subcommands/decrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/encrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/help.rb' - 'lib/hiera/backend/eyaml/subcommands/unknown_command.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: MaxKeyValuePairs. Performance/RedundantMerge: Exclude: - 'lib/hiera/backend/eyaml/logginghelper.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Performance/StringInclude: Exclude: - 'lib/hiera/backend/eyaml/utils.rb' # Offense count: 1 Security/Open: Exclude: - 'lib/hiera/backend/eyaml/encrypthelper.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Security/YAMLLoad: Exclude: - 'features/step_definitions/decrypt_steps.rb' - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals Style/AndOr: Exclude: - 'lib/hiera/backend/eyaml/encryptors/pkcs7.rb' - 'lib/hiera/backend/eyaml/highlinehelper.rb' - 'lib/hiera/backend/eyaml/logginghelper.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: MinBranchesCount. Style/CaseLikeIf: Exclude: - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 18 Style/ClassVars: Exclude: - 'lib/hiera/backend/eyaml.rb' - 'lib/hiera/backend/eyaml/commands.rb' - 'lib/hiera/backend/eyaml/options.rb' - 'lib/hiera/backend/eyaml/parser/encrypted_tokens.rb' - 'lib/hiera/backend/eyaml/plugins.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml/subcommands/unknown_command.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. Style/CollectionCompact: Exclude: - 'lib/hiera/backend/eyaml/parser/parser.rb' # Offense count: 35 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Style/EnvHome: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated # Offense count: 36 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: - 'lib/hiera/backend/eyaml/logginghelper.rb' - 'lib/hiera/backend/eyaml/subcommands/decrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/encrypt.rb' # Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). Style/IdenticalConditionalBranches: Exclude: - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml/subcommands/decrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/edit.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: - 'lib/hiera/backend/eyaml/encryptors/pkcs7.rb' - 'lib/hiera/backend/eyaml/parser/encrypted_tokens.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods. # AllowedMethods: nonzero? Style/IfWithBooleanLiteralBranches: Exclude: - 'lib/hiera/backend/eyaml/highlinehelper.rb' - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapIntoArray: Exclude: - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 Style/MultilineBlockChain: Exclude: - 'lib/hiera/backend/eyaml/parser/parser.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Exclude: - 'lib/hiera/backend/eyaml.rb' # Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/hiera/backend/eyaml/logginghelper.rb' - 'lib/hiera/backend/eyaml/parser/parser.rb' - 'lib/hiera/backend/eyaml/subcommands/edit.rb' - 'lib/hiera/backend/eyaml/utils.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: short, verbose Style/PreferredHashMethods: Exclude: - 'lib/hiera/backend/eyaml/subcommand.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Methods. Style/RedundantArgument: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantFilterChain: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect, AllowComments. Style/RedundantInitialize: Exclude: - 'lib/hiera/backend/eyaml/parser/token.rb' # Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: - 'features/support/env.rb' - 'lib/hiera/backend/eyaml/encrypthelper.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml/subcommands/edit.rb' - 'lib/hiera/backend/eyaml/subcommands/recrypt.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/RedundantSelf: Exclude: - 'lib/hiera/backend/eyaml/logginghelper.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/hiera/backend/eyaml/subcommand.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SelectByRegexp: Exclude: - 'hiera-eyaml.gemspec' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: - 'lib/hiera/backend/eyaml/edithelper.rb' # Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: RequireEnglish, EnforcedStyle. # SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names Style/SpecialGlobalVars: Exclude: - 'hiera-eyaml.gemspec' - 'lib/hiera/backend/eyaml/subcommands/edit.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/StderrPuts: Exclude: - 'lib/hiera/backend/eyaml/logginghelper.rb' # Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'features/support/env.rb' - 'lib/hiera/backend/eyaml/edithelper.rb' - 'lib/hiera/backend/eyaml/parser/encrypted_tokens.rb' - 'lib/hiera/backend/eyaml/subcommand.rb' - 'lib/hiera/backend/eyaml/subcommands/decrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/help.rb' - 'lib/hiera/backend/eyaml/subcommands/unknown_command.rb' - 'lib/hiera/backend/eyaml/utils.rb' # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. # AllowedMethods: define_method Style/SymbolProc: Exclude: - 'features/step_definitions/recrypt_steps.rb' - 'lib/hiera/backend/eyaml/parser/parser.rb' - 'lib/hiera/backend/eyaml/subcommands/decrypt.rb' - 'lib/hiera/backend/eyaml/subcommands/edit.rb' - 'lib/hiera/backend/eyaml/subcommands/encrypt.rb' - 'lib/hiera/backend/eyaml/utils.rb' - 'lib/hiera/backend/eyaml_backend.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: - 'lib/hiera/backend/eyaml/parser/parser.rb' # Offense count: 11 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. # URISchemes: http, https Layout/LineLength: Max: 194 hiera-eyaml-4.2.0/Gemfile0000644000004100000410000000222314716447232015246 0ustar www-datawww-datasource 'https://rubygems.org/' # Find a location or specific version for a gem. place_or_version can be a # version, which is most often used. It can also be git, which is specified as # `git://somewhere.git#branch`. You can also use a file source location, which # is specified as `file://some/location/on/disk`. def location_for(place_or_version, fake_version = nil) if place_or_version =~ /^(https[:@][^#]*)#(.*)/ [fake_version, { git: Regexp.last_match(1), branch: Regexp.last_match(2), require: false }].compact elsif place_or_version =~ %r{^file://(.*)} ['>= 0', { path: File.expand_path(Regexp.last_match(1)), require: false }] else [place_or_version, { require: false }] end end gemspec group :development do gem 'activesupport' gem 'aruba', '~> 2.2' gem 'cucumber', '~> 9.2' gem 'hiera-eyaml-plaintext' gem 'puppet', *location_for(ENV['PUPPET_VERSION']) if ENV['PUPPET_VERSION'] end group :release do gem 'faraday-retry', require: false gem 'github_changelog_generator', require: false end group :coverage, optional: ENV['COVERAGE'] != 'yes' do gem 'codecov', require: false gem 'simplecov-console', require: false end hiera-eyaml-4.2.0/README.md0000644000004100000410000005355614716447232015251 0ustar www-datawww-data# Hiera eyaml [![License](https://img.shields.io/github/license/voxpupuli/hiera-eyaml.svg)](https://github.com/voxpupuli/hiera-eyaml/blob/master/LICENSE.txt) [![Test](https://github.com/voxpupuli/hiera-eyaml/actions/workflows/test.yml/badge.svg)](https://github.com/voxpupuli/hiera-eyaml/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/voxpupuli/hiera-eyaml/branch/master/graph/badge.svg)](https://codecov.io/gh/voxpupuli/hiera-eyaml) [![Release](https://github.com/voxpupuli/hiera-eyaml/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/hiera-eyaml/actions/workflows/release.yml) [![RubyGem Version](https://img.shields.io/gem/v/hiera-eyaml.svg)](https://rubygems.org/gems/hiera-eyaml) [![RubyGem Downloads](https://img.shields.io/gem/dt/hiera-eyaml.svg)](https://rubygems.org/gems/hiera-eyaml) hiera-eyaml is a backend for Hiera that provides per-value encryption of sensitive data within yaml files to be used by Puppet. ------------------------- :new: **hiera-eyaml is now part of Vox Pupuli** hiera-eyaml has a new home https://github.com/voxpupuli/hiera-eyaml. Hopefully this will mean more frequent feature updates and bug fixes! Advantages over hiera-gpg ------------------------- A few people found that [hiera-gpg](https://github.com/crayfishx/hiera-gpg) just wasn't cutting it for all use cases, one of the best expressed frustrations was [written back in June 2013](http://slashdevslashrandom.wordpress.com/2013/06/03/my-griefs-with-hiera-gpg/). So [Tom created an initial version](http://themettlemonkey.wordpress.com/2013/07/15/hiera-eyaml-per-value-encrypted-backend-for-hiera-and-puppet/) and this was refined into an elegant solution over the following months. Unlike `hiera-gpg`, `hiera-eyaml`: - only encrypts the values (which allows files to be swiftly reviewed without decryption) - encrypts the value of each key individually (this means that `git diff` is meaningful) - includes a command line tool for encrypting, decrypting, editing and rotating keys (makes it almost as easy as using clear text files) - uses basic asymmetric encryption (PKCS#7) by default (doesn't require any native libraries that need to be compiled & allows users without the private key to encrypt values that the puppet master can decrypt) - has a pluggable encryption framework (e.g. GPG encryption ([hiera-eyaml-gpg](https://github.com/voxpupuli/hiera-eyaml-gpg)) can be used if you have the need for multiple keys and easier key rotation) The Hiera eyaml backend uses yaml formatted files with the .eyaml extension. The encrypted strings are prefixed with the encryption method, wrapped with ENC[] and placed in an eyaml file. You can mix your plain values in as well or separate them into different files. Encrypted values can occur within arrays, hashes, nested arrays and nested hashes. For instance: ```yaml --- plain-property: You can see me encrypted-property: > ENC[PKCS7,Y22exl+OvjDe+drmik2XEeD3VQtl1uZJXFFF2NnrMXDWx0csyqLB/2NOWefv NBTZfOlPvMlAesyr4bUY4I5XeVbVk38XKxeriH69EFAD4CahIZlC8lkE/uDh jJGQfh052eonkungHIcuGKY/5sEbbZl/qufjAtp/ufor15VBJtsXt17tXP4y l5ZP119Fwq8xiREGOL0lVvFYJz2hZc1ppPCNG5lwuLnTekXN/OazNYpf4CMd /HjZFXwcXRtTlzewJLc+/gox2IfByQRhsI/AgogRfYQKocZgFb/DOZoXR7wm IZGeunzwhqfmEtGiqpvJJQ5wVRdzJVpTnANBA5qxeA==] ``` To edit this you can use the command `eyaml edit important.eyaml` which will decrypt the file, fire up an editor with the decrypted values and re-encrypt any edited values when you exit the editor. This tool makes editing your encrypted files as simple as clear text files. Setup ----- ### Installing hiera-eyaml #### RubyGems $ gem install hiera-eyaml #### Apt (Ubuntu 18.04+) $ sudo apt install hiera-eyaml ### Installing hiera-eyaml for [puppetserver](https://github.com/puppetlabs/puppetserver) All commands need to be executed as root. Puppet Enterprise vendors hiera-eyaml already, so you don't need to install it there. ```sh puppetserver gem install hiera-eyaml ``` or via puppet: ```sh puppet resource package hiera-eyaml ensure=installed provider=puppetserver_gem ``` or via Puppet DSL: ```puppet package { 'hiera-eyaml': ensure => 'installed', provider => 'puppetserver_gem', } ``` ### Generate keys The first step is to create a pair of keys: $ eyaml createkeys This creates a public and private key with default names in the default location. (./keys) #### Storing the keys securely when using Puppet Since the point of using this module is to securely store sensitive information, it's important to store these keys securely. If using Hiera with Puppet, Your puppetmaster will need to access these keys to perform decryption when the puppet agent runs on a remote node. So for this reason, a suggested location might be to store them in `/etc/puppetlabs/puppet/eyaml` or `/var/lib/puppet/keys` depending on your setup. The permissions for this folder should allow the puppet user (normally 'puppet') execute access to the keys directory, read only access to the keys themselves and restrict everyone else: $ chown -R puppet:puppet /etc/puppetlabs/puppet/eyaml $ chmod -R 0500 /etc/puppetlabs/puppet/eyaml $ chmod 0400 /etc/puppetlabs/puppet/eyaml/*.pem $ ls -lha /etc/puppetlabs/puppet/eyaml -r-------- 1 puppet puppet 1.7K Sep 24 16:24 private_key.pkcs7.pem -r-------- 1 puppet puppet 1.1K Sep 24 16:24 public_key.pkcs7.pem You may also load the keypair into an environment variable and use the `pkcs7_private_key_env_var` and `pkcs7_public_key_env_var` options to specify the environment variable names to avoid writing the secret key to disk. Basic usage ----------- ### Encryption To encrypt something, you only need the public_key, so distribute that to people creating hiera properties $ eyaml encrypt -f filename # Encrypt a file $ eyaml encrypt -s 'hello there' # Encrypt a string $ eyaml encrypt -p # Encrypt a password (prompt for it) Use the -l parameter to pass in a label for the encrypted value, $ eyaml encrypt -l 'some_easy_to_use_label' -s 'yourSecretString' ### Decryption To decrypt something, you need the private_key. To test decryption you can use the eyaml tool $ eyaml decrypt -f filename # Decrypt a file $ eyaml decrypt -s 'ENC[PKCS7,.....]' # Decrypt a string ### Editing files with a mixture of eyaml-encrypted and plain-text content This is, perhaps, the most common use of eyaml where you have created a few eyaml files, with a mixture of encrypted and non-encrypted properties, you can edit the encrypted values in place, using the special edit mode of the eyaml utility. Edit mode opens a decrypted copy of the eyaml file in your `$EDITOR` and will encrypt and modified values when you exit the editor. $ eyaml edit filename.eyaml # Edit an eyaml file in place When editing eyaml files, you will see that the unencrypted plaintext is marked to allow the eyaml tool to identify each encrypted block, along with the encryption method. This is used to make sure that the block is encrypted again only if the clear text value has changed, and is encrypted using the original encryption mechanism (see plugable encryption later). A decrypted file might look like this: ```yaml --- plain-property: You can see me cipher-property : > DEC(1)::PKCS7[You can't see me]! environments: development: host: localhost password: password production: host: prod.org.com password: > DEC(2)::PKCS7[securepassword]! things: - thing 1 - - nested thing 1.0 - > DEC(3)::PKCS7[secure nested thing 1.1]! - - nested thing 2.0 - nested thing 2.1 ``` Whilst editing you can delete existing values and add new one using the same format (as below). Note that it is important to omit the number in brackets for new values. If any duplicate IDs are found then the re-encryption process will be abandoned by the eyaml tool. some_new_key: DEC::PKCS7[a new value to encrypt]! ### Encrypting an entire file While not as common, sometimes you need to encrypt an entire file. Maybe this file is binary data that isn't meant for loading into an editor. One example might be a Kerberos keytab file. No problem! Just encrypt the entire file: $ eyaml encrypt -f filename As with encrypting short strings on the command-line, the encrypted equivalent will be sent to stdout as an ASCII text string and thus now plays nice with your editor. Notice that the file itself, however, remains unchanged. The output is presented in two blocks: once as a single, long string and once in a nice line-wrapped form. Copy the one of your preference, starting with the `ENC[` and ending at the matching `]`. Paste this into your Puppet or Hiera file just like any other eyaml string and your done. If the file is rather large, you may wish to use a helper like `xclip` to copy the stdout directly to your clipboard. ### Encrypting multiline values The following step-by-step example shows you how to encrypt multiline values. - Copy the YAML text below to a file named `multiline_example.eyaml` ``` --- accounts::key_sets: dummy: private: | ---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- Comment: "dummy-key-hiera-eyaml-issue-rsa-key-20200911" P2/56wAAANwAAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS 1wa2NzMXYyLW9hZXB9fQAAAARub25lAAAAjQAAAIkAAAAGJQAAAP93ZtrMIRZutZ/SZUyw JWwyI4YxNvr5tBt9UnSJ7K0+rQAAAQDohO1ykUahsogS+ymM6o9WEmdROJZpWShCqdv8Dj 2roQAAAIDG1G8hY90Xlz/YiFhDZLLWAAAAgOzMWTfAlHbJ4AdEhG5uU/EAAACA+1/AlcSr QEPM5xLW0unCsQ== ---- END SSH2 ENCRYPTED PRIVATE KEY ---- ``` - Use `edit` to ... - replace '|' with '>', - prepend `DEC::PKCS7[` before the first line, - remove all whitespaces used for indentation, - and append `]!` to the last line of the multiline value. `eyaml edit multiline_example.eyaml` ``` --- accounts::key_sets: dummy: private: > DEC::PKCS7[---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- Comment: "dummy-key-hiera-eyaml-issue-rsa-key-20170123" P2/56wAAANwAAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS 1wa2NzMXYyLW9hZXB9fQAAAARub25lAAAAjQAAAIkAAAAGJQAAAP93ZtrMIRZutZ/SZUyw JWwyI4YxNvr5tBt9UnSJ7K0+rQAAAQDohO1ykUahsogS+ymM6o9WEmdROJZpWShCqdv8Dj 2roQAAAIDG1G8hY90Xlz/YiFhDZLLWAAAAgOzMWTfAlHbJ4AdEhG5uU/EAAACA+1/AlcSr QEPM5xLW0unCsQ== ---- END SSH2 ENCRYPTED PRIVATE KEY ----]! ``` ``` # resulting encrypted file --- accounts::key_sets: dummy: private: > ENC[PKCS7,MIIDTQYJKoZIhvcNAQcDoIIDPjCCAzoCAQAxggEhMIIBHQIBADAFMAACAQEw DQYJKoZIhvcNAQEBBQAEggEAXH7xB1xuzoMAqA/3jSXO0ZUR6+UCb3DsTTj3 Lsrcx5oQBnJ/ml7GfBCPxBKfArZunLcnxmSk4hECKXdfgKsVjAa++JQWvtEm HUNTFqvwd76Ku+nMfI9c8g+X+l6obLjzWfJdg3t6Ja7CJKl8UNFtSmbfYKVi nZ0xBubgdY4plLAFcZyD5/A/lNFqwb051TRLbZOIRRfLUlRL7RNkKRC59Aog S5aJXjmqx6vRzFifNK0JFZvYHGD75TiHJ5LFjg4rjgFd43AnK8iNo773ZWP2 48Gly5Zx7qVQDCDDi1YBgNFb0NIBQw+kWy7HcPH2REvPnXu/HV2FWvDP3Ond yr2EbTCCAg4GCSqGSIb3DQEHATAdBglghkgBZQMEASoEEH+CjZJ1gKfaQIrr N5zef7OAggHgBmRVsfaoiNEOzhmHZ5SxxZztmpBNtLv7mteaSqSL5o0TtKQh SDgxBhaQmlL51+JM1Jsnvqm57ikZhj7Vtek/vr5DhYhWs0AxttH5rNaw0zKU 4bMppVu+SNKCtT+2Qw31x/S7gF7yVl+mwmXhq3qAj9ExWRX3d/8/zTuC61Io f+7O6YUOucZ/m/YPrQnC5v7bDSKlIf1aFaKqukjM3QO8FZlAOHGPvRuWV2Om QIgxQE6F8r+bTkW3KiVIx5FEIthRZ90VS3tz/2wjj77svddBhlid9ov/0ard GGVNGsl1BFpLqxC0mpZXz237cL/aM58naqmX52J6YmC0xQM3DNmahWlYx1HV J/Ogk12pOYPLJB/09OuoHPzKC4WfpB9B7wAC6pghRkO/84cOw6rgSdbzze5W WMPvo181Y74BSBKhJDdO3lWYmEcDyx4TEsMUlpxd9PBDcOHqf9qHviXrwGzO oSm2bUV0Fum5ueU+D2vu3mO0yIQ6fwyvDZLBRjfJV7K/PyDz81feWT6+g38t AC27c0h8wk9b7HYfqG28nZE7F13qrhwCKnOaYLglsmbszNpRrBhfo1IHF6oM YZRZrnrGQg5qQcxMsLq37RAfRgkY0rRLs78EEAhkf4NDxw0A/ovt] ``` - Output of `eyaml decrypt -f multiline_example.eyaml`: ``` --- accounts::key_sets: dummy: private: | ---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- Comment: "dummy-key-hiera-eyaml-issue-rsa-key-20200911" P2/56wAAANwAAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS 1wa2NzMXYyLW9hZXB9fQAAAARub25lAAAAjQAAAIkAAAAGJQAAAP93ZtrMIRZutZ/SZUyw JWwyI4YxNvr5tBt9UnSJ7K0+rQAAAQDohO1ykUahsogS+ymM6o9WEmdROJZpWShCqdv8Dj 2roQAAAIDG1G8hY90Xlz/YiFhDZLLWAAAAgOzMWTfAlHbJ4AdEhG5uU/EAAACA+1/AlcSr QEPM5xLW0unCsQ== ---- END SSH2 ENCRYPTED PRIVATE KEY ---- ``` - The output *does NOT* have to be valid YAML for usage with Puppet. Hiera ----- To use eyaml with hiera and puppet, first configure hiera.yaml to use the eyaml backend. Eyaml works with [Hiera 3.x](https://docs.puppet.com/hiera/latest), as well as with [Hiera 5](https://docs.puppet.com/puppet/latest/hiera_intro.html) (Puppet 4.9.3 and later). ### With Hiera 5 In Hiera 5, each hierarchy level has one designated backend, as well as its own independent configuration for that backend. Hierarchy levels that use eyaml must set the following keys: * `name`. * `lookup_key` (must be set to `eyaml_lookup_key`). * `path`/`paths`/`glob`/`globs` (choose one). * `datadir` (can be omitted if you've set a default). * `options` — a hash of eyaml-specific settings; by default, this should include `pkcs7_private_key` and `pkcs7_public_key`, or `pkcs7_public_key_env_var` and `pkcs7_private_key_env_var`, but alternate encryption plugins use alternate options. Anything from the old `:eyaml` config section (except `datadir`) goes here. You do not need to specify key names as `:symbols`; normal strings are fine. ``` yaml --- version: 5 defaults: datadir: data hierarchy: - name: "Secret data: per-node, per-datacenter, common" lookup_key: eyaml_lookup_key # eyaml backend paths: - "secrets/nodes/%{trusted.certname}.eyaml" # Include explicit file extension - "secrets/location/%{facts.whereami}.eyaml" - "common.eyaml" options: pkcs7_private_key: /etc/puppetlabs/puppet/eyaml/private_key.pkcs7.pem pkcs7_public_key: /etc/puppetlabs/puppet/eyaml/public_key.pkcs7.pem - name: "Normal data" data_hash: yaml_data # Standard yaml backend paths: - "nodes/%{trusted.certname}.yaml" - "location/%{facts.whereami}/%{facts.group}.yaml" - "groups/%{facts.group}.yaml" - "os/%{facts.os.family}.yaml" - "common.yaml" ``` Unlike with Hiera 3, there's no default file extension for eyaml files, so you can specify your own file extension directly in the path name. For more details, see the [hiera.yaml (version 5) reference page](https://docs.puppet.com/puppet/latest/hiera_config_yaml_5.html). ### With Hiera 3 In Hiera 3, hierarchy levels don't have a backend assigned to them, and Hiera loops through the entire hierarchy for each backend. Options for the backend are set globally, in an `:eyaml` config section. ```yaml --- :backends: - eyaml - yaml :hierarchy: - %{environment} - common :yaml: :datadir: '/etc/puppet/hieradata' :eyaml: :datadir: '/etc/puppet/hieradata' # If using the pkcs7 encryptor (default) :pkcs7_private_key: /path/to/private_key.pkcs7.pem :pkcs7_public_key: /path/to/public_key.pkcs7.pem # Optionally cache decrypted data (default: false) :cache_decrypted: false ``` Then, edit your hiera yaml files, and insert your encrypted values. The default eyaml file extension is .eyaml, however this can be configured in the :eyaml block to set :extension, ```yaml :eyaml: :extension: 'yaml' ``` ### Data formatting note *Important Note:* The eyaml backend will not parse internally json formatted yaml files, whereas the regular yaml backend will. You'll need to ensure any existing yaml files using json format are converted to syntactically correct yaml format. ```yaml --- plain-property: You can see me cipher-property : > ENC[PKCS7,Y22exl+OvjDe+drmik2XEeD3VQtl1uZJXFFF2NnrMXDWx0csyqLB/2NOWefv NBTZfOlPvMlAesyr4bUY4I5XeVbVk38XKxeriH69EFAD4CahIZlC8lkE/uDh jJGQfh052eonkungHIcuGKY/5sEbbZl/qufjAtp/ufor15VBJtsXt17tXP4y l5ZP119Fwq8xiREGOL0lVvFYJz2hZc1ppPCNG5lwuLnTekXN/OazNYpf4CMd /HjZFXwcXRtTlzewJLc+/gox2IfByQRhsI/AgogRfYQKocZgFb/DOZoXR7wm IZGeunzwhqfmEtGiqpvJJQ5wVRdzJVpTnANBA5qxeA==] environments: development: host: localhost password: password production: host: prod.org.com password: > ENC[PKCS7,Y22exl+OvjDe+drmik2XEeD3VQtl1uZJXFFF2NnrMXDWx0csyqLB/2NOWefv NBTZfOlPvMlAesyr4bUY4I5XeVbVk38XKxeriH69EFAD4CahIZlC8lkE/uDh jJGQfh052eonkungHIcuGKY/5sEbbZl/qufjAtp/ufor15VBJtsXt17tXP4y l5ZP119Fwq8xiREGOL0lVvFYJz2hZc1ppPCNG5lwuLnTekXN/OazNYpf4CMd /HjZFXwcXRtTlzewJLc+/gox2IfByQRhsI/AgogRfYQKocZgFb/DOZoXR7wm IZGeunzwhqfmEtGiqpvJJQ5wVRdzJVpTnANBA5qxeA==] things: - thing 1 - - nested thing 1.0 - > ENC[PKCS7,Y22exl+OvjDe+drmik2XEeD3VQtl1uZJXFFF2NnrMXDWx0csyqLB/2NOWefv NBTZfOlPvMlAesyr4bUY4I5XeVbVk38XKxeriH69EFAD4CahIZlC8lkE/uDh jJGQfh052eonkungHIcuGKY/5sEbbZl/qufjAtp/ufor15VBJtsXt17tXP4y l5ZP119Fwq8xiREGOL0lVvFYJz2hZc1ppPCNG5lwuLnTekXN/OazNYpf4CMd /HjZFXwcXRtTlzewJLc+/gox2IfByQRhsI/AgogRfYQKocZgFb/DOZoXR7wm IZGeunzwhqfmEtGiqpvJJQ5wVRdzJVpTnANBA5qxeA==] - - nested thing 2.0 - nested thing 2.1 ``` Configuration file for eyaml ---------------------------- Default parameters for the eyaml command line tool can be provided by creating a configuration YAML file. Config files will be read in following order: * first from system-wide `/etc/eyaml/config.yaml` * then from user home directory `~/.eyaml/config.yaml` * then from current working directory `.eyaml/config.yaml` * finally by anything referenced in the `EYAML_CONFIG` environment variable The file takes any long form argument that you can provide on the command line. For example, to override the pkcs7 keys: ```yaml --- pkcs7_private_key: './keys/eyaml/private_key.pkcs7.pem' pkcs7_public_key: './keys/eyaml/public_key.pkcs7.pem' ``` Or to override to use GPG by default: ```yaml --- encrypt_method: 'gpg' gpg_gnupghome: './alternative_gnupghome' gpg_recipients: 'sihil@example.com,gtmtech@example.com,tpoulton@example.com' ``` Pluggable Encryption -------------------- hiera-eyaml backend is pluggable, so that further encryption types can be added as separate gems to the general mechanism which hiera-eyaml uses. Hiera-eyaml ships with one default mechanism of 'pkcs7', the encryption type widely used to sign smime email messages. Other encryption types (if the gems for them have been loaded) can be specified using the following formats: ENC[PKCS7,SOME_ENCRYPTED_VALUE] # a PKCS7 encrypted value ENC[GPG,SOME_ENCRYPTED_VALUE] # a GPG encrypted value (hiera-eyaml-gpg) ... etc ... When editing eyaml files, you will see that the unencrypted plaintext is marked in such a way as to identify the encryption method. This is so that the eyaml tool knows to encrypt it back using the correct method afterwards: some_key: DEC(1)::PKCS7[very secret password]! ### Encryption plugins This is a list of available plugins: - [hiera-eyaml-gpg](https://github.com/sihil/hiera-eyaml-gpg) - Provide GPG encryption - [hiera-eyaml-plaintext](https://github.com/gtmtechltd/hiera-eyaml-plaintext) - This is a no-op encryption plugin that simply base64 encodes the values. It exists as an example plugin to create your own and to do integration tests on hiera-eyaml. **THIS SHOULD NOT BE USED IN PRODUCTION** - [hiera-eyaml-twofac](https://github.com/gtmtechltd/hiera-eyaml-twofac) - PKCS7 keypair + AES256 symmetric password for two-factor encryption Note that this plugin mandates the user enter a password. It is useful for non-automated scenarios, and is not advised to be used in conjunction with puppet, as it requires entry of a password over a terminal. - [hiera-eyaml-kms](https://github.com/adenot/hiera-eyaml-kms) - Encryption using AWS Key Management Service (KMS) - [hiera-eyaml-gkms](https://github.com/craigwatson/hiera-eyaml-gkms) - Encryption using Google Cloud KMS - [hiera-eyaml-vault](https://github.com/crayfishx/hiera-eyaml-vault) - Use the transit secrets engine from Vault for providing encryption. ### How-To's: - [How to use different Hiera/Eyaml keys for different environments using the AWS Parameter Store to store the encryption keys for Hiera/Eyaml](https://gist.github.com/FransUrbo/88b26033cb513a8aa569bd5392a427b1). Notes ----- If you do not specify an encryption method within ENC[] tags, it will be assumed to be PKCS7 Also remember that after encrypting your sensitive properties, if anyone has access to your git source, they will see what the property was in previous commits before you encrypted. It's recommended that you roll any passwords when switching from unencrypted to encrypted properties. eg, Developers having write access to a DEV branch will be able to read/view the contents of the PRD branch, as per the design of GIT. Github has a great guide on removing sensitive data from repos here: https://help.github.com/articles/remove-sensitive-data Troubleshooting --------------- ### Installing from behind a corporate/application proxy $ export HTTP_PROXY=http://yourcorporateproxy:3128/ $ export HTTPS_PROXY=http://yourcorporateproxy:3128/ then run your install $ gem install hiera-eyaml Issues ------ If you have found a bug then please raise an issue here on github. Some of us hang out on #voxpupuli on [Libera.Chat](https://libera.chat/), please drop by if you want to say hi or have a question. Tests ----- **NOTE** Some testing requirements are not supported on Windows In order to run the tests, simply run `cucumber` in the top level directory of the project. You'll need to have a few requirements installed: * `expect` (via yum/apt-get or system package) * `aruba` (gem) * `cucumber` (gem) * `puppet` (gem) * `hiera-eyaml-plaintext` (gem) Authors ------- - [Tom Poulton](http://github.com/TomPoulton) - Initial author. eyaml backend. - [Geoff Meakin](http://github.com/gtmtech) - Major contributor. eyaml command, tests, CI - [Simon Hildrew](http://github.com/sihil) - Contributor. eyaml edit sub command. - [Robert Fielding](http://github.com/rooprob) - Contributor. eyaml recrypt sub command. hiera-eyaml-4.2.0/.rubocop.yml0000644000004100000410000000015614716447232016230 0ustar www-datawww-data--- inherit_from: .rubocop_todo.yml inherit_gem: voxpupuli-rubocop: rubocop.yml Metrics: Enabled: false hiera-eyaml-4.2.0/CHANGELOG.md0000644000004100000410000004647214716447232015602 0ustar www-datawww-data# Changelog All notable changes to this project will be documented in this file. ## [v4.2.0](https://github.com/voxpupuli/hiera-eyaml/tree/v4.2.0) (2024-10-21) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v4.1.0...v4.2.0) **Implemented enhancements:** - highline: Allow 3.x [\#385](https://github.com/voxpupuli/hiera-eyaml/pull/385) ([bastelfreak](https://github.com/bastelfreak)) **Merged pull requests:** - voxpupuli-rubocop: Fix version pinning, update 2.6-\>2.8 [\#384](https://github.com/voxpupuli/hiera-eyaml/pull/384) ([bastelfreak](https://github.com/bastelfreak)) ## [v4.1.0](https://github.com/voxpupuli/hiera-eyaml/tree/v4.1.0) (2024-05-13) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v4.0.0...v4.1.0) **Implemented enhancements:** - Remove public key requirement to decrypt [\#378](https://github.com/voxpupuli/hiera-eyaml/pull/378) ([cmd-ntrf](https://github.com/cmd-ntrf)) ## [v4.0.0](https://github.com/voxpupuli/hiera-eyaml/tree/v4.0.0) (2024-05-10) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.4.0...v4.0.0) **Breaking changes:** - Drop support for ruby 2.6 [\#366](https://github.com/voxpupuli/hiera-eyaml/pull/366) ([cmd-ntrf](https://github.com/cmd-ntrf)) - Drop EoL Puppet 6 testing [\#358](https://github.com/voxpupuli/hiera-eyaml/pull/358) ([bastelfreak](https://github.com/bastelfreak)) - Drop Ruby 2.5 support [\#351](https://github.com/voxpupuli/hiera-eyaml/pull/351) ([bastelfreak](https://github.com/bastelfreak)) **Implemented enhancements:** - Remove non-essential public certificate attributes [\#380](https://github.com/voxpupuli/hiera-eyaml/pull/380) ([cmd-ntrf](https://github.com/cmd-ntrf)) - Add support to encrypt with an RSA public key [\#379](https://github.com/voxpupuli/hiera-eyaml/pull/379) ([cmd-ntrf](https://github.com/cmd-ntrf)) - Add more jruby interpreter to CI [\#376](https://github.com/voxpupuli/hiera-eyaml/pull/376) ([bastelfreak](https://github.com/bastelfreak)) - CI: Allow newer aruba/cucumber versions [\#373](https://github.com/voxpupuli/hiera-eyaml/pull/373) ([bastelfreak](https://github.com/bastelfreak)) - rubocop: lint for Ruby 2.7 [\#370](https://github.com/voxpupuli/hiera-eyaml/pull/370) ([bastelfreak](https://github.com/bastelfreak)) - Load configuration file from working directory [\#356](https://github.com/voxpupuli/hiera-eyaml/pull/356) ([micmax93](https://github.com/micmax93)) - Update gems and introduce rubocop [\#353](https://github.com/voxpupuli/hiera-eyaml/pull/353) ([bastelfreak](https://github.com/bastelfreak)) **Fixed bugs:** - CI: fix coverage reporting [\#374](https://github.com/voxpupuli/hiera-eyaml/pull/374) ([bastelfreak](https://github.com/bastelfreak)) **Merged pull requests:** - rubocop: switch to voxpupuli-rubocop [\#372](https://github.com/voxpupuli/hiera-eyaml/pull/372) ([bastelfreak](https://github.com/bastelfreak)) - Drop Ruby 2.6 leftovers in CI config [\#369](https://github.com/voxpupuli/hiera-eyaml/pull/369) ([bastelfreak](https://github.com/bastelfreak)) - README.md: Refactor markdown, cleanup links [\#368](https://github.com/voxpupuli/hiera-eyaml/pull/368) ([bastelfreak](https://github.com/bastelfreak)) - fix: upgrade to aruba-2/cucumber-8 [\#360](https://github.com/voxpupuli/hiera-eyaml/pull/360) ([robbat2](https://github.com/robbat2)) - rubocop: autofix [\#357](https://github.com/voxpupuli/hiera-eyaml/pull/357) ([bastelfreak](https://github.com/bastelfreak)) - CI: Build gems with strictness and verbosity [\#348](https://github.com/voxpupuli/hiera-eyaml/pull/348) ([bastelfreak](https://github.com/bastelfreak)) ## [v3.4.0](https://github.com/voxpupuli/hiera-eyaml/tree/v3.4.0) (2023-05-26) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.3.0...v3.4.0) **Implemented enhancements:** - Add Puppet 8 to CI matrix [\#349](https://github.com/voxpupuli/hiera-eyaml/pull/349) ([bastelfreak](https://github.com/bastelfreak)) - Add Ruby 3.2 support [\#340](https://github.com/voxpupuli/hiera-eyaml/pull/340) ([pschrammel](https://github.com/pschrammel)) **Fixed bugs:** - Puppet 7: Ensure we test against 7.24 or newer [\#347](https://github.com/voxpupuli/hiera-eyaml/pull/347) ([bastelfreak](https://github.com/bastelfreak)) - Run puppet main branch on Ruby 3.1 and newer [\#346](https://github.com/voxpupuli/hiera-eyaml/pull/346) ([bastelfreak](https://github.com/bastelfreak)) **Closed issues:** - Encrypt yaml file on my workstation and push to git [\#344](https://github.com/voxpupuli/hiera-eyaml/issues/344) - hiera lookup arbitrary hierachy level [\#185](https://github.com/voxpupuli/hiera-eyaml/issues/185) **Merged pull requests:** - Apply Vox Pupuli CI best practices [\#345](https://github.com/voxpupuli/hiera-eyaml/pull/345) ([bastelfreak](https://github.com/bastelfreak)) - Add dependabot for gems and github actions [\#342](https://github.com/voxpupuli/hiera-eyaml/pull/342) ([bastelfreak](https://github.com/bastelfreak)) - CI: Ensure we use Puppet 6.29 or newer [\#341](https://github.com/voxpupuli/hiera-eyaml/pull/341) ([bastelfreak](https://github.com/bastelfreak)) - README: Add apt to installation methods [\#338](https://github.com/voxpupuli/hiera-eyaml/pull/338) ([AntoineSebert](https://github.com/AntoineSebert)) ## [v3.3.0](https://github.com/voxpupuli/hiera-eyaml/tree/v3.3.0) (2022-05-20) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.2.2...v3.3.0) **Implemented enhancements:** - Build gem during CI [\#330](https://github.com/voxpupuli/hiera-eyaml/pull/330) ([bastelfreak](https://github.com/bastelfreak)) - Add more Ruby/Puppet versions to CI matrix [\#326](https://github.com/voxpupuli/hiera-eyaml/pull/326) ([bastelfreak](https://github.com/bastelfreak)) **Fixed bugs:** - Repair ruby 3.1.x compability with backwards compability. Bumped vers… [\#329](https://github.com/voxpupuli/hiera-eyaml/pull/329) ([mmachner](https://github.com/mmachner)) - Fix an "undefined method" error with rubygems \>= 3.3.0 [\#327](https://github.com/voxpupuli/hiera-eyaml/pull/327) ([davidsansome](https://github.com/davidsansome)) ## [v3.2.2](https://github.com/voxpupuli/hiera-eyaml/tree/v3.2.2) (2021-05-03) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.2.1...v3.2.2) **Fixed bugs:** - Using `3.2.1` for editing an eyaml created with `3.2.0` will mess up formatting [\#318](https://github.com/voxpupuli/hiera-eyaml/issues/318) - Fix block formatting when editing [\#319](https://github.com/voxpupuli/hiera-eyaml/pull/319) ([kenyon](https://github.com/kenyon)) **Closed issues:** - Concerns about the encrypted? method [\#316](https://github.com/voxpupuli/hiera-eyaml/issues/316) ## [v3.2.1](https://github.com/voxpupuli/hiera-eyaml/tree/v3.2.1) (2021-02-16) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.2.0...v3.2.1) **Fixed bugs:** - remove question mark from regex in encrypted? method [\#313](https://github.com/voxpupuli/hiera-eyaml/pull/313) ([mcka1n](https://github.com/mcka1n)) - Fix block folding [\#307](https://github.com/voxpupuli/hiera-eyaml/pull/307) ([kenyon](https://github.com/kenyon)) - add step-by-step how-to encrypting multiline values [\#304](https://github.com/voxpupuli/hiera-eyaml/pull/304) ([kBite](https://github.com/kBite)) **Closed issues:** - eyaml edit should produce evenly folded blocks. [\#281](https://github.com/voxpupuli/hiera-eyaml/issues/281) - Support version 4 hiera config [\#213](https://github.com/voxpupuli/hiera-eyaml/issues/213) **Merged pull requests:** - migrate CI to github actions [\#315](https://github.com/voxpupuli/hiera-eyaml/pull/315) ([bastelfreak](https://github.com/bastelfreak)) - gemspec: fix repo url / Drop Puppet 4/5 tests [\#311](https://github.com/voxpupuli/hiera-eyaml/pull/311) ([bastelfreak](https://github.com/bastelfreak)) - Unpin highline [\#310](https://github.com/voxpupuli/hiera-eyaml/pull/310) ([lucywyman](https://github.com/lucywyman)) ## [v3.2.0](https://github.com/voxpupuli/hiera-eyaml/tree/v3.2.0) (2020-01-31) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.1.1...v3.2.0) **Implemented enhancements:** - Permit reading private key from environment variable [\#294](https://github.com/voxpupuli/hiera-eyaml/pull/294) ([nferch](https://github.com/nferch)) **Fixed bugs:** - Version 3.1.0 does not clear the private/public key when options are changed [\#289](https://github.com/voxpupuli/hiera-eyaml/issues/289) **Merged pull requests:** - \(doc\) Correct order for config file precedence [\#295](https://github.com/voxpupuli/hiera-eyaml/pull/295) ([crayfishx](https://github.com/crayfishx)) - \(maint\) Update Gemfile and README for Ruby 2.5/2.4 [\#293](https://github.com/voxpupuli/hiera-eyaml/pull/293) ([glennsarti](https://github.com/glennsarti)) ## [v3.1.1](https://github.com/voxpupuli/hiera-eyaml/tree/v3.1.1) (2019-11-12) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.1.0...v3.1.1) **Merged pull requests:** - Revert "Cache key strings." [\#290](https://github.com/voxpupuli/hiera-eyaml/pull/290) ([alexjfisher](https://github.com/alexjfisher)) ## [v3.1.0](https://github.com/voxpupuli/hiera-eyaml/tree/v3.1.0) (2019-11-11) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v3.0.0...v3.1.0) **Implemented enhancements:** - Should be able to `edit` a new file [\#84](https://github.com/voxpupuli/hiera-eyaml/issues/84) - Cache key strings. [\#191](https://github.com/voxpupuli/hiera-eyaml/pull/191) ([mkulke](https://github.com/mkulke)) **Closed issues:** - Decryption errors should return error code. [\#282](https://github.com/voxpupuli/hiera-eyaml/issues/282) - Release a new version [\#271](https://github.com/voxpupuli/hiera-eyaml/issues/271) **Merged pull requests:** - \(docs\) Update README with reference to hiera-eyaml-vault [\#287](https://github.com/voxpupuli/hiera-eyaml/pull/287) ([crayfishx](https://github.com/crayfishx)) - fix: don't handle cli exceptions early [\#283](https://github.com/voxpupuli/hiera-eyaml/pull/283) ([stuart-warren](https://github.com/stuart-warren)) - Adding doc for Google KMS plugin [\#279](https://github.com/voxpupuli/hiera-eyaml/pull/279) ([craigwatson](https://github.com/craigwatson)) - catch failed decryption and print a helpful message [\#144](https://github.com/voxpupuli/hiera-eyaml/pull/144) ([GeoffWilliams](https://github.com/GeoffWilliams)) ## [v3.0.0](https://github.com/voxpupuli/hiera-eyaml/tree/v3.0.0) (2019-01-17) [Full Changelog](https://github.com/voxpupuli/hiera-eyaml/compare/v2.1.0...v3.0.0) This is the first release after this project was migrated to Vox Pupuli. **Breaking changes:** - Upgrading trollop to optimist to remove deprecation warnings [\#268](https://github.com/voxpupuli/hiera-eyaml/pull/268) ([chadlyon](https://github.com/chadlyon)) **Implemented enhancements:** - Don't use SHA1 for the digest [\#257](https://github.com/voxpupuli/hiera-eyaml/issues/257) - Update to make use of Backend.datasourcefiles\(\) [\#92](https://github.com/voxpupuli/hiera-eyaml/issues/92) - allow setting an individual keysize [\#227](https://github.com/voxpupuli/hiera-eyaml/pull/227) ([tuxmea](https://github.com/tuxmea)) **Fixed bugs:** - on OSX, eyaml isn't expanding `~` into /Users/$USER [\#170](https://github.com/voxpupuli/hiera-eyaml/issues/170) - Performance bug: unnecessary double-decryption of blocks [\#182](https://github.com/voxpupuli/hiera-eyaml/pull/182) ([peculater](https://github.com/peculater)) **Closed issues:** - PuppetDB gets base64 encoded string on exported ressources [\#273](https://github.com/voxpupuli/hiera-eyaml/issues/273) - DEPRECATION - trollop gem is deprecated, need to switch to optimist [\#267](https://github.com/voxpupuli/hiera-eyaml/issues/267) - Puppet can't find key on server [\#266](https://github.com/voxpupuli/hiera-eyaml/issues/266) - Re-encryption is broken [\#258](https://github.com/voxpupuli/hiera-eyaml/issues/258) - AWS KMS/IAM integration? [\#234](https://github.com/voxpupuli/hiera-eyaml/issues/234) - Feature Request: Ability to use edit without the private key [\#231](https://github.com/voxpupuli/hiera-eyaml/issues/231) - Not decrypting/working with puppetserver 2.7.2 \(Function lookup\(\) did not find a value for the name\) [\#228](https://github.com/voxpupuli/hiera-eyaml/issues/228) - Allow stronger than 2048 bit keys [\#226](https://github.com/voxpupuli/hiera-eyaml/issues/226) - failed: DataBinding 'hiera': No such file or directory - /var/lib/puppet/keys/private\_key.pkcs7.pem [\#225](https://github.com/voxpupuli/hiera-eyaml/issues/225) - Migrate to Vox Pupuli [\#224](https://github.com/voxpupuli/hiera-eyaml/issues/224) - Allow to `decrypt` while keeping the "DEC::..." [\#217](https://github.com/voxpupuli/hiera-eyaml/issues/217) - secret in the logs [\#216](https://github.com/voxpupuli/hiera-eyaml/issues/216) - eyaml produces base64 string for complex data [\#209](https://github.com/voxpupuli/hiera-eyaml/issues/209) - Hiera-eyaml cannot decrypt with key, plain gpg works [\#206](https://github.com/voxpupuli/hiera-eyaml/issues/206) - Unable to decrypt on remote nodes [\#202](https://github.com/voxpupuli/hiera-eyaml/issues/202) - Backend not found in tests [\#200](https://github.com/voxpupuli/hiera-eyaml/issues/200) - ArgumentError [\#193](https://github.com/voxpupuli/hiera-eyaml/issues/193) - High CPU consumption [\#192](https://github.com/voxpupuli/hiera-eyaml/issues/192) - hiera call from manifeast not able to locate key [\#174](https://github.com/voxpupuli/hiera-eyaml/issues/174) - PE 3.8 - sporadically failing to load eyaml backend. [\#173](https://github.com/voxpupuli/hiera-eyaml/issues/173) - eyaml and templates [\#171](https://github.com/voxpupuli/hiera-eyaml/issues/171) - cucumber failures with puppet 3.7.5 [\#154](https://github.com/voxpupuli/hiera-eyaml/issues/154) - issue with jruby under PE 3.7 [\#150](https://github.com/voxpupuli/hiera-eyaml/issues/150) - hiera eyaml does not work on PE 3.7.2 [\#126](https://github.com/voxpupuli/hiera-eyaml/issues/126) - invalid byte sequence in UTF-8 on encrypted binary [\#124](https://github.com/voxpupuli/hiera-eyaml/issues/124) - having an issue when loding hiera-eyaml [\#117](https://github.com/voxpupuli/hiera-eyaml/issues/117) - Puppet hiera\(\): Cannot load backend eyaml: no such file to load [\#115](https://github.com/voxpupuli/hiera-eyaml/issues/115) - Public/private keys undefined for Vagrant [\#101](https://github.com/voxpupuli/hiera-eyaml/issues/101) - bug in hiera 1.3.2-1 vs rubygem-hiera 1.3.2-1 [\#85](https://github.com/voxpupuli/hiera-eyaml/issues/85) - Errors of yaml and no eyaml files exist. Fine if just eyaml files exist. [\#82](https://github.com/voxpupuli/hiera-eyaml/issues/82) **Merged pull requests:** - Use UTF-8 as the encoding for plain text data [\#274](https://github.com/voxpupuli/hiera-eyaml/pull/274) ([jarretlavallee](https://github.com/jarretlavallee)) - Fix regem.sh shebang, it does not need bash [\#265](https://github.com/voxpupuli/hiera-eyaml/pull/265) ([AMDmi3](https://github.com/AMDmi3)) - Allow selection of digest, default to SHA256 [\#261](https://github.com/voxpupuli/hiera-eyaml/pull/261) ([juniorsysadmin](https://github.com/juniorsysadmin)) - expand README on whole-file encryption usage [\#260](https://github.com/voxpupuli/hiera-eyaml/pull/260) ([jflorian](https://github.com/jflorian)) - Add encrypt-only flag for 'edit' command. [\#256](https://github.com/voxpupuli/hiera-eyaml/pull/256) ([benjunmun](https://github.com/benjunmun)) - Test only with current Puppet and Ruby combination [\#254](https://github.com/voxpupuli/hiera-eyaml/pull/254) ([vinzent](https://github.com/vinzent)) - Update \#{self.prefix} to match yamllint rules [\#248](https://github.com/voxpupuli/hiera-eyaml/pull/248) ([jordanconway](https://github.com/jordanconway)) - Fix badge, link to AWS KMS/IAM integration [\#245](https://github.com/voxpupuli/hiera-eyaml/pull/245) ([rnelson0](https://github.com/rnelson0)) - Remove tildes that don't expand from configuration examples [\#242](https://github.com/voxpupuli/hiera-eyaml/pull/242) ([rnelson0](https://github.com/rnelson0)) - Disable deprecation warnings [\#241](https://github.com/voxpupuli/hiera-eyaml/pull/241) ([rnelson0](https://github.com/rnelson0)) - Add a cache for decrypted values [\#240](https://github.com/voxpupuli/hiera-eyaml/pull/240) ([stlava](https://github.com/stlava)) - Suppressing logging of configuration files on init [\#237](https://github.com/voxpupuli/hiera-eyaml/pull/237) ([sigv](https://github.com/sigv)) - Update the keys' example directory [\#236](https://github.com/voxpupuli/hiera-eyaml/pull/236) ([sigv](https://github.com/sigv)) - Modify edit command to not recrypt unchanged values [\#233](https://github.com/voxpupuli/hiera-eyaml/pull/233) ([ccojocar](https://github.com/ccojocar)) - Modify recrypt command to allow recrypting file with different encryp… [\#232](https://github.com/voxpupuli/hiera-eyaml/pull/232) ([ccojocar](https://github.com/ccojocar)) - \(docs\) Update README with instructions for using Hiera 5 [\#229](https://github.com/voxpupuli/hiera-eyaml/pull/229) ([nfagerlund](https://github.com/nfagerlund)) - Attempt to resolve Travis CI issues [\#220](https://github.com/voxpupuli/hiera-eyaml/pull/220) ([rnelson0](https://github.com/rnelson0)) - Make it clear that the ID and parens must be deleted, not just the ID [\#188](https://github.com/voxpupuli/hiera-eyaml/pull/188) ([sdotz](https://github.com/sdotz)) - Refactor highline import [\#187](https://github.com/voxpupuli/hiera-eyaml/pull/187) ([petems](https://github.com/petems)) - Adding hiera-eyaml-kms plugin to readme file [\#184](https://github.com/voxpupuli/hiera-eyaml/pull/184) ([adenot](https://github.com/adenot)) - Make output of `eyaml decrypt` valid yaml with multiline values. [\#183](https://github.com/voxpupuli/hiera-eyaml/pull/183) ([peculater](https://github.com/peculater)) - Add testing support for puppet 4 [\#181](https://github.com/voxpupuli/hiera-eyaml/pull/181) ([peculater](https://github.com/peculater)) ## v2.1.0 (2016-03-02) - (#187) - Change the way third party highline library is imported to avoid memory leak when running under puppet server (@petems) - (#181) - Improve test suite to run against a variety of puppet versions (@peculater) ## v2.0.8 (2015-04-15) - (#149) - Fix to tempfile permissions and invalid editor scenario (@elyscape) ## v2.0.7 (2015-03-04) - (#142) - Fixed highline dependency to exclude newer versions that are not compatible with ruby 1.8.7 (@elyscape) - (#136) - \t and \r characters are now supported in encrypted blocks (@elyscape) - (#138) - Added missing tags and new tagging tool (@elyscape) ## v2.0.6 (2014-12-13) - (#131) - Fix another EDITOR bug (#130) that could erase command line flags to the specified editor (@elyscape) ## v2.0.5 (2014-12-11) - (#128) - Fix a bug (#127) that caused `eyaml edit` to break when `$EDITOR` was a command on PATH rather than a path to a command (@elyscape) ## v2.0.4 (2014-11-24) - Add change log - (#118) - Some initial support for spaces in filenames (primarily targeted at windows platforms) (@elyscape) - (#114) - Add new config file resolution so that a system wide /etc/eyaml/config.yaml is processed first (@gtmtech) - (#112) - Improve debugging options and colorise output (@gtmtech) - (#102) - Extension of temp files should be yaml to help editors provide syntax highlighting (@ColinHebert) - (#90), #121, #122 - Add preamble in edit mode to make it easier to remember how to edit (@sihil) - (#96), #111, #116 - Various updates to docs \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*