Different pillars are needed
Pillars are a secure (secure) data store inside Salt. Therefore, first of all, they are used to delimit access to critical data (certificates, logins, passwords).
In addition to the default pillars, Salt also has the ext_pillar module , which provides an interface for connecting to external data sources and generates pillars from these sources into one common dictionary.
For example, you can take data from mysql, postgres, redis, mongo, git , or even from the output of the script / command - cmd_json , cmd_yaml .
A complete list of modules is published on the official SaltStack website .
If you have a non-standard situation and the available modules do not suit you, you can write your own and put it in / usr / lib / python3 / dist-packages / salt / pillar /, after which you need to restart the wizard.
An example of such a module:
#/etc/salt/master/conf.d/pillar.conf
ext_pillar:
- dummy: dummy
#/usr/lib/python3/dist-packages/salt/pillar/dummy.py
def ext_pillar(minion_id, pillar, *args, **kwargs):
dummy = {'dummy': 'what u want mann?'}
return dummy
Of all the available modules, pillarstack turned out to be the most interesting and relevant for our team. Now let's tell you why.
Introduction to PillarStack
Stack or pillarstack is "a simple and flexible YAML pillar that can read pillars from pillars," according to the official documentation on the site .
It allows you to use the pillars read earlier inside the pillars. This means that we can use the pillar from the previous config. And it's mega-convenient! Let's show how it works:
, :
#/etc/salt/conf.d/pillarstack.conf
ext_pillar:
- stack:
- /srv/pillar/stack1.cfg
- /srv/pillar/stack2.cfg
- /srv/pillar/stack3.cfg
#/srv/pillar/stack1.cfg
# stack "" ,
# 2 yml ,
core.yml
common/*.yml
osarchs/{{ __grains__['osarch'] }}.yml
oscodenames/{{ __grains__['oscodename'] }}.yml
hosts/{{ minion_id }}/roles.yml
#/srv/pillar/stack2.cfg
# stack stack1.cfg
# , , roles
# stack yml
{% for role in stack.roles %}
roles/{{ role }}/*.yml
{% endfor %}
#/srv/pillar/stack3.cfg
# stack "" stack1.cfg stack2.cfg
#
creds/{{ stack.db.host }}/*.yml
Each config can be represented as a layer. In each such layer, we can use data from the previous layers.
The following variables are available when configuring pillars:
- grains - targeting configs by grains
- opts - targeting configs by options in config
- pillar - pillars that were formed before processing the current ext_pillar: stack
# , stack
# stack grains pillar
# stack.cfg , pillar
ext_pillar:
- stack:
grains:cpuarch:
x86_64:
- /srv/pillar/stack1.cfg
- /srv/pillar/stack2.cfg
pillar:environment:
dev: /srv/pillar/dev/stack.cfg
prod: /srv/pillar/prod/stack.cfg
# stack: grains, pillar
# pillar stack1.cfg, stack2.cfg
# 'environment', stack.cfg
ext_pillar:
- stack:
grains:custom:grain:
value:
- /srv/pillar/stack1.cfg
- /srv/pillar/stack2.cfg
- stack:
pillar:environment:
dev: /srv/pillar/dev/stack.cfg
prod: /srv/pillar/prod/stack.cfg
In yml files you can use:
- __opts__: config options
- __grains__: minion grains
- pillar: pillars from other ext_pillar or from default pillars (those in top.sls)
- stack: accumulated pillar stack, including the current config.
If you are just going to switch to pillarstack, then below are a couple of points worth paying attention to:
1. Recursive inclusion works only in pillar. Stack doesn't include directories, only files.
# pillar
# top.sls
base:
'*':
- dir1.* # /dir1/dir2/dir3/*
# stack
# stack.cfg
/dir1/*
/dir1/dir2/*
/dir1/dir2/dir3/*
2. Default behavior when merging lists:
- pillar - lists are overwritten
- stack - the merge-last strategy is used (the lists are added).
Pillar fusion strategies allow for very flexible customization of pillars.
A detailed description with examples is in the documentation.
To summarize each strategy:
- merge-last (default): recursive merge, last dictionary / list takes precedence
- merge-first: recursive merge, priority is given to first dictionary / list
- remove: remove the specified elements
- overwrite: dictionary / list override.
Explanation: in each merge two dictionaries / lists are involved, let's call them the first and the last The
strategy is indicated as the first element of the dictionary / list to which it must be applied.
The main thing is not to get carried away with excessive use of strategies, as this will complicate the configuration. Perhaps, in this case, it is worth reconsidering the organization of the pillars.
Conclusion
Pillars allow you to restrict access to sensitive data. As the data grows, the scenarios for its use are becoming more sophisticated, so it is important to use a convenient tool for organizing it. At the moment for our team this is pillarstack, in the future we plan to deploy vault.
It seems to us surprising why pillarstack has not yet supplanted the default pillar, because it is much more convenient and flexible, and strategies are very helpful when it is necessary to change the behavior of the stack variable pointwise. What do you think? Do you use pillarstack in your work?