Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mine minion acl #54100

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion doc/topics/mine/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,57 @@ 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 until
version Aluminium.

.. 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 +115,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 +186,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
28 changes: 28 additions & 0 deletions doc/topics/releases/sodium.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
: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
be equal to the syntax used in :py:mod:`module.run <salt.states.module.run>`.
This means that if you supply the mine_function as a dict, or as a list with dicts
that contain more than exactly one key, you will get deprecation warnings.

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>`.
50 changes: 35 additions & 15 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 @@ -643,36 +644,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 not isinstance(fdata, dict):
mine_data = self.cache.fetch('minions/{0}'.format(minion), 'mine')
if not isinstance(mine_data, dict):
continue

if not _ret_dict and functions_allowed and functions_allowed[0] in fdata:
ret[minion] = fdata.get(functions_allowed[0])
elif _ret_dict:
for fun in list(set(functions_allowed) & set(fdata.keys())):
ret.setdefault(fun, {})[minion] = fdata.get(fun)

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 @@ -772,7 +793,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