Merge pull request #47334 from terminalmage/ldap_pillar

pillar_ldap: Fix cryptic errors when config file fails to load
This commit is contained in:
Nicole Thomas 2018-04-27 13:53:51 -04:00 committed by GitHub
commit bcef34f7e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -50,66 +50,76 @@ possible to reference grains within the configuration.
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 (``mode: map``), where each user is a dictionary
containing the configured string or list attributes.
- 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:**
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
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
**Result:**
search_order:
- salt-users
.. code-block:: yaml
Result
******
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
.. code-block:: python
List Mode
---------
TODO: see also ``_result_to_dict()`` documentation
{
'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 python libs
@ -123,7 +133,7 @@ from salt.exceptions import SaltInvocationError
# Import third party libs
import yaml
from jinja2 import Environment, FileSystemLoader
import jinja2
try:
import ldap # pylint: disable=W0611
HAS_LDAP = True
@ -149,10 +159,9 @@ def _render_template(config_file):
Render config template, substituting grains where found.
'''
dirname, filename = os.path.split(config_file)
env = Environment(loader=FileSystemLoader(dirname))
env = jinja2.Environment(loader=jinja2.FileSystemLoader(dirname))
template = env.get_template(filename)
config = template.render(__grains__)
return config
return template.render(__grains__)
def _config(name, conf):
@ -186,18 +195,18 @@ def _result_to_dict(data, result, conf, source):
For example, search result:
{ saltKeyValue': ['ntpserver=ntp.acme.local', 'foo=myfoo'],
'saltList': ['vhost=www.acme.net', 'vhost=www.acme.local' }
'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' }
'vhost': ['www.acme.net', 'www.acme.local'] }
'''
attrs = _config('attrs', conf) or []
lists = _config('lists', conf) or []
# TODO:
# deprecate the default 'mode: split' and make the more
# straightforward 'mode: dict' the new default
# straightforward 'mode: map' the new default
mode = _config('mode', conf) or 'split'
if mode == 'map':
data[source] = []
@ -277,21 +286,44 @@ def ext_pillar(minion_id, # pylint: disable=W0613
'''
Execute LDAP searches and return the aggregated data
'''
if os.path.isfile(config_file):
try:
#open(config_file, 'r') as raw_config:
config = _render_template(config_file) or {}
opts = yaml.safe_load(config) or {}
opts['conf_file'] = config_file
except Exception as err:
import salt.log
msg = 'Error parsing configuration file: {0} - {1}'
if salt.log.is_console_configured():
log.warning(msg.format(config_file, err))
else:
print(msg.format(config_file, err))
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:
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 {}
try:
opts = yaml.safe_load(config_template) or {}
opts['conf_file'] = config_file
except Exception as err:
import salt.log
msg = 'pillar_ldap: error parsing configuration file: {0} - {1}'
if salt.log.is_console_configured():
log.warning(msg.format(config_file, err))
else:
print(msg.format(config_file, err))
return {}
else:
log.debug('Missing configuration file: {0}'.format(config_file))
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.'
)
data = {}
for source in opts['search_order']: