golden hour
/opt/saltstack/salt/lib/python3.10/site-packages/salt/pillar
⬆️ Go Up
Upload
File/Folder
Size
Actions
__init__.py
50.98 KB
Del
OK
__pycache__
-
Del
OK
azureblob.py
13.88 KB
Del
OK
cmd_json.py
787 B
Del
OK
cmd_yaml.py
893 B
Del
OK
cmd_yamlex.py
674 B
Del
OK
cobbler.py
1.64 KB
Del
OK
confidant.py
3.29 KB
Del
OK
consul_pillar.py
11.61 KB
Del
OK
csvpillar.py
1.85 KB
Del
OK
digicert.py
1007 B
Del
OK
django_orm.py
7.74 KB
Del
OK
ec2_pillar.py
10.08 KB
Del
OK
etcd_pillar.py
4.04 KB
Del
OK
extra_minion_data_in_pillar.py
2.18 KB
Del
OK
file_tree.py
18.03 KB
Del
OK
foreman.py
3.66 KB
Del
OK
git_pillar.py
19.61 KB
Del
OK
gpg.py
560 B
Del
OK
hg_pillar.py
3.2 KB
Del
OK
hiera.py
846 B
Del
OK
http_json.py
3.37 KB
Del
OK
http_yaml.py
3.08 KB
Del
OK
libvirt.py
5.78 KB
Del
OK
makostack.py
21.99 KB
Del
OK
mongo.py
6.05 KB
Del
OK
mysql.py
3.76 KB
Del
OK
nacl.py
744 B
Del
OK
netbox.py
36.22 KB
Del
OK
neutron.py
2.41 KB
Del
OK
nodegroups.py
1.7 KB
Del
OK
pepa.py
20.99 KB
Del
OK
pillar_ldap.py
10.82 KB
Del
OK
postgres.py
2.82 KB
Del
OK
puppet.py
846 B
Del
OK
reclass_adapter.py
4 KB
Del
OK
redismod.py
3.28 KB
Del
OK
rethinkdb_pillar.py
4.71 KB
Del
OK
s3.py
14.4 KB
Del
OK
saltclass.py
1.49 KB
Del
OK
sql_base.py
16.51 KB
Del
OK
sqlcipher.py
3.42 KB
Del
OK
sqlite3.py
2.67 KB
Del
OK
stack.py
22.92 KB
Del
OK
svn_pillar.py
5.74 KB
Del
OK
varstack_pillar.py
1.1 KB
Del
OK
vault.py
6.54 KB
Del
OK
venafi.py
966 B
Del
OK
virtkey.py
586 B
Del
OK
vmware_pillar.py
16.83 KB
Del
OK
Edit: pillar_ldap.py
""" Use LDAP data as a Pillar source This pillar module executes a series of LDAP searches. Data returned by these searches are aggregated, whereby data returned by later searches override data by previous searches with the same key. The final result is merged with existing pillar data. The configuration of this external pillar module is done via an external file which provides the actual configuration for the LDAP searches. =============================== Configuring the LDAP ext_pillar =============================== The basic configuration is part of the `master configuration <master-configuration-ext-pillar>`_. .. code-block:: yaml ext_pillar: - pillar_ldap: /etc/salt/master.d/pillar_ldap.yaml .. note:: When placing the file in the ``master.d`` directory, make sure its name doesn't end in ``.conf``, otherwise the salt-master process will attempt to parse its content. .. warning:: Make sure this file has very restrictive permissions, as it will contain possibly sensitive LDAP credentials! The only required key in the master configuration is ``pillar_ldap`` pointing to a file containing the actual configuration. Configuring the LDAP searches ============================= The file is processed using `Salt's Renderers <renderers>` which makes it possible to reference grains within the configuration. .. warning:: When using Jinja in this file, make sure to do it in a way which prevents leaking sensitive information. A rogue minion could send arbitrary grains to trick the master into returning secret data. Use only the 'id' grain which is verified through the minion's key/cert. Map Mode -------- The ``it-admins`` configuration below returns the Pillar ``it-admins`` by: - filtering for: - members of the group ``it-admins`` - objects with ``objectclass=user`` - returning the data of users, where each user is a dictionary containing the configured string or list attributes. Configuration ************* .. code-block:: yaml salt-users: server: ldap.company.tld port: 389 tls: true dn: 'dc=company,dc=tld' binddn: 'cn=salt-pillars,ou=users,dc=company,dc=tld' bindpw: bi7ieBai5Ano referrals: false anonymous: false mode: map dn: 'ou=users,dc=company,dc=tld' filter: '(&(memberof=cn=it-admins,ou=groups,dc=company,dc=tld)(objectclass=user))' attrs: - cn - displayName - givenName - sn lists: - memberOf search_order: - salt-users Result ****** .. code-block:: python { 'salt-users': [ { 'cn': 'cn=johndoe,ou=users,dc=company,dc=tld', 'displayName': 'John Doe' 'givenName': 'John' 'sn': 'Doe' 'memberOf': [ 'cn=it-admins,ou=groups,dc=company,dc=tld', 'cn=team01,ou=groups,dc=company' ] }, { 'cn': 'cn=janedoe,ou=users,dc=company,dc=tld', 'displayName': 'Jane Doe', 'givenName': 'Jane', 'sn': 'Doe', 'memberOf': [ 'cn=it-admins,ou=groups,dc=company,dc=tld', 'cn=team02,ou=groups,dc=company' ] } ] } """ import logging import os import jinja2 import salt.utils.data from salt.exceptions import SaltInvocationError try: import ldap # pylint: disable=W0611 HAS_LDAP = True except ImportError: HAS_LDAP = False # Set up logging log = logging.getLogger(__name__) def __virtual__(): """ Only return if ldap module is installed """ if HAS_LDAP: return "pillar_ldap" else: return False def _render_template(config_file): """ Render config template, substituting grains where found. """ dirname, filename = os.path.split(config_file) env = jinja2.Environment(loader=jinja2.FileSystemLoader(dirname)) template = env.get_template(filename) return template.render(__grains__) def _config(name, conf, default=None): """ Return a value for 'name' from the config file options. If the 'name' is not in the config, the 'default' value is returned. This method converts unicode values to str type under python 2. """ try: value = conf[name] except KeyError: value = default return salt.utils.data.decode(value, to_str=True) def _result_to_dict(data, result, conf, source): """ Aggregates LDAP search result based on rules, returns a dictionary. Rules: Attributes tagged in the pillar config as 'attrs' or 'lists' are scanned for a 'key=value' format (non matching entries are ignored. Entries matching the 'attrs' tag overwrite previous values where the key matches a previous result. Entries matching the 'lists' tag are appended to list of values where the key matches a previous result. All Matching entries are then written directly to the pillar data dictionary as data[key] = value. For example, search result: { saltKeyValue': ['ntpserver=ntp.acme.local', 'foo=myfoo'], 'saltList': ['vhost=www.acme.net', 'vhost=www.acme.local'] } is written to the pillar data dictionary as: { 'ntpserver': 'ntp.acme.local', 'foo': 'myfoo', 'vhost': ['www.acme.net', 'www.acme.local'] } """ attrs = _config("attrs", conf) or [] lists = _config("lists", conf) or [] dict_key_attr = _config("dict_key_attr", conf) or "dn" # TODO: # deprecate the default 'mode: split' and make the more # straightforward 'mode: map' the new default mode = _config("mode", conf) or "split" if mode == "map": data[source] = [] for record in result: ret = {} if "dn" in attrs or "distinguishedName" in attrs: log.debug("dn: %s", record[0]) ret["dn"] = record[0] record = record[1] log.debug("record: %s", record) for key in record: if key in attrs: for item in record.get(key): ret[key] = item if key in lists: ret[key] = record.get(key) data[source].append(ret) elif mode == "dict": data[source] = {} for record in result: ret = {} distinguished_name = record[0] log.debug("dn: %s", distinguished_name) if "dn" in attrs or "distinguishedName" in attrs: ret["dn"] = distinguished_name record = record[1] log.debug("record: %s", record) for key in record: if key in attrs: for item in record.get(key): ret[key] = item if key in lists: ret[key] = record.get(key) if dict_key_attr in ["dn", "distinguishedName"]: dict_key = distinguished_name else: dict_key = ",".join(sorted(record.get(dict_key_attr, []))) try: data[source][dict_key].append(ret) except KeyError: data[source][dict_key] = [ret] elif mode == "split": for key in result[0][1]: if key in attrs: for item in result.get(key): skey, sval = item.split("=", 1) data[skey] = sval elif key in lists: for item in result.get(key): if "=" in item: skey, sval = item.split("=", 1) if skey not in data: data[skey] = [sval] else: data[skey].append(sval) return data def _do_search(conf): """ Builds connection and search arguments, performs the LDAP search and formats the results as a dictionary appropriate for pillar use. """ # Build LDAP connection args connargs = {} for name in ["server", "port", "tls", "binddn", "bindpw", "anonymous"]: connargs[name] = _config(name, conf) if connargs["binddn"] and connargs["bindpw"]: connargs["anonymous"] = False # Build search args try: _filter = conf["filter"] except KeyError: raise SaltInvocationError("missing filter") _dn = _config("dn", conf) scope = _config("scope", conf) _lists = _config("lists", conf) or [] _attrs = _config("attrs", conf) or [] _dict_key_attr = _config("dict_key_attr", conf, "dn") attrs = _lists + _attrs + [_dict_key_attr] if not attrs: attrs = None # Perform the search try: result = __salt__["ldap.search"](_filter, _dn, scope, attrs, **connargs)[ "results" ] except IndexError: # we got no results for this search log.debug("LDAP search returned no results for filter %s", _filter) result = {} except Exception: # pylint: disable=broad-except log.critical("Failed to retrieve pillar data from LDAP:\n", exc_info=True) return {} return result def ext_pillar( minion_id, pillar, config_file # pylint: disable=W0613 # pylint: disable=W0613 ): """ Execute LDAP searches and return the aggregated data """ config_template = None try: config_template = _render_template(config_file) except jinja2.exceptions.TemplateNotFound: log.debug("pillar_ldap: missing configuration file %s", config_file) except Exception: # pylint: disable=broad-except log.debug( "pillar_ldap: failed to render template for %s", config_file, exc_info=True ) if not config_template: # We don't have a config file return {} import salt.utils.yaml try: opts = salt.utils.yaml.safe_load(config_template) or {} opts["conf_file"] = config_file except Exception as err: # pylint: disable=broad-except log.warning( "pillar_ldap: error parsing configuration file: %s - %s", config_file, err ) return {} else: if not isinstance(opts, dict): log.warning( "pillar_ldap: %s is invalidly formatted, must be a YAML " "dictionary. See the documentation for more information.", config_file, ) return {} if "search_order" not in opts: log.warning( "pillar_ldap: search_order missing from configuration. See the " "documentation for more information." ) return {} data = {} for source in opts["search_order"]: config = opts[source] result = _do_search(config) log.debug("source %s got result %s", source, result) if result: data = _result_to_dict(data, result, config, source) return data
Save