diff --git a/_lastrevision b/_lastrevision index d9d4a41..274ed81 100644 --- a/_lastrevision +++ b/_lastrevision @@ -1 +1 @@ -06f92313dcbe69d75634862b15653b81dc74e340 \ No newline at end of file +691a47044edb2338cd754103a09089b4a8cf9994 \ No newline at end of file diff --git a/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch b/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch new file mode 100644 index 0000000..f726aba --- /dev/null +++ b/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch @@ -0,0 +1,153 @@ +From b2faa019f0f5aa03b03e6c54c9aa60b7f6aa4f91 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Fri, 30 Aug 2024 14:35:33 +0200 +Subject: [PATCH] Avoid crash on wrong output of systemctl version + (bsc#1229539) + +* Better handling output of systemctl --version + +* Add more cases to test grains.core._systemd +--- + salt/grains/core.py | 27 +++++++- + tests/pytests/unit/grains/test_core.py | 89 ++++++++++++++++++++++++++ + 2 files changed, 113 insertions(+), 3 deletions(-) + +diff --git a/salt/grains/core.py b/salt/grains/core.py +index 4454c303fe..98bbd3868e 100644 +--- a/salt/grains/core.py ++++ b/salt/grains/core.py +@@ -2432,10 +2432,31 @@ def _systemd(): + """ + Return the systemd grain + """ +- systemd_info = __salt__["cmd.run"]("systemctl --version").splitlines() ++ systemd_version = "UNDEFINED" ++ systemd_features = "" ++ try: ++ systemd_output = __salt__["cmd.run_all"]("systemctl --version") ++ except Exception: # pylint: disable=broad-except ++ log.error("Exception while executing `systemctl --version`", exc_info=True) ++ return { ++ "version": systemd_version, ++ "features": systemd_features, ++ } ++ if systemd_output.get("retcode") == 0: ++ systemd_info = systemd_output.get("stdout", "").splitlines() ++ try: ++ if systemd_info[0].startswith("systemd "): ++ systemd_version = systemd_info[0].split()[1] ++ systemd_features = systemd_info[1] ++ except IndexError: ++ pass ++ if systemd_version == "UNDEFINED" or systemd_features == "": ++ log.error( ++ "Unexpected output returned by `systemctl --version`: %s", systemd_output ++ ) + return { +- "version": systemd_info[0].split()[1], +- "features": systemd_info[1], ++ "version": systemd_version, ++ "features": systemd_features, + } + + +diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py +index 36545287b9..b64b8c4bf8 100644 +--- a/tests/pytests/unit/grains/test_core.py ++++ b/tests/pytests/unit/grains/test_core.py +@@ -3593,3 +3593,92 @@ def test_virtual_set_virtual_ec2(): + + assert virtual_grains["virtual"] == "Nitro" + assert virtual_grains["virtual_subtype"] == "Amazon EC2" ++ ++ ++@pytest.mark.parametrize( ++ "systemd_data,expected", ++ ( ++ ( ++ { ++ "pid": 1234, ++ "retcode": 0, ++ "stdout": "systemd 254 (254.3-1)\n+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK " ++ "+SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS " ++ "+FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 " ++ "-PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD " ++ "+BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified", ++ "stderr": "", ++ }, ++ { ++ "version": "254", ++ "features": "+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL " ++ "+ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP " ++ "+LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ " ++ "+ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified", ++ }, ++ ), ++ ( ++ { ++ "pid": 2345, ++ "retcode": 1, ++ "stdout": "", ++ "stderr": "some garbage in the output", ++ }, ++ { ++ "version": "UNDEFINED", ++ "features": "", ++ }, ++ ), ++ ( ++ { ++ "pid": 3456, ++ "retcode": 0, ++ "stdout": "unexpected stdout\none more line", ++ "stderr": "", ++ }, ++ { ++ "version": "UNDEFINED", ++ "features": "", ++ }, ++ ), ++ ( ++ { ++ "pid": 4567, ++ "retcode": 0, ++ "stdout": "", ++ "stderr": "", ++ }, ++ { ++ "version": "UNDEFINED", ++ "features": "", ++ }, ++ ), ++ ( ++ Exception("Some exception on calling `systemctl --version`"), ++ { ++ "version": "UNDEFINED", ++ "features": "", ++ }, ++ ), ++ ), ++) ++def test__systemd(systemd_data, expected): ++ """ ++ test _systemd ++ """ ++ ++ def mock_run_all_systemd(_): ++ if isinstance(systemd_data, Exception): ++ raise systemd_data ++ return systemd_data ++ ++ with patch.dict( ++ core.__salt__, ++ { ++ "cmd.run_all": mock_run_all_systemd, ++ }, ++ ): ++ ret = core._systemd() ++ assert "version" in ret ++ assert "features" in ret ++ assert ret == expected +-- +2.46.0 + diff --git a/improve-error-handling-with-different-openssl-versio.patch b/improve-error-handling-with-different-openssl-versio.patch new file mode 100644 index 0000000..4d16812 --- /dev/null +++ b/improve-error-handling-with-different-openssl-versio.patch @@ -0,0 +1,98 @@ +From 4e226426d0897f2d9dc64891ced78487b181d40e Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Fri, 30 Aug 2024 14:33:51 +0200 +Subject: [PATCH] Improve error handling with different OpenSSL + versions + +* Make error checking of x509 more flexible + +for most recent cryptography and openSSL versions + +* Add test for different exception value on loading private key + +* Add fix for test_privkey_new_with_prereq on old OpenSSL +--- + salt/utils/x509.py | 3 +- + .../pytests/functional/states/test_x509_v2.py | 29 +++++++++++++++++++ + .../integration/states/test_x509_v2.py | 7 +++++ + 3 files changed, 38 insertions(+), 1 deletion(-) + +diff --git a/salt/utils/x509.py b/salt/utils/x509.py +index 5b2ae15882..f9fdca64d9 100644 +--- a/salt/utils/x509.py ++++ b/salt/utils/x509.py +@@ -695,7 +695,8 @@ def load_privkey(pk, passphrase=None, get_encoding=False): + return pk, "pem", None + return pk + except ValueError as err: +- if "Bad decrypt" in str(err): ++ str_err = str(err) ++ if "Bad decrypt" in str_err or "Could not deserialize key data" in str_err: + raise SaltInvocationError( + "Bad decrypt - is the password correct?" + ) from err +diff --git a/tests/pytests/functional/states/test_x509_v2.py b/tests/pytests/functional/states/test_x509_v2.py +index 929be014cd..47a1c555f8 100644 +--- a/tests/pytests/functional/states/test_x509_v2.py ++++ b/tests/pytests/functional/states/test_x509_v2.py +@@ -3,6 +3,8 @@ from pathlib import Path + + import pytest + ++from tests.support.mock import patch ++ + try: + import cryptography + import cryptography.x509 as cx509 +@@ -2826,3 +2828,30 @@ def _get_privkey(pk, encoding="pem", passphrase=None): + pk = base64.b64decode(pk) + return pkcs12.load_pkcs12(pk, passphrase).key + raise ValueError("Need correct encoding") ++ ++ ++@pytest.mark.usefixtures("existing_pk") ++@pytest.mark.parametrize("existing_pk", [{"passphrase": "password"}], indirect=True) ++def test_exceptions_on_calling_load_pem_private_key(x509, pk_args): ++ pk_args["passphrase"] = "hunter1" ++ pk_args["overwrite"] = True ++ ++ with patch( ++ "cryptography.hazmat.primitives.serialization.load_pem_private_key", ++ side_effect=ValueError("Bad decrypt. Incorrect password?"), ++ ): ++ ret = x509.private_key_managed(**pk_args) ++ _assert_pk_basic(ret, "rsa", passphrase="hunter1") ++ ++ with patch( ++ "cryptography.hazmat.primitives.serialization.load_pem_private_key", ++ side_effect=ValueError( ++ "Could not deserialize key data. The data may be in an incorrect format, " ++ "the provided password may be incorrect, " ++ "it may be encrypted with an unsupported algorithm, " ++ "or it may be an unsupported key type " ++ "(e.g. EC curves with explicit parameters)." ++ ), ++ ): ++ ret = x509.private_key_managed(**pk_args) ++ _assert_pk_basic(ret, "rsa", passphrase="hunter1") +diff --git a/tests/pytests/integration/states/test_x509_v2.py b/tests/pytests/integration/states/test_x509_v2.py +index 4f94341295..ad8d904c92 100644 +--- a/tests/pytests/integration/states/test_x509_v2.py ++++ b/tests/pytests/integration/states/test_x509_v2.py +@@ -195,6 +195,13 @@ Certificate: + """ + with x509_salt_master.state_tree.base.temp_file("manage_cert.sls", state): + ret = x509_salt_call_cli.run("state.apply", "manage_cert") ++ if ( ++ ret.returncode == 1 ++ and "NotImplementedError: ECDSA keys with unnamed curves" in ret.stdout ++ ): ++ pytest.skip( ++ "The version of OpenSSL doesn't support ECDSA keys with unnamed curves" ++ ) + assert ret.returncode == 0 + assert ret.data[next(iter(ret.data))]["changes"] + assert (tmp_path / "priv.key").exists() +-- +2.46.0 + diff --git a/remove-redundant-run_func-from-salt.master.mworker._.patch b/remove-redundant-run_func-from-salt.master.mworker._.patch new file mode 100644 index 0000000..5fd4641 --- /dev/null +++ b/remove-redundant-run_func-from-salt.master.mworker._.patch @@ -0,0 +1,224 @@ +From ff789d88541954e4fc1678dff728bc6a3ea7472e Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Fri, 30 Aug 2024 14:30:27 +0200 +Subject: [PATCH] Remove redundant run_func from + salt.master.MWorker._handle_aes + +* New request context + +* Fix docs + +* Remove redundant run_func from salt.master.MWorker._handle_aes + +* Get rid of run_func in salt.Minion._target + +--------- + +Co-authored-by: Daniel A. Wozniak +--- + doc/topics/releases/3007.0.rst | 0 + salt/_logging/impl.py | 15 ++++++--- + salt/master.py | 12 ++----- + salt/minion.py | 10 ++---- + salt/utils/ctx.py | 60 +++++++++++----------------------- + 5 files changed, 34 insertions(+), 63 deletions(-) + create mode 100644 doc/topics/releases/3007.0.rst + +diff --git a/doc/topics/releases/3007.0.rst b/doc/topics/releases/3007.0.rst +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py +index 1d71cb8be8..4d1ebd2495 100644 +--- a/salt/_logging/impl.py ++++ b/salt/_logging/impl.py +@@ -26,6 +26,8 @@ GARBAGE = logging.GARBAGE = 1 + QUIET = logging.QUIET = 1000 + + import salt.defaults.exitcodes # isort:skip pylint: disable=unused-import ++import salt.utils.ctx ++ + from salt._logging.handlers import DeferredStreamHandler # isort:skip + from salt._logging.handlers import RotatingFileHandler # isort:skip + from salt._logging.handlers import StreamHandler # isort:skip +@@ -33,7 +35,6 @@ from salt._logging.handlers import SysLogHandler # isort:skip + from salt._logging.handlers import WatchedFileHandler # isort:skip + from salt._logging.mixins import LoggingMixinMeta # isort:skip + from salt.exceptions import LoggingRuntimeError # isort:skip +-from salt.utils.ctx import RequestContext # isort:skip + from salt.utils.immutabletypes import freeze, ImmutableDict # isort:skip + from salt.utils.textformat import TextFormat # isort:skip + +@@ -242,10 +243,14 @@ class SaltLoggingClass(LOGGING_LOGGER_CLASS, metaclass=LoggingMixinMeta): + if extra is None: + extra = {} + +- # pylint: disable=no-member +- current_jid = RequestContext.current.get("data", {}).get("jid", None) +- log_fmt_jid = RequestContext.current.get("opts", {}).get("log_fmt_jid", None) +- # pylint: enable=no-member ++ current_jid = ( ++ salt.utils.ctx.get_request_context().get("data", {}).get("jid", None) ++ ) ++ log_fmt_jid = ( ++ salt.utils.ctx.get_request_context() ++ .get("opts", {}) ++ .get("log_fmt_jid", None) ++ ) + + if current_jid is not None: + extra["jid"] = current_jid +diff --git a/salt/master.py b/salt/master.py +index d7182d10b5..49cfb68860 100644 +--- a/salt/master.py ++++ b/salt/master.py +@@ -38,6 +38,7 @@ import salt.state + import salt.utils.args + import salt.utils.atomicfile + import salt.utils.crypt ++import salt.utils.ctx + import salt.utils.event + import salt.utils.files + import salt.utils.gitfs +@@ -58,10 +59,8 @@ import salt.wheel + from salt.cli.batch_async import BatchAsync, batch_async_required + from salt.config import DEFAULT_INTERVAL + from salt.defaults import DEFAULT_TARGET_DELIM +-from salt.ext.tornado.stack_context import StackContext + from salt.transport import TRANSPORTS + from salt.utils.channel import iter_transport_opts +-from salt.utils.ctx import RequestContext + from salt.utils.debug import ( + enable_sigusr1_handler, + enable_sigusr2_handler, +@@ -1108,13 +1107,8 @@ class MWorker(salt.utils.process.SignalHandlingProcess): + start = time.time() + self.stats[cmd]["runs"] += 1 + +- def run_func(data): +- return self.aes_funcs.run_func(data["cmd"], data) +- +- with StackContext( +- functools.partial(RequestContext, {"data": data, "opts": self.opts}) +- ): +- ret = run_func(data) ++ with salt.utils.ctx.request_context({"data": data, "opts": self.opts}): ++ ret = self.aes_funcs.run_func(data["cmd"], data) + + if self.opts["master_stats"]: + self._post_stats(start, cmd) +diff --git a/salt/minion.py b/salt/minion.py +index 2ccd0cd5a9..e21a017cfd 100644 +--- a/salt/minion.py ++++ b/salt/minion.py +@@ -39,6 +39,7 @@ import salt.transport + import salt.utils.args + import salt.utils.context + import salt.utils.crypt ++import salt.utils.ctx + import salt.utils.data + import salt.utils.dictdiffer + import salt.utils.dictupdate +@@ -70,7 +71,6 @@ from salt.exceptions import ( + SaltSystemExit, + ) + from salt.template import SLS_ENCODING +-from salt.utils.ctx import RequestContext + from salt.utils.debug import enable_sigusr1_handler + from salt.utils.event import tagify + from salt.utils.network import parse_host_port +@@ -1805,18 +1805,12 @@ class Minion(MinionBase): + uid = salt.utils.user.get_uid(user=opts.get("user", None)) + minion_instance.proc_dir = get_proc_dir(opts["cachedir"], uid=uid) + +- def run_func(minion_instance, opts, data): ++ with salt.utils.ctx.request_context({"data": data, "opts": opts}): + if isinstance(data["fun"], tuple) or isinstance(data["fun"], list): + return Minion._thread_multi_return(minion_instance, opts, data) + else: + return Minion._thread_return(minion_instance, opts, data) + +- with salt.ext.tornado.stack_context.StackContext( +- functools.partial(RequestContext, {"data": data, "opts": opts}) +- ): +- with salt.ext.tornado.stack_context.StackContext(minion_instance.ctx): +- run_func(minion_instance, opts, data) +- + def _execute_job_function( + self, function_name, function_args, executors, opts, data + ): +diff --git a/salt/utils/ctx.py b/salt/utils/ctx.py +index a9c0931bd8..2f4b5b4c9b 100644 +--- a/salt/utils/ctx.py ++++ b/salt/utils/ctx.py +@@ -1,49 +1,27 @@ +-import threading ++import contextlib + ++try: ++ # Try the stdlib C extension first ++ import _contextvars as contextvars ++except ImportError: ++ # Py<3.7 ++ import contextvars + +-class ClassProperty(property): +- """ +- Use a classmethod as a property +- http://stackoverflow.com/a/1383402/1258307 +- """ ++DEFAULT_CTX_VAR = "request_ctxvar" ++request_ctxvar = contextvars.ContextVar(DEFAULT_CTX_VAR) + +- def __get__(self, cls, owner): +- return self.fget.__get__(None, owner)() # pylint: disable=no-member + +- +-class RequestContext: ++@contextlib.contextmanager ++def request_context(data): + """ +- A context manager that saves some per-thread state globally. +- Intended for use with Tornado's StackContext. +- https://gist.github.com/simon-weber/7755289 +- Simply import this class into any module and access the current request handler by this +- class's class method property 'current'. If it returns None, there's no active request. +- .. code:: python +- from raas.utils.ctx import RequestContext +- current_request_handler = RequestContext.current ++ A context manager that sets and un-sets the loader context + """ ++ tok = request_ctxvar.set(data) ++ try: ++ yield ++ finally: ++ request_ctxvar.reset(tok) + +- _state = threading.local() +- _state.current_request = {} +- +- def __init__(self, current_request): +- self._current_request = current_request +- +- @ClassProperty +- @classmethod +- def current(cls): +- if not hasattr(cls._state, "current_request"): +- return {} +- return cls._state.current_request +- +- def __enter__(self): +- self._prev_request = self.__class__.current +- self.__class__._state.current_request = self._current_request +- +- def __exit__(self, *exc): +- self.__class__._state.current_request = self._prev_request +- del self._prev_request +- return False + +- def __call__(self): +- return self ++def get_request_context(): ++ return request_ctxvar.get({}) +-- +2.46.0 + diff --git a/salt.changes b/salt.changes index 3b621a4..a327def 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Fri Aug 30 14:42:00 UTC 2024 - Victor Zhestkov + +- Avoid crash on wrong output of systemctl version (bsc#1229539) +- Improve error handling with different OpenSSL versions +- Remove redundant run_func from salt.master.MWorker._handle_aes + +- Added: + * avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch + * improve-error-handling-with-different-openssl-versio.patch + * remove-redundant-run_func-from-salt.master.mworker._.patch + ------------------------------------------------------------------- Thu Aug 29 11:54:27 UTC 2024 - Yeray Gutiérrez Cedrés diff --git a/salt.spec b/salt.spec index f214b3f..ce12849 100644 --- a/salt.spec +++ b/salt.spec @@ -420,6 +420,13 @@ Patch129: replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch # PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/671 # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64173 Patch130: join-masters-if-it-is-a-list-671.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66509 +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/0c3ebc0795f9c2adec90118281343cae3070e0f6 +Patch131: remove-redundant-run_func-from-salt.master.mworker._.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66818 +Patch132: improve-error-handling-with-different-openssl-versio.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66856 +Patch133: avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch ### IMPORTANT: The line below is used as a snippet marker. Do not touch it. ### SALT PATCHES LIST END