salt.renderers.gpg

Renderer that will decrypt GPG ciphers

Any key in the SLS file can be a GPG cipher, and this renderer will decrypt it before passing it off to Salt. This allows you to safely store secrets in source control, in such a way that only your Salt master can decrypt them and distribute them only to the minions that need them.

The typical use-case would be to use ciphers in your pillar data, and keep a secret key on your master. You can put the public key in source control so that developers can add new secrets quickly and easily.

This renderer requires the python-gnupg package. Be careful to install the python-gnupg package, not the gnupg package, or you will get errors.

To set things up, you will first need to generate a keypair. On your master, run:

# gpg --gen-key --homedir /etc/salt/gpgkeys

Do not supply a password for your keypair, and use a name that makes sense for your application. Be sure to back up your gpg directory someplace safe!

Note

Unfortunately, there are some scenarios - for example, on virtual machines which don’t have real hardware - where insufficient entropy causes key generation to be extremely slow. If you come across this problem, you should investigate means of increasing the system entropy. On virtualised Linux systems, this can often be achieved by installing the rng-tools package.

To retrieve the public key:

# gpg --armor --homedir /etc/salt/gpgkeys --armor --export <KEY-NAME>           > exported_pubkey.gpg

Now, to encrypt secrets, copy the public key to your local machine and run:

$ gpg --import exported_pubkey.gpg

To generate a cipher from a secret:

$ echo -n "supersecret" | gpg --armor --encrypt -r <KEY-name>

Set up the renderer on your master by adding something like this line to your config:

renderer: jinja | yaml | gpg

Now you can include your ciphers in your pillar data like so:

a-secret: |
  -----BEGIN PGP MESSAGE-----
  Version: GnuPG v1

  hQEMAweRHKaPCfNeAQf9GLTN16hCfXAbPwU6BbBK0unOc7i9/etGuVc5CyU9Q6um
  QuetdvQVLFO/HkrC4lgeNQdM6D9E8PKonMlgJPyUvC8ggxhj0/IPFEKmrsnv2k6+
  cnEfmVexS7o/U1VOVjoyUeliMCJlAz/30RXaME49Cpi6No2+vKD8a4q4nZN1UZcG
  RhkhC0S22zNxOXQ38TBkmtJcqxnqT6YWKTUsjVubW3bVC+u2HGqJHu79wmwuN8tz
  m4wBkfCAd8Eyo2jEnWQcM4TcXiF01XPL4z4g1/9AAxh+Q4d8RIRP4fbw7ct4nCJv
  Gr9v2DTF7HNigIMl4ivMIn9fp+EZurJNiQskLgNbktJGAeEKYkqX5iCuB1b693hJ
  FKlwHiJt5yA8X2dDtfk8/Ph1Jx2TwGS+lGjlZaNqp3R1xuAZzXzZMLyZDe5+i3RJ
  skqmFTbOiA==
  =Eqsm
  -----END PGP MESSAGE-----
salt.renderers.gpg.decrypt_ciphertext(c, gpg)

Given a block of ciphertext as a string, and a gpg object, try to decrypt the cipher and return the decrypted string. If the cipher cannot be decrypted, log the error, and return the ciphertext back out.

salt.renderers.gpg.decrypt_object(o, gpg)

Recursively try to decrypt any object. If the object is a string, and it contains a valid GPG header, decrypt it, otherwise keep going until a string is found.

salt.renderers.gpg.render(gpg_data, saltenv='base', sls='', argline='', **kwargs)

Create a gpg object given a gpg_keydir, and then use it to try to decrypt the data to be rendered.