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!

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 --homedir ~/.gnupg --armor --encrypt -r <KEY-name>

There are two ways to configure salt for the usage of this renderer:

  1. Set up the renderer on your master by adding something like this line to your

    config:

    renderer: jinja | yaml | gpg
    

    This will apply the renderers to all pillars and states while requiring python-gnupg to be installed on all minions since the decryption will happen on the minions.

  2. To apply the renderer on a file-by-file basis add the following line to the top of any pillar with gpg data in it:

    #!yaml|gpg
    

Now with your renderers configured, 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(cypher, gpg, safe=False)

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.

Parameters:safe -- Raise an exception on failure instead of returning the ciphertext
salt.renderers.gpg.decrypt_object(obj, 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.