Re: [RFC cip-dev][isar-cip-core][PATCH 14/14] Add doc/README.cis-hardening.md
Jan Kiszka <[email protected]> Mon, 27 Jul 2026 17:24:30 +0200
| Newsgroups | org.cip-project.lists.cip-dev |
|---|---|
| Message-ID | <[email protected]> |
On 24.07.26 14:47, Quirin Gylstorff wrote: > From: Quirin Gylstorff <[email protected]> > > 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]> > --- > README.md | 1 + > doc/README.cis-hardening.md | 183 ++++++++++++++++++++++++++++++++++++ > 2 files changed, 184 insertions(+) > create mode 100644 doc/README.cis-hardening.md > > diff --git a/README.md b/README.md > index 435d037..f9f4f01 100644 > --- a/README.md > +++ b/README.md > @@ -60,6 +60,7 @@ The following topics are in separate READMEs: > - [Update and Secure boot signing](./doc/README.signing.md) > - [Encryption with TPM2](./doc/README.tpm2.encryption.md) > - [Factory-reset](./doc/README.factory-reset.md) > + - [CIS hardening](./doc/README.cis-hardening.md) > - [Release policy](./doc/README_version-release-policy.md) > > ## Community Resources > diff --git a/doc/README.cis-hardening.md b/doc/README.cis-hardening.md > new file mode 100644 > index 0000000..104818a > --- /dev/null > +++ b/doc/README.cis-hardening.md > @@ -0,0 +1,183 @@ > +# CIS Hardening > + > +CIP Core can generate and apply a selected set of CIS hardening rules during > +the image build. The rule catalogue is derived from OVH `debian-cis` and is > +exposed as a Kconfig menu. The selected rules are exported as JSON, converted > +into Debian package content, and reused for kernel configuration and optional > +runtime validation. > + Ok, here is hopefully my architectural overview. Let's see. First, some links to CIS (what is this? why/when do you want this?) and to that OVH repo you refer to would be good. > +## Key design decisions > + > +- CIS hardening is optional. > +- Hardening is enabled by a single image configuration option, > + `CIP_CIS_HARDENING`, plus the selected rule configuration. > +- Hardening and validation are independent. An image may apply hardening, > + include validation support, or do both depending on the product workflow. ...but we do not allow to select only hardening in the Kconfig menu. Why then this separation? How do we test their independence? BTW, how do we test at all? Nothing of this is built in CI. > +- Hardening and validation configuration must be overridable from a > + third-party layer, for example through a `cip-cis-rules-config_%.bbappend` that > + provides a checked-in configuration. > +- The default level `2` configuration must harden all generic settings. > + A conformity level below `100%` is acceptable for this default because the > + remaining rules are product specific and must be completed downstream. What is "level 2"? > +- CIS `99.x` rules are ignored. What is that, and why do we ignore that? > + > +## Enabling CIS hardening > + > +Enable hardening for an image by setting `CIP_CIS_HARDENING`: > + > +``` > +CIP_CIS_HARDENING = "1" > +``` > + > +When this option is enabled, `cip-core-image`: > + > +- inherits the CIS hardening image class > +- installs the `cip-cis-rules` and `cip-cis-rules-config` package > +- applies the generated hardening package to the root file system, > +- adds the generated kernel hardening fragment to the CIP kernel build. > + > +## Configuring rules > + > +Rules can be selected with the recipe menuconfig task: > + > +``` > +./kas-container shell <kas files> > +bitbake cip-cis-rules-config -c menuconfig > +bitbake cip-core-image Simpler and more generic: $ kas-container build --target cip-cis-rules-config -c menuconfig $ kas-container build > +``` > + > +The menu contains: > + > +- a hardening level choice, currently levels `1` to `5`, > +- one boolean option per CIS rule, > +- justification strings for rules disabled even though the selected level > + requires them. > + > +Changing the hardening level in the menu resets rule selections to the defaults > +for the new level. Existing justification strings are kept. > + > +The menu writes `.config.json` in the `cip-cis-rules` work directory. To use > +this configuration for hardening it must be copied into `cip-cis-rules-config` > +and added there in the `SRC_URI` and `HARDENING_CFG`. That again raises the questions why this is .config.json has a leading ".", why we are checking in some prefilled version of it, and what that default contains. > + > +For product builds, copy the generated `.config.json` into a local layer, add > +it to `SRC_URI` from a `cip-cis-rules-config_%.bbappend`, and check both files into > +version control. You can have multiple configurations checked in this way, and > +select which one should be applied with `HARDENING_CFG`. Could someone run that menuconfig in a way that the resulting config is directly placed into a downstream layer? That is particularly important when we continue to check in a default version into isar-cip-core. > + > +The JSON format is validated against > +`recipes-security/cip-cis-rules/files/cip-cis-rules/generator/config.schema.json`, > +whereby `apply` controls whether the rule is enabled. `justification` documents > +why a required rule is disabled. > + > +## What the hardening package installs > + > +The `cip-cis-rules` package is a configuration package. It installs only the > +files for rules with `apply: true`; rules without implemented snippets are > +kept in the JSON selection but do not add files to the root file system. > + > +Implemented snippets currently cover: > + > +- `/etc/modprobe.d/` files for disabled filesystems and USB storage, > +- `/etc/sysctl.d/` files for kernel and network sysctl settings, > +- `/etc/ssh/sshd_config.d/` files for SSH daemon hardening, > +- `/etc/sudoers.d/` files for sudo hardening, > +- `/etc/security/limits.d/` and `/etc/security/pwquality.d/`, > +- PAM snippets under `/usr/share/pam-configs` and `/etc/pam.d`, > +- chrony configuration under `/etc/chrony/conf.d`, > +- helper scripts under `/usr/share/cip-cis-rules/configuration` for settings > + that require an install-time action, such as file permissions, > + `pam-auth-update`, and `/etc/login.defs` edits. > + > +The `cip-cis-rules-config` package installs the generated data artifacts under > +`/usr/share/cip-cis-rules/`: > + > +- `.config.json`, the selected rule set, > +- `hardening.cfg`, the kernel config fragment generated from enabled > + kernel-relevant rules. > + > +The same artifacts are also deployed to `DEPLOY_DIR_IMAGE` as: > + > +- `${CIP_CIS_HARDENING_CONFIG}`, > +- `${CIP_CIS_HARDENING_KCONFIG}`. > + > +## Kernel hardening > + > +When `CIP_CIS_HARDENING` is enabled, the CIP kernel includes > +`${DISTRO}-hardening.kernel.cfg` as `hardening.kernel.cfg` through > +`KERNEL_CONFIG_FRAGMENTS`. > + > +The fragment is generated from enabled rules only. Disabled rules are omitted > +from the kernel fragment and should carry a justification in `.config.json` if > +they are required by the selected level. It's likely important to note the ordering issue here: Any own .cfg snippets added to the SRC_URI after that hardening fragment may override decision there. And that will only be noticed during validation. > + > +## Validation > + > +Set `CIP_CIS_HARDENING_VALIDATION` to enable validation support in the image: > + > +``` > +CIP_CIS_HARDENING_VALIDATION = "1" > +``` > + > +With validation enabled, `cip-core-image` installs `cip-cis-validation` and > +inherits the kernel hardening validation class. Validation uses the selected > +CIS rule configuration and can be enabled independently from hardening. > + > +Runtime validation uses the OVH `cis-hardening` audit tooling. During the > +image build, disabled rules from `${CIP_CIS_HARDENING_CONFIG}` are converted > +into override files with `status=disabled`, so the audit run checks the selected > +policy instead of the complete upstream rule set. > + > +The validation command runs the audit in a temporary CIS configuration tree and > +succeeds only when the reported `CONFORMITY_PERCENTAGE` is `100.00`. > + > +Kernel validation runs during the image build. It compares the generated > +hardening kernel fragment with the kernel config installed under `/boot`. A > +JSON report is written to `DEPLOY_DIR_IMAGE` as: > + > +- `${CIP_CIS_HARDENING_REPORT}` > + > +If a kernel option that should be disabled by CIS hardening is still enabled as > +`y` or `m`, the build fails and the report lists the non-compliant rules. > + As commented on the related patch already: This should be moved into the kernel recipe, and a validation result should only be checked when assembling the system. Avoids being bitten my a missing config on the final rootfs of an embedded device. > +## Notes and limitations > + > +Some CIS rules are inherently use case specific. Examples are partition layout > +rules, firewall policy, SSH access lists, password ageing, and rules that > +inspect existing users or files. Configure or justify those rules according > +to the target product requirements before relying on validation results. > + > +The hardening package will **not** install additional packages to fulfill > +a rule, for example iptables or ssh. These use case specific packages need to > +be installed by the user. Actually, ssh should be *deinstalled* prior to shipping a typical embedded device ;) > + > +### Comparison to security-customizations > + > +The recipe `security-customizations` sets some hardening options similar to the recipe `cip-cis-rules`. > +Because `security-customizations` is part of the certification efforts, it is not modified. The following sections > +list the touch points. > + > +#### SSH hardening > + > +The recipe `security-customizations` contains hardening rules for ssh. This table summarizes the differences from > +the cip-cis-rules: > +| security-customizations | cis-rules |Comment| > +| ------ | ------ |------| > +|ssh-remote-session-term.conf|cis-5.2.16-sshd-idle-timeout.conf| adapt cis hardening to lower values of cip security| > +|ssh-pam-remote.conf | - | no rule implemented in cip-cis-rules | > +|ssh-hardening.conf |cis-5.2.7-sshd-maxauthtries.conf | 3 vs 4 | > +|ssh-hardening.conf |cis-5.2.23-limit-ssh-max-sessions.conf |2 vs 10| > +|ssh-hardening.conf |cis-5.2.21-disable-ssh-allow-tcp-forwarding.conf || > +|ssh-hardening.conf |cis-5.2.6-disable-x11-forwarding.conf || > +|ssh-hardening.conf | - | AllowAgentForwarding is allowed according to cis rules| So, we already had harder rules in place than CIS demands? Didn't CIS allow to set harder rules, or what was needed then to merge that? > + > +#### Other hardening > + > +The script `postinst` of the recipe `security-customizations` contains the following changes: > +|security-customizations|cis-rules|Comment| > +| ------ | ------ |------| > +| set password creation parameters with pam.cracklib| | parameter differ between the implementations| > +|lock user account after unsuccessful attempts| - | no rule implemented in cip-cis-rules| > +| Concurrent session control| - |no rule implemented in cip-cis-rules| > +| Audit storage capacity| - |no rule implemented in cip-cis-rules| > +| Enable Multi Factor Authentication for Local and Remote Session| - |no rule implemented in cip-cis-rules| Again, we were already going beyond CIS in the security image, right? CIS rules weren't applied because of that, or didn't they even exist? Are there other cases where the CIS rule set what modified, justifying it to be called "CIP-flavored"? Where are those deviations documented? How will be maintain them in the future (Debian 14+, CIS updates)? Jan -- Siemens AG, Foundational Technologies Linux Expert Center