29 KiB
Storing Static Data in the Pillar
Pillar is an interface for Salt designed to offer global values that can be distributed to minions. Pillar data is managed in a similar way as the Salt State Tree.
Pillar was added to Salt in version 0.9.8
Note
Storing sensitive data
Pillar data is compiled on the master. Additionally, pillar data for a given minion is only accessible by the minion for which it is targeted in the pillar configuration. This makes pillar useful for storing sensitive data specific to a particular minion.
Declaring the Master Pillar
The Salt Master server maintains a pillar_roots
setup that matches the structure
of the file_roots
used in the Salt file server. Like
file_roots
, the
pillar_roots
option maps environments to directories. The pillar data is then mapped
to minions based on matchers in a top file which is laid out in the same
way as the state top file. Salt pillars can use the same matcher types
as the standard top
file <states-top>
.
conf_master:`pillar_roots` is configured just like file_roots
. For
example:
pillar_roots:
base:
- /srv/pillar
This example configuration declares that the base environment will be
located in the /srv/pillar
directory. It must not be in a
subdirectory of the state tree.
The top file used matches the name of the top file used for States, and has the same structure:
/srv/pillar/top.sls
base:
'*':
- packages
In the above top file, it is declared that in the base
environment, the glob matching all minions will have the pillar data
found in the packages
pillar available to it. Assuming the
pillar_roots
value of /srv/pillar
taken from
above, the packages
pillar would be located at
/srv/pillar/packages.sls
.
Any number of matchers can be added to the base environment. For example, here is an expanded version of the Pillar top file stated above:
/srv/pillar/top.sls:
base:
'*':
- packages
'web*':
- vim
In this expanded top file, minions that match web*
will
have access to the /srv/pillar/packages.sls
file, as well
as the /srv/pillar/vim.sls
file.
Another example shows how to use other standard top matching types to deliver specific salt pillar data to minions with different properties.
Here is an example using the grains
matcher to target
pillars to minions by their os
grain:
dev:
'os:Debian':
- match: grain
- servers
Pillar definitions can also take a keyword argument
ignore_missing
. When the value of
ignore_missing
is True
, all errors for missing
pillar files are ignored. The default value for
ignore_missing
is False
.
Here is an example using the ignore_missing
keyword
parameter to ignore errors for missing pillar files:
base:
'*':
- servers
- systems
- ignore_missing: True
Assuming that the pillar servers
exists in the
fileserver backend and the pillar systems
doesn't, all
pillar data from servers
pillar is delivered to minions and
no error for the missing pillar systems
is noted under the
key _errors
in the pillar data delivered to minions.
Should the ignore_missing
keyword parameter have the
value False
, an error for the missing pillar
systems
would produce the value
Specified SLS 'servers' in environment 'base' is not available on the salt master
under the key _errors
in the pillar data delivered to
minions.
/srv/pillar/packages.sls
{% if grains['os'] == 'RedHat' %}
apache: httpd
git: git
{% elif grains['os'] == 'Debian' %}
apache: apache2
git: git-core
{% endif %}
company: Foo Industries
Important
See Is Targeting using Grain Data Secure? <faq-grain-security>
for important security information.
The above pillar sets two key/value pairs. If a minion is running
RedHat, then the apache
key is set to httpd
and the git
key is set to the value of git
. If
the minion is running Debian, those values are changed to
apache2
and git-core
respectively. All minions
that have this pillar targeting to them via a top file will have the key
of company
with a value of Foo Industries
.
Consequently this data can be used from within modules, renderers, State SLS files, and more via the shared pillar dictionary:
apache:
pkg.installed:
- name: {{ pillar['apache'] }}
git:
pkg.installed:
- name: {{ pillar['git'] }}
Finally, the above states can utilize the values provided to them via Pillar. All pillar values targeted to a minion are available via the 'pillar' dictionary. As seen in the above example, Jinja substitution can then be utilized to access the keys and values in the Pillar dictionary.
Note that you cannot just list key/value-information in
top.sls
. Instead, target a minion to a pillar file and then
list the keys and values in the pillar. Here is an example top file that
illustrates this point:
base:
'*':
- common_pillar
And the actual pillar file at '/srv/pillar/common_pillar.sls':
foo: bar
boo: baz
Note
When working with multiple pillar environments, assuming that each
pillar environment has its own top file, the jinja placeholder
{{ saltenv }}
can be used in place of the environment
name:
{{ saltenv }}:
'*':
- common_pillar
Yes, this is {{ saltenv }}
, and not
{{ pillarenv }}
. The reason for this is because the Pillar
top files are parsed using some of the same code which parses top files
when running states <running-highstate>
, so the
pillar environment takes the place of {{ saltenv }}
in the
jinja context.
Dynamic Pillar Environments
If environment __env__
is specified in pillar_roots
, all
environments that are not explicitly specified in pillar_roots
will map
to the directories from __env__
. This allows one to use
dynamic git branch based environments for state/pillar files with the
same file-based pillar applying to all environments. For example:
pillar_roots:
__env__:
- /srv/pillar
ext_pillar:
- git:
- __env__ https://example.com/git-pillar.git
2017.7.5,2018.3.1
Taking it one step further, __env__
can also be used in
the pillar_root
filesystem path. It will be replaced with
the actual pillarenv
and searched for Pillar data to
provide to the minion. Note this substitution ONLY occurs for the
__env__
environment. For instance, this configuration:
pillar_roots:
__env__:
- /srv/__env__/pillar
is equivalent to this static configuration:
pillar_roots:
dev:
- /srv/dev/pillar
test:
- /srv/test/pillar
prod:
- /srv/prod/pillar
3005
Pillar Namespace Flattening
The separate pillar SLS files all merge down into a single dictionary of key-value pairs. When the same key is defined in multiple SLS files, this can result in unexpected behavior if care is not taken to how the pillar SLS files are laid out.
For example, given a top.sls
containing the
following:
base:
'*':
- packages
- services
with packages.sls
containing:
bind: bind9
and services.sls
containing:
bind: named
Then a request for the bind
pillar key will only return
named
. The bind9
value will be lost, because
services.sls
was evaluated later.
Note
Pillar files are applied in the order they are listed in the top
file. Therefore conflicting keys will be overwritten in a 'last one
wins' manner! For example, in the above scenario conflicting key values
in services
will overwrite those in packages
because it's at the bottom of the list.
It can be better to structure your pillar files with more hierarchy.
For example the package.sls
file could be configured like
so:
packages:
bind: bind9
This would make the packages
pillar key a nested
dictionary containing a bind
key.
Pillar Dictionary Merging
If the same pillar key is defined in multiple pillar SLS files, and the keys in both files refer to nested dictionaries, then the content from these dictionaries will be recursively merged.
For example, keeping the top.sls
the same, assume the
following modifications to the pillar SLS files:
packages.sls
:
bind:
package-name: bind9
version: 9.9.5
services.sls
:
bind:
port: 53
listen-on: any
The resulting pillar dictionary will be:
$ salt-call pillar.get bind
local:
----------
listen-on:
any
package-name:
bind9
port:
53
version:
9.9.5
Since both pillar SLS files contained a bind
key which
contained a nested dictionary, the pillar dictionary's bind
key contains the combined contents of both SLS files' bind
keys.
Including Other Pillars
0.16.0
Pillar SLS files may include other pillar files, similar to State files. Two syntaxes are available for this purpose. The simple form simply includes the additional pillar as if it were part of the same file:
include:
- users
The full include form allows two additional options -- passing default values to the templating engine for the included pillar file as well as an optional key under which to nest the results of the included pillar:
include:
- users:
defaults:
sudo: ['bob', 'paul']
key: users
With this form, the included file (users.sls) will be nested within the 'users' key of the compiled pillar. Additionally, the 'sudo' value will be available as a template variable to users.sls.
In-Memory Pillar Data vs. On-Demand Pillar Data
Since compiling pillar data is computationally expensive, the minion
will maintain a copy of the pillar data in memory to avoid needing to
ask the master to recompile and send it a copy of the pillar data each
time pillar data is requested. This in-memory pillar data is what is
returned by the pillar.item <salt.modules.pillar.item>
,
pillar.get
<salt.modules.pillar.get>
, and pillar.raw <salt.modules.pillar.raw>
functions.
Also, for those writing custom execution modules, or contributing to
Salt's existing execution modules, the in-memory pillar data is
available as the __pillar__
dunder dictionary.
The in-memory pillar data is generated on minion start, and can be
refreshed using the saltutil.refresh_pillar
<salt.modules.saltutil.refresh_pillar>
function:
salt '*' saltutil.refresh_pillar
This function triggers the minion to asynchronously refresh the
in-memory pillar data and will always return None
.
In contrast to in-memory pillar data, certain actions trigger pillar data to be compiled to ensure that the most up-to-date pillar data is available. These actions include:
- Running states
- Running
pillar.items <salt.modules.pillar.items>
Performing these actions will not refresh the in-memory
pillar data. So, if pillar data is modified, and then states are run,
the states will see the updated pillar data, but pillar.item <salt.modules.pillar.item>
,
pillar.get <salt.modules.pillar.get>
, and
pillar.raw
<salt.modules.pillar.raw>
will not see this data unless
refreshed using saltutil.refresh_pillar <salt.modules.saltutil.refresh_pillar>
.
If you are using the Pillar Cache and have set pillar_cache
to True, the pillar cache can be updated either
when you run saltutil.refresh_pillar
<salt.modules.saltutil.refresh_pillar>
, or using the pillar
runner function pillar.clear_pillar_cache <salt.runners.pillar.clear_pillar_cache>
:
salt-run pillar.clear_pillar_cache 'minion'
The pillar will not be updated when running pillar.items
<salt.modules.pillar.items>
or a state for example. If you
are using a Salt version before 3003, you would need to manually delete
the cache file, located in Salt's master cache. For example, on linux
the file would be in this directory:
/var/cache/salt/master/pillar_cache/
How Pillar Environments Are Handled
When multiple pillar environments are used, the default behavior is for the pillar data from all environments to be merged together. The pillar dictionary will therefore contain keys from all configured environments.
The pillarenv
minion config option can be used to
force the minion to only consider pillar configuration from a single
environment. This can be useful in cases where one needs to run states
with alternate pillar data, either in a testing/QA environment or to
test changes to the pillar data before pushing them live.
For example, assume that the following is set in the minion config file:
pillarenv: base
This would cause that minion to ignore all other pillar environments
besides base
when compiling the in-memory pillar data.
Then, when running states, the pillarenv
CLI argument can
be used to override the minion's pillarenv
config value:
salt '*' state.apply mystates pillarenv=testing
The above command will run the states with pillar data sourced
exclusively from the testing
environment, without modifying
the in-memory pillar data.
Note
When running states, the pillarenv
CLI option does not
require a pillarenv
option to be set in the minion
config file. When pillarenv
is left unset, as mentioned above
all configured environments will be combined. Running states with
pillarenv=testing
in this case would still restrict the
states' pillar data to just that of the testing
pillar
environment.
Starting in the 2017.7.0 release, it is possible to pin the pillarenv
to the effective saltenv, using the pillarenv_from_saltenv
minion config option.
When this is set to True
, if a specific saltenv is
specified when running states, the pillarenv
will be the
same. This essentially makes the following two commands equivalent:
salt '*' state.apply mystates saltenv=dev
salt '*' state.apply mystates saltenv=dev pillarenv=dev
However, if a pillarenv is specified, it will override this behavior.
So, the following command will use the qa
pillar
environment but source the SLS files from the dev
saltenv:
salt '*' state.apply mystates saltenv=dev pillarenv=qa
So, if a pillarenv
is set in the minion config file,
pillarenv_from_saltenv
will be ignored, and
passing a pillarenv
on the CLI will temporarily override
pillarenv_from_saltenv
.
Viewing Pillar Data
To view pillar data, use the pillar <salt.modules.pillar>
execution module.
This module includes several functions, each of them with their own use.
These functions include:
pillar.item <salt.modules.pillar.item>
- Retrieves the value of one or more keys from thein-memory pillar data <pillar-in-memory>
.pillar.items <salt.modules.pillar.items>
- Compiles a fresh pillar dictionary and returns it, leaving thein-memory pillar data <pillar-in-memory>
untouched. If pillar keys are passed to this function however, this function acts likepillar.item <salt.modules.pillar.item>
and returns their values from thein-memory pillar data <pillar-in-memory>
.pillar.raw <salt.modules.pillar.raw>
- Likepillar.items <salt.modules.pillar.items>
, it returns the entire pillar dictionary, but from thein-memory pillar data <pillar-in-memory>
instead of compiling fresh pillar data.pillar.get <salt.modules.pillar.get>
- Described in detail below.
The pillar.get <salt.modules.pillar.get>
Function
0.14.0
The pillar.get <salt.modules.pillar.get>
function
works much in the same way as the get
method in a python
dict, but with an enhancement: nested dictionaries can be traversed
using a colon as a delimiter.
If a structure like this is in pillar:
foo:
bar:
baz: qux
Extracting it from the raw pillar in an sls formula or file template is done this way:
{{ pillar['foo']['bar']['baz'] }}
Now, with the new pillar.get <salt.modules.pillar.get>
function
the data can be safely gathered and a default can be set, allowing the
template to fall back if the value is not available:
{{ salt['pillar.get']('foo:bar:baz', 'qux') }}
This makes handling nested structures much easier.
Note
pillar.get()
vs salt['pillar.get']()
It should be noted that within templating, the pillar
variable is just a dictionary. This means that calling
pillar.get()
inside of a template will just use the default
dictionary .get()
function which does not include the extra
:
delimiter functionality. It must be called using the
above syntax (salt['pillar.get']('foo:bar:baz', 'qux')
) to
get the salt function, instead of the default dictionary behavior.
Setting Pillar Data at the Command Line
Pillar data can be set at the command line like the following example:
salt '*' state.apply pillar='{"cheese": "spam"}'
This will add a pillar key of cheese
with its value set
to spam
.
Note
Be aware that when sending sensitive data via pillar on the command-line that the publication containing that data will be received by all minions and will not be restricted to the targeted minions. This may represent a security concern in some cases.
Pillar Encryption
Salt's renderer system can be used to decrypt pillar data. This allows for pillar items to be stored in an encrypted state, and decrypted during pillar compilation.
Encrypted Pillar SLS
2017.7.0
Consider the following pillar SLS file:
secrets:
vault:
foo: |
-----BEGIN PGP MESSAGE-----
hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
iFndxegN9w==
=bAuo
-----END PGP MESSAGE----- bar: this was unencrypted already
baz: |
-----BEGIN PGP MESSAGE-----
hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz
gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf
9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7
Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2
q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V
kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl
JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY
1OZi
=7epf
-----END PGP MESSAGE----- qux:
- foo
- bar
- |
-----BEGIN PGP MESSAGE-----
hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS
ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI
gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA
YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF
f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE
uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd
4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=
=LrCQ -----END PGP MESSAGE-----
When the pillar data is compiled, the results will be decrypted:
# salt myminion pillar.items
myminion:
----------
secrets:
----------
vault:
----------
bar:
this was unencrypted already
baz:
rosebud
foo:
supersecret
qux:
- foo
- bar
- baz
Salt must be told what portions of the pillar data to decrypt. This
is done using the decrypt_pillar
config option:
decrypt_pillar:
- 'secrets:vault': gpg
The notation used to specify the pillar item(s) to be decrypted is
the same as the one used in pillar.get <salt.modules.pillar.get>
function.
If a different delimiter is needed, it can be specified using the
decrypt_pillar_delimiter
config option:
decrypt_pillar:
- 'secrets|vault': gpg
decrypt_pillar_delimiter: '|'
The name of the renderer used to decrypt a given pillar item can be
omitted, and if so it will fall back to the value specified by the decrypt_pillar_default
config option, which
defaults to gpg
. So, the first example above could be
rewritten as:
decrypt_pillar:
- 'secrets:vault'
Encrypted Pillar Data on the CLI
2016.3.0
The following functions support passing pillar data on the CLI via
the pillar
argument:
pillar.items <salt.modules.pillar.items>
state.apply <salt.modules.state.apply_>
state.highstate <salt.modules.state.highstate>
state.sls <salt.modules.state.sls>
Triggering decryption of this CLI pillar data can be done in one of two ways:
Using the
pillar_enc
argument:# salt myminion pillar.items pillar_enc=gpg pillar='{foo: "-----BEGIN PGP MESSAGE-----\n\nhQEMAw2B674HRhwSAQf+OvPqEdDoA2fk15I5dYUTDoj1yf/pVolAma6iU4v8Zixn\nRDgWsaAnFz99FEiFACsAGDEFdZaVOxG80T0Lj+PnW4pVy0OXmXHnY2KjV9zx8FLS\nQxfvmhRR4t23WSFybozfMm0lsN8r1vfBBjbK+A72l0oxN78d1rybJ6PWNZiXi+aC\nmqIeunIbAKQ21w/OvZHhxH7cnIiGQIHc7N9nQH7ibyoKQzQMSZeilSMGr2abAHun\nmLzscr4wKMb+81Z0/fdBfP6g3bLWMJga3hSzSldU9ovu7KR8rDJI1qOlENj3Wm8C\nwTpDOB33kWIKMqiAjY3JFtb5MCHrafyggwQL7cX1+tI+AbSO6kZpbcDfzetb77LZ\nxc5NWnnGK4pGoqq4MAmZshw98RpecSHKMosto2gtiuWCuo9Zn5cV/FbjZ9CTWrQ=\n=0hO/\n-----END PGP MESSAGE-----"}'
The newlines in this example are specified using a literal
\n
. Newlines can be replaced with a literal\n
usingsed
:$ echo -n bar | gpg --armor --trust-model always --encrypt -r user@domain.tld | sed ':a;N;$!ba;s/\n/\\n/g'
Note
Using
pillar_enc
will perform the decryption minion-side, so for this to work it will be necessary to set up the keyring in/etc/salt/gpgkeys
on the minion just as one would typically do on the master. The easiest way to do this is to first export the keys from the master:# gpg --homedir /etc/salt/gpgkeys --export-secret-key -a user@domain.tld >/tmp/keypair.gpg
Then, copy the file to the minion, setup the keyring, and import:
# mkdir -p /etc/salt/gpgkeys # chmod 0700 /etc/salt/gpgkeys # gpg --homedir /etc/salt/gpgkeys --list-keys # gpg --homedir /etc/salt/gpgkeys --import --allow-secret-key-import keypair.gpg
The
--list-keys
command is run create a keyring in the newly-created directory.Pillar data which is decrypted minion-side will still be securely transferred to the master, since the data sent between minion and master is encrypted with the master's public key.
Use the
decrypt_pillar
option. This is less flexible in that the pillar key passed on the CLI must be pre-configured on the master, but it doesn't require a keyring to be setup on the minion. One other caveat to this method is that pillar decryption on the master happens at the end of pillar compilation, so if the encrypted pillar data being passed on the CLI needs to be referenced by pillar or ext_pillar during pillar compilation, it must be decrypted minion-side.
Adding New Renderers for Decryption
Those looking to add new renderers for decryption should look at the
gpg
<salt.renderers.gpg>
renderer for an example of how to do
so. The function that performs the decryption should be recursive and be
able to traverse a mutable type such as a dictionary, and modify the
values in-place.
Once the renderer has been written, decrypt_pillar_renderers
should be modified so
that Salt allows it to be used for decryption.
If the renderer is being submitted upstream to the Salt project, the renderer should be added in salt/renderers/. Additionally, the following should be done:
- Both occurrences of
decrypt_pillar_renderers
in salt/config/__init__.py should be updated to include the name of the new renderer so that it is included in the default value for this config option. - The documentation for the
decrypt_pillar_renderers
config option in the master config file and minion config file should be updated to show the correct new default value. - The commented example for the
decrypt_pillar_renderers
config option in the master config template should be updated to show the correct new default value.
Binary Data in the Pillar
Salt has partial support for binary pillar data.
Note
There are some situations (such as salt-ssh) where only text (ASCII or Unicode) is allowed.
The simplest way to embed binary data in your pillar is to make use of YAML's built-in binary data type, which requires base64 encoded data.
salt_pic: !!binary
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAA
Then you can use it as a contents_pillar
in a state:
/tmp/salt.png:
file.managed:
- contents_pillar: salt_pic
It is also possible to add ASCII-armored encrypted data to pillars, as mentioned in the Pillar Encryption section.
Master Config in Pillar
For convenience the data stored in the master configuration file can be made available in all minion's pillars. This makes global configuration of services and systems very easy but may not be desired if sensitive data is stored in the master configuration. This option is disabled by default.
To enable the master config from being added to the pillar set pillar_opts
to
True
in the minion config file:
pillar_opts: True
Minion Config in Pillar
Minion configuration options can be set on pillars. Any option that you want to modify, should be in the first level of the pillars, in the same way you set the options in the config file. For example, to configure the MySQL root password to be used by MySQL Salt execution module, set the following pillar variable:
mysql.pass: hardtoguesspassword
Master Provided Pillar Error
By default if there is an error rendering a pillar, the detailed error is hidden and replaced with:
Rendering SLS 'my.sls' failed. Please see master log for details.
The error is protected because it's possible to contain templating data which would give that minion information it shouldn't know, like a password!
To have the master provide the detailed error that could potentially
carry protected data set pillar_safe_render_error
to
False
:
pillar_safe_render_error: False
../tutorials/pillar