SaltStack, like any good configuration management system, provides a way to distribute sensitive data (like passwords or API keys) to minion nodes in a secure manner. This is supported by the Pillar system, where this data can be added to a tree-like data structure and are then made available to the relevant minions. While this "distribution" of data is secure, "adding" pillar entries still works (at least by default) by editing YAML-formatted plain text files. This means places like the salt master node and the source code repository containing the state/pillar tree are a weak spot, since there's no at-rest encryption for the included pillars.
One solution is to encrypt the pillars using GPG. This requires GnuPG, and works using a public/private keypair where the secrets are encrypted using the public key, and the salt master decrypts them using the private key.
GPG Renderer
Salt supports using renderers which define how data from a given file should
be extracted. By default this is set to yaml
, but renderers can also be
combined using pipes (|
). So something like yaml|gpg
means pass the input
first to the YAML renderer, take the output, and then feed it to the GPG
renderer.
The GPG renderer decrypts GPG ciphers. It works by first generating a public/private keypair, and then using the public key to encrypt secrets. These encrypted values are then added to the pillar tree instead of the plain text secrets. The salt master node can then decrypt these values using the private key. This ensures that the pillar data is also encrypted at rest.
Generating keys
The first step is to generate the keypair that will be used for encrypting/decrypting secrets. The following commands (and the rest of the commands in this article) should be run on the salt master node.
$ sudo mkdir /etc/salt/gpgkeys
$ sudo chmod 0700 /etc/salt/gpgkeys
$ sudo gpg --gen-key --homedir /etc/salt/gpgkeys
Now that the keypair is generated, we can export the public key that will be used to encrypt pillars.
$ sudo gpg --homedir /etc/salt/gpgkeys --export --armor > /path/to/public.key
Placing this public key alongside the state/pillar tree in version control is a good idea, so that other developers on the team can add secrets too.
Adding encrypted pillars
To encrypt stuff, the public key needs to first be imported into the development environment.
$ gpg --import /path/to/public.key
The gpg
command can now be used to encrypt values.
$ echo -n "hunter2" | gpg --armor --batch --trust-model always --encrypt -r key-name
This prints out a long and cryptic looking string on the terminal, which is the encrypted secret that should be added to the pillar tree.
#!yaml|gpg
app:
key: |
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
hQEMA0JzlBGmNEs6AQgAtuhy4tfowtXBXZTEQF1ZFZSvif7pvStK1SfdynbUmp7A
DLX9Xs1QeFlsgWmooZIN31f/XR9enUGzkfqjECuys8flvL0zS4FmDnsqUipd4l6E
PLe0sAYEBZITidMxlru1mSbzn3CSGuUnDmfhPd7AYcGaMxIZHwb3OYgijpuv8Gzs
fCFzYHP/vlovVFcpMVYCKjztPRng66akWeCI0uu9t8FBxQNWYFMBlzHrNyKSPrEt
q0vSIkS+lR+te8MKCVO+4KCas7UiKXfJ3pI0faigcRFyzX6FkwEI1xZgV+V9Rbun
Df2+zEax+YUk2YSy5S4rIb8qB6A0zskxGdfKvF9KB9JCAXoti6zde7iqhbu6/V92
rupyOWaJpRUXEdqb2ANrR2aQNSv+iA06bjwBMdixfaLWQ7TtyxuWjl9X4fQkwnvK
FEYW
=QHlJ
-----END PGP MESSAGE-----
With some delay (that Salt takes to refresh pillars automatically), requesting
the pillar value for app:key
should now show the decrypted value.
$ sudo salt '*' pillar.get app:key
test-minion:
hunter2