From 34a53def4fcc4baacf7634c4036a84c1f882b577 Mon Sep 17 00:00:00 2001 From: Evgeny Kolesnikov Date: Mon, 18 Mar 2024 21:23:06 +0100 Subject: [PATCH 01/19] Version bump after release. --- release_tools/versions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release_tools/versions.sh b/release_tools/versions.sh index 9e9fbd8b11..430a46df85 100644 --- a/release_tools/versions.sh +++ b/release_tools/versions.sh @@ -1,4 +1,4 @@ -version=1.3.10 -previous_version=1.3.9 +version=1.3.11 +previous_version=1.3.10 version_major_minor="${version%.*}" From 6e69487452e2a39f38b6ff7eef8d9019c5da3239 Mon Sep 17 00:00:00 2001 From: Evgeny Kolesnikov Date: Mon, 15 Apr 2024 16:26:55 +0200 Subject: [PATCH 02/19] Enable Packit builds on commit --- .packit.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.packit.yaml b/.packit.yaml index e6d7a22dc4..e16f647f94 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -10,7 +10,8 @@ srpm_build_deps: - bash jobs: -- job: copr_build +- &build + job: copr_build trigger: pull_request metadata: targets: @@ -25,3 +26,6 @@ jobs: - fedora-all-x86_64 - centos-stream-8-x86_64 - centos-stream-9-x86_64 + +- <<: *build + trigger: commit From 29a97f0fcd996a55ab0fcc1279f0e02f330410a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 30 Apr 2024 09:16:21 +0200 Subject: [PATCH 03/19] Disable Perl bindings on Windows Accidentally, the GitHub CI started to build Perl bindings on Windows because the dependencies become available. However, the build fails because our code isn't ready to be built on Windows. This change will ensure the Perl bindings are kept disabled on Windows. See: https://github.com/OpenSCAP/openscap/actions/runs/8879756994/job/24378248762?pr=2104 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fdeda6eb4d..320247f1e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -360,7 +360,7 @@ option(ENABLE_MITRE "enables MITRE tests -- requires specific environment suppor # ---------- LANGUAGE BINDINGS cmake_dependent_option(ENABLE_PYTHON3 "if enabled, the python3 swig bindings will be built" ON "PYTHONINTERP_FOUND;SWIG_FOUND;PYTHONLIBS_FOUND" OFF) -cmake_dependent_option(ENABLE_PERL "if enabled, the perl swig bindings will be built" ON "PERLLIBS_FOUND;SWIG_FOUND" OFF) +cmake_dependent_option(ENABLE_PERL "if enabled, the perl swig bindings will be built" ON "PERLLIBS_FOUND;SWIG_FOUND;NOT WIN32" OFF) # ---------- NO IDEA WHAT THIS IS FOR set(WANT_BASE64 TRUE CACHE BOOL "wants builtin Base64") From 6b0615fac8a3bac1ee613d3401f9a80fd3b320b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Mon, 29 Apr 2024 13:31:38 +0200 Subject: [PATCH 04/19] Ensure xlink namespace exists If the document doesn't contain the xlink namespace, we need to define it before we create the `xlink:href` attribute. This situation happens if the `data-stream-collection` root element of the input SCAP source data stream doesn't have the xlink namespace defined. Resolves: RHEL-34104 --- src/DS/rds.c | 7 +- tests/API/XCCDF/tailoring/all.sh | 21 +++++ .../XCCDF/tailoring/xlink-test-simple-ds.xml | 87 +++++++++++++++++++ 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/API/XCCDF/tailoring/xlink-test-simple-ds.xml diff --git a/src/DS/rds.c b/src/DS/rds.c index d2553b2dbe..346d3f7728 100644 --- a/src/DS/rds.c +++ b/src/DS/rds.c @@ -757,12 +757,7 @@ static int _ds_rds_create_from_dom(xmlDocPtr *ret, xmlDocPtr sds_doc, free(tailoring_component_ref_id); xmlNsPtr xlink_ns = xmlSearchNsByHref(doc, sds_res_node, BAD_CAST xlink_ns_uri); if (!xlink_ns) { - oscap_seterr(OSCAP_EFAMILY_XML, - "Unable to find namespace '%s' in the XML DOM tree. " - "This is most likely an internal error!.", - xlink_ns_uri); - free(tailoring_component_id); - return -1; + xlink_ns = xmlNewNs(tailoring_component_ref, BAD_CAST xlink_ns_uri, BAD_CAST "xlink"); } char *tailoring_cref_href = oscap_sprintf("#%s", tailoring_component_id); free(tailoring_component_id); diff --git a/tests/API/XCCDF/tailoring/all.sh b/tests/API/XCCDF/tailoring/all.sh index fc2ccd7436..de6cc043e8 100755 --- a/tests/API/XCCDF/tailoring/all.sh +++ b/tests/API/XCCDF/tailoring/all.sh @@ -98,6 +98,26 @@ function test_api_xccdf_tailoring_simple_include_in_arf { rm -f $result } +function test_api_xccdf_tailoring_simple_include_in_arf_xlink_namespace { + # This test case is a regression test for RHEL-34104 + + local INPUT=$srcdir/$1 + local TAILORING=$srcdir/$2 + + result=`mktemp` + stderr=`mktemp` + $OSCAP xccdf eval --tailoring-file $TAILORING --results-arf $result $INPUT 2>"$stderr" + if [ "$?" != "0" ]; then + return 1 + fi + + [ ! -s "$stderr" ] + assert_exists 1 '/arf:asset-report-collection/arf:report-requests/arf:report-request/arf:content/ds:data-stream-collection/ds:component/Tailoring' + + rm -f "$result" + rm -f "$stderr" +} + function test_api_xccdf_tailoring_profile_include_in_arf { local INPUT=$srcdir/$1 local TAILORING=$srcdir/$2 @@ -167,6 +187,7 @@ test_run "test_api_xccdf_tailoring_oscap_info_11" test_api_xccdf_tailoring_oscap test_run "test_api_xccdf_tailoring_oscap_info_12" test_api_xccdf_tailoring_oscap_info simple-tailoring.xml 1 test_run "test_api_xccdf_tailoring_autonegotiation" test_api_xccdf_tailoring_autonegotiation simple-tailoring-autonegotiation.xml xccdf_org.open-scap_profile_default 1 test_run "test_api_xccdf_tailoring_simple_include_in_arf" test_api_xccdf_tailoring_simple_include_in_arf simple-xccdf.xml simple-tailoring.xml +test_run "test_api_xccdf_tailoring_simple_include_in_arf_xlink_namespace" test_api_xccdf_tailoring_simple_include_in_arf_xlink_namespace xlink-test-simple-ds.xml simple-tailoring.xml test_run "test_api_xccdf_tailoring_profile_include_in_arf" test_api_xccdf_tailoring_profile_include_in_arf baseline.xccdf.xml baseline.tailoring.xml test_run "test_api_xccdf_tailoring_profile_generate_fix" test_api_xccdf_tailoring_profile_generate_fix baseline.xccdf.xml baseline.tailoring.xml test_run "test_api_xccdf_tailoring_profile_generate_guide" test_api_xccdf_tailoring_profile_generate_guide baseline.xccdf.xml baseline.tailoring.xml diff --git a/tests/API/XCCDF/tailoring/xlink-test-simple-ds.xml b/tests/API/XCCDF/tailoring/xlink-test-simple-ds.xml new file mode 100644 index 0000000000..a8d5701e0f --- /dev/null +++ b/tests/API/XCCDF/tailoring/xlink-test-simple-ds.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + 5.10 + 0001-01-01T00:00:00+00:00 + + + + + x + x + + x + + + + + + + + + + x + x + + x + + + + + + + + + + + + + + + + + + + oval:x:var:1 + + + + + x + + + + + + + incomplete + 1.0 + + Override + + + + Rule 1: Enable Audit Service + + + + + + + + + + + + #!/bin/bash +echo "Hello how are you" +exit "$XCCDF_RESULT_FAIL" + + + diff --git a/tests/sce/test_prefer_sce.sh b/tests/sce/test_prefer_sce.sh new file mode 100755 index 0000000000..460ece0ac1 --- /dev/null +++ b/tests/sce/test_prefer_sce.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +. $builddir/tests/test_common.sh + +set -e -o pipefail + + +function test_prefer_sce_on () { + stdout=$(mktemp) + stderr=$(mktemp) + arf=$(mktemp) + + OSCAP_PREFERRED_ENGINE="SCE" $OSCAP xccdf eval --verbose INFO --progress --profile common --results-arf "$arf" > "$stdout" 2> "$stderr" "$srcdir/test_prefer_sce.ds.xml" || ret="$?" + grep -q "xccdf_org.openscap.www_rule_1:fail" "$stdout" + ! grep -q "I: oscap: Evaluating definition 'oval:org.openscap.www:def:1': OVAL check for rule 1." "$stderr" + grep -q "I: oscap: Executing SCE check 'fedora/checks/sce/rule_1.sh'" "$stderr" + + rm -rf "$stdout" "$stderr" "$arf" +} + +test_prefer_sce_off () { + stdout=$(mktemp) + stderr=$(mktemp) + arf=$(mktemp) + + $OSCAP xccdf eval --verbose INFO --progress --profile common --results-arf "$arf" > "$stdout" 2> "$stderr" "$srcdir/test_prefer_sce.ds.xml" || ret="$?" + grep -q "xccdf_org.openscap.www_rule_1:pass" "$stdout" + grep -q "I: oscap: Evaluating definition 'oval:org.openscap.www:def:1': OVAL check for rule 1." "$stderr" + ! grep -q "I: oscap: Executing SCE check 'fedora/checks/sce/rule_1.sh'" "$stderr" + + rm -rf "$stdout" "$stderr" "$arf" +} + +test_prefer_oval_explicit () { + stdout=$(mktemp) + stderr=$(mktemp) + arf=$(mktemp) + + OSCAP_PREFERRED_ENGINE="OVAL" $OSCAP xccdf eval --verbose INFO --progress --profile common --results-arf "$arf" > "$stdout" 2> "$stderr" "$srcdir/test_prefer_sce.ds.xml" || ret="$?" + grep -q "xccdf_org.openscap.www_rule_1:pass" "$stdout" + grep -q "I: oscap: Evaluating definition 'oval:org.openscap.www:def:1': OVAL check for rule 1." "$stderr" + ! grep -q "I: oscap: Executing SCE check 'fedora/checks/sce/rule_1.sh'" "$stderr" + + rm -rf "$stdout" "$stderr" "$arf" +} + + +test_prefer_sce_on +test_prefer_sce_off +test_prefer_oval_explicit From d977ad569ecec37e1124ae52493f09ebf38ae256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 15 Oct 2024 11:22:52 +0200 Subject: [PATCH 08/19] Add a test case for the invalid value --- tests/sce/test_prefer_sce.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/sce/test_prefer_sce.sh b/tests/sce/test_prefer_sce.sh index 460ece0ac1..990dfbe9d7 100755 --- a/tests/sce/test_prefer_sce.sh +++ b/tests/sce/test_prefer_sce.sh @@ -44,7 +44,21 @@ test_prefer_oval_explicit () { rm -rf "$stdout" "$stderr" "$arf" } +test_invalid_envi_variable () { + stdout=$(mktemp) + stderr=$(mktemp) + arf=$(mktemp) + + OSCAP_PREFERRED_ENGINE="FOOBARVIM" $OSCAP xccdf eval --verbose INFO --progress --profile common --results-arf "$arf" > "$stdout" 2> "$stderr" "$srcdir/test_prefer_sce.ds.xml" || ret="$?" + grep -q "Unknown value of OSCAP_PREFFERED_ENGINE: 'FOOBARVIM'. It will be ignored." "$stderr" + grep -q "xccdf_org.openscap.www_rule_1:pass" "$stdout" + grep -q "I: oscap: Evaluating definition 'oval:org.openscap.www:def:1': OVAL check for rule 1." "$stderr" + ! grep -q "I: oscap: Executing SCE check 'fedora/checks/sce/rule_1.sh'" "$stderr" + + rm -rf "$stdout" "$stderr" "$arf" +} test_prefer_sce_on test_prefer_sce_off test_prefer_oval_explicit +test_invalid_envi_variable From 7ba95a91dc1123f09ebcd0f7db2bfddfb0f12074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Fri, 11 Oct 2024 17:07:06 +0200 Subject: [PATCH 09/19] Introduce generating bootc remediation --- src/XCCDF_POLICY/xccdf_policy_remediate.c | 145 ++++++++++++++++++++++ utils/oscap-xccdf.c | 10 +- utils/oscap.8 | 3 +- 3 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c index 8c2aaf98c9..592114a8e3 100644 --- a/src/XCCDF_POLICY/xccdf_policy_remediate.c +++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c @@ -49,6 +49,11 @@ #include "public/xccdf_policy.h" #include "oscap_helpers.h" +struct bootc_commands { + struct oscap_list *package_install; + struct oscap_list *package_remove; +}; + static int _rule_add_info_message(struct xccdf_rule_result *rr, ...) { va_list ap; @@ -1286,6 +1291,144 @@ static int _xccdf_policy_generate_fix_other(struct oscap_list *rules_to_fix, str return ret; } +static int _parse_bootc_line(const char *line, struct bootc_commands *cmds) +{ + int ret = 0; + char *dup = strdup(line); + char **words = oscap_split(dup, " "); + enum states { + BOOTC_START, + BOOTC_PACKAGE, + BOOTC_PACKAGE_INSTALL, + BOOTC_PACKAGE_REMOVE, + BOOTC_ERROR + }; + int state = BOOTC_START; + for (unsigned int i = 0; words[i] != NULL; i++) { + char *word = oscap_trim(words[i]); + if (*word == '\0') + continue; + switch (state) { + case BOOTC_START: + if (!strcmp(word, "package")) { + state = BOOTC_PACKAGE; + } else { + ret = 1; + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unsupported command keyword '%s' in command: '%s'", word, line); + goto cleanup; + } + break; + case BOOTC_PACKAGE: + if (!strcmp(word, "install")) { + state = BOOTC_PACKAGE_INSTALL; + } else if (!strcmp(word, "remove")) { + state = BOOTC_PACKAGE_REMOVE; + } else { + ret = 1; + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unsupported 'package' command keyword '%s' in command:'%s'", word, line); + goto cleanup; + } + break; + case BOOTC_PACKAGE_INSTALL: + oscap_list_add(cmds->package_install, strdup(word)); + break; + case BOOTC_PACKAGE_REMOVE: + oscap_list_add(cmds->package_remove, strdup(word)); + break; + case BOOTC_ERROR: + ret = 1; + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unexpected string '%s' in command: '%s'", word, line); + goto cleanup; + default: + break; + } + } + +cleanup: + free(words); + free(dup); + return ret; +} + +static int _xccdf_policy_rule_generate_bootc_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, struct bootc_commands *cmds) +{ + char *fix_text = NULL; + int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); + if (fix_text == NULL) { + return ret; + } + char *dup = strdup(fix_text); + char **lines = oscap_split(dup, "\n"); + for (unsigned int i = 0; lines[i] != NULL; i++) { + char *line = lines[i]; + char *trim_line = oscap_trim(strdup(line)); + if (*trim_line != '#' && *trim_line != '\0') { + _parse_bootc_line(trim_line, cmds); + } + free(trim_line); + } + free(lines); + free(dup); + free(fix_text); + return ret; +} + +static int _generate_bootc_packages(struct bootc_commands *cmds, int output_fd) +{ + struct oscap_iterator *package_install_it = oscap_iterator_new(cmds->package_install); + if (oscap_iterator_has_more(package_install_it)) { + _write_text_to_fd(output_fd, "dnf -y install \\\n"); + while (oscap_iterator_has_more(package_install_it)) { + char *package = (char *) oscap_iterator_next(package_install_it); + _write_text_to_fd(output_fd, " "); + _write_text_to_fd(output_fd, package); + if (oscap_iterator_has_more(package_install_it)) + _write_text_to_fd(output_fd, " \\\n"); + } + _write_text_to_fd(output_fd, "\n\n"); + } + oscap_iterator_free(package_install_it); + + struct oscap_iterator *package_remove_it = oscap_iterator_new(cmds->package_remove); + if (oscap_iterator_has_more(package_remove_it)) { + _write_text_to_fd(output_fd, "dnf -y remove \\\n"); + while (oscap_iterator_has_more(package_remove_it)) { + char *package = (char *) oscap_iterator_next(package_remove_it); + _write_text_to_fd(output_fd, " "); + _write_text_to_fd(output_fd, package); + if (oscap_iterator_has_more(package_remove_it)) + _write_text_to_fd(output_fd, " \\\n"); + } + _write_text_to_fd(output_fd, "\n"); + } + oscap_iterator_free(package_remove_it); + return 0; +} + +static int _xccdf_policy_generate_fix_bootc(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd) +{ + struct bootc_commands cmds = { + .package_install = oscap_list_new(), + .package_remove = oscap_list_new(), + }; + int ret = 0; + struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); + while (oscap_iterator_has_more(rules_to_fix_it)) { + struct xccdf_rule *rule = (struct xccdf_rule *) oscap_iterator_next(rules_to_fix_it); + ret = _xccdf_policy_rule_generate_bootc_fix(policy, rule, sys, &cmds); + if (ret != 0) + break; + } + oscap_iterator_free(rules_to_fix_it); + + _write_text_to_fd(output_fd, "#!/bin/bash\n"); + _generate_bootc_packages(&cmds, output_fd); + + oscap_list_free(cmds.package_install, free); + oscap_list_free(cmds.package_remove, free); + return ret; +} + int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result *result, const char *sys, int output_fd) { __attribute__nonnull__(policy); @@ -1342,6 +1485,8 @@ int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result * ret = _xccdf_policy_generate_fix_ansible(rules_to_fix, policy, sys, output_fd); } else if (strcmp(sys, "urn:redhat:osbuild:blueprint") == 0) { ret = _xccdf_policy_generate_fix_blueprint(rules_to_fix, policy, sys, output_fd); + } else if (strcmp(sys, "urn:xccdf:fix:script:bootc") == 0) { + ret = _xccdf_policy_generate_fix_bootc(rules_to_fix, policy, sys, output_fd); } else { ret = _xccdf_policy_generate_fix_other(rules_to_fix, policy, sys, output_fd); } diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 2bcdac2e1c..54680b3595 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -285,7 +285,7 @@ static struct oscap_module XCCDF_GEN_FIX = { .help = GEN_OPTS "\nFix Options:\n" " --fix-type - Fix type. Should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes,\n" - " blueprint (default: bash).\n" + " blueprint, bootc (default: bash).\n" " --output - Write the script into file.\n" " --result-id - Fixes will be generated for failed rule-results of the specified TestResult.\n" " --template - Fix template. (default: bash)\n" @@ -971,10 +971,12 @@ int app_generate_fix(const struct oscap_action *action) template = "urn:xccdf:fix:script:kubernetes"; } else if (strcmp(action->fix_type, "blueprint") == 0) { template = "urn:redhat:osbuild:blueprint"; + } else if (strcmp(action->fix_type, "bootc") == 0) { + template = "urn:xccdf:fix:script:bootc"; } else { fprintf(stderr, "Unknown fix type '%s'.\n" - "Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint.\n" + "Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint, bootc.\n" "Or provide a custom template using '--template' instead.\n", action->fix_type); return OSCAP_ERROR; @@ -984,6 +986,10 @@ int app_generate_fix(const struct oscap_action *action) } else { template = "urn:xccdf:fix:script:sh"; } + if (action->id != NULL && action->fix_type != NULL && !strcmp(action->fix_type, "bootc")) { + fprintf(stderr, "It isn't possible to generate results-oriented bootc remediations.\n"); + return OSCAP_ERROR; + } int ret = OSCAP_ERROR; struct oscap_source *source = oscap_source_new_from_file(action->f_xccdf); diff --git a/utils/oscap.8 b/utils/oscap.8 index 09da46d008..23c6c80cca 100644 --- a/utils/oscap.8 +++ b/utils/oscap.8 @@ -430,11 +430,12 @@ To use the ability to include additional information from SCE in XCCDF result fi Generate a script that shall bring the system to a state of compliance with given XCCDF Benchmark. There are 2 possibilities when generating fixes: Result-oriented fixes (--result-id) or Profile-oriented fixes (--profile). Result-oriented takes precedences over Profile-oriented, if result-id is given, oscap will ignore any profile provided. .TP Result-oriented fixes are generated using result-id provided to select only the failing rules from results in xccdf-file, it skips all other rules. +It isn't possible to generate result-oriented fixes for the bootc fix type. .TP Profile-oriented fixes are generated using all rules within the provided profile. If no result-id/profile are provided, (default) profile will be used to generate fixes. .TP \fB\-\-fix-type TYPE\fR -Specify fix type. There are multiple programming languages in which the fix script can be generated. TYPE should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint. Default is bash. This option is mutually exclusive with --template, because fix type already determines the template URN. +Specify fix type. There are multiple programming languages in which the fix script can be generated. TYPE should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint, bootc. Default is bash. This option is mutually exclusive with --template, because fix type already determines the template URN. .TP \fB\-\-output FILE\fR Write the report to this file instead of standard output. From c2c3a9f2c1488ea0028a79422ff06762f0f2ebf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Fri, 11 Oct 2024 17:52:50 +0200 Subject: [PATCH 10/19] Add a simple test --- tests/API/XCCDF/unittests/CMakeLists.txt | 1 + .../unittests/test_remediation_bootc.ds.xml | 87 +++++++++++++++++++ .../XCCDF/unittests/test_remediation_bootc.sh | 19 ++++ .../test_remediation_bootc_expected_output.sh | 8 ++ 4 files changed, 115 insertions(+) create mode 100644 tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml create mode 100755 tests/API/XCCDF/unittests/test_remediation_bootc.sh create mode 100644 tests/API/XCCDF/unittests/test_remediation_bootc_expected_output.sh diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt index 7a9b3b452c..164b795e0e 100644 --- a/tests/API/XCCDF/unittests/CMakeLists.txt +++ b/tests/API/XCCDF/unittests/CMakeLists.txt @@ -110,3 +110,4 @@ add_oscap_test("test_skip_rule.sh") add_oscap_test("test_no_newline_between_select_elements.sh") add_oscap_test("test_single_line_tailoring.sh") add_oscap_test("test_reference.sh") +add_oscap_test("test_remediation_bootc.sh") diff --git a/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml b/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml new file mode 100644 index 0000000000..311068bf8d --- /dev/null +++ b/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + 5.11.2 + 2021-02-01T08:07:06+01:00 + + + + + PASS + pass + + + + + + + + + + + + + + oval:org.openscap.www:var:1 + + + + + 100 + + + + + + + accepted + 1.0 + + Common hardening profile + This is a very cool profile + + + + + Rule 1: Install rsyslog package + + package install rsyslog + + + + Rule 2: Remove USBGuard + + package remove usbguard + + + + Rule 3: Install reboot package + + package install reboot + + + + Rule 4: Install podman package + + package install podman + + + + + diff --git a/tests/API/XCCDF/unittests/test_remediation_bootc.sh b/tests/API/XCCDF/unittests/test_remediation_bootc.sh new file mode 100755 index 0000000000..cf193e75a5 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_remediation_bootc.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +. $builddir/tests/test_common.sh + +set -e +set -o pipefail + +name=$(basename $0 .sh) +result=$(mktemp) +stderr=$(mktemp) + +echo "Result file = $result" +echo "Stderr file = $stderr" + +$OSCAP xccdf generate fix --fix-type bootc --profile common "$srcdir/test_remediation_bootc.ds.xml" > "$result" 2> "$stderr" +[ -e $stderr ] + +diff -u "$srcdir/test_remediation_bootc_expected_output.sh" "$result" + +rm -rf "$stdout" "$stderr" "$result" diff --git a/tests/API/XCCDF/unittests/test_remediation_bootc_expected_output.sh b/tests/API/XCCDF/unittests/test_remediation_bootc_expected_output.sh new file mode 100644 index 0000000000..57a7ffb3df --- /dev/null +++ b/tests/API/XCCDF/unittests/test_remediation_bootc_expected_output.sh @@ -0,0 +1,8 @@ +#!/bin/bash +dnf -y install \ + rsyslog \ + reboot \ + podman + +dnf -y remove \ + usbguard From 4b6d0f2a6d34f6bfb380401b23d7dfa884a7e4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 17 Oct 2024 14:48:38 +0200 Subject: [PATCH 11/19] Rename "package" keyword to "dnf" This will allow us in future to extend the code to support remediations for systems where a different package manager than "dnf" is used. --- src/XCCDF_POLICY/xccdf_policy_remediate.c | 66 +++++++++---------- .../unittests/test_remediation_bootc.ds.xml | 8 +-- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c index 592114a8e3..70eb188838 100644 --- a/src/XCCDF_POLICY/xccdf_policy_remediate.c +++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c @@ -50,8 +50,8 @@ #include "oscap_helpers.h" struct bootc_commands { - struct oscap_list *package_install; - struct oscap_list *package_remove; + struct oscap_list *dnf_install; + struct oscap_list *dnf_remove; }; static int _rule_add_info_message(struct xccdf_rule_result *rr, ...) @@ -1298,9 +1298,9 @@ static int _parse_bootc_line(const char *line, struct bootc_commands *cmds) char **words = oscap_split(dup, " "); enum states { BOOTC_START, - BOOTC_PACKAGE, - BOOTC_PACKAGE_INSTALL, - BOOTC_PACKAGE_REMOVE, + BOOTC_DNF, + BOOTC_DNF_INSTALL, + BOOTC_DNF_REMOVE, BOOTC_ERROR }; int state = BOOTC_START; @@ -1310,30 +1310,30 @@ static int _parse_bootc_line(const char *line, struct bootc_commands *cmds) continue; switch (state) { case BOOTC_START: - if (!strcmp(word, "package")) { - state = BOOTC_PACKAGE; + if (!strcmp(word, "dnf")) { + state = BOOTC_DNF; } else { ret = 1; oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unsupported command keyword '%s' in command: '%s'", word, line); goto cleanup; } break; - case BOOTC_PACKAGE: + case BOOTC_DNF: if (!strcmp(word, "install")) { - state = BOOTC_PACKAGE_INSTALL; + state = BOOTC_DNF_INSTALL; } else if (!strcmp(word, "remove")) { - state = BOOTC_PACKAGE_REMOVE; + state = BOOTC_DNF_REMOVE; } else { ret = 1; - oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unsupported 'package' command keyword '%s' in command:'%s'", word, line); + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unsupported 'dnf' command keyword '%s' in command:'%s'", word, line); goto cleanup; } break; - case BOOTC_PACKAGE_INSTALL: - oscap_list_add(cmds->package_install, strdup(word)); + case BOOTC_DNF_INSTALL: + oscap_list_add(cmds->dnf_install, strdup(word)); break; - case BOOTC_PACKAGE_REMOVE: - oscap_list_add(cmds->package_remove, strdup(word)); + case BOOTC_DNF_REMOVE: + oscap_list_add(cmds->dnf_remove, strdup(word)); break; case BOOTC_ERROR: ret = 1; @@ -1373,43 +1373,43 @@ static int _xccdf_policy_rule_generate_bootc_fix(struct xccdf_policy *policy, st return ret; } -static int _generate_bootc_packages(struct bootc_commands *cmds, int output_fd) +static int _generate_bootc_dnf(struct bootc_commands *cmds, int output_fd) { - struct oscap_iterator *package_install_it = oscap_iterator_new(cmds->package_install); - if (oscap_iterator_has_more(package_install_it)) { + struct oscap_iterator *dnf_install_it = oscap_iterator_new(cmds->dnf_install); + if (oscap_iterator_has_more(dnf_install_it)) { _write_text_to_fd(output_fd, "dnf -y install \\\n"); - while (oscap_iterator_has_more(package_install_it)) { - char *package = (char *) oscap_iterator_next(package_install_it); + while (oscap_iterator_has_more(dnf_install_it)) { + char *package = (char *) oscap_iterator_next(dnf_install_it); _write_text_to_fd(output_fd, " "); _write_text_to_fd(output_fd, package); - if (oscap_iterator_has_more(package_install_it)) + if (oscap_iterator_has_more(dnf_install_it)) _write_text_to_fd(output_fd, " \\\n"); } _write_text_to_fd(output_fd, "\n\n"); } - oscap_iterator_free(package_install_it); + oscap_iterator_free(dnf_install_it); - struct oscap_iterator *package_remove_it = oscap_iterator_new(cmds->package_remove); - if (oscap_iterator_has_more(package_remove_it)) { + struct oscap_iterator *dnf_remove_it = oscap_iterator_new(cmds->dnf_remove); + if (oscap_iterator_has_more(dnf_remove_it)) { _write_text_to_fd(output_fd, "dnf -y remove \\\n"); - while (oscap_iterator_has_more(package_remove_it)) { - char *package = (char *) oscap_iterator_next(package_remove_it); + while (oscap_iterator_has_more(dnf_remove_it)) { + char *package = (char *) oscap_iterator_next(dnf_remove_it); _write_text_to_fd(output_fd, " "); _write_text_to_fd(output_fd, package); - if (oscap_iterator_has_more(package_remove_it)) + if (oscap_iterator_has_more(dnf_remove_it)) _write_text_to_fd(output_fd, " \\\n"); } _write_text_to_fd(output_fd, "\n"); } - oscap_iterator_free(package_remove_it); + oscap_iterator_free(dnf_remove_it); return 0; } static int _xccdf_policy_generate_fix_bootc(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd) { struct bootc_commands cmds = { - .package_install = oscap_list_new(), - .package_remove = oscap_list_new(), + .dnf_install = oscap_list_new(), + .dnf_remove = oscap_list_new(), }; int ret = 0; struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); @@ -1422,10 +1422,10 @@ static int _xccdf_policy_generate_fix_bootc(struct oscap_list *rules_to_fix, str oscap_iterator_free(rules_to_fix_it); _write_text_to_fd(output_fd, "#!/bin/bash\n"); - _generate_bootc_packages(&cmds, output_fd); + _generate_bootc_dnf(&cmds, output_fd); - oscap_list_free(cmds.package_install, free); - oscap_list_free(cmds.package_remove, free); + oscap_list_free(cmds.dnf_install, free); + oscap_list_free(cmds.dnf_remove, free); return ret; } diff --git a/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml b/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml index 311068bf8d..6134381d4c 100644 --- a/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml +++ b/tests/API/XCCDF/unittests/test_remediation_bootc.ds.xml @@ -61,25 +61,25 @@ Rule 1: Install rsyslog package - package install rsyslog + dnf install rsyslog Rule 2: Remove USBGuard - package remove usbguard + dnf remove usbguard Rule 3: Install reboot package - package install reboot + dnf install reboot Rule 4: Install podman package - package install podman + dnf install podman From afe3b673673d92fa2b1fe7686dff5a5c0965c9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Mon, 21 Oct 2024 15:24:28 +0200 Subject: [PATCH 12/19] Introduce oscap-bootc The `oscap-bootc` tool is a convenience script that makes building hardened bootable container images easier. This tool is designed to be used during the build of the bootable container image. Include `oscap-bootc` in your `Containerfile` that will be used to build your bootable container image. --- CMakeLists.txt | 2 + docs/manual/manual.adoc | 37 ++++++++++++ utils/CMakeLists.txt | 8 +++ utils/oscap-bootc | 122 ++++++++++++++++++++++++++++++++++++++++ utils/oscap-bootc.8 | 29 ++++++++++ 5 files changed, 198 insertions(+) create mode 100755 utils/oscap-bootc create mode 100644 utils/oscap-bootc.8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 320247f1e3..76f70d015e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,6 +337,7 @@ cmake_dependent_option(ENABLE_OSCAP_UTIL_AS_RPM "enable the scap-as-rpm utility, cmake_dependent_option(ENABLE_OSCAP_UTIL_SSH "enables the oscap-ssh utility, this lets you scan remote machines over ssh" ON "NOT WIN32" OFF) cmake_dependent_option(ENABLE_OSCAP_UTIL_VM "enables the oscap-vm utility, this lets you scan VMs and VM storage images" ON "NOT WIN32" OFF) cmake_dependent_option(ENABLE_OSCAP_UTIL_PODMAN "enables the oscap-podman utility, this lets you scan Podman containers and container images" ON "NOT WIN32" OFF) +cmake_dependent_option(ENABLE_OSCAP_UTIL_BOOTC "enables the oscap-bootc utility, this lets you build hardened bootable container images" ON "NOT WIN32" OFF) cmake_dependent_option(ENABLE_OSCAP_UTIL_CHROOT "enables the oscap-chroot utility, this lets you scan entire chroots using offline scanning" ON "NOT WIN32" OFF) option(ENABLE_OSCAP_UTIL_AUTOTAILOR "enables the autotailor utility that is able to perform command-line tailoring" TRUE) option(ENABLE_OSCAP_REMEDIATE_SERVICE "enables the oscap-remediate service" FALSE) @@ -476,6 +477,7 @@ message(STATUS "scap-as-rpm: ${ENABLE_OSCAP_UTIL_AS_RPM}") message(STATUS "oscap-ssh: ${ENABLE_OSCAP_UTIL_SSH}") message(STATUS "oscap-vm: ${ENABLE_OSCAP_UTIL_VM}") message(STATUS "oscap-podman: ${ENABLE_OSCAP_UTIL_PODMAN}") +message(STATUS "oscap-bootc: ${ENABLE_OSCAP_UTIL_BOOTC}") message(STATUS "oscap-chroot: ${ENABLE_OSCAP_UTIL_CHROOT}") message(STATUS "autotailor: ${ENABLE_OSCAP_UTIL_AUTOTAILOR}") message(STATUS " ") diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc index 5b96000858..9dd4e60563 100644 --- a/docs/manual/manual.adoc +++ b/docs/manual/manual.adoc @@ -2143,6 +2143,43 @@ registry.access.redhat.com/ubi8 latest 3269c37eae33 2 months ago 208 MB Note that the `oscap-podman` command requires root privileges. +=== Building hardened bootable container images using oscap-bootc + +The `oscap-bootc` tool is a convenience script that makes building hardened bootable container images easier. +This tool is designed to be used during the build of the bootable container image. + +Include `oscap-bootc` in your `Containerfile` that will be used to build your bootable container image. +The `Containerfile` first needs to install the `openscap-utils` package which ships the `oscap-bootc` tool. + +Also, SCAP content needs to be installed to the image before `oscap-bootc` will be run. +Although any SCAP content can be consumed by the tool, the SCAP source data streams shipped in `scap-security-guide` are specially cared to be compatible with bootable containers. + +Example `Containerfile`: + +---- +FROM quay.io/centos-bootc/centos-bootc:stream9 + +RUN dnf install -y openscap-utils scap-security-guide + +RUN oscap-bootc --profile stig /usr/share/xml/scap/ssg/content/ssg-cs9-ds.xml +---- + +Once you have your `Containerfile`, execute the image build: + +---- +podman build -t hardened_image . +---- + +The `oscap-bootc` tool installs and removes all packages required by the selected profile to or from the image. +Then, it runs a scan and remediation with the selected profile. +It doesn't use offline scanning. +The configuration files and other content in the image are modified by this process, depending on the used SCAP content. + +The built bootable container image can be then deployed and booted. +After booting the image, the state of the resulting system will be in line with the selected security profile. + +The `oscap-bootc` tool can't be used anywhere else than in a `Containerfile`. + === Scanning of Docker containers and images using oscap-docker The `oscap-docker` is used to scan Docker containers and images. It can diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 9347c29767..7b9274434e 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -139,6 +139,14 @@ if(ENABLE_OSCAP_UTIL_PODMAN) DESTINATION "${CMAKE_INSTALL_MANDIR}/man8" ) endif() +if(ENABLE_OSCAP_UTIL_BOOTC) + install(PROGRAMS "oscap-bootc" + DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + install(FILES "oscap-bootc.8" + DESTINATION "${CMAKE_INSTALL_MANDIR}/man8" + ) +endif() if(ENABLE_OSCAP_UTIL_AS_RPM) install(PROGRAMS "scap-as-rpm" DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/utils/oscap-bootc b/utils/oscap-bootc new file mode 100755 index 0000000000..5ef7fab265 --- /dev/null +++ b/utils/oscap-bootc @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# Copyright 2024 Red Hat Inc., Durham, North Carolina. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import argparse +import subprocess +import sys +import tempfile + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Use in your Containerfile to build hardened bootable " + "container images. Performs OpenSCAP scan and remediation of the " + "image.") + parser.add_argument( + "--profile", + help="The name of profile to be evaluated") + parser.add_argument( + "--tailoring-file", + help="Use given XCCDF Tailoring file") + parser.add_argument( + "--tailoring-id", metavar="COMPONENT_ID", + help="Use given DS component as XCCDF Tailoring file") + parser.add_argument( + "--results-arf", + help="Write ARF (result data stream) into file") + parser.add_argument( + "--report", + help="Write HTML report into file") + parser.add_argument( + "data_stream", metavar="DATA_STREAM", + help="Path to a SCAP source data stream, eg. " + "/usr/share/xml/scap/ssg/content/ssg-rhel10-ds.xml") + # Unfortunately, we can't add "--rule", "--skip-rule", or "--reference" + # because the "oscap xccdf generate fix" submodule doesn't support these + # options. + return parser.parse_args() + + +def ensure_sce_installed(): + query_cmd = ["rpm", "-q", "openscap-engine-sce"] + query_process = subprocess.run(query_cmd, capture_output=True) + if query_process.returncode != 0: + install_cmd = ["dnf", "-y", "install", "openscap-engine-sce"] + try: + subprocess.run(install_cmd, check=True, capture_output=True) + except subprocess.CalledProcessError as e: + print(e.stderr.decode("utf-8"), file=sys.stderr) + + +def add_args(option_args_list, cmd): + for o, a in option_args_list: + if a: + cmd.append(o) + cmd.append(a) + + +def add_common_args(args, cmd): + oal = [ + ("--profile", args.profile), + ("--tailoring-file", args.tailoring_file), + ("--tailoring-id", args.tailoring_id) + ] + add_args(oal, cmd) + + +def add_eval_args(args, cmd): + oal = [ + ("--results-arf", args.results_arf), + ("--report", args.report), + ] + add_args(oal, cmd) + + +def pre_scan_fix(args): + with tempfile.NamedTemporaryFile(delete=False) as remediation_script: + gen_fix_cmd = [ + "oscap", "xccdf", "generate", "fix", "--fix-type", "bootc", + "--output", remediation_script.name] + add_common_args(args, gen_fix_cmd) + gen_fix_cmd.append(args.data_stream) + subprocess.run(gen_fix_cmd, check=True) + subprocess.run(["bash", remediation_script.name], check=True) + + +def scan_and_remediate(args): + oscap_cmd = ["oscap", "xccdf", "eval", "--progress", "--remediate"] + add_common_args(args, oscap_cmd) + add_eval_args(args, oscap_cmd) + oscap_cmd.append(args.data_stream) + env = {"OSCAP_PREFERRED_ENGINE": "SCE", "OSCAP_BOOTC_BUILD": "YES"} + try: + subprocess.run(oscap_cmd, env=env, check=True) + except subprocess.CalledProcessError as e: + if e.returncode not in [0, 2]: + print(e, file=sys.stderr) + + +def main(): + args = parse_args() + ensure_sce_installed() + pre_scan_fix(args) + scan_and_remediate(args) + + +if __name__ == "__main__": + main() diff --git a/utils/oscap-bootc.8 b/utils/oscap-bootc.8 new file mode 100644 index 0000000000..b0727dd9d6 --- /dev/null +++ b/utils/oscap-bootc.8 @@ -0,0 +1,29 @@ +.TH oscap-bootc "8" "November 2024" "Red Hat, Inc." "System Administration Utilities" + +.SH NAME +oscap-bootc \- Tool for building hardened bootable container images + +.SH DESCRIPTION +The oscap-bootc tool is a convenience script that makes building hardened bootable container images easier. +This tool is designed to be used during the build of the bootable container image. +Include oscap-bootc in your Containerfile that will be used to build your bootable container image. +The oscap-bootc runs oscap tool on a given container image. + +The oscap-bootc tool can't be used anywhere else than in a Containerfile. + +.SH USAGE + +Usage of the tool mimics usage and options of oscap(8) tool. + +oscap-bootc OSCAP_ARGUMENT [OSCAP_ARGUMENT...] + +Refer to oscap(8) to learn about OSCAP_ARGUMENT options. + +.SH REPORTING BUGS +.nf +Please report bugs using https://github.com/OpenSCAP/openscap/issues + +.SH AUTHORS +.nf +Jan Černý +.fi From 967892cb6381fe90f272428f0140ee4a4b73b3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 22 Oct 2024 10:16:23 +0200 Subject: [PATCH 13/19] Change python to python3 Addressing: *** ERROR: ambiguous python shebang in /usr/bin/oscap-bootc: #!/usr/bin/env python. Change it to python3 (or python2) explicitly. --- utils/oscap-bootc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/oscap-bootc b/utils/oscap-bootc index 5ef7fab265..7a141ed1db 100755 --- a/utils/oscap-bootc +++ b/utils/oscap-bootc @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2024 Red Hat Inc., Durham, North Carolina. # From e0634de6cfdf008ceac89175d85bc030642667a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 23 Oct 2024 11:46:36 +0200 Subject: [PATCH 14/19] Document command line options properly --- utils/oscap-bootc | 2 +- utils/oscap-bootc.8 | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/utils/oscap-bootc b/utils/oscap-bootc index 7a141ed1db..2cb84e96f7 100755 --- a/utils/oscap-bootc +++ b/utils/oscap-bootc @@ -29,7 +29,7 @@ def parse_args(): "image.") parser.add_argument( "--profile", - help="The name of profile to be evaluated") + help="ID of the profile to be evaluated") parser.add_argument( "--tailoring-file", help="Use given XCCDF Tailoring file") diff --git a/utils/oscap-bootc.8 b/utils/oscap-bootc.8 index b0727dd9d6..9c911807b6 100644 --- a/utils/oscap-bootc.8 +++ b/utils/oscap-bootc.8 @@ -13,11 +13,36 @@ The oscap-bootc tool can't be used anywhere else than in a Containerfile. .SH USAGE -Usage of the tool mimics usage and options of oscap(8) tool. +oscap-bootc [OPTION...] DATASTREAM_FILE -oscap-bootc OSCAP_ARGUMENT [OSCAP_ARGUMENT...] +Usage of the tool mimics usage and options of oscap(8) tool. -Refer to oscap(8) to learn about OSCAP_ARGUMENT options. +.SH OPTIONS +.TP +\fB\-\-profile PROFILE_ID\fR +.RS +ID of the profile to be evaluated. +.RE +.TP +\fB\-\-tailoring-file TAILORING_FILE\fR +.RS +Use given file for XCCDF tailoring. Select profile from tailoring file to apply using --profile. If both --tailoring-file and --tailoring-id are specified, --tailoring-file takes priority. +.RE +.TP +\fB\-\-tailoring-id COMPONENT_REF_ID\fR +.RS +Use tailoring component in input source data stream for XCCDF tailoring. The tailoring component must be specified by its Ref-ID (value of component-ref/@id attribute in input source data stream). Select profile from tailoring component to apply using --profile. If both --tailoring-file and --tailoring-id are specified, --tailoring-file takes priority. +.RE +.TP +\fB\-\-results-arf FILE\fR +.RS +Writes results to a given FILE in Asset Reporting Format. +.RE +.TP +\fB\-\-report FILE\fR +.RS +Write HTML report into FILE. +.RE .SH REPORTING BUGS .nf From 008d333726a4fa63ad1224c81851db5710d50ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 23 Oct 2024 11:54:37 +0200 Subject: [PATCH 15/19] Make openscap-utils require openscap-engine-sce --- openscap.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/openscap.spec b/openscap.spec index fbc844192f..4cd0d87a48 100644 --- a/openscap.spec +++ b/openscap.spec @@ -92,6 +92,7 @@ Summary: OpenSCAP Utilities Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} Requires: rpmdevtools rpm-build Requires: %{name}-scanner%{?_isa} = %{epoch}:%{version}-%{release} +Requires: %{name}-engine-sce%{?_isa} = %{epoch}:%{version}-%{release} %description utils The %{name}-utils package contains command-line tools build on top From 4383299e6f544d6db1f6b887b3ba1fb356f4c161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 23 Oct 2024 11:55:05 +0200 Subject: [PATCH 16/19] Do not install openscap-engine-sce as a part of the script --- utils/oscap-bootc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/utils/oscap-bootc b/utils/oscap-bootc index 2cb84e96f7..83ef333419 100755 --- a/utils/oscap-bootc +++ b/utils/oscap-bootc @@ -56,11 +56,9 @@ def ensure_sce_installed(): query_cmd = ["rpm", "-q", "openscap-engine-sce"] query_process = subprocess.run(query_cmd, capture_output=True) if query_process.returncode != 0: - install_cmd = ["dnf", "-y", "install", "openscap-engine-sce"] - try: - subprocess.run(install_cmd, check=True, capture_output=True) - except subprocess.CalledProcessError as e: - print(e.stderr.decode("utf-8"), file=sys.stderr) + raise RuntimeError( + "The script requires to have the openscap-engine-sce package " + "installed.") def add_args(option_args_list, cmd): From 502d41bf534576e655791b884e8b4daf8c63fb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 22 Oct 2024 10:06:13 +0200 Subject: [PATCH 17/19] Pass OSCAP_BOOTC_BUILD to remediations Starting with this commit, the OSCAP_BOOTC_BUILD environment variable will be passed down to the bash remediation. If this environment variable is set in the caller environment, the Bash remediations will be able to access it and read its value. This will be useful because the bash remediations will be able to contain a condition depending on this variable. Using this feature we can influence the behavior of our Bash remediations in the process of building bootable container images. The `oscap-bootc` utility will export the `OSCAP_BOOTC_BUILD` environment variable and the Bash remeditions will check this variable and for example they will not start systemd services. This commit also adds a small test. --- src/XCCDF_POLICY/xccdf_policy_remediate.c | 12 +++- tests/API/XCCDF/unittests/CMakeLists.txt | 1 + .../test_oscap_bootc_pass_down.ds.xml | 66 +++++++++++++++++++ .../unittests/test_oscap_bootc_pass_down.sh | 31 +++++++++ 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.ds.xml create mode 100755 tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.sh diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c index 70eb188838..72b8bed34b 100644 --- a/src/XCCDF_POLICY/xccdf_policy_remediate.c +++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c @@ -450,9 +450,9 @@ static inline int _xccdf_fix_execute(struct xccdf_rule_result *rr, struct xccdf_ int fork_result = fork(); if (fork_result >= 0) { - /* fork succeded */ + /* fork succeeded */ if (fork_result == 0) { - /* Execute fix and forward output to the parrent. */ + /* Execute fix and forward output to the parent. */ close(pipefd[0]); dup2(pipefd[1], fileno(stdout)); dup2(pipefd[1], fileno(stderr)); @@ -464,8 +464,14 @@ static inline int _xccdf_fix_execute(struct xccdf_rule_result *rr, struct xccdf_ NULL }; - char *const envp[2] = { + char *oscap_bootc_build = getenv("OSCAP_BOOTC_BUILD"); + char *oscap_bootc_build_kvarg = NULL; + if (oscap_bootc_build != NULL) { + oscap_bootc_build_kvarg = oscap_sprintf("OSCAP_BOOTC_BUILD=%s", oscap_bootc_build); + } + char *const envp[3] = { "PATH=/bin:/sbin:/usr/bin:/usr/sbin", + oscap_bootc_build_kvarg, NULL }; diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt index 164b795e0e..d8c5432e5f 100644 --- a/tests/API/XCCDF/unittests/CMakeLists.txt +++ b/tests/API/XCCDF/unittests/CMakeLists.txt @@ -111,3 +111,4 @@ add_oscap_test("test_no_newline_between_select_elements.sh") add_oscap_test("test_single_line_tailoring.sh") add_oscap_test("test_reference.sh") add_oscap_test("test_remediation_bootc.sh") +add_oscap_test("test_oscap_bootc_pass_down.sh") diff --git a/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.ds.xml b/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.ds.xml new file mode 100644 index 0000000000..660575d55e --- /dev/null +++ b/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.ds.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + 5.11.1 + 2009-01-12T10:41:00-05:00 + + + + + FAIL + fail + + + + + + + + + + + + + + oval:x:var:1 + + + + + 100 + + + + + + + accepted + 1.0 + + This rule always fails + + if [[ "$OSCAP_BOOTC_BUILD" == "YES" ]] ; then + printf "WE ARE BUILDING BOOTABLE CONTAINER IMAGE NOW" + fi + + + + + + + + diff --git a/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.sh b/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.sh new file mode 100755 index 0000000000..09783de1d3 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_oscap_bootc_pass_down.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +. $builddir/tests/test_common.sh + +set -e -o pipefail + +function test_pass_env_var_down() { + stdout=$(mktemp) + stderr=$(mktemp) + result=$(mktemp) + + OSCAP_BOOTC_BUILD=YES $OSCAP xccdf eval --remediate --results "$result" "$srcdir/test_oscap_bootc_pass_down.ds.xml" > "$stdout" 2> "$stderr" || ret=$? + assert_exists 1 '//rule-result/message[text()="WE ARE BUILDING BOOTABLE CONTAINER IMAGE NOW"]' + + rm -rf "$stdout" "$stderr" "$result" +} + +function test_no_env_var() { + stdout=$(mktemp) + stderr=$(mktemp) + result=$(mktemp) + + $OSCAP xccdf eval --remediate --results "$result" "$srcdir/test_oscap_bootc_pass_down.ds.xml" > "$stdout" 2> "$stderr" || ret=$? + assert_exists 0 '//rule-result/message[text()="WE ARE BUILDING BOOTABLE CONTAINER IMAGE NOW"]' + + rm -rf "$stdout" "$stderr" "$result" +} + + +test_pass_env_var_down +test_no_env_var From 391d736968a3e81557674f5524a9cd7176f2935b Mon Sep 17 00:00:00 2001 From: Matus Marhefka Date: Fri, 1 Nov 2024 16:50:52 +0100 Subject: [PATCH 18/19] Extend oscap-bootc to install SCE dependencies Some SCE checks which are used instead of OVAL checks when building a bootable container require additional packages. This commit introduces `install_sce_dependencies` function in `oscap-bootc` script which will handle their installation. --- utils/oscap-bootc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/utils/oscap-bootc b/utils/oscap-bootc index 83ef333419..8ac7c17b87 100755 --- a/utils/oscap-bootc +++ b/utils/oscap-bootc @@ -61,6 +61,19 @@ def ensure_sce_installed(): "installed.") +def install_sce_dependencies(): + required_packages = [ + "setools-console" # seinfo is used by the sebool template + ] + install_cmd = ["dnf", "-y", "install"] + required_packages + install_process = subprocess.run( + install_cmd, universal_newlines=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if install_process.returncode != 0: + raise RuntimeError( + f"{install_process.stdout}\nFailed to install SCE dependencies.") + + def add_args(option_args_list, cmd): for o, a in option_args_list: if a: @@ -112,6 +125,7 @@ def scan_and_remediate(args): def main(): args = parse_args() ensure_sce_installed() + install_sce_dependencies() pre_scan_fix(args) scan_and_remediate(args) From 8d6ea6c149b8966371017e0e0ccf99f1c8525a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 13 Nov 2024 16:56:01 +0100 Subject: [PATCH 19/19] Revert changes to GH workflows --- .github/workflows/build.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 544099334e..89d598b5b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,9 +6,9 @@ name: Gating # events but only for the main branch on: push: - branches: [ maint-1.3 ] + branches: [ '*' ] pull_request: - branches: [ maint-1.3 ] + branches: [ main, maint-1.3 ] # A workflow run is made up of one or more jobs that can run sequentially or in parallel @@ -21,7 +21,7 @@ jobs: strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-20.04, ubuntu-latest] # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -148,7 +148,7 @@ jobs: path: ${{ github.workspace }}/vcpkg repository: microsoft/vcpkg fetch-depth: 1 - + - name: Bootstrap vcpkg shell: pwsh run: "${{ github.workspace }}\\vcpkg\\scripts\\bootstrap.ps1 -disableMetrics" @@ -159,8 +159,9 @@ jobs: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + - name: Install Deps - run: "${{ github.workspace }}\\vcpkg\\vcpkg.exe install curl libxml2 libxslt bzip2 pcre pthreads zlib getopt-win32 xmlsec --triplet x64-windows" + run: "${{ github.workspace }}\\vcpkg\\vcpkg.exe install curl libxml2 libxslt bzip2 pcre2 pthreads zlib getopt-win32 xmlsec --triplet x64-windows" - name: Configure working-directory: ./build @@ -169,7 +170,7 @@ jobs: - name: Build run: cmake --build . --config Release working-directory: ./build - + - name: Package run: cpack working-directory: build