Skip to content

feat!: follow the CIS Benchmarks in the setup_basic defaults - #460

Open
markuslf wants to merge 13 commits into
mainfrom
feat/cis-security-defaults
Open

markuslf wants to merge 13 commits into
mainfrom
feat/cis-security-defaults

Conversation

@markuslf

@markuslf markuslf commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Brings the LFOps defaults closer to the CIS Benchmarks (RHEL 10 v1.0.1, Debian 12 v1.0.1, Level 1 Server) and makes the lynis role accept what LFOps deliberately does differently from Lynis. Where CIS and Lynis cover the same setting, CIS wins; a suggestion only Lynis makes is skipped only for a technical reason, which ends up as a comment in custom.prf.

Breaking Changes

  • sshd: PermitRootLogin no (CIS 5.1.20) and ClientAliveInterval 15 (CIS 5.1.7). A preflight check aborts the run before sshd is changed when Ansible itself logs in as root; setup_basic runs it in its pre_tasks.
  • kernel_modules: usb-storage is blocked (CIS 1.1.1.10), and every blocked module also gets a blacklist line, which the CIS audit checks for. The run aborts when a USB disk is attached, since dracut copies /etc/modprobe.d into the initramfs.
  • kernel_settings: A sysctl hardening baseline (CIS 1.5.x, 3.3.x, plus net.core.bpf_jit_harden = 2 and kernel.unprivileged_bpf_disabled = 1 for Lynis). Forwarding, strict rp_filter and accept_ra are left alone. Ubuntu 22.04 and 24.04 get kernel.kptr_restrict = 1, since TuneD re-applies their /etc/sysctl.d/10-kernel-hardening.conf over its profile.

Added / Changed / Fixed

  • cron_permissions (new role, in setup_basic): makes /etc/crontab, /etc/cron.deny and /etc/cron.* root-only where cron is installed (CIS 2.4.1.x). It neither installs nor starts cron, since LFOps uses systemd timers.
  • at: /etc/at.deny root-only on the Red Hat family (CIS 2.4.2.1).
  • lynis: default skip list with a technical reason per entry, atomic where Lynis supports it (KRNL-6000:<key>, SSH-7408:<option>). BOOT-5264 is skipped, since Lynis suggests it on every host with systemd 240 or newer. ACCT-9622, ACCT-9626, FILE-6310 and LOGG-2154 stay visible on purpose.
  • mariadb_server: no longer sets sunrpc.tcp_slot_table_entries (NFS client only) and removes it from existing TuneD profiles.
  • kernel_settings: loads sunrpc for a sunrpc.* value an earlier run left in the TuneD profile, instead of failing tuned-adm verify.

Testing

  • Lynis on 001-p-mon01 (Rocky 8) with the affected roles from this branch: 35 suggestions / hardening index 70 before, 4 / 88 after (ACCT-9622, ACCT-9626, FILE-6354, LOGG-2154 remain visible on purpose).
  • Molecule, full matrix: see the comment below. On RHEL, setup_basic stops in verify.yml at the AIDE and about-me checks because of the corrupt bytecode in the monitoring-plugins 8.0.0 RPMs (dmesg: "EOFError: marshal data too short" since v8.0.0 monitoring-plugins#1543, fixed upstream, not released yet); main fails the same way.

@markuslf
markuslf requested a review from NavidSassan October 16, 2026 14:48
@markuslf

Copy link
Copy Markdown
Member Author

Molecule, full matrix (8 VMs per scenario, no parallel runs):

  • cron_permissions, lynis: passed on Debian 12/13, RHEL 8/9/10, Ubuntu 22.04/24.04/26.04.
  • kernel_settings/sysctl: passed (RHEL 8/9/10).
  • setup_basic: converge and idempotence passed on all eight, the CIS checks in verify.yml on Debian 12/13 and Ubuntu 22.04/24.04/26.04. On RHEL 8/9/10 verify.yml stops earlier at the AIDE and about-me checks: the monitoring-plugins 8.0.0 RPMs ship corrupt .pyc files (dmesg: "EOFError: marshal data too short" since v8.0.0 monitoring-plugins#1543, fixed upstream in e653e9af4, not released yet). The same failures occur on main (90f35fd), so they are not caused by this PR.
  • On 001-p-mon01 (Rocky 8): Lynis suggestions 35 → 4, hardening index 70 → 88.

…the profile

fedora.linux_system_roles.kernel_settings merges new settings into the
profile it wrote before, so a sunrpc.* value from an earlier
mariadb_server run stays in it and tuned-adm verify checks it on every
run. Without the sunrpc module the run aborted. Load the module for
those keys as well, and restart TuneD when the module was missing, since
the role only applies an unchanged profile at TuneD start. Verified with
fedora.linux_system_roles 1.121.0 on 001-p-mon01 (Rocky 8).
The value only concerns the NFS client: it is the number of RPC slots
preallocated per TCP transport, while further slots are allocated on
demand up to tcp_max_slot_table_entries (xprt_dynamic_alloc_slot() in
net/sunrpc/xprt.c). MariaDB has no use for it, and it loaded the sunrpc
module on every database server. state: 'absent' removes it from the
TuneD profile of existing hosts.
…val 15

CIS 5.1.20 (Level 1) requires PermitRootLogin no, CIS 5.1.7 a non-zero
ClientAliveInterval. A preflight check aborts the run before sshd_config
is deployed when Ansible itself logs in as root and the new value would
lock it out; setup_basic runs it in its pre_tasks. Molecule logs in as
root and keeps prohibit-password in the shared inventory.
…ocked module

CIS 1.1.1.10 (Level 1) requires usb-storage to be unavailable, and the
CIS audit checks for a blacklist line next to the install line. The run
aborts when a USB disk is attached, since dracut copies /etc/modprobe.d
into the initramfs and a host booting from it would no longer boot.
…fault

The CIS Level 1 sysctls of RHEL 10 (1.5.x, 3.3.x) plus the two BPF
settings Lynis asks for, checked against the kernel source. Forwarding,
strict rp_filter and accept_ra stay untouched. Ubuntu 22.04 and 24.04
get kernel.kptr_restrict = 1, since TuneD re-applies their
/etc/sysctl.d/10-kernel-hardening.conf over its profile
(_apply_system_sysctl() in tuned/plugins/plugin_sysctl.py).
Keeps the cron daemon running (CIS 2.4.1.1) and makes /etc/crontab,
/etc/cron.deny and the /etc/cron.* directories readable by root only
(CIS 2.4.1.2 to 2.4.1.9, Lynis FILE-7524). Runs in setup_basic.
CIS 2.4.2.1 and Lynis FILE-7524. at is setuid root there. On Debian and
Ubuntu it is setuid daemon and reads the file as group daemon, so the
root:daemon 0640 of the package stays.
… does not follow

Where Lynis and CIS cover the same setting, LFOps follows CIS; a
suggestion only Lynis makes is skipped only for a technical reason,
which each entry carries as a comment into custom.prf. Single values
are skipped where Lynis supports it (KRNL-6000, SSH-7408). On
001-p-mon01 the suggestions drop from 35 to 4.
Asks the running system: sshd -T, the sysctl values after the reboot,
modprobe for usb-storage, a cron job in the 0700 /etc/cron.d, and a
Lynis audit that must not report the sshd, file permission and sysctl
defaults.
…ead of installing it

LFOps schedules its jobs with systemd timers, and CIS 2.4.1 only applies
where cron is installed, so the role no longer installs or starts cron.
It sets the CIS modes on the paths that exist and changes nothing on a
host without cron, such as the Debian cloud images.
…-6640

BOOT-5264 is suggested on every host with systemd 240 or newer, whatever
systemd-analyze security reports. mod_evasive only hooks the access
checker of Apache httpd, counts per TCP peer and child process, and is
unmaintained upstream since 2017.
…Lynis accepts

Lynis rejects a space in any argument before 3.1.6 (SafeInput() in
include/functions), which aborted the audit on Debian 13. It looks for
each test ID as a substring of --tests, so a slash works as separator.
The cron probe only runs where the image ships cron.
@markuslf
markuslf force-pushed the feat/cis-security-defaults branch from bc3e44e to 5284347 Compare October 18, 2026 19:44

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant