Skip to content

Commit

Permalink
Merge pull request #55760 from ogd-software/mine-minion-acl-master
Browse files Browse the repository at this point in the history
Mine minion acl master
  • Loading branch information
dwoz authored Jan 8, 2020
2 parents df2bd32 + 9318139 commit 5a6ed7c
Show file tree
Hide file tree
Showing 13 changed files with 1,337 additions and 344 deletions.
65 changes: 64 additions & 1 deletion doc/topics/mine/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,56 @@ the example below:
test.ping: []
network.ip_addrs:
interface: eth0
cidr: '10.0.0.0/8'
cidr: 10.0.0.0/8
In the example above :py:mod:`salt.modules.network.ip_addrs` has additional
filters to help narrow down the results. In the above example IP addresses
are only returned if they are on a eth0 interface and in the 10.0.0.0/8 IP
range.

.. versionchanged:: Sodium

The format to define mine_functions has been changed to allow the same format
as used for module.run. The old format (above) will still be supported.

.. code-block:: yaml
mine_functions:
test.ping: []
network.ip_addrs:
- interface: eth0
- cidr: 10.0.0.0/8
test.arg:
- isn't
- this
- fun
- this: that
- salt: stack
.. _mine_minion-side-acl:

Minion-side Access Control
--------------------------

.. versionadded:: Sodium

Mine functions can be targeted to only be available to specific minions. This
uses the same targeting parameters as :ref:`targeting` but with keywords ``allow_tgt``
and ``allow_tgt_type``. When a minion requests a function from the salt mine that
is not allowed to be requested by that minion (i.e. when looking up the combination
of ``allow_tgt`` and ``allow_tgt_type`` and the requesting minion is not in the list)
it will get no data, just as if the requested function is not present in the salt mine.

.. code-block:: yaml
mine_functions:
network.ip_addrs:
- interface: eth0
- cidr: 10.0.0.0/8
- allow_tgt: 'G@role:master'
- allow_tgt_type: 'compound'
Mine Functions Aliases
----------------------

Expand All @@ -71,6 +114,25 @@ positional and key-value arguments is not supported.
- mine_function: grains.get
- ip_interfaces
.. versionchanged:: Sodium

With the addition of the module.run-like format for defining mine_functions, the
method of adding aliases remains similar. Just add a ``mine_function`` kwarg with
the name of the real function to call, making the key below ``mine_functions``
the alias:

.. code-block:: yaml
mine_functions:
alias_name:
- mine_function: network.ip_addrs
- eth0
internal_ip_addrs:
- mine_function: network.ip_addrs
- cidr: 192.168.0.0/16
ip_list:
- mine_function: grains.get
- ip_interfaces
.. _mine_interval:

Expand Down Expand Up @@ -123,6 +185,7 @@ stored in a different location. Here is an example of a flat roster containing
of the Minion in question. This results in a non-trivial delay in
retrieving the requested data.


Minions Targeting with Mine
===========================

Expand Down
29 changes: 29 additions & 0 deletions doc/topics/releases/sodium.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
:orphan:

====================================
Salt Release Notes - Codename Sodium
====================================


Salt mine updates
=================

Syntax update
-------------

The syntax for defining salt functions in config or pillar files has changed to
also support the syntax used in :py:mod:`module.run <salt.states.module.run>`.
The old syntax for the mine_function - as a dict, or as a list with dicts that
contain more than exactly one key - is still supported but discouraged in favor
of the more uniform syntax of module.run.

Minion-side ACL
---------------

Salt has had master-side ACL for the salt mine for some time, where the master
configuration contained `mine_get` that specified which minions could request
which functions. However, now you can specify which minions can access a function
in the salt mine function definition itself (or when calling :py:func:`mine.send <salt.modules.mine.send>`).
This targeting works the same as the generic minion targeting as specified
:ref:`here <targeting>`. The parameters used are ``allow_tgt`` and ``allow_tgt_type``.
See also :ref:`the documentation of the Salt Mine <mine_minion-side-acl>`.
67 changes: 56 additions & 11 deletions salt/daemons/masterapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import salt.utils.files
import salt.utils.gitfs
import salt.utils.verify
import salt.utils.mine
import salt.utils.minions
import salt.utils.gzip_util
import salt.utils.jid
Expand Down Expand Up @@ -548,6 +549,18 @@ def _mine_get(self, load, skip_verify=False):
if not skip_verify:
if any(key not in load for key in ('id', 'tgt', 'fun')):
return {}

if isinstance(load['fun'], six.string_types):
functions = list(set(load['fun'].split(',')))
_ret_dict = len(functions) > 1
elif isinstance(load['fun'], list):
functions = load['fun']
_ret_dict = True
else:
return {}

functions_allowed = []

if 'mine_get' in self.opts:
# If master side acl defined.
if not isinstance(self.opts['mine_get'], dict):
Expand All @@ -557,11 +570,18 @@ def _mine_get(self, load, skip_verify=False):
if re.match(match, load['id']):
if isinstance(self.opts['mine_get'][match], list):
perms.update(self.opts['mine_get'][match])
if not any(re.match(perm, load['fun']) for perm in perms):
for fun in functions:
if any(re.match(perm, fun) for perm in perms):
functions_allowed.append(fun)
if not functions_allowed:
return {}
else:
functions_allowed = functions

ret = {}
if not salt.utils.verify.valid_id(self.opts, load['id']):
return ret

expr_form = load.get('expr_form')
# keep both expr_form and tgt_type to ensure
# comptability between old versions of salt
Expand All @@ -580,30 +600,56 @@ def _mine_get(self, load, skip_verify=False):
greedy=False
)
minions = _res['minions']
minion_side_acl = {} # Cache minion-side ACL
for minion in minions:
fdata = self.cache.fetch('minions/{0}'.format(minion), 'mine')
if isinstance(fdata, dict):
fdata = fdata.get(load['fun'])
if fdata:
ret[minion] = fdata
mine_data = self.cache.fetch('minions/{0}'.format(minion), 'mine')
if not isinstance(mine_data, dict):
continue
for function in functions_allowed:
if function not in mine_data:
continue
mine_entry = mine_data[function]
mine_result = mine_data[function]
if isinstance(mine_entry, dict) and salt.utils.mine.MINE_ITEM_ACL_ID in mine_entry:
mine_result = mine_entry[salt.utils.mine.MINE_ITEM_ACL_DATA]
# Check and fill minion-side ACL cache
if function not in minion_side_acl.get(minion, {}):
if 'allow_tgt' in mine_entry:
# Only determine allowed targets if any have been specified.
# This prevents having to add a list of all minions as allowed targets.
salt.utils.dictupdate.set_dict_key_value(
minion_side_acl,
'{}:{}'.format(minion, function),
checker.check_minions(
mine_entry['allow_tgt'],
mine_entry.get('allow_tgt_type', 'glob')
)['minions']
)
if salt.utils.mine.minion_side_acl_denied(minion_side_acl, minion, function, load['id']):
continue
if _ret_dict:
ret.setdefault(function, {})[minion] = mine_result
else:
# There is only one function in functions_allowed.
ret[minion] = mine_result
return ret

def _mine(self, load, skip_verify=False):
'''
Return the mine data
Store/update the mine data in cache.
'''
if not skip_verify:
if 'id' not in load or 'data' not in load:
return False
if self.opts.get('minion_data_cache', False) or self.opts.get('enforce_mine_cache', False):
cbank = 'minions/{0}'.format(load['id'])
ckey = 'mine'
new_data = load['data']
if not load.get('clear', False):
data = self.cache.fetch(cbank, ckey)
if isinstance(data, dict):
data.update(load['data'])
load['data'] = data
self.cache.store(cbank, ckey, load['data'])
data.update(new_data)
self.cache.store(cbank, ckey, data)
return True

def _mine_delete(self, load):
Expand Down Expand Up @@ -703,7 +749,6 @@ def _pillar(self, load):
'''
if any(key not in load for key in ('id', 'grains')):
return False
# pillar = salt.pillar.Pillar(
log.debug('Master _pillar using ext: %s', load.get('ext'))
pillar = salt.pillar.get_pillar(
self.opts,
Expand Down
Loading

0 comments on commit 5a6ed7c

Please sign in to comment.