2686359b2c
- Update to Salt release version 3006.0 (jsc#PED-3139) * See release notes: https://docs.saltproject.io/en/latest/topics/releases/3006.0.html - Add python3-looseversion as new dependency for salt - Add python3-packaging as new dependency for salt - Drop conflictive patch dicarded from upstream - Fix SLS rendering error when Jinja macros are used - Fix version detection and avoid building and testing failures - Added: * fix-version-detection-and-avoid-building-and-testing.patch * make-sure-the-file-client-is-destroyed-upon-used.patch - Modified: * 3005.1-implement-zypper-removeptf-573.patch * activate-all-beacons-sources-config-pillar-grains.patch * add-custom-suse-capabilities-as-grains.patch * add-environment-variable-to-know-if-yum-is-invoked-f.patch * add-migrated-state-and-gpg-key-management-functions-.patch * add-publish_batch-to-clearfuncs-exposed-methods.patch * add-salt-ssh-support-with-venv-salt-minion-3004-493.patch * add-sleep-on-exception-handling-on-minion-connection.patch * add-standalone-configuration-file-for-enabling-packa.patch * add-support-for-gpgautoimport-539.patch * allow-vendor-change-option-with-zypper.patch * async-batch-implementation.patch * avoid-excessive-syslogging-by-watchdog-cronjob-58.patch * bsc-1176024-fix-file-directory-user-and-group-owners.patch * change-the-delimeters-to-prevent-possible-tracebacks.patch * control-the-collection-of-lvm-grains-via-config.patch * debian-info_installed-compatibility-50453.patch * dnfnotify-pkgset-plugin-implementation-3002.2-450.patch * do-not-load-pip-state-if-there-is-no-3rd-party-depen.patch * don-t-use-shell-sbin-nologin-in-requisites.patch * drop-serial-from-event.unpack-in-cli.batch_async.patch * early-feature-support-config.patch * enable-passing-a-unix_socket-for-mysql-returners-bsc.patch * enhance-openscap-module-add-xccdf_eval-call-386.patch * fix-bsc-1065792.patch * fix-for-suse-expanded-support-detection.patch * fix-issue-2068-test.patch * fix-missing-minion-returns-in-batch-mode-360.patch * fix-ownership-of-salt-thin-directory-when-using-the-.patch * fix-regression-with-depending-client.ssh-on-psutil-b.patch * fix-salt-ssh-opts-poisoning-bsc-1197637-3004-501.patch * fix-salt.utils.stringutils.to_str-calls-to-make-it-w.patch * fix-the-regression-for-yumnotify-plugin-456.patch * fix-traceback.print_exc-calls-for-test_pip_state-432.patch * fixes-for-python-3.10-502.patch * include-aliases-in-the-fqdns-grains.patch * info_installed-works-without-status-attr-now.patch * let-salt-ssh-use-platform-python-binary-in-rhel8-191.patch * make-aptpkg.list_repos-compatible-on-enabled-disable.patch * make-setup.py-script-to-not-require-setuptools-9.1.patch * pass-the-context-to-pillar-ext-modules.patch * prevent-affection-of-ssh.opts-with-lazyloader-bsc-11.patch * prevent-pkg-plugins-errors-on-missing-cookie-path-bs.patch * prevent-shell-injection-via-pre_flight_script_args-4.patch * read-repo-info-without-using-interpolation-bsc-11356.patch * restore-default-behaviour-of-pkg-list-return.patch * return-the-expected-powerpc-os-arch-bsc-1117995.patch * revert-fixing-a-use-case-when-multiple-inotify-beaco.patch * run-salt-api-as-user-salt-bsc-1064520.patch * run-salt-master-as-dedicated-salt-user.patch * save-log-to-logfile-with-docker.build.patch * skip-package-names-without-colon-bsc-1208691-578.patch * switch-firewalld-state-to-use-change_interface.patch * temporary-fix-extend-the-whitelist-of-allowed-comman.patch * update-target-fix-for-salt-ssh-to-process-targets-li.patch * use-adler32-algorithm-to-compute-string-checksums.patch * use-rlock-to-avoid-deadlocks-in-salt-ssh.patch * use-salt-bundle-in-dockermod.patch * x509-fixes-111.patch * zypperpkg-ignore-retcode-104-for-search-bsc-1176697-.patch - Removed: * add-amazon-ec2-detection-for-virtual-grains-bsc-1195.patch * add-support-for-name-pkgs-and-diff_attr-parameters-t.patch * align-amazon-ec2-nitro-grains-with-upstream-pr-bsc-1.patch * allow-entrypoint-compatibility-for-importlib-metadat.patch * clarify-pkg.installed-pkg_verify-documentation.patch * detect-module.run-syntax.patch * fix-salt.states.file.managed-for-follow_symlinks-tru.patch * fix-state.apply-in-test-mode-with-file-state-module-.patch * fix-test_ipc-unit-tests.patch * fixes-pkg.version_cmp-on-openeuler-systems-and-a-few.patch * fopen-workaround-bad-buffering-for-binary-mode-563.patch * ignore-erros-on-reading-license-files-with-dpkg_lowp.patch * ignore-extend-declarations-from-excluded-sls-files.patch * ignore-non-utf8-characters-while-reading-files-with-.patch * include-stdout-in-error-message-for-zypperpkg-559.patch * make-pass-renderer-configurable-other-fixes-532.patch * make-sure-saltcacheloader-use-correct-fileclient-519.patch * normalize-package-names-once-with-pkg.installed-remo.patch * retry-if-rpm-lock-is-temporarily-unavailable-547.patch * set-default-target-for-pip-from-venv_pip_target-envi.patch * state.apply-don-t-check-for-cached-pillar-errors.patch * state.orchestrate_single-does-not-pass-pillar-none-4.patch OBS-URL: https://build.opensuse.org/request/show/1084999 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=210
426 lines
14 KiB
Diff
426 lines
14 KiB
Diff
From 17452801e950b3f49a9ec7ef444e3d57862cd9bf Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
|
<psuarezhernandez@suse.com>
|
|
Date: Wed, 7 Jul 2021 15:41:48 +0100
|
|
Subject: [PATCH] Enhance openscap module: add "xccdf_eval" call (#386)
|
|
|
|
* Enhance openscap module: add xccdf_eval call
|
|
|
|
* Allow 'tailoring_file' and 'tailoring_id' parameters
|
|
|
|
* Fix wrong reference to subprocess.PIPE in openscap unit tests
|
|
|
|
* Add changes suggested by pre-commit
|
|
|
|
Co-authored-by: Michael Calmer <mc@suse.de>
|
|
|
|
Fix error handling in openscap module (bsc#1188647) (#409)
|
|
---
|
|
changelog/59756.added | 1 +
|
|
salt/modules/openscap.py | 116 +++++++++++++-
|
|
tests/unit/modules/test_openscap.py | 234 ++++++++++++++++++++++++++++
|
|
3 files changed, 350 insertions(+), 1 deletion(-)
|
|
create mode 100644 changelog/59756.added
|
|
|
|
diff --git a/changelog/59756.added b/changelog/59756.added
|
|
new file mode 100644
|
|
index 0000000000..a59fb21eef
|
|
--- /dev/null
|
|
+++ b/changelog/59756.added
|
|
@@ -0,0 +1 @@
|
|
+adding new call for openscap xccdf eval supporting new parameters
|
|
diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py
|
|
index 770c8e7c04..216fd89eef 100644
|
|
--- a/salt/modules/openscap.py
|
|
+++ b/salt/modules/openscap.py
|
|
@@ -4,6 +4,7 @@ Module for OpenSCAP Management
|
|
"""
|
|
|
|
|
|
+import os.path
|
|
import shlex
|
|
import shutil
|
|
import tempfile
|
|
@@ -55,6 +56,117 @@ _OSCAP_EXIT_CODES_MAP = {
|
|
}
|
|
|
|
|
|
+def xccdf_eval(xccdffile, ovalfiles=None, **kwargs):
|
|
+ """
|
|
+ Run ``oscap xccdf eval`` commands on minions.
|
|
+ It uses cp.push_dir to upload the generated files to the salt master
|
|
+ in the master's minion files cachedir
|
|
+ (defaults to ``/var/cache/salt/master/minions/minion-id/files``)
|
|
+
|
|
+ It needs ``file_recv`` set to ``True`` in the master configuration file.
|
|
+
|
|
+ xccdffile
|
|
+ the path to the xccdf file to evaluate
|
|
+
|
|
+ ovalfiles
|
|
+ additional oval definition files
|
|
+
|
|
+ profile
|
|
+ the name of Profile to be evaluated
|
|
+
|
|
+ rule
|
|
+ the name of a single rule to be evaluated
|
|
+
|
|
+ oval_results
|
|
+ save OVAL results as well (True or False)
|
|
+
|
|
+ results
|
|
+ write XCCDF Results into given file
|
|
+
|
|
+ report
|
|
+ write HTML report into given file
|
|
+
|
|
+ fetch_remote_resources
|
|
+ download remote content referenced by XCCDF (True or False)
|
|
+
|
|
+ tailoring_file
|
|
+ use given XCCDF Tailoring file
|
|
+
|
|
+ tailoring_id
|
|
+ use given DS component as XCCDF Tailoring file
|
|
+
|
|
+ remediate
|
|
+ automatically execute XCCDF fix elements for failed rules.
|
|
+ Use of this option is always at your own risk. (True or False)
|
|
+
|
|
+ CLI Example:
|
|
+
|
|
+ .. code-block:: bash
|
|
+
|
|
+ salt '*' openscap.xccdf_eval /usr/share/openscap/scap-yast2sec-xccdf.xml profile=Default
|
|
+
|
|
+ """
|
|
+ success = True
|
|
+ error = None
|
|
+ upload_dir = None
|
|
+ returncode = None
|
|
+ if not ovalfiles:
|
|
+ ovalfiles = []
|
|
+
|
|
+ cmd_opts = ["oscap", "xccdf", "eval"]
|
|
+ if kwargs.get("oval_results"):
|
|
+ cmd_opts.append("--oval-results")
|
|
+ if "results" in kwargs:
|
|
+ cmd_opts.append("--results")
|
|
+ cmd_opts.append(kwargs["results"])
|
|
+ if "report" in kwargs:
|
|
+ cmd_opts.append("--report")
|
|
+ cmd_opts.append(kwargs["report"])
|
|
+ if "profile" in kwargs:
|
|
+ cmd_opts.append("--profile")
|
|
+ cmd_opts.append(kwargs["profile"])
|
|
+ if "rule" in kwargs:
|
|
+ cmd_opts.append("--rule")
|
|
+ cmd_opts.append(kwargs["rule"])
|
|
+ if "tailoring_file" in kwargs:
|
|
+ cmd_opts.append("--tailoring-file")
|
|
+ cmd_opts.append(kwargs["tailoring_file"])
|
|
+ if "tailoring_id" in kwargs:
|
|
+ cmd_opts.append("--tailoring-id")
|
|
+ cmd_opts.append(kwargs["tailoring_id"])
|
|
+ if kwargs.get("fetch_remote_resources"):
|
|
+ cmd_opts.append("--fetch-remote-resources")
|
|
+ if kwargs.get("remediate"):
|
|
+ cmd_opts.append("--remediate")
|
|
+ cmd_opts.append(xccdffile)
|
|
+ cmd_opts.extend(ovalfiles)
|
|
+
|
|
+ if not os.path.exists(xccdffile):
|
|
+ success = False
|
|
+ error = "XCCDF File '{}' does not exist".format(xccdffile)
|
|
+ for ofile in ovalfiles:
|
|
+ if success and not os.path.exists(ofile):
|
|
+ success = False
|
|
+ error = "Oval File '{}' does not exist".format(ofile)
|
|
+
|
|
+ if success:
|
|
+ tempdir = tempfile.mkdtemp()
|
|
+ proc = Popen(cmd_opts, stdout=PIPE, stderr=PIPE, cwd=tempdir)
|
|
+ (stdoutdata, error) = proc.communicate()
|
|
+ success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False)
|
|
+ if proc.returncode < 0:
|
|
+ error += "\nKilled by signal {}\n".format(proc.returncode).encode('ascii')
|
|
+ returncode = proc.returncode
|
|
+ if success:
|
|
+ __salt__["cp.push_dir"](tempdir)
|
|
+ upload_dir = tempdir
|
|
+ shutil.rmtree(tempdir, ignore_errors=True)
|
|
+
|
|
+ return dict(
|
|
+ success=success, upload_dir=upload_dir, error=error, returncode=returncode
|
|
+ )
|
|
+
|
|
+
|
|
def xccdf(params):
|
|
"""
|
|
Run ``oscap xccdf`` commands on minions.
|
|
@@ -92,7 +204,9 @@ def xccdf(params):
|
|
tempdir = tempfile.mkdtemp()
|
|
proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir)
|
|
(stdoutdata, error) = proc.communicate()
|
|
- success = _OSCAP_EXIT_CODES_MAP[proc.returncode]
|
|
+ success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False)
|
|
+ if proc.returncode < 0:
|
|
+ error += "\nKilled by signal {}\n".format(proc.returncode).encode('ascii')
|
|
returncode = proc.returncode
|
|
if success:
|
|
__salt__["cp.push_dir"](tempdir)
|
|
diff --git a/tests/unit/modules/test_openscap.py b/tests/unit/modules/test_openscap.py
|
|
index 045c37f7c9..301c1869ec 100644
|
|
--- a/tests/unit/modules/test_openscap.py
|
|
+++ b/tests/unit/modules/test_openscap.py
|
|
@@ -21,6 +21,7 @@ class OpenscapTestCase(TestCase):
|
|
"salt.modules.openscap.tempfile.mkdtemp",
|
|
Mock(return_value=self.random_temp_dir),
|
|
),
|
|
+ patch("salt.modules.openscap.os.path.exists", Mock(return_value=True)),
|
|
]
|
|
for patcher in patchers:
|
|
self.apply_patch(patcher)
|
|
@@ -211,3 +212,236 @@ class OpenscapTestCase(TestCase):
|
|
"returncode": None,
|
|
},
|
|
)
|
|
+
|
|
+ def test_new_openscap_xccdf_eval_success(self):
|
|
+ with patch(
|
|
+ "salt.modules.openscap.Popen",
|
|
+ MagicMock(
|
|
+ return_value=Mock(
|
|
+ **{"returncode": 0, "communicate.return_value": ("", "")}
|
|
+ )
|
|
+ ),
|
|
+ ):
|
|
+ response = openscap.xccdf_eval(
|
|
+ self.policy_file,
|
|
+ profile="Default",
|
|
+ oval_results=True,
|
|
+ results="results.xml",
|
|
+ report="report.html",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
|
|
+ expected_cmd = [
|
|
+ "oscap",
|
|
+ "xccdf",
|
|
+ "eval",
|
|
+ "--oval-results",
|
|
+ "--results",
|
|
+ "results.xml",
|
|
+ "--report",
|
|
+ "report.html",
|
|
+ "--profile",
|
|
+ "Default",
|
|
+ self.policy_file,
|
|
+ ]
|
|
+ openscap.Popen.assert_called_once_with(
|
|
+ expected_cmd,
|
|
+ cwd=openscap.tempfile.mkdtemp.return_value,
|
|
+ stderr=subprocess.PIPE,
|
|
+ stdout=subprocess.PIPE,
|
|
+ )
|
|
+ openscap.__salt__["cp.push_dir"].assert_called_once_with(
|
|
+ self.random_temp_dir
|
|
+ )
|
|
+ self.assertEqual(openscap.shutil.rmtree.call_count, 1)
|
|
+ self.assertEqual(
|
|
+ response,
|
|
+ {
|
|
+ "upload_dir": self.random_temp_dir,
|
|
+ "error": "",
|
|
+ "success": True,
|
|
+ "returncode": 0,
|
|
+ },
|
|
+ )
|
|
+
|
|
+ def test_new_openscap_xccdf_eval_success_with_extra_ovalfiles(self):
|
|
+ with patch(
|
|
+ "salt.modules.openscap.Popen",
|
|
+ MagicMock(
|
|
+ return_value=Mock(
|
|
+ **{"returncode": 0, "communicate.return_value": ("", "")}
|
|
+ )
|
|
+ ),
|
|
+ ):
|
|
+ response = openscap.xccdf_eval(
|
|
+ self.policy_file,
|
|
+ ["/usr/share/xml/another-oval.xml", "/usr/share/xml/oval.xml"],
|
|
+ profile="Default",
|
|
+ oval_results=True,
|
|
+ results="results.xml",
|
|
+ report="report.html",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
|
|
+ expected_cmd = [
|
|
+ "oscap",
|
|
+ "xccdf",
|
|
+ "eval",
|
|
+ "--oval-results",
|
|
+ "--results",
|
|
+ "results.xml",
|
|
+ "--report",
|
|
+ "report.html",
|
|
+ "--profile",
|
|
+ "Default",
|
|
+ self.policy_file,
|
|
+ "/usr/share/xml/another-oval.xml",
|
|
+ "/usr/share/xml/oval.xml",
|
|
+ ]
|
|
+ openscap.Popen.assert_called_once_with(
|
|
+ expected_cmd,
|
|
+ cwd=openscap.tempfile.mkdtemp.return_value,
|
|
+ stderr=subprocess.PIPE,
|
|
+ stdout=subprocess.PIPE,
|
|
+ )
|
|
+ openscap.__salt__["cp.push_dir"].assert_called_once_with(
|
|
+ self.random_temp_dir
|
|
+ )
|
|
+ self.assertEqual(openscap.shutil.rmtree.call_count, 1)
|
|
+ self.assertEqual(
|
|
+ response,
|
|
+ {
|
|
+ "upload_dir": self.random_temp_dir,
|
|
+ "error": "",
|
|
+ "success": True,
|
|
+ "returncode": 0,
|
|
+ },
|
|
+ )
|
|
+
|
|
+ def test_new_openscap_xccdf_eval_success_with_failing_rules(self):
|
|
+ with patch(
|
|
+ "salt.modules.openscap.Popen",
|
|
+ MagicMock(
|
|
+ return_value=Mock(
|
|
+ **{"returncode": 2, "communicate.return_value": ("", "some error")}
|
|
+ )
|
|
+ ),
|
|
+ ):
|
|
+ response = openscap.xccdf_eval(
|
|
+ self.policy_file,
|
|
+ profile="Default",
|
|
+ oval_results=True,
|
|
+ results="results.xml",
|
|
+ report="report.html",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
|
|
+ expected_cmd = [
|
|
+ "oscap",
|
|
+ "xccdf",
|
|
+ "eval",
|
|
+ "--oval-results",
|
|
+ "--results",
|
|
+ "results.xml",
|
|
+ "--report",
|
|
+ "report.html",
|
|
+ "--profile",
|
|
+ "Default",
|
|
+ self.policy_file,
|
|
+ ]
|
|
+ openscap.Popen.assert_called_once_with(
|
|
+ expected_cmd,
|
|
+ cwd=openscap.tempfile.mkdtemp.return_value,
|
|
+ stderr=subprocess.PIPE,
|
|
+ stdout=subprocess.PIPE,
|
|
+ )
|
|
+ openscap.__salt__["cp.push_dir"].assert_called_once_with(
|
|
+ self.random_temp_dir
|
|
+ )
|
|
+ self.assertEqual(openscap.shutil.rmtree.call_count, 1)
|
|
+ self.assertEqual(
|
|
+ response,
|
|
+ {
|
|
+ "upload_dir": self.random_temp_dir,
|
|
+ "error": "some error",
|
|
+ "success": True,
|
|
+ "returncode": 2,
|
|
+ },
|
|
+ )
|
|
+
|
|
+ def test_new_openscap_xccdf_eval_success_ignore_unknown_params(self):
|
|
+ with patch(
|
|
+ "salt.modules.openscap.Popen",
|
|
+ MagicMock(
|
|
+ return_value=Mock(
|
|
+ **{"returncode": 2, "communicate.return_value": ("", "some error")}
|
|
+ )
|
|
+ ),
|
|
+ ):
|
|
+ response = openscap.xccdf_eval(
|
|
+ "/policy/file",
|
|
+ param="Default",
|
|
+ profile="Default",
|
|
+ oval_results=True,
|
|
+ results="results.xml",
|
|
+ report="report.html",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ response,
|
|
+ {
|
|
+ "upload_dir": self.random_temp_dir,
|
|
+ "error": "some error",
|
|
+ "success": True,
|
|
+ "returncode": 2,
|
|
+ },
|
|
+ )
|
|
+ expected_cmd = [
|
|
+ "oscap",
|
|
+ "xccdf",
|
|
+ "eval",
|
|
+ "--oval-results",
|
|
+ "--results",
|
|
+ "results.xml",
|
|
+ "--report",
|
|
+ "report.html",
|
|
+ "--profile",
|
|
+ "Default",
|
|
+ "/policy/file",
|
|
+ ]
|
|
+ openscap.Popen.assert_called_once_with(
|
|
+ expected_cmd,
|
|
+ cwd=openscap.tempfile.mkdtemp.return_value,
|
|
+ stderr=subprocess.PIPE,
|
|
+ stdout=subprocess.PIPE,
|
|
+ )
|
|
+
|
|
+ def test_new_openscap_xccdf_eval_evaluation_error(self):
|
|
+ with patch(
|
|
+ "salt.modules.openscap.Popen",
|
|
+ MagicMock(
|
|
+ return_value=Mock(
|
|
+ **{
|
|
+ "returncode": 1,
|
|
+ "communicate.return_value": ("", "evaluation error"),
|
|
+ }
|
|
+ )
|
|
+ ),
|
|
+ ):
|
|
+ response = openscap.xccdf_eval(
|
|
+ self.policy_file,
|
|
+ profile="Default",
|
|
+ oval_results=True,
|
|
+ results="results.xml",
|
|
+ report="report.html",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ response,
|
|
+ {
|
|
+ "upload_dir": None,
|
|
+ "error": "evaluation error",
|
|
+ "success": False,
|
|
+ "returncode": 1,
|
|
+ },
|
|
+ )
|
|
--
|
|
2.39.2
|
|
|
|
|