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.
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
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.
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.
To encrypt stuff, the public key needs to first be imported into the development environment.
$ gpg --import /path/to/public.key
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