Secret variables in Puppet with Hiera and GPG
Last week I wrote an article on Puppet configuration variables and Hiera. This almost sorted out all my configuration variable requirements, bar one; what do I do with sensitive data like database passwords, hashed user passwords…etc that I don’t want to store in my VCS repo as plaintext.
Hiera allows you to quite easily add new backends, so I came up with hiera-gpg, a backend plugin for Hiera that will GPG decrypt a YAML file on the fly. It’s quite minimal and there is some stuff I’d like to do better – for instance it currently shells out to the GPG command, hopefully someone has some code they can contribute that’ll use the GPGME gem instead to do the encryption bit.
Once you’re up and running with Hiera, you can get the hiera-gpg backend from Rubygems…
# gem install hiera-gpg
We run several Puppetmasters, so for each one I create a GPG key and add the public key to a public keyring that’s kept in my VCS repo. For security reasons I maintain a dev and a live keyring so only live Puppetmasters can see live data.
# gpg --gen-key
Currently hiera-gpg doesn’t support key passwords, I’ll probably add this feature in soon but 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.
So I have my GPG secret key set up in roots homedir:
# gpg --list-keys /root/.gnupg/pubring.gpg ------------------------ pub 1024D/1F77BE20 2011-10-06 uid puppet.live.mycorp.com (Live Puppet) <root@puppet.live.mycorp.com> sub 2048g/CB8EE07E 2011-10-06
Next I add my GPG public key to the keyring for live puppetmasters (in my set up, /etc/puppet/keyrings/live is a subversion checkout)
# gpg --homedir=/etc/puppet/keyrings/live --import ~/.gnupg/pubkey.txt gpg: key 1F77BE20: public key "puppet.live.mycorp.com (Live Puppet) <root@puppet.live.mycorp.com>" imported gpg: Total number processed: 1 gpg: imported: 1
Now I can create a YAML file in my hieradata folder and encrypt it for the servers in my live keyring.
# cd /etc/puppet/hieradata/live # cat mysql.yaml --- rootpwd: jhns732ns # gpg --trust-model=always --homedir=/etc/puppet/keyrings/live --encrypt -o mysql.gpg -r puppet.live.mycorp.com mysql.yaml # rm mysql.yaml # ls mysql.yaml.gpg
If like me you have more than one puppetmaster in your live keyrings, multiple -r entries can be specified on the command line for gpg, you should encrypt your file for all the puppet master keys that are allowed to decrypt it.
Now you just need to tell Hiera about the GPG backend, My previous Hiera configuration now becomes:
# cat /etc/puppet/hiera.yaml --- :backends: - yaml - gpg :logger: console :hierarchy: - %{env}/%{location}/%{calling_module} - %{env}/%{calling_module} - common :yaml: :datadir: /etc/puppet/hieradata :gpg: :datadir: /etc/puppet/hieradata
Here we’re telling Hiera to behave exactly as it used to when we just had the YAML back end, and if it doesn’t find the value you are requesting from YAML it will query the GPG back end which will pick up on your %{calling_module}.gpg.
Now I can query Hiera on the command line to find my live MySQL root password with:
# hiera -c /etc/puppet/hiera.yaml rootpwd calling_module=mysql env=live jhns732ns
In Puppet, I reference my variables in exactly the same way as any other variable
class mysql ($rootpwd=hiera("rootpwd")) {
Theres probably lots of stuff I can improve here, but I have the basics of what I need, a transparent method of data storage using GPG encryption and no sensitive data stored in my VCS repo as plain text.