Commit a351db4a authored by Tom Poulton's avatar Tom Poulton

Inital commit

parent ed535fe2
Hiera eYaml
A backend for Hiera that provides per-value asymmetric encryption of sensitive data
An alternative to [hiera-gpg]( for encrypting sensitive
data within yaml type files to be used by Puppet.
The main reasons to create an alternative backend for hiera are summed up in
[this blog](
which I stumbled on whilst looking for options, but the main one is the ability to
encrypt each value individually and not the whole file. This provides a bit more transparency
and allows those configuring Puppet to know where each value is defined.
I also ran into problems using hiera-gpg (actually not hiera-gpg's fault
but another project it uses internally [ruby-gpgme](
and the whole thing seemed a bit brittle.
The Hiera eYaml uses yaml formatted files with .eyaml extension. Simply wrap your
encrypted string with ENC[ ] and place it in an eyaml file. You can mix your plain values
in as well or separate them into different files.
plain-property: You can see me
encrypted-property : >
eYaml also supports encrypted values within arrays, hashes, nested arrays and nested hashes
N.B. when using the multi-line string syntax (i.e. >) **don't wrap encrypted strings with "" or ''**
## Generate keys
The first step is to create a pair of keys on the Puppet master
$ sudo mkdir -p /etc/hiera/keys
$ sudo openssl genrsa -out /etc/hiera/keys/private_key.pem 2048
$ sudo openssl rsa -in /etc/hiera/keys/private_key.pem -pubout -out /etc/hiera/keys/public_key.pem
This creates a public and private key with default names in the default location.
eYaml doesn't support keys with a passphrase yet, but as Craig Dunn explains in his
[post about hiera-gpg](
"it would mean having the password stored in /etc/puppet/hiera.yaml as plaintext anyway,
so I don’t see that as adding much in the way of security."
Change the permissions so that the private key is only readable by the user that hiera (puppet) is
running as.
## Install eYaml backend
I'm new to ruby and tight on deadlines so I will create a gem when I get a chance, but for now just
copy eyaml_backend.rb to the same directory as the existing backends e.g.
You can find the directory by running
$ sudo find / -name yaml_backend.rb
## Configure Hiera
Next configure hiera.yaml to use the eyaml backend
- yaml
- eyaml
- %{environment}
- common
:datadir: '/etc/puppet/hieradata'
:datadir: '/etc/puppet/hieradata'
# Optional. Default is /etc/hiera/keys/private_key.pem
:private_key: /etc/hiera/gpg
## Encrypt value
Copy public_key.pem created earlier to any machine where values will be encrypted and
use openssl to encrypt sensitive strings.
There is a very basic helper file encrypt_value.rb which will do this for you. Just copy the
public key to the same directory as encrypt_value.rb (or vice versa), navigate to that
directory and run
$ ruby encrypt_value.rb "my secret thing"
The encrypted value is printed to the command line
If you wish to rename your key or keep it in another directory run
$ ruby encrypt_value.rb "my secret thing" /path/to/key/my_key.pem
## Insert encrypted value
As above, once the value is encrypted, wrap it in ENC[ ] and place it in the .eyaml file.
require 'openssl'
require 'base64'
# Run from this directory using: ruby encrypt_value.rb "value to encrypt"
public_key_path = './public_key.pem'
plain_text = ARGV[0]
public_key_arg = ARGV[1]
if public_key_arg
public_key_path = public_key_arg
puts "using #{public_key_path} to encrypt value"
public_key = public_key_path ))
cipher_binary = public_key.public_encrypt( plain_text )
cipher_text = Base64.encode64( cipher_binary )
puts cipher_text
class Hiera
module Backend
class Eyaml_backend
def initialize
require 'openssl'
require 'base64'
def lookup(key, scope, order_override, resolution_type)
debug("Lookup called for key #{key}")
answer = nil
Backend.datasources(scope, order_override) do |source|
eyamlFile = Backend.datafile(:eyaml, scope, source, "eyaml") || next
debug("Processing datasource: #{eyamlFile}")
eyaml_text = eyamlFile )
data = YAML.load( eyaml_text )
next if !data
next if data.empty?
debug ("Data contains valid YAML")
next unless data.include?(key)
debug ("Key #{key} found in YAML document")
parsed_answer = Backend.parse_answer(data[key], scope)
answer = decrypt(parsed_answer, scope)
return answer
def decrypt(value, scope)
if is_encrypted(value)
private_key_path = Backend.parse_string(Config[:eyaml][:private_key], scope) || '/etc/hiera/keys/private_key.pem'
debug("Using #{private_key_path} to decrypt value")
# remove enclosing 'ENC( )'
cipher_text = value[4..-2]
private_key = private_key_path ))
plain_text = private_key.private_decrypt( Base64.decode64(cipher_text) )
return plain_text
return value
def is_encrypted(value)
if value.start_with?('ENC(')
return true
return false
def debug(msg)
Hiera.debug("[eyaml_backend]: #{msg}")
def warn(msg)
Hiera.warn("[eyaml_backend]: #{msg}")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment