class Hiera::Backend::Eyaml::Encryptors::Gpg

Constants

VERSION

Public Class Methods

create_keys() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 158
def self.create_keys
  STDERR.puts 'The GPG encryptor does not support creation of keys, use the GPG command lines tools instead'
end
decrypt(ciphertext) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 119
def self.decrypt(ciphertext)
  gnupghome = self.gnupghome

  unless defined?(GPGME)
    gpg = Hiera::Backend::Eyaml::GpgPuppetserver
    gpg.config.homedir = gnupghome if gnupghome
    return gpg.decrypt_string(ciphertext)
  end

  GPGME::Engine.home_dir = gnupghome

  ctx = if hiera?
          GPGME::Ctx.new
        else
          GPGME::Ctx.new(passphrase_callback: method(:passfunc))
        end

  if !ctx.keys.empty?
    raw = GPGME::Data.new(ciphertext)
    txt = GPGME::Data.new

    begin
      txt = ctx.decrypt(raw)
    rescue GPGME::Error::DecryptFailed => e
      warn('Fatal: Failed to decrypt ciphertext (check settings and that you are a recipient)')
      raise e
    rescue StandardError => e
      warn('Warning: General exception decrypting GPG file')
      raise e
    end

    txt.seek 0
    txt.read
  else
    warn("No usable keys found in #{gnupghome}. Check :gpg_gnupghome value in hiera.yaml is correct")
    raise ArgumentError, "No usable keys found in #{gnupghome}. Check :gpg_gnupghome value in hiera.yaml is correct"
  end
end
encrypt(plaintext) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 77
def self.encrypt(plaintext)
  unless defined?(GPGME)
    raise RecoverableError, "Encryption is only supported when using the 'gpgme' gem"
  end

  GPGME::Engine.home_dir = gnupghome

  ctx = GPGME::Ctx.new

  recipients = find_recipients
  debug("Recipents are #{recipients}")

  raise RecoverableError, 'No recipients provided, don\'t know who to encrypt to' if recipients.empty?

  keys = recipients.map do |r|
    key_to_use = ctx.keys(r).first
    if key_to_use.nil?
      raise RecoverableError, "No key found on keyring for #{r}"
    end
    key_to_use
  end
  debug("Keys: #{keys}")

  always_trust = option(:always_trust)
  unless always_trust
    # check validity of recipients (this is possibly naive, but better than the unhelpful
    # error that it would spit out otherwise)
    keys.each do |key|
      unless key.primary_uid.validity >= GPGME::VALIDITY_FULL
        raise RecoverableError, "Key #{key.sha} (#{key.email}) not trusted (if key trust is established by another means then specify always-trust)"
      end
    end
  end

  data = GPGME::Data.from_str(plaintext)
  crypto = GPGME::Crypto.new(always_trust: always_trust)

  ciphertext = crypto.encrypt(data, recipients: keys)
  ciphertext.seek 0
  ciphertext.read
end
gnupghome() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 59
def self.gnupghome
  gnupghome = if ENV['HIERA_EYAML_GPG_GNUPGHOME'].nil?
                option :gnupghome
              else
                ENV['HIERA_EYAML_GPG_GNUPGHOME']
              end
  debug("GNUPGHOME is #{gnupghome}")
  if gnupghome.nil? || gnupghome.empty?
    warn('No GPG home directory configured, check gpg_gnupghome configuration value is correct')
    raise ArgumentError, 'No GPG home directory configured, check gpg_gnupghome configuration value is correct'
  elsif !File.directory?(gnupghome)
    warn("Configured GPG home directory #{gnupghome} doesn't exist, check gpg_gnupghome configuration value is correct")
    raise ArgumentError, "Configured GPG home directory #{gnupghome} doesn't exist, check gpg_gnupghome configuration value is correct"
  else
    gnupghome
  end
end
passfunc(_hook, uid_hint, _passphrase_info, _prev_was_bad, fd) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 42
def self.passfunc(_hook, uid_hint, _passphrase_info, _prev_was_bad, fd)
  system('stty -echo')

  unless @passphrase_cache.key?(uid_hint)
    @passphrase_cache[uid_hint] = ask("Enter passphrase for #{uid_hint}: ") { |q| q.echo = '' }
    $stderr.puts
  end
  passphrase = @passphrase_cache[uid_hint]

  io = IO.for_fd(fd, 'w')
  io.puts(passphrase)
  io.flush
ensure
  (0...$LAST_READ_LINE.length).each { |i| $LAST_READ_LINE[i] = '0' } if $LAST_READ_LINE
  system('stty echo')
end

Private Class Methods

find_recipient_file() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 189
def find_recipient_file
  # if we are editing a file, look for a hiera-eyaml-gpg.recipients file
  filename = case Eyaml::Options[:source]
             when :file
               Eyaml::Options[:file]
             when :eyaml
               Eyaml::Options[:eyaml]
             end

  return if filename.nil?

  root = Pathname.new(filename).realpath.dirname
  selected_file = nil
  root.descend do |path|
    potential_file = path.join('hiera-eyaml-gpg.recipients')
    selected_file = potential_file if potential_file.exist?
  end
  debug("Using file at #{selected_file}")
  selected_file
end
find_recipients() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/gpg.rb, line 165
def find_recipients
  recipient_option = option :recipients

  unless recipient_option.nil?
    debug('Using --recipients option')
    return recipient_option.split(',')
  end

  recipient_file_option = option :recipients_file
  recipient_file = if recipient_file_option.nil?
                     debug('Searching for any hiera-eyaml-gpg.recipients files in path')
                     find_recipient_file
                   else
                     debug('Using --recipients-file option')
                     Pathname.new(recipient_file_option)
                   end

  return [] if recipient_file.nil?

  recipient_file.readlines.map do |line|
    line.strip unless line.start_with?('#') || line.strip.empty?
  end.compact
end