SHA256
1
0
forked from pool/salt

Accepting request 636187 from home:mdinca:branches:systemsmanagement:saltstack

- Prepend current directory when path is just filename (bsc#1095942)
- Integration of MSI authentication for azurearm
- Adds fix for SUSE Expanded Support os grain detection
- Fixes 509x remote signing
- Fix for StringIO import in Python2
- Use Adler32 algorithm to compute string checksums (bsc#1102819)
- Only do reverse DNS lookup on IPs for salt-ssh (bsc#1104154)
- Add support for Python 3.7
- Fix license macro to build on SLE12SP2
- Decode file contents for python2 (bsc#1102013)
- Fix for sorting of multi-version packages (bsc#1097174 and bsc#1097413)
- Fix mine.get not returning data - workaround for #48020 (bsc#1100142)
- Added:
  * change-stringio-import-in-python2-to-import-the-clas.patch
  * integration-of-msi-authentication-with-azurearm-clou.patch
  * x509-fixes-for-remote-signing-106.patch
  * fix-for-suse-expanded-support-detection.patch
  * only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch
  * prepend-current-directory-when-path-is-just-filename.patch
  * add-support-for-python-3.7.patch
  * decode-file-contents-for-python2-bsc-1102013.patch
  * fix-mine.get-not-returning-data-workaround-for-48020.patch
  * x509-fixes-111.patch
  * use-adler32-algorithm-to-compute-string-checksums.patch
- Modified:
  * fix-for-sorting-of-multi-version-packages-bsc-109717.patch

OBS-URL: https://build.opensuse.org/request/show/636187
OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=129
This commit is contained in:
Bo Maryniuk 2018-09-17 14:18:45 +00:00 committed by Git OBS Bridge
parent 7966fde1ef
commit c0d7cc4bb8
15 changed files with 2374 additions and 1 deletions

View File

@ -1 +1 @@
eb0ca38e07c96eb021ac7490ac1f61a54dc9d904
ebc77d067d9fa300bdc5bb5dcccaa09e1787f688

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
From a0d5af98c8d2a22c5eb56943ff320ca287fa79ea Mon Sep 17 00:00:00 2001
From: Florian Bergmann <bergmannf@users.noreply.github.com>
Date: Tue, 11 Sep 2018 14:03:33 +0200
Subject: [PATCH] Change StringIO import in python2 to import the class.
(#107)
Instead of using StringIO in python3, use the correct BytesIO class instead.
---
salt/modules/hashutil.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/salt/modules/hashutil.py b/salt/modules/hashutil.py
index 721957973d..5123cc7cd7 100644
--- a/salt/modules/hashutil.py
+++ b/salt/modules/hashutil.py
@@ -17,9 +17,10 @@ import salt.utils.hashutils
import salt.utils.stringutils
if six.PY2:
- import StringIO
+ from StringIO import StringIO
+ BytesIO = StringIO
elif six.PY3:
- from io import StringIO
+ from io import BytesIO, StringIO
def digest(instr, checksum='md5'):
@@ -155,13 +156,13 @@ def base64_encodefile(fname):
salt '*' hashutil.base64_encodefile /path/to/binary_file
'''
- encoded_f = StringIO.StringIO()
+ encoded_f = BytesIO()
with salt.utils.files.fopen(fname, 'rb') as f:
base64.encode(f, encoded_f)
encoded_f.seek(0)
- return encoded_f.read()
+ return salt.utils.stringutils.to_str(encoded_f.read())
def base64_decodestring(instr):
@@ -192,7 +193,7 @@ def base64_decodefile(instr, outfile):
salt '*' hashutil.base64_decodefile instr='Z2V0IHNhbHRlZAo=' outfile='/path/to/binary_file'
'''
- encoded_f = StringIO.StringIO(instr)
+ encoded_f = StringIO(instr)
with salt.utils.files.fopen(outfile, 'wb') as f:
base64.decode(encoded_f, f)
--
2.19.0

View File

@ -0,0 +1,27 @@
From 58913b6801b92bd59374cd53fa48fa74171abb73 Mon Sep 17 00:00:00 2001
From: Abid Mehmood <amehmood@suse.de>
Date: Wed, 1 Aug 2018 17:19:11 +0200
Subject: [PATCH] Decode file contents for python2(bsc#1102013)
---
salt/states/file.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/salt/states/file.py b/salt/states/file.py
index e1d247ae4f..db82098a33 100644
--- a/salt/states/file.py
+++ b/salt/states/file.py
@@ -1105,8 +1105,7 @@ def _get_template_texts(source_list=None,
tmplines = None
with salt.utils.files.fopen(rndrd_templ_fn, 'rb') as fp_:
tmplines = fp_.read()
- if six.PY3:
- tmplines = tmplines.decode(__salt_system_encoding__)
+ tmplines = tmplines.decode(__salt_system_encoding__)
tmplines = tmplines.splitlines(True)
if not tmplines:
msg = 'Failed to read rendered template file {0} ({1})'
--
2.17.1

View File

@ -0,0 +1,35 @@
From f0f63dc8dd5979b51db71cf759d4350da1078383 Mon Sep 17 00:00:00 2001
From: Jochen Breuer <jbreuer@suse.de>
Date: Wed, 13 Jun 2018 17:51:13 +0200
Subject: [PATCH] Fix for sorting of multi-version packages (bsc#1097174
and bsc#1097413)
---
salt/modules/rpm.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py
index 3683234f59..8e71992f81 100644
--- a/salt/modules/rpm.py
+++ b/salt/modules/rpm.py
@@ -9,6 +9,7 @@ import logging
import os
import re
import datetime
+from distutils.version import LooseVersion
# Import Salt libs
import salt.utils.decorators.path
@@ -609,7 +610,7 @@ def info(*packages, **kwargs):
# pick only latest versions
# (in case multiple packages installed, e.g. kernel)
ret = dict()
- for pkg_data in reversed(sorted(_ret, key=lambda x: x['edition'])):
+ for pkg_data in reversed(sorted(_ret, key=lambda x: LooseVersion(x['edition']))):
pkg_name = pkg_data.pop('name')
# Filter out GPG public keys packages
if pkg_name.startswith('gpg-pubkey'):
--
2.17.1

View File

@ -0,0 +1,43 @@
From 1c9cba3a397d53e399b82320507fb5141234c67f Mon Sep 17 00:00:00 2001
From: Jochen Breuer <jbreuer@suse.de>
Date: Thu, 6 Sep 2018 17:15:18 +0200
Subject: [PATCH] Fix for SUSE Expanded Support detection
A SUSE ES installation has both, the centos-release and redhat-release
file. Since os_data only used the centos-release file to detect a
CentOS installation, this lead to SUSE ES being detected as CentOS.
This change also adds a check for redhat-release and then marks the
'lsb_distrib_id' as RedHat.
---
salt/grains/core.py | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/salt/grains/core.py b/salt/grains/core.py
index dc472a6c0a..a5c3a6a8cf 100644
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
@@ -1593,8 +1593,17 @@ def os_data():
grains['lsb_distrib_codename'] = \
comps[3].replace('(', '').replace(')', '')
elif os.path.isfile('/etc/centos-release'):
- # CentOS Linux
- grains['lsb_distrib_id'] = 'CentOS'
+ log.trace('Parsing distrib info from /etc/centos-release')
+ # Maybe CentOS Linux; could also be SUSE Expanded Support.
+ # SUSE ES has both, centos-release and redhat-release.
+ if os.path.isfile('/etc/redhat-release'):
+ with salt.utils.files.fopen('/etc/redhat-release') as ifile:
+ for line in ifile:
+ if "red hat enterprise linux server" in line.lower():
+ # This is a SUSE Expanded Support Rhel installation
+ grains['lsb_distrib_id'] = 'RedHat'
+ break
+ grains.setdefault('lsb_distrib_id', 'CentOS')
with salt.utils.files.fopen('/etc/centos-release') as ifile:
for line in ifile:
# Need to pull out the version and codename
--
2.19.0

View File

@ -0,0 +1,34 @@
From f8c0811c3a05ef334eef1943a906fe01b13c1afc Mon Sep 17 00:00:00 2001
From: Federico Ceratto <federico.ceratto@suse.com>
Date: Wed, 25 Jul 2018 10:33:09 +0000
Subject: [PATCH] Fix mine.get not returning data (Workaround for #48020)
---
salt/utils/minions.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/salt/utils/minions.py b/salt/utils/minions.py
index c3acc6ba90..bb0cbaa589 100644
--- a/salt/utils/minions.py
+++ b/salt/utils/minions.py
@@ -239,12 +239,12 @@ class CkMinions(object):
Retreive complete minion list from PKI dir.
Respects cache if configured
'''
- if self.opts.get('__role') == 'master' and self.opts.get('__cli') == 'salt-run':
- # Compiling pillar directly on the master, just return the master's
- # ID as that is the only one that is available.
- return [self.opts['id']]
minions = []
pki_cache_fn = os.path.join(self.opts['pki_dir'], self.acc, '.key_cache')
+ try:
+ os.makedirs(os.path.dirname(pki_cache_fn))
+ except OSError:
+ pass
try:
if self.opts['key_cache'] and os.path.exists(pki_cache_fn):
log.debug('Returning cached minion list')
--
2.17.1

View File

@ -0,0 +1,118 @@
From 06aff97c83342cf9635fa750222f774ab1664a0d Mon Sep 17 00:00:00 2001
From: ed lane <ed.lane.0@gmail.com>
Date: Thu, 30 Aug 2018 06:07:08 -0600
Subject: [PATCH] Integration of MSI authentication with azurearm cloud
driver (#105)
---
salt/cloud/clouds/azurearm.py | 47 +++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/salt/cloud/clouds/azurearm.py b/salt/cloud/clouds/azurearm.py
index bd9a25a7e2..8b9a9e8903 100644
--- a/salt/cloud/clouds/azurearm.py
+++ b/salt/cloud/clouds/azurearm.py
@@ -25,6 +25,9 @@ The Azure cloud module is used to control access to Microsoft Azure
* ``client_id``
* ``secret``
+ if using MSI-style authentication:
+ * ``subscription_id``
+
Example ``/etc/salt/cloud.providers`` or
``/etc/salt/cloud.providers.d/azure.conf`` configuration:
@@ -48,6 +51,10 @@ Example ``/etc/salt/cloud.providers`` or
For example, this creates a service principal with 'owner' role for the whole subscription:
az ad sp create-for-rbac -n "http://mysaltapp" --role owner --scopes /subscriptions/3287abc8-f98a-c678-3bde-326766fd3617
*Note: review the details of Service Principals. Owner role is more than you normally need, and you can restrict scope to a resource group or individual resources.
+
+ Or my-azure-config with MSI-style authentication:
+ driver: azure
+ subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617
'''
# pylint: disable=E0102
@@ -86,6 +93,7 @@ try:
UserPassCredentials,
ServicePrincipalCredentials,
)
+ from msrestazure.azure_active_directory import MSIAuthentication
from azure.mgmt.compute import ComputeManagementClient
from azure.mgmt.compute.models import (
CachingTypes,
@@ -166,19 +174,30 @@ def get_configured_provider():
'''
Return the first configured instance.
'''
+ # check if using Service Principle style authentication...
provider = config.is_provider_configured(
__opts__,
__active_provider_name__ or __virtualname__,
- ('subscription_id', 'tenant', 'client_id', 'secret')
+ required_keys=('subscription_id', 'tenant', 'client_id', 'secret'),
+ log_message=False #... allowed to fail so no need to log warnings
)
if provider is False:
- return config.is_provider_configured(
+ # check if using username/password style authentication...
+ provider = config.is_provider_configured(
__opts__,
__active_provider_name__ or __virtualname__,
- ('subscription_id', 'username', 'password')
+ required_keys=('subscription_id', 'username', 'password'),
+ log_message=False
)
- else:
- return provider
+ if provider is False:
+ # check if using MSI style credentials...
+ provider = config.is_provider_configured(
+ __opts__,
+ __active_provider_name__ or __virtualname__,
+ required_keys=('subscription_id',),
+ log_message=False
+ )
+ return provider
def get_dependencies():
@@ -210,6 +229,7 @@ def get_conn(Client=None):
get_configured_provider(), __opts__, search_global=False
)
if tenant is not None:
+ # using Service Principle style authentication...
client_id = config.get_cloud_config_value(
'client_id',
get_configured_provider(), __opts__, search_global=False
@@ -224,15 +244,20 @@ def get_conn(Client=None):
'username',
get_configured_provider(), __opts__, search_global=False
)
- password = config.get_cloud_config_value(
- 'password',
- get_configured_provider(), __opts__, search_global=False
- )
- credentials = UserPassCredentials(username, password)
+ if username is not None:
+ # using username/password style authentication...
+ password = config.get_cloud_config_value(
+ 'password',
+ get_configured_provider(), __opts__, search_global=False
+ )
+ credentials = UserPassCredentials(username, password)
+ else:
+ # using MSI style authentication ...
+ credentials = MSIAuthentication()
client = Client(
credentials=credentials,
- subscription_id=subscription_id,
+ subscription_id=str(subscription_id),
)
client.config.add_user_agent('SaltCloud/{0}'.format(salt.version.__version__))
return client
--
2.19.0

View File

@ -0,0 +1,29 @@
From f346e83f6d4651a1cdcaad8c995642b55f66ddbc Mon Sep 17 00:00:00 2001
From: Daniel Wallace <danielwallace@gtmanfred.com>
Date: Wed, 25 Jul 2018 09:48:29 -0500
Subject: [PATCH] only do reverse dns lookup on ips for salt-ssh
Fixes #48676
---
salt/client/ssh/__init__.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py
index 8a85cc2480..d6ff0c3479 100644
--- a/salt/client/ssh/__init__.py
+++ b/salt/client/ssh/__init__.py
@@ -349,7 +349,9 @@ class SSH(object):
return
hostname = self.opts['tgt'].split('@')[-1]
- needs_expansion = '*' not in hostname and salt.utils.network.is_reachable_host(hostname)
+ needs_expansion = '*' not in hostname and \
+ salt.utils.network.is_reachable_host(hostname) and \
+ salt.utils.network.is_ip(hostname)
if needs_expansion:
hostname = salt.utils.network.ip_to_host(hostname)
if hostname is None:
--
2.17.1

View File

@ -0,0 +1,38 @@
From 341ee0c44cabf2f34bdd2f4b54e4b83053a3133e Mon Sep 17 00:00:00 2001
From: Mihai Dinca <mdinca@suse.de>
Date: Thu, 23 Aug 2018 16:14:36 +0200
Subject: [PATCH] Prepend current directory when path is just filename
(bsc#1095942)
---
salt/utils/parsers.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py
index 5a415ab576..9a7f27ac11 100644
--- a/salt/utils/parsers.py
+++ b/salt/utils/parsers.py
@@ -591,10 +591,19 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)):
)
)
+ def _logfile_callback(option, opt, value, parser, *args, **kwargs):
+ if not os.path.dirname(value):
+ # if the path is only a file name (no parent directory), assume current directory
+ value = os.path.join(os.path.curdir, value)
+ setattr(parser.values, self._logfile_config_setting_name_, value)
+
group.add_option(
'--log-file',
dest=self._logfile_config_setting_name_,
default=None,
+ action='callback',
+ type='string',
+ callback=_logfile_callback,
help='Log file path. Default: \'{0}\'.'.format(
self._default_logging_logfile_
)
--
2.19.0

View File

@ -1,3 +1,35 @@
-------------------------------------------------------------------
Mon Sep 17 13:47:09 UTC 2018 - bo@suse.de
- Prepend current directory when path is just filename (bsc#1095942)
- Integration of MSI authentication for azurearm
- Adds fix for SUSE Expanded Support os grain detection
- Fixes 509x remote signing
- Fix for StringIO import in Python2
- Use Adler32 algorithm to compute string checksums (bsc#1102819)
- Only do reverse DNS lookup on IPs for salt-ssh (bsc#1104154)
- Add support for Python 3.7
- Fix license macro to build on SLE12SP2
- Decode file contents for python2 (bsc#1102013)
- Fix for sorting of multi-version packages (bsc#1097174 and bsc#1097413)
- Fix mine.get not returning data - workaround for #48020 (bsc#1100142)
- Added:
* change-stringio-import-in-python2-to-import-the-clas.patch
* integration-of-msi-authentication-with-azurearm-clou.patch
* x509-fixes-for-remote-signing-106.patch
* fix-for-suse-expanded-support-detection.patch
* only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch
* prepend-current-directory-when-path-is-just-filename.patch
* add-support-for-python-3.7.patch
* decode-file-contents-for-python2-bsc-1102013.patch
* fix-mine.get-not-returning-data-workaround-for-48020.patch
* x509-fixes-111.patch
* use-adler32-algorithm-to-compute-string-checksums.patch
- Modified:
* fix-for-sorting-of-multi-version-packages-bsc-109717.patch
-------------------------------------------------------------------
Mon Jul 30 10:42:01 UTC 2018 - mihai.dinca@suse.com

View File

@ -126,6 +126,30 @@ Patch29: remove-old-hack-when-reporting-multiversion-packages.patch
Patch30: add-engine-relaying-libvirt-events.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48781
Patch31: avoid-incomprehensive-message-if-crashes.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48765
Patch32: fix-mine.get-not-returning-data-workaround-for-48020.patch
# PATCH-FIX_OPENSUSE bsc#1097174 and bsc#1097413
Patch33: fix-for-sorting-of-multi-version-packages-bsc-109717.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48863
Patch34: decode-file-contents-for-python2-bsc-1102013.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49052
Patch35: add-support-for-python-3.7.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48771
Patch36: only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49277
Patch37: prepend-current-directory-when-path-is-just-filename.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49063
Patch38: integration-of-msi-authentication-with-azurearm-clou.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49538
Patch39: fix-for-suse-expanded-support-detection.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49508
Patch40: x509-fixes-for-remote-signing-106.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49555
Patch41: change-stringio-import-in-python2-to-import-the-clas.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48812
Patch42: use-adler32-algorithm-to-compute-string-checksums.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49497
Patch43: x509-fixes-111.patch
# BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRoot: %{_tmppath}/%{name}-%{version}-build
@ -615,6 +639,18 @@ cp %{S:5} ./.travis.yml
%patch29 -p1
%patch30 -p1
%patch31 -p1
%patch32 -p1
%patch33 -p1
%patch34 -p1
%patch35 -p1
%patch36 -p1
%patch37 -p1
%patch38 -p1
%patch39 -p1
%patch40 -p1
%patch41 -p1
%patch42 -p1
%patch43 -p1
%build
%if 0%{?build_py2}
@ -1256,8 +1292,12 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version
%{_mandir}/man1/salt-call.1.gz
%{_mandir}/man1/spm.1.gz
%config(noreplace) %{_sysconfdir}/logrotate.d/salt
%if 0%{?suse_version} < 1500
%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE
%else
%license LICENSE
%doc AUTHORS README.rst HACKING.rst README.SUSE
%endif
#
%dir %attr(0750, root, salt) %{_sysconfdir}/salt
%dir %attr(0750, root, salt) %{_sysconfdir}/salt/pki

View File

@ -0,0 +1,151 @@
From 1cb2d2bc6c1cf1a39e735120c184d6ade9e64c34 Mon Sep 17 00:00:00 2001
From: Bo Maryniuk <bo@suse.de>
Date: Sat, 28 Jul 2018 22:59:04 +0200
Subject: [PATCH] Use Adler32 algorithm to compute string checksums
Generate the same numeric value across all Python versions and platforms
Re-add getting hash by Python shell-out method
Add an option to choose between default hashing, Adler32 or CRC32 algorithms
Set default config option for server_id hashing to False on minion
Choose CRC method, default to faster but less reliable "adler32", if crc is in use
Add warning for Sodium.
---
salt/config/__init__.py | 13 +++++++++-
salt/grains/core.py | 54 +++++++++++++++++++++++++++--------------
2 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/salt/config/__init__.py b/salt/config/__init__.py
index feda0abac1..59df7e1cba 100644
--- a/salt/config/__init__.py
+++ b/salt/config/__init__.py
@@ -1186,6 +1186,16 @@ VALID_OPTS = {
# Enable calling ssh minions from the salt master
'enable_ssh_minions': bool,
+
+ # Thorium saltenv
+ 'thoriumenv': (type(None), six.string_types),
+
+ # Thorium top file location
+ 'thorium_top': six.string_types,
+
+ # Use Adler32 hashing algorithm for server_id (default False until Sodium, "adler32" after)
+ # Possible values are: False, adler32, crc32
+ 'server_id_use_crc': (bool, six.string_types),
}
# default configurations
@@ -1486,7 +1496,8 @@ DEFAULT_MINION_OPTS = {
},
'discovery': False,
'schedule': {},
- 'ssh_merge_pillar': True
+ 'ssh_merge_pillar': True,
+ 'server_id_use_crc': False,
}
DEFAULT_MASTER_OPTS = {
diff --git a/salt/grains/core.py b/salt/grains/core.py
index a5c3a6a8cf..6aaf38096d 100644
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
@@ -20,6 +20,7 @@ import platform
import logging
import locale
import uuid
+import zlib
from errno import EACCES, EPERM
import datetime
@@ -46,6 +47,8 @@ import salt.utils.files
import salt.utils.network
import salt.utils.path
import salt.utils.platform
+import salt.utils.stringutils
+import salt.utils.versions
from salt.ext import six
from salt.ext.six.moves import range
@@ -2420,40 +2423,55 @@ def _hw_data(osdata):
return grains
-def get_server_id():
+def _get_hash_by_shell():
'''
- Provides an integer based on the FQDN of a machine.
- Useful as server-id in MySQL replication or anywhere else you'll need an ID
- like this.
+ Shell-out Python 3 for compute reliable hash
+ :return:
'''
- # Provides:
- # server_id
-
- if salt.utils.platform.is_proxy():
- return {}
id_ = __opts__.get('id', '')
id_hash = None
py_ver = sys.version_info[:2]
if py_ver >= (3, 3):
# Python 3.3 enabled hash randomization, so we need to shell out to get
# a reliable hash.
- id_hash = __salt__['cmd.run'](
- [sys.executable, '-c', 'print(hash("{0}"))'.format(id_)],
- env={'PYTHONHASHSEED': '0'}
- )
+ id_hash = __salt__['cmd.run']([sys.executable, '-c', 'print(hash("{0}"))'.format(id_)],
+ env={'PYTHONHASHSEED': '0'})
try:
id_hash = int(id_hash)
except (TypeError, ValueError):
- log.debug(
- 'Failed to hash the ID to get the server_id grain. Result of '
- 'hash command: %s', id_hash
- )
+ log.debug('Failed to hash the ID to get the server_id grain. Result of hash command: %s', id_hash)
id_hash = None
if id_hash is None:
# Python < 3.3 or error encountered above
id_hash = hash(id_)
- return {'server_id': abs(id_hash % (2 ** 31))}
+ return abs(id_hash % (2 ** 31))
+
+
+def get_server_id():
+ '''
+ Provides an integer based on the FQDN of a machine.
+ Useful as server-id in MySQL replication or anywhere else you'll need an ID
+ like this.
+ '''
+ # Provides:
+ # server_id
+
+ if salt.utils.platform.is_proxy():
+ server_id = {}
+ else:
+ use_crc = __opts__.get('server_id_use_crc')
+ if bool(use_crc):
+ id_hash = getattr(zlib, use_crc, zlib.adler32)(__opts__.get('id', '').encode()) & 0xffffffff
+ else:
+ salt.utils.versions.warn_until('Sodium', 'This server_id is computed nor by Adler32 neither by CRC32. '
+ 'Please use "server_id_use_crc" option and define algorithm you'
+ 'prefer (default "Adler32"). The server_id will be computed with'
+ 'Adler32 by default.')
+ id_hash = _get_hash_by_shell()
+ server_id = {'server_id': id_hash}
+
+ return server_id
def get_master():
--
2.19.0

518
x509-fixes-111.patch Normal file
View File

@ -0,0 +1,518 @@
From 053d97afcc7486f7300e339bc56cb3c850cc523b Mon Sep 17 00:00:00 2001
From: Florian Bergmann <bergmannf@users.noreply.github.com>
Date: Fri, 14 Sep 2018 10:30:39 +0200
Subject: [PATCH] X509 fixes (#111)
* Return proper content type for the x509 certificate
* Remove parenthesis
* Remove extra-variables during the import
* Comment fix
* Remove double returns
* Change log level from trace to debug
* Remove 'pass' and add logging instead
* Remove unnecessary wrapping
Remove wrapping
* PEP 8: line too long
PEP8: line too long
* PEP8: Redefine RSAError variable in except clause
* Do not return None if name was not found
* Do not return None if no matched minions found
* Fix unit tests
---
salt/modules/publish.py | 8 +-
salt/modules/x509.py | 132 ++++++++++++--------------------
salt/states/x509.py | 22 ++++--
tests/unit/modules/test_x509.py | 9 ++-
4 files changed, 74 insertions(+), 97 deletions(-)
diff --git a/salt/modules/publish.py b/salt/modules/publish.py
index 2de99583f4..ac31b4b65f 100644
--- a/salt/modules/publish.py
+++ b/salt/modules/publish.py
@@ -83,10 +83,8 @@ def _publish(
in minion configuration but `via_master` was specified.')
else:
# Find the master in the list of master_uris generated by the minion base class
- matching_master_uris = [master for master
- in __opts__['master_uri_list']
- if '//{0}:'.format(via_master)
- in master]
+ matching_master_uris = [master for master in __opts__['master_uri_list']
+ if '//{0}:'.format(via_master) in master]
if not matching_master_uris:
raise SaltInvocationError('Could not find match for {0} in \
@@ -176,6 +174,8 @@ def _publish(
else:
return ret
+ return {}
+
def publish(tgt,
fun,
diff --git a/salt/modules/x509.py b/salt/modules/x509.py
index 9901bc5bd9..45afcccd99 100644
--- a/salt/modules/x509.py
+++ b/salt/modules/x509.py
@@ -36,14 +36,13 @@ from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS
# Import 3rd Party Libs
try:
import M2Crypto
- HAS_M2 = True
except ImportError:
- HAS_M2 = False
+ M2Crypto = None
+
try:
import OpenSSL
- HAS_OPENSSL = True
except ImportError:
- HAS_OPENSSL = False
+ OpenSSL = None
__virtualname__ = 'x509'
@@ -81,10 +80,7 @@ def __virtual__():
'''
only load this module if m2crypto is available
'''
- if HAS_M2:
- return __virtualname__
- else:
- return (False, 'Could not load x509 module, m2crypto unavailable')
+ return __virtualname__ if M2Crypto is not None else False, 'Could not load x509 module, m2crypto unavailable'
class _Ctx(ctypes.Structure):
@@ -127,10 +123,8 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1):
doesn't support getting the publickeyidentifier from the issuer
to create the authoritykeyidentifier extension.
'''
- if name == 'subjectKeyIdentifier' and \
- value.strip('0123456789abcdefABCDEF:') is not '':
- raise salt.exceptions.SaltInvocationError(
- 'value must be precomputed hash')
+ if name == 'subjectKeyIdentifier' and value.strip('0123456789abcdefABCDEF:') is not '':
+ raise salt.exceptions.SaltInvocationError('value must be precomputed hash')
# ensure name and value are bytes
name = salt.utils.stringutils.to_str(name)
@@ -145,7 +139,7 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1):
x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf(None, ctx, name, value)
lhash = None
except AttributeError:
- lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member
+ lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member
ctx = M2Crypto.m2.x509v3_set_conf_lhash(
lhash) # pylint: disable=no-member
# ctx not zeroed
@@ -196,10 +190,8 @@ def _get_csr_extensions(csr):
csrtempfile.flush()
csryaml = _parse_openssl_req(csrtempfile.name)
csrtempfile.close()
- if csryaml and 'Requested Extensions' in \
- csryaml['Certificate Request']['Data']:
- csrexts = \
- csryaml['Certificate Request']['Data']['Requested Extensions']
+ if csryaml and 'Requested Extensions' in csryaml['Certificate Request']['Data']:
+ csrexts = csryaml['Certificate Request']['Data']['Requested Extensions']
if not csrexts:
return ret
@@ -294,7 +286,7 @@ def _get_signing_policy(name):
signing_policy = policies.get(name)
if signing_policy:
return signing_policy
- return __salt__['config.get']('x509_signing_policies', {}).get(name)
+ return __salt__['config.get']('x509_signing_policies', {}).get(name) or {}
def _pretty_hex(hex_str):
@@ -321,9 +313,11 @@ def _text_or_file(input_):
'''
if os.path.isfile(input_):
with salt.utils.files.fopen(input_) as fp_:
- return salt.utils.stringutils.to_str(fp_.read())
+ out = salt.utils.stringutils.to_str(fp_.read())
else:
- return salt.utils.stringutils.to_str(input_)
+ out = salt.utils.stringutils.to_str(input_)
+
+ return out
def _parse_subject(subject):
@@ -341,7 +335,7 @@ def _parse_subject(subject):
ret[nid_name] = val
nids.append(nid_num)
except TypeError as err:
- log.trace("Missing attribute '%s'. Error: %s", nid_name, err)
+ log.debug("Missing attribute '%s'. Error: %s", nid_name, err)
return ret
@@ -520,8 +514,8 @@ def get_pem_entries(glob_path):
if os.path.isfile(path):
try:
ret[path] = get_pem_entry(text=path)
- except ValueError:
- pass
+ except ValueError as err:
+ log.debug('Unable to get PEM entries from %s: %s', path, err)
return ret
@@ -599,8 +593,8 @@ def read_certificates(glob_path):
if os.path.isfile(path):
try:
ret[path] = read_certificate(certificate=path)
- except ValueError:
- pass
+ except ValueError as err:
+ log.debug('Unable to read certificate %s: %s', path, err)
return ret
@@ -629,12 +623,10 @@ def read_csr(csr):
# Get size returns in bytes. The world thinks of key sizes in bits.
'Subject': _parse_subject(csr.get_subject()),
'Subject Hash': _dec2hex(csr.get_subject().as_hash()),
- 'Public Key Hash': hashlib.sha1(csr.get_pubkey().get_modulus())\
- .hexdigest()
+ 'Public Key Hash': hashlib.sha1(csr.get_pubkey().get_modulus()).hexdigest(),
+ 'X509v3 Extensions': _get_csr_extensions(csr),
}
- ret['X509v3 Extensions'] = _get_csr_extensions(csr)
-
return ret
@@ -937,7 +929,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
# pyOpenSSL Note due to current limitations in pyOpenSSL it is impossible
# to specify a digest For signing the CRL. This will hopefully be fixed
# soon: https://github.com/pyca/pyopenssl/pull/161
- if not HAS_OPENSSL:
+ if OpenSSL is None:
raise salt.exceptions.SaltInvocationError(
'Could not load OpenSSL module, OpenSSL unavailable'
)
@@ -962,8 +954,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
continue
if 'revocation_date' not in rev_item:
- rev_item['revocation_date'] = datetime.datetime\
- .now().strftime('%Y-%m-%d %H:%M:%S')
+ rev_item['revocation_date'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
rev_date = datetime.datetime.strptime(
rev_item['revocation_date'], '%Y-%m-%d %H:%M:%S')
@@ -1002,8 +993,9 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
try:
crltext = crl.export(**export_kwargs)
except (TypeError, ValueError):
- log.warning(
- 'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.')
+ log.warning('Error signing crl with specified digest. '
+ 'Are you using pyopenssl 0.15 or newer? '
+ 'The default md5 digest will be used.')
export_kwargs.pop('digest', None)
crltext = crl.export(**export_kwargs)
@@ -1042,8 +1034,7 @@ def sign_remote_certificate(argdic, **kwargs):
if 'signing_policy' in argdic:
signing_policy = _get_signing_policy(argdic['signing_policy'])
if not signing_policy:
- return 'Signing policy {0} does not exist.'.format(
- argdic['signing_policy'])
+ return 'Signing policy {0} does not exist.'.format(argdic['signing_policy'])
if isinstance(signing_policy, list):
dict_ = {}
@@ -1080,6 +1071,7 @@ def get_signing_policy(signing_policy_name):
signing_policy = _get_signing_policy(signing_policy_name)
if not signing_policy:
return 'Signing policy {0} does not exist.'.format(signing_policy_name)
+
if isinstance(signing_policy, list):
dict_ = {}
for item in signing_policy:
@@ -1092,10 +1084,9 @@ def get_signing_policy(signing_policy_name):
pass
try:
- signing_policy['signing_cert'] = get_pem_entry(
- signing_policy['signing_cert'], 'CERTIFICATE')
+ signing_policy['signing_cert'] = get_pem_entry(signing_policy['signing_cert'], 'CERTIFICATE')
except KeyError:
- pass
+ log.debug('Unable to get "certificate" PEM entry')
return signing_policy
@@ -1346,8 +1337,7 @@ def create_certificate(
signing_private_key='/etc/pki/myca.key' csr='/etc/pki/myca.csr'}
'''
- if not path and not text and \
- ('testrun' not in kwargs or kwargs['testrun'] is False):
+ if not path and not text and ('testrun' not in kwargs or kwargs['testrun'] is False):
raise salt.exceptions.SaltInvocationError(
'Either path or text must be specified.')
if path and text:
@@ -1376,8 +1366,7 @@ def create_certificate(
# Including listen_in and preqreuired because they are not included
# in STATE_INTERNAL_KEYWORDS
# for salt 2014.7.2
- for ignore in list(_STATE_INTERNAL_KEYWORDS) + \
- ['listen_in', 'preqrequired', '__prerequired__']:
+ for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['listen_in', 'preqrequired', '__prerequired__']:
kwargs.pop(ignore, None)
certs = __salt__['publish.publish'](
@@ -1484,8 +1473,7 @@ def create_certificate(
continue
# Use explicitly set values first, fall back to CSR values.
- extval = kwargs.get(extname) or kwargs.get(extlongname) or \
- csrexts.get(extname) or csrexts.get(extlongname)
+ extval = kwargs.get(extname) or kwargs.get(extlongname) or csrexts.get(extname) or csrexts.get(extlongname)
critical = False
if extval.startswith('critical '):
@@ -1608,8 +1596,8 @@ def create_csr(path=None, text=False, **kwargs):
if 'private_key' not in kwargs and 'public_key' in kwargs:
kwargs['private_key'] = kwargs['public_key']
- log.warning(
- "OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key")
+ log.warning("OpenSSL no longer allows working with non-signed CSRs. "
+ "A private_key must be specified. Attempting to use public_key as private_key")
if 'private_key' not in kwargs:
raise salt.exceptions.SaltInvocationError('private_key is required')
@@ -1621,11 +1609,9 @@ def create_csr(path=None, text=False, **kwargs):
kwargs['private_key_passphrase'] = None
if 'public_key_passphrase' not in kwargs:
kwargs['public_key_passphrase'] = None
- if kwargs['public_key_passphrase'] and not kwargs[
- 'private_key_passphrase']:
+ if kwargs['public_key_passphrase'] and not kwargs['private_key_passphrase']:
kwargs['private_key_passphrase'] = kwargs['public_key_passphrase']
- if kwargs['private_key_passphrase'] and not kwargs[
- 'public_key_passphrase']:
+ if kwargs['private_key_passphrase'] and not kwargs['public_key_passphrase']:
kwargs['public_key_passphrase'] = kwargs['private_key_passphrase']
csr.set_pubkey(get_public_key(kwargs['public_key'],
@@ -1669,18 +1655,10 @@ def create_csr(path=None, text=False, **kwargs):
extstack.push(ext)
csr.add_extensions(extstack)
-
csr.sign(_get_private_key_obj(kwargs['private_key'],
passphrase=kwargs['private_key_passphrase']), kwargs['algorithm'])
- if path:
- return write_pem(
- text=csr.as_pem(),
- path=path,
- pem_type='CERTIFICATE REQUEST'
- )
- else:
- return csr.as_pem()
+ return write_pem(text=csr.as_pem(), path=path, pem_type='CERTIFICATE REQUEST') if path else csr.as_pem()
def verify_private_key(private_key, public_key, passphrase=None):
@@ -1705,8 +1683,7 @@ def verify_private_key(private_key, public_key, passphrase=None):
salt '*' x509.verify_private_key private_key=/etc/pki/myca.key \\
public_key=/etc/pki/myca.crt
'''
- return bool(get_public_key(private_key, passphrase)
- == get_public_key(public_key))
+ return get_public_key(private_key, passphrase) == get_public_key(public_key)
def verify_signature(certificate, signing_pub_key=None,
@@ -1760,9 +1737,8 @@ def verify_crl(crl, cert):
salt '*' x509.verify_crl crl=/etc/pki/myca.crl cert=/etc/pki/myca.crt
'''
if not salt.utils.path.which('openssl'):
- raise salt.exceptions.SaltInvocationError(
- 'openssl binary not found in path'
- )
+ raise salt.exceptions.SaltInvocationError('External command "openssl" not found')
+
crltext = _text_or_file(crl)
crltext = get_pem_entry(crltext, pem_type='X509 CRL')
crltempfile = tempfile.NamedTemporaryFile()
@@ -1783,10 +1759,7 @@ def verify_crl(crl, cert):
crltempfile.close()
certtempfile.close()
- if 'verify OK' in output:
- return True
- else:
- return False
+ return 'verify OK' in output
def expired(certificate):
@@ -1823,8 +1796,9 @@ def expired(certificate):
ret['expired'] = True
else:
ret['expired'] = False
- except ValueError:
- pass
+ except ValueError as err:
+ log.debug('Failed to get data of expired certificate: %s', err)
+ log.trace(err, exc_info=True)
return ret
@@ -1847,6 +1821,7 @@ def will_expire(certificate, days):
salt '*' x509.will_expire "/etc/pki/mycert.crt" days=30
'''
+ ts_pt = "%Y-%m-%d %H:%M:%S"
ret = {}
if os.path.isfile(certificate):
@@ -1856,18 +1831,13 @@ def will_expire(certificate, days):
cert = _get_certificate_obj(certificate)
- _check_time = datetime.datetime.utcnow() + \
- datetime.timedelta(days=days)
+ _check_time = datetime.datetime.utcnow() + datetime.timedelta(days=days)
_expiration_date = cert.get_not_after().get_datetime()
ret['cn'] = _parse_subject(cert.get_subject())['CN']
-
- if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= \
- _check_time.strftime("%Y-%m-%d %H:%M:%S"):
- ret['will_expire'] = True
- else:
- ret['will_expire'] = False
- except ValueError:
- pass
+ ret['will_expire'] = _expiration_date.strftime(ts_pt) <= _check_time.strftime(ts_pt)
+ except ValueError as err:
+ log.debug('Unable to return details of a sertificate expiration: %s', err)
+ log.trace(err, exc_info=True)
return ret
diff --git a/salt/states/x509.py b/salt/states/x509.py
index 7bb941f393..3ba4f79c79 100644
--- a/salt/states/x509.py
+++ b/salt/states/x509.py
@@ -163,6 +163,7 @@ import copy
# Import Salt Libs
import salt.exceptions
+import salt.utils.stringutils
# Import 3rd-party libs
from salt.ext import six
@@ -170,7 +171,7 @@ from salt.ext import six
try:
from M2Crypto.RSA import RSAError
except ImportError:
- pass
+ RSAError = Exception('RSA Error')
def __virtual__():
@@ -180,7 +181,7 @@ def __virtual__():
if 'x509.get_pem_entry' in __salt__:
return 'x509'
else:
- return (False, 'Could not load x509 state: m2crypto unavailable')
+ return False, 'Could not load x509 state: the x509 is not available'
def _revoked_to_list(revs):
@@ -267,7 +268,8 @@ def private_key_managed(name,
new:
Always create a new key. Defaults to False.
- Combining new with :mod:`prereq <salt.states.requsities.preqreq>`, or when used as part of a `managed_private_key` can allow key rotation whenever a new certificiate is generated.
+ Combining new with :mod:`prereq <salt.states.requsities.preqreq>`, or when used as part of a
+ `managed_private_key` can allow key rotation whenever a new certificiate is generated.
overwrite:
Overwrite an existing private key if the provided passphrase cannot decrypt it.
@@ -453,8 +455,10 @@ def certificate_managed(name,
private_key_args['name'], pem_type='RSA PRIVATE KEY')
else:
new_private_key = True
- private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'], passphrase=private_key_args[
- 'passphrase'], cipher=private_key_args['cipher'], verbose=private_key_args['verbose'])
+ private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'],
+ passphrase=private_key_args['passphrase'],
+ cipher=private_key_args['cipher'],
+ verbose=private_key_args['verbose'])
kwargs['public_key'] = private_key
@@ -664,8 +668,10 @@ def crl_managed(name,
else:
current = '{0} does not exist.'.format(name)
- new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, signing_private_key_passphrase=signing_private_key_passphrase,
- signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired)
+ new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key,
+ signing_private_key_passphrase=signing_private_key_passphrase,
+ signing_cert=signing_cert, revoked=revoked, days_valid=days_valid,
+ digest=digest, include_expired=include_expired)
new = __salt__['x509.read_crl'](crl=new_crl)
new_comp = new.copy()
@@ -707,6 +713,6 @@ def pem_managed(name,
Any arguments supported by :state:`file.managed <salt.states.file.managed>` are supported.
'''
file_args, kwargs = _get_file_args(name, **kwargs)
- file_args['contents'] = __salt__['x509.get_pem_entry'](text=text)
+ file_args['contents'] = salt.utils.stringutils.to_str(__salt__['x509.get_pem_entry'](text=text))
return __states__['file.managed'](**file_args)
diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py
index c300a56d64..7e00c97140 100644
--- a/tests/unit/modules/test_x509.py
+++ b/tests/unit/modules/test_x509.py
@@ -67,10 +67,11 @@ class X509TestCase(TestCase, LoaderModuleMockMixin):
subj = FakeSubject()
x509._parse_subject(subj)
- x509.log.trace.assert_called_once()
- assert x509.log.trace.call_args[0][0] == "Missing attribute '%s'. Error: %s"
- assert x509.log.trace.call_args[0][1] == list(subj.nid.keys())[0]
- assert isinstance(x509.log.trace.call_args[0][2], TypeError)
+ x509.log.debug.assert_called_once()
+
+ assert x509.log.debug.call_args[0][0] == "Missing attribute '%s'. Error: %s"
+ assert x509.log.debug.call_args[0][1] == list(subj.nid.keys())[0]
+ assert isinstance(x509.log.debug.call_args[0][2], TypeError)
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble')
def test_get_pem_entry(self):
--
2.19.0

View File

@ -0,0 +1,80 @@
From 6276eb2cd3f2b396c13118a111998230477cc65a Mon Sep 17 00:00:00 2001
From: Florian Bergmann <bergmannf@users.noreply.github.com>
Date: Tue, 11 Sep 2018 14:02:55 +0200
Subject: [PATCH] X509 fixes for remote signing (#106)
* Use to_str salt.utils when writing to a file.
* Assign the certificate as a string.
* Convert to string before sending via 'publish'.
Otherwise the publish call with receive a "b''" string, which can not be used
in the functions.
* Do not silently ignore errors.
At least log the occurring errors to debug and trace.
---
salt/modules/x509.py | 10 +++++-----
salt/states/x509.py | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/salt/modules/x509.py b/salt/modules/x509.py
index 15de06e200..9901bc5bd9 100644
--- a/salt/modules/x509.py
+++ b/salt/modules/x509.py
@@ -658,7 +658,7 @@ def read_crl(crl):
text = get_pem_entry(text, pem_type='X509 CRL')
crltempfile = tempfile.NamedTemporaryFile()
- crltempfile.write(text)
+ crltempfile.write(salt.utils.stringutils.to_str(text))
crltempfile.flush()
crlparsed = _parse_openssl_crl(crltempfile.name)
crltempfile.close()
@@ -1368,9 +1368,9 @@ def create_certificate(
pem_type='CERTIFICATE REQUEST').replace('\n', '')
if 'public_key' in kwargs:
# Strip newlines to make passing through as cli functions easier
- kwargs['public_key'] = get_public_key(
+ kwargs['public_key'] = salt.utils.stringutils.to_str(get_public_key(
kwargs['public_key'],
- passphrase=kwargs['public_key_passphrase']).replace('\n', '')
+ passphrase=kwargs['public_key_passphrase'])).replace('\n', '')
# Remove system entries in kwargs
# Including listen_in and preqreuired because they are not included
@@ -1766,13 +1766,13 @@ def verify_crl(crl, cert):
crltext = _text_or_file(crl)
crltext = get_pem_entry(crltext, pem_type='X509 CRL')
crltempfile = tempfile.NamedTemporaryFile()
- crltempfile.write(crltext)
+ crltempfile.write(salt.utils.stringutils.to_str(crltext))
crltempfile.flush()
certtext = _text_or_file(cert)
certtext = get_pem_entry(certtext, pem_type='CERTIFICATE')
certtempfile = tempfile.NamedTemporaryFile()
- certtempfile.write(certtext)
+ certtempfile.write(salt.utils.stringutils.to_str(certtext))
certtempfile.flush()
cmd = ('openssl crl -noout -in {0} -CAfile {1}'.format(
diff --git a/salt/states/x509.py b/salt/states/x509.py
index 832f74168c..7bb941f393 100644
--- a/salt/states/x509.py
+++ b/salt/states/x509.py
@@ -545,7 +545,7 @@ def certificate_managed(name,
if not private_ret['result']:
return private_ret
- file_args['contents'] += certificate
+ file_args['contents'] += salt.utils.stringutils.to_str(certificate)
if not append_certs:
append_certs = []
--
2.19.0