mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into 'develop'
Conflicts: - salt/modules/yumpkg.py - tests/unit/templates/test_jinja.py
This commit is contained in:
commit
add5b118a3
35 changed files with 1670 additions and 229 deletions
14
conf/master
14
conf/master
|
@ -414,6 +414,20 @@
|
|||
# will cause minion to throw an exception and drop the message.
|
||||
# sign_pub_messages: False
|
||||
|
||||
# Signature verification on messages published from minions
|
||||
# This requires that minions cryptographically sign the messages they
|
||||
# publish to the master. If minions are not signing, then log this information
|
||||
# at loglevel 'INFO' and drop the message without acting on it.
|
||||
# require_minion_sign_messages: False
|
||||
|
||||
# The below will drop messages when their signatures do not validate.
|
||||
# Note that when this option is False but `require_minion_sign_messages` is True
|
||||
# minions MUST sign their messages but the validity of their signatures
|
||||
# is ignored.
|
||||
# These two config options exist so a Salt infrastructure can be moved
|
||||
# to signing minion messages gradually.
|
||||
# drop_messages_signature_fail: False
|
||||
|
||||
# Use TLS/SSL encrypted connection between master and minion.
|
||||
# Can be set to a dictionary containing keyword arguments corresponding to Python's
|
||||
# 'ssl.wrap_socket' method.
|
||||
|
|
|
@ -309,3 +309,5 @@ def setup(app):
|
|||
indextemplate="pair: %s; conf/proxy")
|
||||
app.add_crossref_type(directivename="conf_log", rolename="conf_log",
|
||||
indextemplate="pair: %s; conf/logging")
|
||||
app.add_crossref_type(directivename="jinja_ref", rolename="jinja_ref",
|
||||
indextemplate="pair: %s; jinja filters")
|
||||
|
|
|
@ -117,6 +117,8 @@ execution modules
|
|||
drac
|
||||
dracr
|
||||
drbd
|
||||
dummyproxy_package
|
||||
dummyproxy_service
|
||||
ebuild
|
||||
eix
|
||||
elasticsearch
|
||||
|
|
6
doc/ref/modules/all/salt.modules.dummyproxy_package.rst
Normal file
6
doc/ref/modules/all/salt.modules.dummyproxy_package.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
salt.modules.dummyproxy_package module
|
||||
======================================
|
||||
|
||||
.. automodule:: salt.modules.dummyproxy_package
|
||||
:members:
|
||||
:undoc-members:
|
6
doc/ref/modules/all/salt.modules.dummyproxy_service.rst
Normal file
6
doc/ref/modules/all/salt.modules.dummyproxy_service.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
salt.modules.dummyproxy_service module
|
||||
======================================
|
||||
|
||||
.. automodule:: salt.modules.dummyproxy_service
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -17,6 +17,7 @@ pillar modules
|
|||
confidant
|
||||
consul_pillar
|
||||
csvpillar
|
||||
digicert
|
||||
django_orm
|
||||
ec2_pillar
|
||||
etcd_pillar
|
||||
|
|
6
doc/ref/pillar/all/salt.pillar.digicert.rst
Normal file
6
doc/ref/pillar/all/salt.pillar.digicert.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
salt.pillar.digicert module
|
||||
===========================
|
||||
|
||||
.. automodule:: salt.pillar.digicert
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -12,6 +12,7 @@ proxy modules
|
|||
|
||||
chronos
|
||||
cisconso
|
||||
dummy
|
||||
esxi
|
||||
fx2
|
||||
junos
|
||||
|
|
6
doc/ref/proxy/all/salt.proxy.dummy.rst
Normal file
6
doc/ref/proxy/all/salt.proxy.dummy.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
salt.proxy.dummy module
|
||||
=======================
|
||||
|
||||
.. automodule:: salt.proxy.dummy
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -16,6 +16,7 @@ runner modules
|
|||
cache
|
||||
cloud
|
||||
ddns
|
||||
digicertapi
|
||||
doc
|
||||
drac
|
||||
error
|
||||
|
|
6
doc/ref/runners/all/salt.runners.digicertapi.rst
Normal file
6
doc/ref/runners/all/salt.runners.digicertapi.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
salt.runners.digicertapi module
|
||||
===============================
|
||||
|
||||
.. automodule:: salt.runners.digicertapi
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -36,21 +36,14 @@ The 64bit installer has been tested on Windows 7 64bit and Windows Server
|
|||
Please file a bug report on our GitHub repo if issues for other platforms are
|
||||
found.
|
||||
|
||||
There are two installers available.
|
||||
There are installers available for Python 2 and Python 3.
|
||||
|
||||
============================================= =================================
|
||||
Filename Details
|
||||
============================================= =================================
|
||||
``Salt-Minion-<version>-<cpuarch>-Setup.exe`` Just the salt Minion
|
||||
``Salt-<version>-<cpuarch>-Setup.exe`` Salt Minion and Master
|
||||
============================================= =================================
|
||||
The installer will detect previous installations of Salt and ask if you would
|
||||
like to remove them. Clicking OK will remove the Salt binaries and related files
|
||||
but leave any existing config, cache, and PKI information.
|
||||
|
||||
When run, both installers will detect previous installations of Salt and ask if
|
||||
you would like to remove them. Clicking OK will remove the Salt binaries and
|
||||
related files but leave any existing config, cache, and PKI information.
|
||||
|
||||
Salt Minion Only Installation
|
||||
=============================
|
||||
Salt Minion Installation
|
||||
========================
|
||||
|
||||
After the Welcome and the License Agreement, the installer asks for two bits of
|
||||
information to configure the minion; the master hostname and the minion name.
|
||||
|
@ -83,18 +76,6 @@ be managed there or from the command line like any other Windows service.
|
|||
2008 x64 SP1 redistributable. Allow all Windows updates to run salt-minion
|
||||
smoothly.
|
||||
|
||||
Salt Minion/Master Installation
|
||||
===============================
|
||||
|
||||
This installer behaves the same as the Minion installer but adds an additional
|
||||
installer page. You will be prompted to choose to install the minion and master.
|
||||
The minion check box is checked by default. To also install the master, check
|
||||
the master checkbox. To install only the master, uncheck the minion checkbox. At
|
||||
least one item must be selected.
|
||||
|
||||
You will also be prompted on the final page to start the ``salt-master``
|
||||
service.
|
||||
|
||||
Installation Prerequisites
|
||||
--------------------------
|
||||
|
||||
|
@ -109,7 +90,7 @@ Silent Installer Options
|
|||
========================
|
||||
|
||||
The installer can be run silently by providing the ``/S`` option at the command
|
||||
line. Both installers also accept the following options for configuring the Salt
|
||||
line. The installer also accepts the following options for configuring the Salt
|
||||
Minion silently:
|
||||
|
||||
========================= =====================================================
|
||||
|
@ -126,19 +107,6 @@ Option Description
|
|||
``Automatic (Delayed Start)``
|
||||
========================= =====================================================
|
||||
|
||||
The Master/Minion installer also supports the following options:
|
||||
|
||||
========================= =====================================================
|
||||
Option Description
|
||||
========================= =====================================================
|
||||
``/start-master=`` Either a 1 or 0. '1' will start the salt-master
|
||||
service, '0' will not. Default is to start the
|
||||
service after installation.
|
||||
``/install-master`` Will install the master along with the minion.
|
||||
Default is to install minion only
|
||||
``/master-only`` Will only install the master.
|
||||
========================= =====================================================
|
||||
|
||||
.. note::
|
||||
``/start-service`` has been deprecated but will continue to function as
|
||||
expected for the time being.
|
||||
|
@ -147,24 +115,17 @@ Here are some examples of using the silent installer:
|
|||
|
||||
.. code-block:: bat
|
||||
|
||||
# install both minion and master
|
||||
# configure the minion and start both services
|
||||
# Install the Salt Minion
|
||||
# Configure the minion and start the service
|
||||
|
||||
Salt-2016.9.1-Setup-amd64.exe /S /master=yoursaltmaster /minion-name=yourminionname /install-master
|
||||
Salt-Minion-2017.7.1-Py2-AMD64-Setup.exe /S /master=yoursaltmaster /minion-name=yourminionname
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
# install only the master but don't start the salt-master service
|
||||
# Install the Salt Minion
|
||||
# Configure the minion but don't start the minion service
|
||||
|
||||
*-Setup-*.exe /S /master=yoursaltmaster /minion-name=yourminionname
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
# install the minion and master
|
||||
# configure the minion to talk to the local master
|
||||
# start both services
|
||||
|
||||
*-Setup-*.exe /S /master=yoursaltmaster /minion-name=yourminionname /start-minion=0
|
||||
Salt-Minion-2017.7.1-Py3-AMD64-Setup.exe /S /master=yoursaltmaster /minion-name=yourminionname /start-minion=0
|
||||
|
||||
|
||||
Running the Salt Minion on Windows as an Unprivileged User
|
||||
|
@ -268,21 +229,26 @@ code. They are located in the ``pkg\windows`` directory in the Salt repo
|
|||
Scripts:
|
||||
--------
|
||||
|
||||
================= ===========
|
||||
Script Description
|
||||
================= ===========
|
||||
``build_env.ps1`` A PowerShell script that sets up the build environment
|
||||
``build_pkg.bat`` A batch file that builds a Windows installer based on the
|
||||
contents of the ``C:\Python27`` directory
|
||||
``build.bat`` A batch file that fully automates the building of the Windows
|
||||
installer using the above two scripts
|
||||
================= ===========
|
||||
=================== ===========
|
||||
Script Description
|
||||
=================== ===========
|
||||
``build_env_2.ps1`` A PowerShell script that sets up a Python 2 build
|
||||
environment
|
||||
``build_env_3.ps1`` A PowerShell script that sets up a Python 3 build
|
||||
environment
|
||||
``build_pkg.bat`` A batch file that builds a Windows installer based on the
|
||||
contents of the ``C:\Python27`` directory
|
||||
``build.bat`` A batch file that fully automates the building of the
|
||||
Windows installer using the above two scripts
|
||||
=================== ===========
|
||||
|
||||
.. note::
|
||||
The ``build.bat`` and ``build_pkg.bat`` scripts both accept a single
|
||||
parameter to specify the version of Salt that will be displayed in the
|
||||
Windows installer. If no version is passed, the version will be determined
|
||||
using git.
|
||||
The ``build.bat`` and ``build_pkg.bat`` scripts both accept a parameter to
|
||||
specify the version of Salt that will be displayed in the Windows installer.
|
||||
If no version is passed, the version will be determined using git.
|
||||
|
||||
Both scripts also accept an additional parameter to specify the version of
|
||||
Python to use. The default is 2.
|
||||
|
||||
Prerequisite Software
|
||||
---------------------
|
||||
|
@ -316,7 +282,7 @@ Go into the ``salt`` directory and checkout the version of salt to work with
|
|||
.. code-block:: bat
|
||||
|
||||
cd salt
|
||||
git checkout 2016.3
|
||||
git checkout 2017.7.2
|
||||
|
||||
2. Setup the Python Environment
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -327,14 +293,14 @@ PowerShell script.
|
|||
.. code-block:: bat
|
||||
|
||||
cd pkg\windows
|
||||
powershell -file build_env.ps1
|
||||
powershell -file build_env_2.ps1
|
||||
|
||||
.. note::
|
||||
You can also do this from Explorer by navigating to the ``pkg\windows``
|
||||
directory, right clicking the **build_env.ps1** powershell script and
|
||||
directory, right clicking the **build_env_2.ps1** powershell script and
|
||||
selecting **Run with PowerShell**
|
||||
|
||||
This will download and install Python with all the dependencies needed to
|
||||
This will download and install Python 2 with all the dependencies needed to
|
||||
develop and build Salt.
|
||||
|
||||
.. note::
|
||||
|
@ -367,6 +333,10 @@ with ``pip``
|
|||
If ``pip`` is not recognized, you may need to restart your shell to get the
|
||||
updated path
|
||||
|
||||
.. note::
|
||||
If ``pip`` is still not recognized make sure that the Python Scripts folder
|
||||
is in the System ``%PATH%``. (``C:\Python2\Scripts``)
|
||||
|
||||
4. Setup Salt Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -381,9 +351,9 @@ easiest way to set this up is to copy the contents of the
|
|||
md salt
|
||||
xcopy /s /e \Salt-Dev\salt\pkg\windows\buildenv\* \salt\
|
||||
|
||||
Now go into the ``C:\salt\conf`` directory and edit the file name ``minion`` (no
|
||||
extension). You need to configure the master and id parameters in this file.
|
||||
Edit the following lines:
|
||||
Now go into the ``C:\salt\conf`` directory and edit the minion config file named
|
||||
``minion`` (no extension). You need to configure the master and id parameters in
|
||||
this file. Edit the following lines:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
|
@ -414,16 +384,20 @@ Navigate to the root ``salt`` directory and install Salt.
|
|||
-------------------------------
|
||||
|
||||
Navigate to the ``pkg\windows`` directory and run the ``build_pkg.bat``
|
||||
with the build version (2016.3) script.
|
||||
with the build version (2017.7.2) and the Python version as parameters.
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
cd pkg\windows
|
||||
build_pkg.bat 2016.3
|
||||
build_pkg.bat 2017.7.2 2
|
||||
^^^^^^^^ ^
|
||||
| |
|
||||
# build version -- |
|
||||
# python version ------
|
||||
|
||||
.. note::
|
||||
If no version is passed, the ``build_pkg.bat`` will guess the version number
|
||||
using git.
|
||||
using git. If the python version is not passed, the default is 2.
|
||||
|
||||
.. _create-windows-installer-easy:
|
||||
|
||||
|
@ -446,7 +420,7 @@ build.
|
|||
.. code-block:: bat
|
||||
|
||||
cd salt
|
||||
git checkout 2016.3
|
||||
git checkout 2017.7.2
|
||||
|
||||
Then navigate to ``pkg\windows`` and run the ``build.bat`` script with the
|
||||
version you're building.
|
||||
|
@ -454,10 +428,14 @@ version you're building.
|
|||
.. code-block:: bat
|
||||
|
||||
cd pkg\windows
|
||||
build.bat 2016.3
|
||||
build.bat 2017.7.2 3
|
||||
^^^^^^^^ ^
|
||||
| |
|
||||
# build version |
|
||||
# python version --
|
||||
|
||||
This will install everything needed to build a Windows installer for Salt. The
|
||||
binary will be in the ``salt\pkg\windows\installer`` directory.
|
||||
This will install everything needed to build a Windows installer for Salt using
|
||||
Python 3. The binary will be in the ``salt\pkg\windows\installer`` directory.
|
||||
|
||||
.. _test-salt-minion:
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,3 +9,18 @@ controls whether a minion can request that the master revoke its key. When True
|
|||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
||||
|
|
|
@ -579,6 +579,11 @@ Beacons
|
|||
|
||||
- :mod:`salt.beacons.log <salt.beacons.log>`
|
||||
|
||||
Cache
|
||||
-----
|
||||
|
||||
- :mod:`salt.cache.redis_cache <salt.cache.redis_cache>`
|
||||
|
||||
Engines
|
||||
-------
|
||||
|
||||
|
@ -601,7 +606,6 @@ Execution modules
|
|||
- :mod:`salt.modules.icinga2 <salt.modules.icinga2>`
|
||||
- :mod:`salt.modules.logmod <salt.modules.logmod>`
|
||||
- :mod:`salt.modules.mattermost <salt.modules.mattermost>`
|
||||
- :mod:`salt.modules.mattermost <salt.modules.mattermost>`
|
||||
- :mod:`salt.modules.namecheap_dns <salt.modules.namecheap_dns>`
|
||||
- :mod:`salt.modules.namecheap_domains <salt.modules.namecheap_domains>`
|
||||
- :mod:`salt.modules.namecheap_ns <salt.modules.namecheap_ns>`
|
||||
|
|
|
@ -261,17 +261,6 @@ DownloadFileWithProgress $url $file
|
|||
# Install
|
||||
Start_Process_and_test_exitcode "$($ini['Settings']['Scripts2Dir'])\pip.exe" "install --no-index --find-links=$($ini['Settings']['DownloadDir']) $file " "pip install PyCrypto"
|
||||
|
||||
#==============================================================================
|
||||
# Download sitecustomize.py
|
||||
#==============================================================================
|
||||
Write-Output " ----------------------------------------------------------------"
|
||||
Write-Output " - $script_name :: Download sitecustomize . . ."
|
||||
Write-Output " ----------------------------------------------------------------"
|
||||
$file = "sitecustomize.py"
|
||||
$url = "$($ini['Settings']['SaltRepo'])/$file"
|
||||
$file = "$($ini['Settings']['SitePkgs2Dir'])\$file"
|
||||
DownloadFileWithProgress $url $file
|
||||
|
||||
#==============================================================================
|
||||
# Copy DLLs to Python Directory
|
||||
#==============================================================================
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
|
||||
lxml==3.6.0
|
||||
pypiwin32==219
|
||||
win-unicode-console==0.5
|
|
@ -1049,6 +1049,19 @@ VALID_OPTS = {
|
|||
|
||||
# File chunk size for salt-cp
|
||||
'salt_cp_chunk_size': int,
|
||||
|
||||
# Require that the minion sign messages it posts to the master on the event
|
||||
# bus
|
||||
'minion_sign_messages': bool,
|
||||
|
||||
# Have master drop messages from minions for which their signatures do
|
||||
# not verify
|
||||
'drop_messages_signature_fail': bool,
|
||||
|
||||
# Require that payloads from minions have a 'sig' entry
|
||||
# (in other words, require that minions have 'minion_sign_messages'
|
||||
# turned on)
|
||||
'require_minion_sign_messages': bool,
|
||||
}
|
||||
|
||||
# default configurations
|
||||
|
@ -1315,6 +1328,7 @@ DEFAULT_MINION_OPTS = {
|
|||
'salt_cp_chunk_size': 65536,
|
||||
'extmod_whitelist': {},
|
||||
'extmod_blacklist': {},
|
||||
'minion_sign_messages': False,
|
||||
}
|
||||
|
||||
DEFAULT_MASTER_OPTS = {
|
||||
|
@ -1615,6 +1629,8 @@ DEFAULT_MASTER_OPTS = {
|
|||
'django_auth_settings': '',
|
||||
'allow_minion_key_revoke': True,
|
||||
'salt_cp_chunk_size': 98304,
|
||||
'require_minion_sign_messages': False,
|
||||
'drop_messages_signature_fail': False,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ if not CDOME:
|
|||
# Import salt libs
|
||||
import salt.defaults.exitcodes
|
||||
import salt.utils
|
||||
import salt.utils.decorators
|
||||
import salt.payload
|
||||
import salt.transport.client
|
||||
import salt.transport.frame
|
||||
|
@ -138,13 +139,41 @@ def gen_keys(keydir, keyname, keysize, user=None):
|
|||
return priv
|
||||
|
||||
|
||||
@salt.utils.decorators.memoize
|
||||
def _get_key_with_evict(path, timestamp):
|
||||
'''
|
||||
Load a key from disk. `timestamp` above is intended to be the timestamp
|
||||
of the file's last modification. This fn is memoized so if it is called with the
|
||||
same path and timestamp (the file's last modified time) the second time
|
||||
the result is returned from the memoiziation. If the file gets modified
|
||||
then the params are different and the key is loaded from disk.
|
||||
'''
|
||||
log.debug('salt.crypt._get_key_with_evict: Loading private key')
|
||||
with salt.utils.fopen(path) as f:
|
||||
key = RSA.importKey(f.read())
|
||||
return key
|
||||
|
||||
|
||||
def _get_rsa_key(path):
|
||||
'''
|
||||
Read a key off the disk. Poor man's simple cache in effect here,
|
||||
we memoize the result of calling _get_rsa_with_evict. This means
|
||||
the first time _get_key_with_evict is called with a path and a timestamp
|
||||
the result is cached. If the file (the private key) does not change
|
||||
then its timestamp will not change and the next time the result is returned
|
||||
from the cache. If the key DOES change the next time _get_rsa_with_evict
|
||||
is called it is called with different parameters and the fn is run fully to
|
||||
retrieve the key from disk.
|
||||
'''
|
||||
log.debug('salt.crypt._get_rsa_key: Loading private key')
|
||||
return _get_key_with_evict(path, str(os.path.getmtime(path)))
|
||||
|
||||
|
||||
def sign_message(privkey_path, message):
|
||||
'''
|
||||
Use Crypto.Signature.PKCS1_v1_5 to sign a message. Returns the signature.
|
||||
'''
|
||||
log.debug('salt.crypt.sign_message: Loading private key')
|
||||
with salt.utils.fopen(privkey_path) as f:
|
||||
key = RSA.importKey(f.read())
|
||||
key = _get_rsa_key(privkey_path)
|
||||
log.debug('salt.crypt.sign_message: Signing message.')
|
||||
signer = PKCS1_v1_5.new(key)
|
||||
return signer.sign(SHA.new(message))
|
||||
|
|
|
@ -255,27 +255,12 @@ def access_keys(opts):
|
|||
acl_users.add(opts['user'])
|
||||
acl_users.add(salt.utils.get_user())
|
||||
if opts['client_acl_verify'] and HAS_PWD:
|
||||
log.profile('Beginning pwd.getpwall() call in masterarpi acess_keys function')
|
||||
log.profile('Beginning pwd.getpwall() call in masterarpi access_keys function')
|
||||
for user in pwd.getpwall():
|
||||
users.append(user.pw_name)
|
||||
log.profile('End pwd.getpwall() call in masterarpi acess_keys function')
|
||||
log.profile('End pwd.getpwall() call in masterarpi access_keys function')
|
||||
for user in acl_users:
|
||||
log.info(
|
||||
'Preparing the {0} key for local communication'.format(
|
||||
user
|
||||
)
|
||||
)
|
||||
|
||||
if opts['client_acl_verify'] and HAS_PWD:
|
||||
if user not in users:
|
||||
try:
|
||||
log.profile('Beginning pwd.getpnam() call in masterarpi acess_keys function')
|
||||
user = pwd.getpwnam(user).pw_name
|
||||
log.profile('Beginning pwd.getpwnam() call in masterarpi acess_keys function')
|
||||
except KeyError:
|
||||
log.error('ACL user {0} is not available'.format(user))
|
||||
continue
|
||||
|
||||
log.info('Preparing the %s key for local communication', user)
|
||||
keys[user] = mk_key(opts, user)
|
||||
|
||||
# Check other users matching ACL patterns
|
||||
|
@ -771,6 +756,7 @@ class RemoteFuncs(object):
|
|||
# If the return data is invalid, just ignore it
|
||||
if any(key not in load for key in ('return', 'jid', 'id')):
|
||||
return False
|
||||
|
||||
if load['jid'] == 'req':
|
||||
# The minion is returning a standalone job, request a jobid
|
||||
prep_fstr = '{0}.prep_jid'.format(self.opts['master_job_cache'])
|
||||
|
|
|
@ -312,9 +312,10 @@ def start(transport='zmq',
|
|||
if not certificate:
|
||||
log.critical('Please use a certificate, or disable the security.')
|
||||
return
|
||||
priv_key, verify_key = napalm_logs.utils.authenticate(certificate,
|
||||
address=auth_address,
|
||||
port=auth_port)
|
||||
auth = napalm_logs.utils.ClientAuth(certificate,
|
||||
address=auth_address,
|
||||
port=auth_port)
|
||||
|
||||
transport_recv_fun = _get_transport_recv(name=transport,
|
||||
address=address,
|
||||
port=port)
|
||||
|
@ -330,7 +331,7 @@ def start(transport='zmq',
|
|||
log.debug('Received from napalm-logs:')
|
||||
log.debug(raw_object)
|
||||
if not disable_security:
|
||||
dict_object = napalm_logs.utils.decrypt(raw_object, verify_key, priv_key)
|
||||
dict_object = auth.decrypt(raw_object)
|
||||
else:
|
||||
dict_object = napalm_logs.utils.unserialize(raw_object)
|
||||
try:
|
||||
|
|
|
@ -538,7 +538,7 @@ def update():
|
|||
os.makedirs(env_cachedir)
|
||||
new_envs = envs(ignore_cache=True)
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
with salt.utils.fopen(env_cache, 'w+') as fp_:
|
||||
with salt.utils.fopen(env_cache, 'wb+') as fp_:
|
||||
fp_.write(serial.dumps(new_envs))
|
||||
log.trace('Wrote env cache data to {0}'.format(env_cache))
|
||||
|
||||
|
|
|
@ -453,7 +453,7 @@ def update():
|
|||
os.makedirs(env_cachedir)
|
||||
new_envs = envs(ignore_cache=True)
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
with salt.utils.fopen(env_cache, 'w+') as fp_:
|
||||
with salt.utils.fopen(env_cache, 'wb+') as fp_:
|
||||
fp_.write(serial.dumps(new_envs))
|
||||
log.trace('Wrote env cache data to {0}'.format(env_cache))
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import signal
|
|||
import stat
|
||||
import logging
|
||||
import multiprocessing
|
||||
import salt.serializers.msgpack
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
|
@ -1109,8 +1110,10 @@ class AESFuncs(object):
|
|||
)
|
||||
)
|
||||
return False
|
||||
|
||||
if 'tok' in load:
|
||||
load.pop('tok')
|
||||
|
||||
return load
|
||||
|
||||
def _master_tops(self, load):
|
||||
|
@ -1396,6 +1399,24 @@ class AESFuncs(object):
|
|||
|
||||
:param dict load: The minion payload
|
||||
'''
|
||||
if self.opts['require_minion_sign_messages'] and 'sig' not in load:
|
||||
log.critical('_return: Master is requiring minions to sign their messages, but there is no signature in this payload from {0}.'.format(load['id']))
|
||||
return False
|
||||
|
||||
if 'sig' in load:
|
||||
log.trace('Verifying signed event publish from minion')
|
||||
sig = load.pop('sig')
|
||||
this_minion_pubkey = os.path.join(self.opts['pki_dir'], 'minions/{0}'.format(load['id']))
|
||||
serialized_load = salt.serializers.msgpack.serialize(load)
|
||||
if not salt.crypt.verify_signature(this_minion_pubkey, serialized_load, sig):
|
||||
log.info('Failed to verify event signature from minion {0}.'.format(load['id']))
|
||||
if self.opts['drop_messages_signature_fail']:
|
||||
log.critical('Drop_messages_signature_fail is enabled, dropping message from {0}'.format(load['id']))
|
||||
return False
|
||||
else:
|
||||
log.info('But \'drop_message_signature_fail\' is disabled, so message is still accepted.')
|
||||
load['sig'] = sig
|
||||
|
||||
try:
|
||||
salt.utils.job.store_job(
|
||||
self.opts, load, event=self.event, mminion=self.mminion)
|
||||
|
@ -1439,6 +1460,9 @@ class AESFuncs(object):
|
|||
ret['fun_args'] = load['arg']
|
||||
if 'out' in load:
|
||||
ret['out'] = load['out']
|
||||
if 'sig' in load:
|
||||
ret['sig'] = load['sig']
|
||||
|
||||
self._return(ret)
|
||||
|
||||
def minion_runner(self, clear_load):
|
||||
|
|
|
@ -20,6 +20,7 @@ import contextlib
|
|||
import multiprocessing
|
||||
from random import randint, shuffle
|
||||
from stat import S_IMODE
|
||||
import salt.serializers.msgpack
|
||||
|
||||
# Import Salt Libs
|
||||
# pylint: disable=import-error,no-name-in-module,redefined-builtin
|
||||
|
@ -1224,11 +1225,25 @@ class Minion(MinionBase):
|
|||
return functions, returners, errors, executors
|
||||
|
||||
def _send_req_sync(self, load, timeout):
|
||||
|
||||
if self.opts['minion_sign_messages']:
|
||||
log.trace('Signing event to be published onto the bus.')
|
||||
minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load))
|
||||
load['sig'] = sig
|
||||
|
||||
channel = salt.transport.Channel.factory(self.opts)
|
||||
return channel.send(load, timeout=timeout)
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def _send_req_async(self, load, timeout):
|
||||
|
||||
if self.opts['minion_sign_messages']:
|
||||
log.trace('Signing event to be published onto the bus.')
|
||||
minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load))
|
||||
load['sig'] = sig
|
||||
|
||||
channel = salt.transport.client.AsyncReqChannel.factory(self.opts)
|
||||
ret = yield channel.send(load, timeout=timeout)
|
||||
raise tornado.gen.Return(ret)
|
||||
|
|
|
@ -2481,9 +2481,6 @@ def create(image,
|
|||
except Exception:
|
||||
pull(image, client_timeout=client_timeout)
|
||||
|
||||
if name is not None and kwargs.get('hostname') is None:
|
||||
kwargs['hostname'] = name
|
||||
|
||||
kwargs, unused_kwargs = _get_create_kwargs(
|
||||
image=image,
|
||||
skip_translate=skip_translate,
|
||||
|
|
|
@ -592,9 +592,7 @@ def install(name=None,
|
|||
# Handle version kwarg for a single package target
|
||||
if pkgs is None and sources is None:
|
||||
version_num = kwargs.get('version')
|
||||
if version_num:
|
||||
pkg_params = {name: version_num}
|
||||
else:
|
||||
if not version_num:
|
||||
version_num = ''
|
||||
if slot is not None:
|
||||
version_num += ':{0}'.format(slot)
|
||||
|
|
|
@ -528,15 +528,6 @@ def install(name=None,
|
|||
if pkg_params is None or len(pkg_params) == 0:
|
||||
return {}
|
||||
|
||||
version_num = kwargs.get('version')
|
||||
if version_num:
|
||||
if pkgs is None and sources is None:
|
||||
# Allow 'version' to work for single package target
|
||||
pkg_params = {name: version_num}
|
||||
else:
|
||||
log.warning('\'version\' parameter will be ignored for multiple '
|
||||
'package targets')
|
||||
|
||||
if 'root' in kwargs:
|
||||
pkg_params['-r'] = kwargs['root']
|
||||
|
||||
|
|
|
@ -1243,13 +1243,6 @@ def install(name=None,
|
|||
return {}
|
||||
|
||||
version_num = kwargs.get('version')
|
||||
if version_num:
|
||||
if pkgs is None and sources is None:
|
||||
# Allow "version" to work for single package target
|
||||
pkg_params = {name: version_num}
|
||||
else:
|
||||
log.warning('"version" parameter will be ignored for multiple '
|
||||
'package targets')
|
||||
|
||||
old = list_pkgs(versions_as_list=False) if not downloadonly else list_downloaded()
|
||||
# Use of __context__ means no duplicate work here, just accessing
|
||||
|
|
|
@ -1085,13 +1085,6 @@ def install(name=None,
|
|||
return {}
|
||||
|
||||
version_num = Wildcard(__zypper__)(name, version)
|
||||
if version_num:
|
||||
if pkgs is None and sources is None:
|
||||
# Allow "version" to work for single package target
|
||||
pkg_params = {name: version_num}
|
||||
else:
|
||||
log.warning("'version' parameter will be ignored for multiple package targets")
|
||||
|
||||
if pkg_type == 'repository':
|
||||
targets = []
|
||||
for param, version_num in six.iteritems(pkg_params):
|
||||
|
|
|
@ -2213,8 +2213,7 @@ class GitBase(object):
|
|||
if refresh_env_cache:
|
||||
new_envs = self.envs(ignore_cache=True)
|
||||
serial = salt.payload.Serial(self.opts)
|
||||
mode = 'wb+' if six.PY3 else 'w+'
|
||||
with salt.utils.fopen(self.env_cache, mode) as fp_:
|
||||
with salt.utils.fopen(self.env_cache, 'wb+') as fp_:
|
||||
fp_.write(serial.dumps(new_envs))
|
||||
log.trace('Wrote env cache data to {0}'.format(self.env_cache))
|
||||
|
||||
|
|
|
@ -324,6 +324,7 @@ from __future__ import absolute_import, with_statement
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
import copy
|
||||
import signal
|
||||
import datetime
|
||||
import itertools
|
||||
|
@ -827,7 +828,7 @@ class Schedule(object):
|
|||
kwargs = {}
|
||||
if 'kwargs' in data:
|
||||
kwargs = data['kwargs']
|
||||
ret['fun_args'].append(data['kwargs'])
|
||||
ret['fun_args'].append(copy.deepcopy(kwargs))
|
||||
|
||||
if func not in self.functions:
|
||||
ret['return'] = self.functions.missing_fun_string(func)
|
||||
|
@ -884,9 +885,9 @@ class Schedule(object):
|
|||
ret['success'] = False
|
||||
ret['retcode'] = 254
|
||||
finally:
|
||||
try:
|
||||
# Only attempt to return data to the master
|
||||
# if the scheduled job is running on a minion.
|
||||
# Only attempt to return data to the master
|
||||
# if the scheduled job is running on a minion.
|
||||
if '__role' in self.opts and self.opts['__role'] == 'minion':
|
||||
if 'return_job' in data and not data['return_job']:
|
||||
pass
|
||||
else:
|
||||
|
@ -908,9 +909,13 @@ class Schedule(object):
|
|||
elif '__role' in self.opts and self.opts['__role'] == 'master':
|
||||
event = salt.utils.event.get_master_event(self.opts,
|
||||
self.opts['sock_dir'])
|
||||
event.fire_event(load, '__schedule_return')
|
||||
try:
|
||||
event.fire_event(load, '__schedule_return')
|
||||
except Exception as exc:
|
||||
log.exception("Unhandled exception firing event: {0}".format(exc))
|
||||
|
||||
log.debug('schedule.handle_func: Removing {0}'.format(proc_fn))
|
||||
log.debug('schedule.handle_func: Removing {0}'.format(proc_fn))
|
||||
try:
|
||||
os.unlink(proc_fn)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST or exc.errno == errno.ENOENT:
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
# Import salt libs
|
||||
import salt.ext.six as six
|
||||
|
||||
try:
|
||||
if sys.version_info >= (3,):
|
||||
# Python 3
|
||||
|
@ -105,82 +108,89 @@ if NO_MOCK is False:
|
|||
NO_MOCK_REASON = 'you need to upgrade your mock version to >= 0.8.0'
|
||||
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
from mock import mock_open
|
||||
else:
|
||||
# backport mock_open from the python 3 unittest.mock library so that we can
|
||||
# mock read, readline, readlines, and file iteration properly
|
||||
# backport mock_open from the python 3 unittest.mock library so that we can
|
||||
# mock read, readline, readlines, and file iteration properly
|
||||
|
||||
file_spec = None
|
||||
file_spec = None
|
||||
|
||||
def _iterate_read_data(read_data):
|
||||
# Helper for mock_open:
|
||||
# Retrieve lines from read_data via a generator so that separate calls to
|
||||
# readline, read, and readlines are properly interleaved
|
||||
|
||||
def _iterate_read_data(read_data):
|
||||
# Helper for mock_open:
|
||||
# Retrieve lines from read_data via a generator so that separate calls to
|
||||
# readline, read, and readlines are properly interleaved
|
||||
if six.PY3 and isinstance(read_data, six.binary_type):
|
||||
data_as_list = ['{0}\n'.format(l.decode(__salt_system_encoding__)) for l in read_data.split(six.b('\n'))]
|
||||
else:
|
||||
data_as_list = ['{0}\n'.format(l) for l in read_data.split('\n')]
|
||||
|
||||
if data_as_list[-1] == '\n':
|
||||
# If the last line ended in a newline, the list comprehension will have an
|
||||
# extra entry that's just a newline. Remove this.
|
||||
data_as_list = data_as_list[:-1]
|
||||
else:
|
||||
# If there wasn't an extra newline by itself, then the file being
|
||||
# emulated doesn't have a newline to end the last line remove the
|
||||
# newline that our naive format() added
|
||||
data_as_list[-1] = data_as_list[-1][:-1]
|
||||
if data_as_list[-1] == '\n':
|
||||
# If the last line ended in a newline, the list comprehension will have an
|
||||
# extra entry that's just a newline. Remove this.
|
||||
data_as_list = data_as_list[:-1]
|
||||
else:
|
||||
# If there wasn't an extra newline by itself, then the file being
|
||||
# emulated doesn't have a newline to end the last line remove the
|
||||
# newline that our naive format() added
|
||||
data_as_list[-1] = data_as_list[-1][:-1]
|
||||
|
||||
for line in data_as_list:
|
||||
for line in data_as_list:
|
||||
yield line
|
||||
|
||||
|
||||
def mock_open(mock=None, read_data=''):
|
||||
"""
|
||||
A helper function to create a mock to replace the use of `open`. It works
|
||||
for `open` called directly or used as a context manager.
|
||||
|
||||
The `mock` argument is the mock object to configure. If `None` (the
|
||||
default) then a `MagicMock` will be created for you, with the API limited
|
||||
to methods or attributes available on standard file handles.
|
||||
|
||||
`read_data` is a string for the `read` methoddline`, and `readlines` of the
|
||||
file handle to return. This is an empty string by default.
|
||||
"""
|
||||
def _readlines_side_effect(*args, **kwargs):
|
||||
if handle.readlines.return_value is not None:
|
||||
return handle.readlines.return_value
|
||||
return list(_data)
|
||||
|
||||
def _read_side_effect(*args, **kwargs):
|
||||
if handle.read.return_value is not None:
|
||||
return handle.read.return_value
|
||||
return ''.join(_data)
|
||||
|
||||
def _readline_side_effect():
|
||||
if handle.readline.return_value is not None:
|
||||
while True:
|
||||
yield handle.readline.return_value
|
||||
for line in _data:
|
||||
yield line
|
||||
|
||||
def mock_open(mock=None, read_data=''):
|
||||
"""
|
||||
A helper function to create a mock to replace the use of `open`. It works
|
||||
for `open` called directly or used as a context manager.
|
||||
|
||||
The `mock` argument is the mock object to configure. If `None` (the
|
||||
default) then a `MagicMock` will be created for you, with the API limited
|
||||
to methods or attributes available on standard file handles.
|
||||
|
||||
`read_data` is a string for the `read` methoddline`, and `readlines` of the
|
||||
file handle to return. This is an empty string by default.
|
||||
"""
|
||||
def _readlines_side_effect(*args, **kwargs):
|
||||
if handle.readlines.return_value is not None:
|
||||
return handle.readlines.return_value
|
||||
return list(_data)
|
||||
|
||||
def _read_side_effect(*args, **kwargs):
|
||||
if handle.read.return_value is not None:
|
||||
return handle.read.return_value
|
||||
return ''.join(_data)
|
||||
|
||||
def _readline_side_effect():
|
||||
if handle.readline.return_value is not None:
|
||||
while True:
|
||||
yield handle.readline.return_value
|
||||
for line in _data:
|
||||
yield line
|
||||
|
||||
global file_spec
|
||||
if file_spec is None:
|
||||
global file_spec
|
||||
if file_spec is None:
|
||||
if six.PY3:
|
||||
import _io
|
||||
file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
|
||||
else:
|
||||
file_spec = file # pylint: disable=undefined-variable
|
||||
|
||||
if mock is None:
|
||||
mock = MagicMock(name='open', spec=open)
|
||||
if mock is None:
|
||||
mock = MagicMock(name='open', spec=open)
|
||||
|
||||
handle = MagicMock(spec=file_spec)
|
||||
handle.__enter__.return_value = handle
|
||||
handle = MagicMock(spec=file_spec)
|
||||
handle.__enter__.return_value = handle
|
||||
|
||||
_data = _iterate_read_data(read_data)
|
||||
_data = _iterate_read_data(read_data)
|
||||
|
||||
handle.write.return_value = None
|
||||
handle.read.return_value = None
|
||||
handle.readline.return_value = None
|
||||
handle.readlines.return_value = None
|
||||
handle.write.return_value = None
|
||||
handle.read.return_value = None
|
||||
handle.readline.return_value = None
|
||||
handle.readlines.return_value = None
|
||||
|
||||
handle.read.side_effect = _read_side_effect
|
||||
handle.readline.side_effect = _readline_side_effect()
|
||||
handle.readlines.side_effect = _readlines_side_effect
|
||||
# This is salt specific and not in the upstream mock
|
||||
handle.read.side_effect = _read_side_effect
|
||||
handle.readline.side_effect = _readline_side_effect()
|
||||
handle.readlines.side_effect = _readlines_side_effect
|
||||
|
||||
mock.return_value = handle
|
||||
return mock
|
||||
mock.return_value = handle
|
||||
return mock
|
||||
|
|
|
@ -522,24 +522,36 @@ class TestCustomExtensions(TestCase):
|
|||
unique = set(dataset)
|
||||
env = Environment(extensions=[SerializerExtension])
|
||||
env.filters.update(JinjaFilter.salt_jinja_filters)
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
if six.PY3:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'{}").split("', '")
|
||||
self.assertEqual(rendered, list(unique))
|
||||
else:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
|
||||
def test_unique_tuple(self):
|
||||
dataset = ('foo', 'foo', 'bar')
|
||||
unique = set(dataset)
|
||||
env = Environment(extensions=[SerializerExtension])
|
||||
env.filters.update(JinjaFilter.salt_jinja_filters)
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
if six.PY3:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'{}").split("', '")
|
||||
self.assertEqual(rendered, list(unique))
|
||||
else:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
|
||||
def test_unique_list(self):
|
||||
dataset = ['foo', 'foo', 'bar']
|
||||
unique = ['foo', 'bar']
|
||||
env = Environment(extensions=[SerializerExtension])
|
||||
env.filters.update(JinjaFilter.salt_jinja_filters)
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
if six.PY3:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'[]").split("', '")
|
||||
self.assertEqual(rendered, unique)
|
||||
else:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
|
||||
def test_serialize_json(self):
|
||||
dataset = {
|
||||
|
|
|
@ -97,8 +97,9 @@ class CryptTestCase(TestCase):
|
|||
salt.utils.fopen.assert_has_calls([open_priv_wb, open_pub_wb], any_order=True)
|
||||
|
||||
def test_sign_message(self):
|
||||
with patch('salt.utils.fopen', mock_open(read_data=PRIVKEY_DATA)):
|
||||
self.assertEqual(SIG, crypt.sign_message('/keydir/keyname.pem', MSG))
|
||||
key = Crypto.PublicKey.RSA.importKey(PRIVKEY_DATA)
|
||||
with patch('salt.crypt._get_rsa_key', return_value=key):
|
||||
self.assertEqual(SIG, salt.crypt.sign_message('/keydir/keyname.pem', MSG))
|
||||
|
||||
def test_verify_signature(self):
|
||||
with patch('salt.utils.fopen', mock_open(read_data=PUBKEY_DATA)):
|
||||
|
|
Loading…
Add table
Reference in a new issue