Skip to content

Add CIS Level 1 profiles for Ubuntu 26.04 - #15097

Open
realstuffie wants to merge 38 commits into
ComplianceAsCode:masterfrom
realstuffie:ubuntu2604-cis-l1
Open

realstuffie wants to merge 38 commits into
ComplianceAsCode:masterfrom
realstuffie:ubuntu2604-cis-l1

Conversation

@realstuffie

@realstuffie realstuffie commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Description:

  • Adds cis_level1_server and cis_level1_workstation for Ubuntu 26.04, driven by a new control file at products/ubuntu2604/controls/cis_ubuntu2604.yml with 274 controls (274 l1_server, 265 l1_workstation, 7 l2_workstation).
  • Adds 15 rules the benchmark needs that were not in the tree yet: four Acquire::* APT settings (AllowInsecureRepositories, AllowWeakRepositories, AllowDowngradeToInsecureRepositories, Check-Date), HTTPS enforcement for sources.list and sources.list.d, the update-notifier-motd service and timer, pam_motd_configured, file_permissions_pam_motd, package_cracklib-runtime_installed, three accounts_user_dot_dirs_* rules and sudo_events_in_journal. All but sudo_events_in_journal, which is for sudo-rs, apply to every Ubuntu release.
  • Adds a cis_ubuntu2604 option to sshd_strong_kex.var. Everything else reuses existing rules, with Ubuntu 26.04 added to the product lists that need it.
  • Covers Level 1 only. The L2 level definitions are kept so they can be filled in later.

Rationale:

  • Ubuntu 26.04 already exists as a product here but has no CIS profiles. USG builds its benchmarks from a ComplianceAsCode tag named in tools/release_metadata/, so the content has to land here first.
  • Profile titles keep a DRAFT - prefix until someone has checked them against the published benchmark independently.

Review Hints:

  • What is still 26.04-only depends on 26.04 itself: sudo-rs logging, the removal of database= in AIDE 0.19, the 26.04 benchmark's GDM checks, and tmp.mount for /tmp. The tmp.mount remediation is detected at runtime and shared by all Ubuntu releases.
  • Tested cis_level1_server on a fresh Ubuntu 26.04 cloud image: 270 pass and 123 fail before remediation, 369 pass and 38 fail after remediation and a reboot, with no errors. Of the 38, 15 are sysctl values lost at reboot, fixed separately in #; 20 depend on packages remediation installs partway through the run (pwquality, cron, postfix) and pass on a second pass; 3 need site input (GRUB password, SSH user list, root PATH).
  • Changed during review: chronyd_specify_remote_server uses the same Ubuntu check and remediation as 22.04 and 24.04, 5.1.8, 5.1.13 and 5.1.21 use sshd_set_keepalive, sshd_use_strong_kex and sshd_disable_empty_passwords as on 24.04, and update-notifier-motd.service and cracklib-runtime use the service_disabled and package_installed templates.
  • Add package_cracklib-runtime_installed rule for CIS Debian 13 v1.1.0 #15273 adds the same package_cracklib-runtime_installed rule. Once it merges I'll drop my copy here.
  • CODEOWNERS has /products/ubuntu*/profiles/ but no /products/ubuntu*/controls/ line, so the control file won't request ubuntu-maintainers review automatically. I can add that line here.

@openshift-ci openshift-ci Bot added the needs-ok-to-test Used by openshift-ci bot. label Sep 5, 2026
@openshift-ci

openshift-ci Bot commented Sep 5, 2026

Copy link
Copy Markdown

Hi @realstuffie. Thanks for your PR.

I'm waiting for a ComplianceAsCode member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@realstuffie realstuffie changed the title Ubuntu2604 cis l1 Add CIS Level 1 profiles for Ubuntu 26.04 Sep 5, 2026
@realstuffie

Copy link
Copy Markdown
Contributor Author

Pushed two more commits.

Added test scenarios for all 13 new rules (47 scenarios). Writing them surfaced a
bug in the Acquire::* remediations: they only removed the option from the file
they write, so a conflicting value in /etc/apt/apt.conf or another apt.conf.d
file survived and the rule stayed non-compliant after remediation. Fixed in its
own commit.

service_update-notifier-motd_disabled ships only a pass scenario. The unit is a
static oneshot, so it is never enabled and never lingers active, and the check
reports the same result whether or not it has been masked. I have left the check
as it is and recorded the limitation in the rule's warnings block rather than
change what it asserts. Happy to tighten it to require masked if you would prefer.

@realstuffie

realstuffie commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Update after additional Ubuntu 26.04 testing.

I tested the L1 workstation content on two Ubuntu 26.04 systems. The scans exposed several release-specific differences, and I have pushed fixes for:

  • sudo-rs logging
  • PAM MOTD handling when referenced files are absent
  • the libpam-pwquality package mapping
  • native systemd /tmp mounts
  • SSH effective-value checks, applicability, and dependencies
  • APT configuration and source parsing
  • home dot-directory and /var/log checks
  • time-service selection
  • sysctl path handling, including the /lib/sysctl.d alias
  • missing package and service remediations
  • SCE content generation

Current branch head: a725d5fca7

Validation completed:

  • Ubuntu 26.04 product build passes
  • all 8 Ubuntu 26.04 product CTests pass
  • controleval.py reports 100% assessment coverage
  • L1 workstation contains 265 assessed controls and 434 selected rules
  • L1 server contains 274 assessed controls and 447 selected rules
  • repeated L1 workstation evaluations complete without OpenSCAP assertion failures
  • the corrected Ubuntu-specific checks pass on the test systems

I have not found any remaining Ubuntu 26.04-specific failures in the exercised L1 workstation content. A live L1 server evaluation is still pending.

At this point, I consider the Ubuntu 26.04 CIS L1 profiles close to merge-ready from an implementation and validation perspective. The remaining gates are upstream CI, reviewer feedback, and a live L1 server evaluation.

@jan-cerny jan-cerny added New Profile Issues or pull requests related to new Profiles. Highlight This PR/Issue should make it to the featured changelog. CIS CIS Benchmark related. Ubuntu Ubuntu product related. labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

This datastream diff is auto generated by the check Compare DS/Generate Diff

Click here to see the full diff
New content has different text for rule 'xccdf_org.ssgproject.content_rule_service_ufw_enabled'.
--- xccdf_org.ssgproject.content_rule_service_ufw_enabled
+++ xccdf_org.ssgproject.content_rule_service_ufw_enabled
@@ -8,6 +8,11 @@
 The ufw service can be enabled with the following command:
 $ sudo systemctl enable ufw.service
 
+[warning]:
+Enabling ufw while connected over the network can lock you out of the
+system. When the default incoming policy is deny, add a rule that allows
+SSH, for example "ufw allow OpenSSH", before enabling ufw.
+
 [reference]:
 SRG-OS-000297-GPOS-00115
 

New content has different text for rule 'xccdf_org.ssgproject.content_rule_mount_option_tmp_noexec'.
--- xccdf_org.ssgproject.content_rule_mount_option_tmp_noexec
+++ xccdf_org.ssgproject.content_rule_mount_option_tmp_noexec
@@ -7,6 +7,7 @@
 [description]:
 The noexec mount option can be used to prevent binaries
 from being executed out of /tmp.
+
 Add the noexec option to the fourth column of
 /etc/fstab for the line which controls mounting of
 /tmp.

@jan-cerny jan-cerny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please resolve the shellcheck issues from the CI


In /home/runner/work/content/content/build/ubuntu2604/fixes/bash/accounts_user_dot_dirs_groupownership.sh line 9:
while IFS=: read -r user _ _ gid _ home shell; do
                    ^--^ SC2034 (warning): user appears unused. Verify use (or export if used externally).


In /home/runner/work/content/content/build/ubuntu2604/fixes/bash/apt_configure_allow_downgrade_to_insecure_repositories.sh line 8:
option='Acquire::AllowDowngradeToInsecureRepositories'
^----^ SC2034 (warning): option appears unused. Verify use (or export if used externally).


In /home/runner/work/content/content/build/ubuntu2604/fixes/bash/apt_configure_allow_insecure_repositories.sh line 8:
option='Acquire::AllowInsecureRepositories'
^----^ SC2034 (warning): option appears unused. Verify use (or export if used externally).


In /home/runner/work/content/content/build/ubuntu2604/fixes/bash/apt_configure_allow_weak_repositories.sh line 8:
option='Acquire::AllowWeakRepositories'
^----^ SC2034 (warning): option appears unused. Verify use (or export if used externally).


In /home/runner/work/content/content/build/ubuntu2604/fixes/bash/apt_configure_check_date.sh line 8:
option='Acquire::Check-Date'
^----^ SC2034 (warning): option appears unused. Verify use (or export if used externally).

For more information:
  https://www.shellcheck.net/wiki/SC2034 -- option appears unused. Verify use...

@jan-cerny jan-cerny added this to the 0.1.83 milestone Sep 7, 2026
@jan-cerny jan-cerny self-assigned this Sep 7, 2026
@jan-cerny

Copy link
Copy Markdown
Member

@realstuffie Adressing YAML lint issues isn't mandatory. You can keep the changes in the PR, but next time you don't have to do it.

@jan-cerny jan-cerny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the PR looks good to me.

@ComplianceAsCode/ubuntu-maintainers Please take a look.

@realstuffie

realstuffie commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Update on the concerns listed in the original description:

  • Confirmed that CIS 2.3.3.1 permits time sources according to local site policy. The Chrony hostname variable remains a remediation default.
  • Replaced the SSH OVAL overrides with dedicated effective-value checks.
  • Removed the global empty-password test exclusion. Ubuntu 26.04 now has a separate rule and tests.
  • Updated the cracklib check to detect available upgrades, with remediation installing the latest version available through APT.
  • Added Chrony local-source coverage and fixed whitespace-only directives incorrectly passing. Targeted OpenSCAP checks and validation passed.

Thanks for the review, Jan.

@dodys

dodys commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

hey @realstuffie are you done with the changes? Because I saw you still pushed more commits after Jan's review.
For us to do a review it is better for it to be in a point that is ready for reviewing and testing. If there are still things you are working/improving/fixing, then I think it is better to convert this to a draft until this is ready for review.

@realstuffie

realstuffie commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Yes, done. Last commit is df18a88 from 8 September and nothing has changed since.

Sorry about the ones that landed after Jan's review. Those were his four points, then fixes
for what live scans on two Ubuntu 26.04 systems turned up. The last was the chrony test
scoping, which I had sitting locally longer than I should have before committing it. I
should have finished the testing before asking for review rather than pushing into the
middle of one.

Say the word if you would still rather I moved it to draft. From my side it is ready.

@alanmcanonical

Copy link
Copy Markdown
Contributor

Could you look at what changes might break the system? I tried automatus.py profile test and the pristine resolute broke in some ways:

  • ssh stuck at debug1: Connecting to sec-resolute-amd64 [192.168.122.194] port 22. debug3: set_sock_tos: set socket 3 IP_TOS 0xb8. Probably due to ufw default drop?
  • On virt-viewer interface, I can't open gnome-shell, it shows This account is currently unavailable

@openshift-ci openshift-ci Bot added the needs-rebase Used by openshift-ci bot. label Sep 15, 2026
@realstuffie

realstuffie commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for testing it.

Yes, ufw. 26.04 dropped 24.04's 4.2.6 "Ensure ufw firewall rules exist for all open
ports", so nothing adds an allow rule before 4.1.3 sets incoming to deny, and 4.1.2
flushes the chains on enable. ufw allow proto tcp from any to any port 22 on the target
first avoids it. I will add warning blocks to the ufw rules.

Worth your view on the bigger question: stay strict to the benchmark, or carry
ufw_rules_for_open_ports as a safety rule outside the control mapping? 24.04 gets it via
4.2.6, so it would be a deliberate deviation here.

I could not reproduce the account one. I applied cis_level1_workstation with
oscap xccdf eval --remediate to a clean 26.04.1 desktop install (default selection,
GNOME), skipping only the filesystem-walking rules and aide_build_database for time.
The only changes to /etc/passwd were two accounts the aide and postfix packages created
during the run, both already /usr/sbin/nologin from their own packaging:

> _aide:x:972:972:Advanced Intrusion Detection Environment:/var/lib/aide:/usr/sbin/nologin
> postfix:x:112:115:Postfix MTA:/var/spool/postfix:/usr/sbin/nologin

No pre-existing account was modified. 5.4.2.7 returned pass, so its remediation never
ran. gdm is UID 975 with /bin/false, which is not in /etc/shells, so the check already
treats it as compliant. The VM boots to a working greeter and logs in locally. SSH hangs
at connect, which is the ufw drop above.

So I need the /etc/passwd line for the affected account. If its UID is below 1000
the rule did the right thing. If not, I have a bug to find. It would also help to know
whether automatus.py profile applies anything beyond the profile remediation, since
several of this rule's own test scenarios write SYS_UID_MAX and UID_MIN drop-ins into
login.defs, and a raised ceiling would put a UID 1000 account in scope.

Also rebased onto master. One conflict in the chrony remediation, where your new "create
the file if missing" guard met my sourcedir detection. Kept both. Full build and all 10
ubuntu2604 CTests pass on the rebased branch.

@openshift-ci openshift-ci Bot removed the needs-rebase Used by openshift-ci bot. label Sep 16, 2026
Require a non-whitespace source on the same line as server or pool in the main configuration and included files. Add a regression scenario for blank sources.

CIS Ubuntu 26.04 section 2.3.3.1 permits authorized sources according to local site policy. Add a passing scenario for a site-selected source and limit the hostname-restricted failure scenario to Ubuntu 22.04 and 24.04.
The check read a recent sudo event as proof that logging was configured.
A freshly remediated system has no such event, so the rule failed until
someone ran sudo. The benchmark's audit has the same shape and prefaces
both methods with "Generate a recent event", which a scanner cannot do.

Check persistent journal storage, or an active rsyslog service with a
loaded authpriv route to a durable file. Keep the classic sudo
Defaults logfile= path for systems without sudo-rs.

Add eight test scenarios for both paths.
Several rules in the 26.04 CIS profiles checked RHEL package or unit
names that do not exist on Ubuntu, so they passed whether or not the
service was installed. Copy the ubuntu2404 names to ubuntu2604 for
avahi, bind, dhcp, dovecot, net-snmp, openldap, rsh, nfs, rsync, samba
and slapd.

On Ubuntu each kea service ships in its own package, so the kea service
rules now check kea-dhcp4-server, kea-dhcp6-server and
kea-dhcp-ddns-server. The 26.04 benchmark audits kea in 2.1.3, so add
the kea rules to that control.

Map the avahi, dconf and gdm package checks to avahi-daemon,
dconf-service and gdm3. The GDM rules no longer pass on systems that
have GNOME installed but not configured.
accounts_password_pam_maxrepeat accepted maxrepeat = 0 on 26.04, which
turns the check off. The 26.04 benchmark requires a value from 1 to 3.

sysctl_kernel_yama_ptrace_scope accepted only 1 on 26.04. The benchmark
accepts 1, 2 or 3.
The 26.04 benchmark keeps every GNOME setting, login screen ones
included, in /etc/dconf/db/local.d with locks in local.d/locks, read
through /etc/dconf/profile/user. Its audit runs gsettings against that
profile.

On ubuntu2604:
- set dconf_gdm_dir to local.d
- require only the user dconf profile, and stop the bash fixes from
  writing /etc/dconf/profile/gdm, which hid the profile gdm3 ships in
  /usr/share/dconf/profile/gdm along with GDM's greeter defaults
- check the banner text lock that 1.7.1 requires, and set it in the fix
- stop counting a banner set only in /etc/gdm3/greeter.dconf-defaults
- drop dconf_gnome_screensaver_lock_enabled from 1.7.3, which audits
  idle-delay and lock-delay only

Limit the two greeter-only pass scenarios to 22.04 and 24.04 and add
26.04 scenarios for the new behaviour. Ubuntu 24.04 content is
unchanged.
package[snmpd] mapped to net-snmp on Ubuntu, which does not exist, so
service_snmpd_disabled was never applicable. Map it to snmpd on 26.04.

no_rsh_trust_files only applied when rsh-server was installed, and
rsh-server is not in the 26.04 archive. The benchmark flags .rhosts
files whether or not rsh is installed, so drop the platform on 26.04.
The profile remediation script joins the Bash remediations, so an exit
in one skips every rule after it. The bash-remediations-no-exit test
flags the exit 0. Use if blocks instead, and report the error cases on
stderr without exiting.
check_ufw_active and service_ufw_enabled turn the firewall on. With the
default incoming policy set to deny and no rule for SSH, a remote session
drops, as happened when the profile was applied to a fresh 26.04 system
over SSH. The other ufw rules in the profile already carry this warning.
The remediation added servers to the first sourcedir, which is
/run/chrony-dhcp in the chrony.conf that Ubuntu 26.04 ships, ahead of
/etc/chrony/sources.d. /run is a tmpfs that starts empty at boot, so the
sources were gone after the next reboot. Skip sourcedirs under /run and
/var/run, and fall back to chrony.conf when no other sourcedir exists.
Drop the sshd_use_strong_kex_ubuntu2604 rule and select
sshd_use_strong_kex, as the Ubuntu 22.04 and 24.04 controls do. The new
cis_ubuntu2604 value of sshd_strong_kex removes the three SHA-1 key
exchanges that CIS Ubuntu 26.04 prohibits. It is the same value the CIS
RHEL profiles use.
Drop the 26.04 OVAL and test scenarios of chronyd_specify_remote_server
and run multiple_incorrect.fail.sh on all Ubuntu releases again. Ubuntu
26.04 now uses oval/ubuntu.xml like 22.04 and 24.04, so each server must
be one of var_multiple_time_servers. correct.pass.sh already covers 26.04.

Restore the bash remediation from master too. It always adds the allowed
servers to chrony.conf, which persists across reboots, so the sourcedir
handling and the /run/chrony-dhcp workaround are no longer needed. On a
stock 26.04 host, where the pools are in /etc/chrony/sources.d, the check
passes after remediation.

Remove the control note that described the old 26.04 check.
sshd_disable_empty_passwords already passes when PermitEmptyPasswords is
not set, so it covers what sshd_disable_empty_passwords_default checked.
For 5.1.8 select sshd_set_keepalive and keep the Ubuntu 24.04 note on
the upper bound. Remove the two new rules and restore the master
version of sshd_set_idle_timeout, which only differed to require
sshd_set_keepalive_positive.
The checks, remediations and test scenarios of the APT, pam_motd,
cracklib-runtime, user dot directory and update-notifier-motd rules
only rely on things that 22.04 and 24.04 have as well. Use
multi_platform_ubuntu for them instead of Ubuntu 26.04.
Rename the Ubuntu 26.04 remediations of mount_option_tmp_noexec to
ubuntu.sh and ubuntu.yml, like bash/debian13.sh does for Debian. They
only change /tmp when tmp.mount is active, which 26.04 uses by default.
22.04 and 24.04 had no remediation for this rule, because the SUSE
shared.sh hides the template one.

The Ansible tasks now skip, instead of failing, when tmp.mount is not
active.
Replace the custom check and bash remediation of
service_update-notifier-motd_disabled with the service_disabled
template, as other service rules do. The template requires the unit to
be masked, which the rule title already asked for, and it adds an
Ansible remediation. The old check could not tell a masked unit from an
unmasked one, because the unit is static. Drop the warning that said so,
and the test that duplicates the template's own service_disabled.pass.sh.
The other "Ensure latest version of ... is installed" controls in the
Ubuntu and Debian CIS profiles select package_installed template rules.
Do the same for package_cracklib-runtime_installed instead of a custom
SCE check and remediations, and note in the control that the rule
checks the package is installed, not that it is the latest version.
@openshift-ci openshift-ci Bot removed the needs-rebase Used by openshift-ci bot. label Nov 10, 2026
@realstuffie

Copy link
Copy Markdown
Contributor Author

Rebased onto master. The only conflict was with #15301 in the DHCP rules, resolved by keeping both ubuntu2204 and ubuntu2604.

@israel-villar

Copy link
Copy Markdown
Contributor

@realstuffie Thanks for moving those rules to #15326, and for keeping file_permissions_pam_motd from #15275.

I'll take the Debian 13 PR once #15326 merges: OVAL for the five SCE-only checks, multi_platform_debian for the fixes, and tests on a Debian 13 VM.

About SCE on debian13: SCE checks need oscap-run-sce-script to run with /tmp mounted noexec (that's how OpenSCAP/openscap#2243 was solved on Debian 13). In trixie (Debian 13), with openscap 1.4.2+dfsg-1, it's only in the openscap-utils package, which openscap-scanner doesn't depend on. Debian moved it to openscap-common in 1.4.5+dfsg-1 (Debian bug #1149128), which is only in unstable and testing (forky). So on Debian 13 with /tmp mounted noexec and just the scanner installed, SCE-only rules end in error.

Also, Debian products aren't in the SSG_SCE_ENABLED list in CMakeLists.txt, so ./build_product debian13 produces no SCE checks. To include SCE checks in the debian13 data stream, we'd either have to ask for the Debian products to be added to that list, as the Ubuntu ones are, or build debian13 with -DSSG_SCE_ENABLED=ON (for example ADDITIONAL_CMAKE_OPTIONS="-DSSG_SCE_ENABLED=ON" ./build_product debian13). Maybe these complications are why Debian rules have relied on OVAL so far.

For the two ownership rules, OVAL can't match each directory to its own user. It isn't a full match for CIS, but the closest OVAL can get is what accounts_user_dot_user_ownership and accounts_users_home_files_ownership already do: check that the directories are owned by an interactive user, and say so in the description.

@realstuffie

realstuffie commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks, sounds good to me.

Two thoughts for the Debian PR. The five checks: if the OVAL fully covers them, I’d drop the SCE scripts so Ubuntu and Debian run the same check. For the two ownership rules, could the OVAL go in oval/debian.xml rather than shared.xml? Then Ubuntu keeps the exact per-user SCE check, and only Debian uses the interactive-user approximation, noted in the description, as you suggest.

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

CIS CIS Benchmark related. Highlight This PR/Issue should make it to the featured changelog. needs-ok-to-test Used by openshift-ci bot. New Profile Issues or pull requests related to new Profiles. Ubuntu Ubuntu product related.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants