Re: [RFC cip-dev][isar-cip-core][PATCH 08/14] Add class for validating the CIS options in the kernel config
Jan Kiszka <[email protected]> Mon, 27 Jul 2026 16:25:34 +0200
| Newsgroups | org.cip-project.lists.cip-dev |
|---|---|
| Message-ID | <[email protected]> |
On 24.07.26 14:39, Quirin Gylstorff wrote: > From: Quirin Gylstorff <[email protected]> > > If CIS hardening options have been configured that change the > kernel configuration, this class checks whether the correct options > are selected in the resulting kernel configuration in ROOTFSDIR. And what if the rootfs was minimized, got that normally useless file removed? Can't we validate an actually built kernel against its effective config and then carry this validation report forward to the installed one, maybe linking kernel binary and report with a hash? Jan > A JSON report is generated in DEPLOY_DIR_IMAGE indicating whether > the CIS rules for the kernel options were correctly applied > (compliant: true/false). > > Co-Authored-by: Clara Kowalsky <[email protected]> > Co-Authored-by: Felix Moessbauer <[email protected]> > Co-Authored-by: Christoph Steiger <[email protected]> > Signed-off-by: Quirin Gylstorff <[email protected]> > --- > .../cis-kernel-hardening-validation.bbclass | 110 ++++++++++++++++++ > 1 file changed, 110 insertions(+) > create mode 100644 classes-recipe/cis-kernel-hardening-validation.bbclass > > diff --git a/classes-recipe/cis-kernel-hardening-validation.bbclass b/classes-recipe/cis-kernel-hardening-validation.bbclass > new file mode 100644 > index 0000000..184da7f > --- /dev/null > +++ b/classes-recipe/cis-kernel-hardening-validation.bbclass > @@ -0,0 +1,110 @@ > +# > +# CIP Core, generic profile > +# > +# Copyright (c) Siemens AG, 2026 > +# > +# Authors: > +# Clara Kowalsky <[email protected]> > +# > +# SPDX-License-Identifier: MIT > +# > +# If CIS hardening options have been configured that change the > +# kernel configuration, this class checks whether the correct options > +# are selected in the resulting kernel configuration in ROOTFSDIR. > +# A JSON report is generated in DEPLOY_DIR_IMAGE indicating whether > +# the CIS rules for the kernel options were correctly applied > +# (compliant: true/false). > +# > +CIP_CIS_HARDENING_REPORT ??= "${DISTRO}-hardening-kernel-report.json" > + > +DEPLOYDIR_HARDENING = "${WORKDIR}/deploy-hardening" > +SSTATETASKS += "do_validate_kernel_config" > + > +do_validate_kernel_config[cleandirs] += "${DEPLOYDIR_HARDENING}" > +do_validate_kernel_config[sstate-inputdirs] = "${DEPLOYDIR_HARDENING}" > +do_validate_kernel_config[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" > +do_validate_kernel_config[depends] += "cip-cis-rules-config:do_deploy_hardening" > +python do_validate_kernel_config() { > + import os > + import json > + import re > + > + deploy_dir_stage = d.getVar('DEPLOYDIR_HARDENING') > + deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') > + boot_dir = os.path.join(d.getVar('ROOTFSDIR'), "boot") > + kernel_config_path = None > + > + if os.path.isdir(boot_dir): > + for f in os.listdir(boot_dir): > + if f.startswith("config-") and os.path.isfile(os.path.join(boot_dir, f)): > + kernel_config_path = os.path.join(boot_dir, f) > + break > + > + if not kernel_config_path: > + return > + > + hardening_config_path = os.path.join(deploy_dir_image, d.getVar('CIP_CIS_HARDENING_KCONFIG')) > + if not os.path.isfile(hardening_config_path): > + return > + > + report = os.path.join(deploy_dir_stage, d.getVar('CIP_CIS_HARDENING_REPORT')) > + > + kernel_options = {} > + with open(kernel_config_path, 'r') as f_kernel: > + for line in f_kernel: > + match = re.match(r"^(CONFIG_[A-Za-z0-9_]+)=(y|m)$", line.strip()) > + if match: > + kernel_options[match.group(1)] = match.group(2) > + > + report_rules = [] > + with open(hardening_config_path, 'r') as f_hardening: > + hardening_lines = f_hardening.readlines() > + > + i = 0 > + while i < len(hardening_lines): > + line = hardening_lines[i].strip() > + cis_match = re.match(r"^# CIS ([0-9.]+): (.*)$", line) > + if cis_match: > + cis_number = cis_match.group(1) > + description = cis_match.group(2) > + config_option = None > + > + if i + 1 < len(hardening_lines): > + next_line = hardening_lines[i+1].strip() > + config_option_match = re.match(r"^# (CONFIG_[A-Za-z0-9_]*) is not set$", next_line) > + if config_option_match: > + config_option = config_option_match.group(1) > + i += 1 > + > + is_compliant = not (config_option and kernel_options.get(config_option) in ['y', 'm']) > + > + report_rules.append({ > + "rule": { > + "name": cis_number, > + "description": description, > + "compliant": is_compliant > + } > + }) > + i += 1 > + > + if not report_rules: > + return > + > + with open(report, 'w') as f: > + json.dump({"rules": report_rules}, f, indent=2) > + > + non_compliant = [r["rule"]["name"] for r in report_rules if not r["rule"]["compliant"]] > + if non_compliant: > + bb.fatal( > + "CIS kernel hardening validation failed. The following CIS rules " > + "have kernel options enabled that should be disabled: %s. " > + "Details in %s." % (", ".join(non_compliant), report) > + ) > +} > + > +python do_validate_kernel_config_setscene () { > + sstate_setscene(d) > +} > +addtask validate_kernel_config_setscene > + > +addtask validate_kernel_config before do_rootfs_finalize after do_rootfs_postprocess -- Siemens AG, Foundational Technologies Linux Expert Center