8bc8eaf64f
- fix detection of base products in SLE11 * 0030-Bugfix-on-SLE11-series-base-product-reported-as-addi.patch - fix rpm info for SLE11 * 0031-Only-use-LONGSIZE-in-rpm.info-if-available.-Otherwis.patch * 0032-Add-error-check-when-retcode-is-0-but-stderr-is-pres.patch - fix init system detection for SLE11 * 0033-fixing-init-system-dectection-on-sles-11-refs-31617.patch - Re-add corrected patch: 0029-Make-use-of-checksum-configurable-defaults-to-MD5-SH.patch - Make checksum configurable (upstream still wants md5, we suggest sha256). bsc#955373 Add: 0029-Make-use-of-checksum-configurable-defaults-to-MD5-SH.patch OBS-URL: https://build.opensuse.org/request/show/370869 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=62
735 lines
26 KiB
Diff
735 lines
26 KiB
Diff
From 2220c5a0ae800988bf83c39b458a8747f01186c0 Mon Sep 17 00:00:00 2001
|
|
From: Bo Maryniuk <bo@suse.de>
|
|
Date: Fri, 12 Feb 2016 16:16:12 +0100
|
|
Subject: [PATCH 29/29] Make use of checksum configurable (defaults to MD5,
|
|
SHA256 suggested)
|
|
|
|
Set config hash_type to SHA1
|
|
Set default hash as SHA1 in config and explain why.
|
|
Use hash_type configuration for the Cloud
|
|
Use configurable hash_type for general Key fingerprinting
|
|
Use SHA1 hash by default
|
|
Use SHA1 hash by default in Tomcat module, refactor for support different algorithms
|
|
Use SHA1 by default instead of MD5
|
|
Remove SHA1 to SHA265 by default
|
|
Add note to the Tomcat module for SHA256
|
|
Remove sha1 to sha265
|
|
Remove SHA1 for SHA256
|
|
Remove SHA1 in favor of SHA256
|
|
Use MD5 hash algorithm by default (until deprecated)
|
|
Create a mixin class that will be reused in the similar instances (daemons)
|
|
Use mixin for the daemon classes
|
|
Report environment failure, if any
|
|
Verify if hash_type is using vulnerable algorithms
|
|
Standardize logging
|
|
Add daemons unit test to verify hash_type settings
|
|
Fix PyLint
|
|
---
|
|
conf/master | 5 +-
|
|
conf/minion | 8 +-
|
|
conf/proxy | 9 +-
|
|
salt/cli/daemons.py | 83 +++++++++++++-----
|
|
salt/cloud/__init__.py | 4 +-
|
|
salt/crypt.py | 10 +--
|
|
salt/key.py | 4 +-
|
|
salt/modules/key.py | 10 +--
|
|
salt/modules/tomcat.py | 26 ++----
|
|
salt/modules/win_file.py | 2 +-
|
|
salt/utils/__init__.py | 13 +--
|
|
salt/utils/cloud.py | 3 +-
|
|
tests/unit/daemons_test.py | 209 +++++++++++++++++++++++++++++++++++++++++++++
|
|
13 files changed, 319 insertions(+), 67 deletions(-)
|
|
create mode 100644 tests/unit/daemons_test.py
|
|
|
|
diff --git a/conf/master b/conf/master
|
|
index 36657e8..cf05ec4 100644
|
|
--- a/conf/master
|
|
+++ b/conf/master
|
|
@@ -466,9 +466,12 @@ syndic_user: salt
|
|
#default_top: base
|
|
|
|
# The hash_type is the hash to use when discovering the hash of a file on
|
|
-# the master server. The default is md5, but sha1, sha224, sha256, sha384
|
|
+# the master server. The default is md5 but sha1, sha224, sha256, sha384
|
|
# and sha512 are also supported.
|
|
#
|
|
+# WARNING: While md5 is supported, do not use it due to the high chance
|
|
+# of possible collisions and thus security breach.
|
|
+#
|
|
# Prior to changing this value, the master should be stopped and all Salt
|
|
# caches should be cleared.
|
|
#hash_type: md5
|
|
diff --git a/conf/minion b/conf/minion
|
|
index 2307f70..e17ec61 100644
|
|
--- a/conf/minion
|
|
+++ b/conf/minion
|
|
@@ -440,12 +440,14 @@
|
|
#fileserver_limit_traversal: False
|
|
|
|
# The hash_type is the hash to use when discovering the hash of a file in
|
|
-# the local fileserver. The default is md5, but sha1, sha224, sha256, sha384
|
|
-# and sha512 are also supported.
|
|
+# the local fileserver. The default is sha256, sha224, sha384 and sha512 are also supported.
|
|
+#
|
|
+# WARNING: While md5 and sha1 are also supported, do not use it due to the high chance
|
|
+# of possible collisions and thus security breach.
|
|
#
|
|
# Warning: Prior to changing this value, the minion should be stopped and all
|
|
# Salt caches should be cleared.
|
|
-#hash_type: md5
|
|
+#hash_type: sha256
|
|
|
|
# The Salt pillar is searched for locally if file_client is set to local. If
|
|
# this is the case, and pillar data is defined, then the pillar_roots need to
|
|
diff --git a/conf/proxy b/conf/proxy
|
|
index 472df35..0de6af8 100644
|
|
--- a/conf/proxy
|
|
+++ b/conf/proxy
|
|
@@ -419,12 +419,15 @@
|
|
#fileserver_limit_traversal: False
|
|
|
|
# The hash_type is the hash to use when discovering the hash of a file in
|
|
-# the local fileserver. The default is md5, but sha1, sha224, sha256, sha384
|
|
-# and sha512 are also supported.
|
|
+# the local fileserver. The default is sha256 but sha224, sha384 and sha512
|
|
+# are also supported.
|
|
+#
|
|
+# WARNING: While md5 and sha1 are also supported, do not use it due to the high chance
|
|
+# of possible collisions and thus security breach.
|
|
#
|
|
# Warning: Prior to changing this value, the minion should be stopped and all
|
|
# Salt caches should be cleared.
|
|
-#hash_type: md5
|
|
+#hash_type: sha256
|
|
|
|
# The Salt pillar is searched for locally if file_client is set to local. If
|
|
# this is the case, and pillar data is defined, then the pillar_roots need to
|
|
diff --git a/salt/cli/daemons.py b/salt/cli/daemons.py
|
|
index 7f8b8c8..b0e7b20 100644
|
|
--- a/salt/cli/daemons.py
|
|
+++ b/salt/cli/daemons.py
|
|
@@ -58,7 +58,50 @@ from salt.exceptions import SaltSystemExit
|
|
logger = salt.log.setup.logging.getLogger(__name__)
|
|
|
|
|
|
-class Master(parsers.MasterOptionParser):
|
|
+class DaemonsMixin(object): # pylint: disable=no-init
|
|
+ '''
|
|
+ Uses the same functions for all daemons
|
|
+ '''
|
|
+ def verify_hash_type(self):
|
|
+ '''
|
|
+ Verify and display a nag-messsage to the log if vulnerable hash-type is used.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+ if self.config['hash_type'].lower() in ['md5', 'sha1']:
|
|
+ logger.warning('IMPORTANT: Do not use {h_type} hashing algorithm! Please set "hash_type" to '
|
|
+ 'SHA256 in Salt {d_name} config!'.format(
|
|
+ h_type=self.config['hash_type'], d_name=self.__class__.__name__))
|
|
+
|
|
+ def start_log_info(self):
|
|
+ '''
|
|
+ Say daemon starting.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+ logger.info('The Salt {d_name} is starting up'.format(d_name=self.__class__.__name__))
|
|
+
|
|
+ def shutdown_log_info(self):
|
|
+ '''
|
|
+ Say daemon shutting down.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+ logger.info('The Salt {d_name} is shut down'.format(d_name=self.__class__.__name__))
|
|
+
|
|
+ def environment_failure(self, error):
|
|
+ '''
|
|
+ Log environment failure for the daemon and exit with the error code.
|
|
+
|
|
+ :param error:
|
|
+ :return:
|
|
+ '''
|
|
+ logger.exception('Failed to create environment for {d_name}: {reason}'.format(
|
|
+ d_name=self.__class__.__name__, reason=error.message))
|
|
+ sys.exit(error.errno)
|
|
+
|
|
+
|
|
+class Master(parsers.MasterOptionParser, DaemonsMixin): # pylint: disable=no-init
|
|
'''
|
|
Creates a master server
|
|
'''
|
|
@@ -114,8 +157,7 @@ class Master(parsers.MasterOptionParser):
|
|
for syndic_file in os.listdir(self.config['syndic_dir']):
|
|
os.remove(os.path.join(self.config['syndic_dir'], syndic_file))
|
|
except OSError as err:
|
|
- logger.exception('Failed to prepare salt environment')
|
|
- sys.exit(err.errno)
|
|
+ self.environment_failure(err)
|
|
|
|
self.setup_logfile_logger()
|
|
verify_log(self.config)
|
|
@@ -153,17 +195,18 @@ class Master(parsers.MasterOptionParser):
|
|
'''
|
|
self.prepare()
|
|
if check_user(self.config['user']):
|
|
- logger.info('The salt master is starting up')
|
|
+ self.verify_hash_type()
|
|
+ self.start_log_info()
|
|
self.master.start()
|
|
|
|
def shutdown(self):
|
|
'''
|
|
If sub-classed, run any shutdown operations on this method.
|
|
'''
|
|
- logger.info('The salt master is shut down')
|
|
+ self.shutdown_log_info()
|
|
|
|
|
|
-class Minion(parsers.MinionOptionParser): # pylint: disable=no-init
|
|
+class Minion(parsers.MinionOptionParser, DaemonsMixin): # pylint: disable=no-init
|
|
'''
|
|
Create a minion server
|
|
'''
|
|
@@ -226,8 +269,7 @@ class Minion(parsers.MinionOptionParser): # pylint: disable=no-init
|
|
verify_files([logfile], self.config['user'])
|
|
os.umask(current_umask)
|
|
except OSError as err:
|
|
- logger.exception('Failed to prepare salt environment')
|
|
- sys.exit(err.errno)
|
|
+ self.environment_failure(err)
|
|
|
|
self.setup_logfile_logger()
|
|
verify_log(self.config)
|
|
@@ -273,7 +315,8 @@ class Minion(parsers.MinionOptionParser): # pylint: disable=no-init
|
|
try:
|
|
self.prepare()
|
|
if check_user(self.config['user']):
|
|
- logger.info('The salt minion is starting up')
|
|
+ self.verify_hash_type()
|
|
+ self.start_log_info()
|
|
self.minion.tune_in()
|
|
finally:
|
|
self.shutdown()
|
|
@@ -310,10 +353,10 @@ class Minion(parsers.MinionOptionParser): # pylint: disable=no-init
|
|
'''
|
|
If sub-classed, run any shutdown operations on this method.
|
|
'''
|
|
- logger.info('The salt minion is shut down')
|
|
+ self.shutdown_log_info()
|
|
|
|
|
|
-class ProxyMinion(parsers.ProxyMinionOptionParser): # pylint: disable=no-init
|
|
+class ProxyMinion(parsers.ProxyMinionOptionParser, DaemonsMixin): # pylint: disable=no-init
|
|
'''
|
|
Create a proxy minion server
|
|
'''
|
|
@@ -388,8 +431,7 @@ class ProxyMinion(parsers.ProxyMinionOptionParser): # pylint: disable=no-init
|
|
os.umask(current_umask)
|
|
|
|
except OSError as err:
|
|
- logger.exception('Failed to prepare salt environment')
|
|
- sys.exit(err.errno)
|
|
+ self.environment_failure(err)
|
|
|
|
self.setup_logfile_logger()
|
|
verify_log(self.config)
|
|
@@ -431,7 +473,8 @@ class ProxyMinion(parsers.ProxyMinionOptionParser): # pylint: disable=no-init
|
|
try:
|
|
self.prepare()
|
|
if check_user(self.config['user']):
|
|
- logger.info('The proxy minion is starting up')
|
|
+ self.verify_hash_type()
|
|
+ self.start_log_info()
|
|
self.minion.tune_in()
|
|
except (KeyboardInterrupt, SaltSystemExit) as exc:
|
|
logger.warn('Stopping the Salt Proxy Minion')
|
|
@@ -449,10 +492,10 @@ class ProxyMinion(parsers.ProxyMinionOptionParser): # pylint: disable=no-init
|
|
if hasattr(self, 'minion') and 'proxymodule' in self.minion.opts:
|
|
proxy_fn = self.minion.opts['proxymodule'].loaded_base_name + '.shutdown'
|
|
self.minion.opts['proxymodule'][proxy_fn](self.minion.opts)
|
|
- logger.info('The proxy minion is shut down')
|
|
+ self.shutdown_log_info()
|
|
|
|
|
|
-class Syndic(parsers.SyndicOptionParser):
|
|
+class Syndic(parsers.SyndicOptionParser, DaemonsMixin): # pylint: disable=no-init
|
|
'''
|
|
Create a syndic server
|
|
'''
|
|
@@ -488,8 +531,7 @@ class Syndic(parsers.SyndicOptionParser):
|
|
verify_files([logfile], self.config['user'])
|
|
os.umask(current_umask)
|
|
except OSError as err:
|
|
- logger.exception('Failed to prepare salt environment')
|
|
- sys.exit(err.errno)
|
|
+ self.environment_failure(err)
|
|
|
|
self.setup_logfile_logger()
|
|
verify_log(self.config)
|
|
@@ -521,7 +563,8 @@ class Syndic(parsers.SyndicOptionParser):
|
|
'''
|
|
self.prepare()
|
|
if check_user(self.config['user']):
|
|
- logger.info('The salt syndic is starting up')
|
|
+ self.verify_hash_type()
|
|
+ self.start_log_info()
|
|
try:
|
|
self.syndic.tune_in()
|
|
except KeyboardInterrupt:
|
|
@@ -532,4 +575,4 @@ class Syndic(parsers.SyndicOptionParser):
|
|
'''
|
|
If sub-classed, run any shutdown operations on this method.
|
|
'''
|
|
- logger.info('The salt syndic is shut down')
|
|
+ self.shutdown_log_info()
|
|
diff --git a/salt/cloud/__init__.py b/salt/cloud/__init__.py
|
|
index 77186a4..733b403 100644
|
|
--- a/salt/cloud/__init__.py
|
|
+++ b/salt/cloud/__init__.py
|
|
@@ -2036,7 +2036,7 @@ class Map(Cloud):
|
|
master_temp_pub = salt.utils.mkstemp()
|
|
with salt.utils.fopen(master_temp_pub, 'w') as mtp:
|
|
mtp.write(pub)
|
|
- master_finger = salt.utils.pem_finger(master_temp_pub)
|
|
+ master_finger = salt.utils.pem_finger(master_temp_pub, sum_type=self.opts['hash_type'])
|
|
os.unlink(master_temp_pub)
|
|
|
|
if master_profile.get('make_minion', True) is True:
|
|
@@ -2121,7 +2121,7 @@ class Map(Cloud):
|
|
# mitigate man-in-the-middle attacks
|
|
master_pub = os.path.join(self.opts['pki_dir'], 'master.pub')
|
|
if os.path.isfile(master_pub):
|
|
- master_finger = salt.utils.pem_finger(master_pub)
|
|
+ master_finger = salt.utils.pem_finger(master_pub, sum_type=self.opts['hash_type'])
|
|
|
|
opts = self.opts.copy()
|
|
if self.opts['parallel']:
|
|
diff --git a/salt/crypt.py b/salt/crypt.py
|
|
index 907ec0c..eaf6d72 100644
|
|
--- a/salt/crypt.py
|
|
+++ b/salt/crypt.py
|
|
@@ -558,11 +558,11 @@ class AsyncAuth(object):
|
|
if self.opts.get('syndic_master', False): # Is syndic
|
|
syndic_finger = self.opts.get('syndic_finger', self.opts.get('master_finger', False))
|
|
if syndic_finger:
|
|
- if salt.utils.pem_finger(m_pub_fn) != syndic_finger:
|
|
+ if salt.utils.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != syndic_finger:
|
|
self._finger_fail(syndic_finger, m_pub_fn)
|
|
else:
|
|
if self.opts.get('master_finger', False):
|
|
- if salt.utils.pem_finger(m_pub_fn) != self.opts['master_finger']:
|
|
+ if salt.utils.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != self.opts['master_finger']:
|
|
self._finger_fail(self.opts['master_finger'], m_pub_fn)
|
|
auth['publish_port'] = payload['publish_port']
|
|
raise tornado.gen.Return(auth)
|
|
@@ -1071,11 +1071,11 @@ class SAuth(AsyncAuth):
|
|
if self.opts.get('syndic_master', False): # Is syndic
|
|
syndic_finger = self.opts.get('syndic_finger', self.opts.get('master_finger', False))
|
|
if syndic_finger:
|
|
- if salt.utils.pem_finger(m_pub_fn) != syndic_finger:
|
|
+ if salt.utils.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != syndic_finger:
|
|
self._finger_fail(syndic_finger, m_pub_fn)
|
|
else:
|
|
if self.opts.get('master_finger', False):
|
|
- if salt.utils.pem_finger(m_pub_fn) != self.opts['master_finger']:
|
|
+ if salt.utils.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != self.opts['master_finger']:
|
|
self._finger_fail(self.opts['master_finger'], m_pub_fn)
|
|
auth['publish_port'] = payload['publish_port']
|
|
return auth
|
|
@@ -1089,7 +1089,7 @@ class SAuth(AsyncAuth):
|
|
'this minion is not subject to a man-in-the-middle attack.'
|
|
.format(
|
|
finger,
|
|
- salt.utils.pem_finger(master_key)
|
|
+ salt.utils.pem_finger(master_key, sum_type=self.opts['hash_type'])
|
|
)
|
|
)
|
|
sys.exit(42)
|
|
diff --git a/salt/key.py b/salt/key.py
|
|
index 08086a0..e4cb4eb 100644
|
|
--- a/salt/key.py
|
|
+++ b/salt/key.py
|
|
@@ -933,7 +933,7 @@ class Key(object):
|
|
path = os.path.join(self.opts['pki_dir'], key)
|
|
else:
|
|
path = os.path.join(self.opts['pki_dir'], status, key)
|
|
- ret[status][key] = salt.utils.pem_finger(path)
|
|
+ ret[status][key] = salt.utils.pem_finger(path, sum_type=self.opts['hash_type'])
|
|
return ret
|
|
|
|
def finger_all(self):
|
|
@@ -948,7 +948,7 @@ class Key(object):
|
|
path = os.path.join(self.opts['pki_dir'], key)
|
|
else:
|
|
path = os.path.join(self.opts['pki_dir'], status, key)
|
|
- ret[status][key] = salt.utils.pem_finger(path)
|
|
+ ret[status][key] = salt.utils.pem_finger(path, sum_type=self.opts['hash_type'])
|
|
return ret
|
|
|
|
|
|
diff --git a/salt/modules/key.py b/salt/modules/key.py
|
|
index 12762df..3e16c2d 100644
|
|
--- a/salt/modules/key.py
|
|
+++ b/salt/modules/key.py
|
|
@@ -21,9 +21,8 @@ def finger():
|
|
|
|
salt '*' key.finger
|
|
'''
|
|
- return salt.utils.pem_finger(
|
|
- os.path.join(__opts__['pki_dir'], 'minion.pub')
|
|
- )
|
|
+ return salt.utils.pem_finger(os.path.join(__opts__['pki_dir'], 'minion.pub'),
|
|
+ sum_type=__opts__.get('hash_type', 'md5'))
|
|
|
|
|
|
def finger_master():
|
|
@@ -36,6 +35,5 @@ def finger_master():
|
|
|
|
salt '*' key.finger_master
|
|
'''
|
|
- return salt.utils.pem_finger(
|
|
- os.path.join(__opts__['pki_dir'], 'minion_master.pub')
|
|
- )
|
|
+ return salt.utils.pem_finger(os.path.join(__opts__['pki_dir'], 'minion_master.pub'),
|
|
+ sum_type=__opts__.get('hash_type', 'md5'))
|
|
diff --git a/salt/modules/tomcat.py b/salt/modules/tomcat.py
|
|
index d3df2dc..4a7f0eb 100644
|
|
--- a/salt/modules/tomcat.py
|
|
+++ b/salt/modules/tomcat.py
|
|
@@ -610,7 +610,7 @@ def deploy_war(war,
|
|
|
|
def passwd(passwd,
|
|
user='',
|
|
- alg='md5',
|
|
+ alg='sha1',
|
|
realm=None):
|
|
'''
|
|
This function replaces the $CATALINA_HOME/bin/digest.sh script
|
|
@@ -625,23 +625,15 @@ def passwd(passwd,
|
|
salt '*' tomcat.passwd secret tomcat sha1
|
|
salt '*' tomcat.passwd secret tomcat sha1 'Protected Realm'
|
|
'''
|
|
- if alg == 'md5':
|
|
- m = hashlib.md5()
|
|
- elif alg == 'sha1':
|
|
- m = hashlib.sha1()
|
|
- else:
|
|
- return False
|
|
-
|
|
- if realm:
|
|
- m.update('{0}:{1}:{2}'.format(
|
|
- user,
|
|
- realm,
|
|
- passwd,
|
|
- ))
|
|
- else:
|
|
- m.update(passwd)
|
|
+ # Shouldn't it be SHA265 instead of SHA1?
|
|
+ digest = hasattr(hashlib, alg) and getattr(hashlib, alg) or None
|
|
+ if digest:
|
|
+ if realm:
|
|
+ digest.update('{0}:{1}:{2}'.format(user, realm, passwd,))
|
|
+ else:
|
|
+ digest.update(passwd)
|
|
|
|
- return m.hexdigest()
|
|
+ return digest and digest.hexdigest() or False
|
|
|
|
|
|
# Non-Manager functions
|
|
diff --git a/salt/modules/win_file.py b/salt/modules/win_file.py
|
|
index 7911bfc..5ea31ae 100644
|
|
--- a/salt/modules/win_file.py
|
|
+++ b/salt/modules/win_file.py
|
|
@@ -842,7 +842,7 @@ def chgrp(path, group):
|
|
return None
|
|
|
|
|
|
-def stats(path, hash_type='md5', follow_symlinks=True):
|
|
+def stats(path, hash_type='sha256', follow_symlinks=True):
|
|
'''
|
|
Return a dict containing the stats for a given file
|
|
|
|
diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py
|
|
index c6a3fd3..4e40caf 100644
|
|
--- a/salt/utils/__init__.py
|
|
+++ b/salt/utils/__init__.py
|
|
@@ -858,10 +858,11 @@ def path_join(*parts):
|
|
))
|
|
|
|
|
|
-def pem_finger(path=None, key=None, sum_type='md5'):
|
|
+def pem_finger(path=None, key=None, sum_type='sha256'):
|
|
'''
|
|
Pass in either a raw pem string, or the path on disk to the location of a
|
|
- pem file, and the type of cryptographic hash to use. The default is md5.
|
|
+ pem file, and the type of cryptographic hash to use. The default is SHA256.
|
|
+
|
|
The fingerprint of the pem will be returned.
|
|
|
|
If neither a key nor a path are passed in, a blank string will be returned.
|
|
@@ -1979,7 +1980,7 @@ def safe_walk(top, topdown=True, onerror=None, followlinks=True, _seen=None):
|
|
yield top, dirs, nondirs
|
|
|
|
|
|
-def get_hash(path, form='md5', chunk_size=65536):
|
|
+def get_hash(path, form='sha256', chunk_size=65536):
|
|
'''
|
|
Get the hash sum of a file
|
|
|
|
@@ -1989,10 +1990,10 @@ def get_hash(path, form='md5', chunk_size=65536):
|
|
``get_sum`` cannot really be trusted since it is vulnerable to
|
|
collisions: ``get_sum(..., 'xyz') == 'Hash xyz not supported'``
|
|
'''
|
|
- try:
|
|
- hash_type = getattr(hashlib, form)
|
|
- except (AttributeError, TypeError):
|
|
+ hash_type = hasattr(hashlib, form) and getattr(hashlib, form) or None
|
|
+ if hash_type is None:
|
|
raise ValueError('Invalid hash type: {0}'.format(form))
|
|
+
|
|
with salt.utils.fopen(path, 'rb') as ifile:
|
|
hash_obj = hash_type()
|
|
# read the file in in chunks, not the entire file
|
|
diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py
|
|
index d546e51..7a21166 100644
|
|
--- a/salt/utils/cloud.py
|
|
+++ b/salt/utils/cloud.py
|
|
@@ -2421,6 +2421,7 @@ def init_cachedir(base=None):
|
|
|
|
def request_minion_cachedir(
|
|
minion_id,
|
|
+ opts=None,
|
|
fingerprint='',
|
|
pubkey=None,
|
|
provider=None,
|
|
@@ -2440,7 +2441,7 @@ def request_minion_cachedir(
|
|
|
|
if not fingerprint:
|
|
if pubkey is not None:
|
|
- fingerprint = salt.utils.pem_finger(key=pubkey)
|
|
+ fingerprint = salt.utils.pem_finger(key=pubkey, sum_type=(opts and opts.get('hash_type') or 'sha256'))
|
|
|
|
init_cachedir(base)
|
|
|
|
diff --git a/tests/unit/daemons_test.py b/tests/unit/daemons_test.py
|
|
new file mode 100644
|
|
index 0000000..47d5e8a
|
|
--- /dev/null
|
|
+++ b/tests/unit/daemons_test.py
|
|
@@ -0,0 +1,209 @@
|
|
+# -*- coding: utf-8 -*-
|
|
+'''
|
|
+ :codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
|
|
+'''
|
|
+
|
|
+# Import python libs
|
|
+from __future__ import absolute_import
|
|
+
|
|
+# Import Salt Testing libs
|
|
+from salttesting import TestCase, skipIf
|
|
+from salttesting.helpers import ensure_in_syspath
|
|
+from salttesting.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON
|
|
+
|
|
+ensure_in_syspath('../')
|
|
+
|
|
+# Import Salt libs
|
|
+import integration
|
|
+from salt.cli import daemons
|
|
+
|
|
+
|
|
+class LoggerMock(object):
|
|
+ '''
|
|
+ Logger data collector
|
|
+ '''
|
|
+
|
|
+ def __init__(self):
|
|
+ '''
|
|
+ init
|
|
+ :return:
|
|
+ '''
|
|
+ self.reset()
|
|
+
|
|
+ def reset(self):
|
|
+ '''
|
|
+ Reset values
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+ self.last_message = self.last_type = None
|
|
+
|
|
+ def info(self, data):
|
|
+ '''
|
|
+ Collects the data from the logger of info type.
|
|
+
|
|
+ :param data:
|
|
+ :return:
|
|
+ '''
|
|
+ self.last_message = data
|
|
+ self.last_type = 'info'
|
|
+
|
|
+ def warning(self, data):
|
|
+ '''
|
|
+ Collects the data from the logger of warning type.
|
|
+
|
|
+ :param data:
|
|
+ :return:
|
|
+ '''
|
|
+ self.last_message = data
|
|
+ self.last_type = 'warning'
|
|
+
|
|
+
|
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
+class DaemonsStarterTestCase(TestCase, integration.SaltClientTestCaseMixIn):
|
|
+ '''
|
|
+ Unit test for the daemons starter classes.
|
|
+ '''
|
|
+
|
|
+ def test_master_daemon_hash_type_verified(self):
|
|
+ '''
|
|
+ Verify if Master is verifying hash_type config option.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+ def _create_master():
|
|
+ '''
|
|
+ Create master instance
|
|
+ :return:
|
|
+ '''
|
|
+ master = daemons.Master()
|
|
+ master.config = {'user': 'dummy', 'hash_type': alg}
|
|
+ for attr in ['master', 'start_log_info', 'prepare']:
|
|
+ setattr(master, attr, MagicMock())
|
|
+
|
|
+ return master
|
|
+
|
|
+ _logger = LoggerMock()
|
|
+ with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)):
|
|
+ with patch('salt.cli.daemons.logger', _logger):
|
|
+ for alg in ['md5', 'sha1']:
|
|
+ _create_master().start()
|
|
+ self.assertEqual(_logger.last_type, 'warning')
|
|
+ self.assertTrue(_logger.last_message)
|
|
+ self.assertTrue(_logger.last_message.find('Do not use {alg}'.format(alg=alg)) > -1)
|
|
+
|
|
+ _logger.reset()
|
|
+
|
|
+ for alg in ['sha224', 'sha256', 'sha384', 'sha512']:
|
|
+ _create_master().start()
|
|
+ self.assertEqual(_logger.last_type, None)
|
|
+ self.assertFalse(_logger.last_message)
|
|
+
|
|
+ def test_minion_daemon_hash_type_verified(self):
|
|
+ '''
|
|
+ Verify if Minion is verifying hash_type config option.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+
|
|
+ def _create_minion():
|
|
+ '''
|
|
+ Create minion instance
|
|
+ :return:
|
|
+ '''
|
|
+ obj = daemons.Minion()
|
|
+ obj.config = {'user': 'dummy', 'hash_type': alg}
|
|
+ for attr in ['minion', 'start_log_info', 'prepare', 'shutdown']:
|
|
+ setattr(obj, attr, MagicMock())
|
|
+
|
|
+ return obj
|
|
+
|
|
+ _logger = LoggerMock()
|
|
+ with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)):
|
|
+ with patch('salt.cli.daemons.logger', _logger):
|
|
+ for alg in ['md5', 'sha1']:
|
|
+ _create_minion().start()
|
|
+ self.assertEqual(_logger.last_type, 'warning')
|
|
+ self.assertTrue(_logger.last_message)
|
|
+ self.assertTrue(_logger.last_message.find('Do not use {alg}'.format(alg=alg)) > -1)
|
|
+
|
|
+ _logger.reset()
|
|
+
|
|
+ for alg in ['sha224', 'sha256', 'sha384', 'sha512']:
|
|
+ _create_minion().start()
|
|
+ self.assertEqual(_logger.last_type, None)
|
|
+ self.assertFalse(_logger.last_message)
|
|
+
|
|
+ def test_proxy_minion_daemon_hash_type_verified(self):
|
|
+ '''
|
|
+ Verify if ProxyMinion is verifying hash_type config option.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+
|
|
+ def _create_proxy_minion():
|
|
+ '''
|
|
+ Create proxy minion instance
|
|
+ :return:
|
|
+ '''
|
|
+ obj = daemons.ProxyMinion()
|
|
+ obj.config = {'user': 'dummy', 'hash_type': alg}
|
|
+ for attr in ['minion', 'start_log_info', 'prepare', 'shutdown']:
|
|
+ setattr(obj, attr, MagicMock())
|
|
+
|
|
+ return obj
|
|
+
|
|
+ _logger = LoggerMock()
|
|
+ with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)):
|
|
+ with patch('salt.cli.daemons.logger', _logger):
|
|
+ for alg in ['md5', 'sha1']:
|
|
+ _create_proxy_minion().start()
|
|
+ self.assertEqual(_logger.last_type, 'warning')
|
|
+ self.assertTrue(_logger.last_message)
|
|
+ self.assertTrue(_logger.last_message.find('Do not use {alg}'.format(alg=alg)) > -1)
|
|
+
|
|
+ _logger.reset()
|
|
+
|
|
+ for alg in ['sha224', 'sha256', 'sha384', 'sha512']:
|
|
+ _create_proxy_minion().start()
|
|
+ self.assertEqual(_logger.last_type, None)
|
|
+ self.assertFalse(_logger.last_message)
|
|
+
|
|
+ def test_syndic_daemon_hash_type_verified(self):
|
|
+ '''
|
|
+ Verify if Syndic is verifying hash_type config option.
|
|
+
|
|
+ :return:
|
|
+ '''
|
|
+
|
|
+ def _create_syndic():
|
|
+ '''
|
|
+ Create syndic instance
|
|
+ :return:
|
|
+ '''
|
|
+ obj = daemons.Syndic()
|
|
+ obj.config = {'user': 'dummy', 'hash_type': alg}
|
|
+ for attr in ['syndic', 'start_log_info', 'prepare', 'shutdown']:
|
|
+ setattr(obj, attr, MagicMock())
|
|
+
|
|
+ return obj
|
|
+
|
|
+ _logger = LoggerMock()
|
|
+ with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)):
|
|
+ with patch('salt.cli.daemons.logger', _logger):
|
|
+ for alg in ['md5', 'sha1']:
|
|
+ _create_syndic().start()
|
|
+ self.assertEqual(_logger.last_type, 'warning')
|
|
+ self.assertTrue(_logger.last_message)
|
|
+ self.assertTrue(_logger.last_message.find('Do not use {alg}'.format(alg=alg)) > -1)
|
|
+
|
|
+ _logger.reset()
|
|
+
|
|
+ for alg in ['sha224', 'sha256', 'sha384', 'sha512']:
|
|
+ _create_syndic().start()
|
|
+ self.assertEqual(_logger.last_type, None)
|
|
+ self.assertFalse(_logger.last_message)
|
|
+
|
|
+if __name__ == '__main__':
|
|
+ from integration import run_tests
|
|
+ run_tests(DaemonsStarterTestCase, needs_daemon=False)
|
|
--
|
|
2.7.2
|
|
|