- Restructured from 6 sections to 7 sections to align with CIS v3.0.0
- Section 3.4 (Firewalls) moved to new Section 4 (Host Based Firewall)
- Section 4 (Access/Auth) moved to Section 5 (Access Control)
- Section 5 (Logging/Auditing) moved to Section 6 (Logging and Auditing)
- Section 6 (System Maintenance) moved to Section 7 (System Maintenance)
- AIDE controls moved from Section 1.2 to Section 6.1
- Cron/At controls moved from Section 4.1 to Section 2.4
- Automounting moved from Section 1.1.9 to Section 2.1.1
- 1.1.1.6 (overlay kernel module), 1.1.1.10 (unused filesystem modules)
- 2.1.12 (rpcbind), 2.1.16 (tftp), 2.1.19 (xinetd), 2.4.1.7 (cron.yearly)
- 4.1.1 (single firewall utility), 4.2.2 (nftables not with ufw)
- 5.1.9 (SSH GSSAPIAuthentication)
- 5.3.1.1-5.3.1.3 (PAM packages), 5.3.2.1, 5.3.2.4 (PAM modules)
- 5.3.3.1.3, 5.3.3.2.3, 5.3.3.2.5, 5.3.3.2.7, 5.3.3.2.8 (PAM quality/faillock)
- 5.3.3.3.2, 5.3.3.3.3 (PAM pwhistory), 5.3.3.4.1, 5.3.3.4.2, 5.3.3.4.4 (PAM unix)
- 5.4.2.2-5.4.2.4, 5.4.2.8 (root/system accounts)
- 6.2.3.8 (logrotate), 6.3.2.4 (audit log space warning)
- 1.4.3 (single user mode auth - removed from benchmark)
- 2.1.4.1-2.1.4.4 (NTP controls - deprecated, chrony/systemd-timesyncd only)
- 4.2.12 (SSH X11 forwarding - absorbed into DisableForwarding)
- 4.5.3 (root default group - absorbed into 5.4.2.x)
- Fixed ~85 ansible-lint issues: key-order, command-instead-of-shell, yaml comment indentation, jinja spacing, name casing, risky-file-permissions
- Fixed yamllint 2-space indentation throughout the playbook
- Capitalized all handler names and notify references for ansible-lint
name[casing]compliance - Converted all single-item
when:lists to inline format across 27 task files (204 occurrences) - Converted all single-item
tags:lists to inline format across 4 task files (23 occurrences) - Task key order normalized:
name → when → tags → block/module
- Fixed duplicate keys in
ansible_vars_goss.yml.j2that caused goss "unable to determine format" error (Go YAML parser rejects duplicates)- Removed duplicate
ubtu20cis_grub_user,ubtu20cis_nis_server,ubtu20cis_samba_server,ubtu20cis_snmp_server
- Removed duplicate
- Renamed
ubtu20cis_time_servicetoubtu20cis_time_sync_toolin template to match goss test variable references - Added missing audit variables to template and
defaults/main.yml:ubtu20cis_bluetooth_service,ubtu20cis_bluetooth_mask(goss test 3.1.3)ubtu20cis_ftp_client(goss test 2.2.6)ubtu20cis_ipv6_disable(goss test 3.1.1)ubtu20cis_remote_log_server(goss tests 6.2.3.6/6.2.3.7)
- Added
---YAML document marker to template - Quoted string values containing YAML special characters
- Standardized all
register:variable prefixes to follow Lockdown conventions:prelim_prefix for all preliminary/discovery variables intasks/prelim.yml(17 variables renamed)discovered_prefix for all section task registered variables (63 variables renamed)
- Renamed prelim
set_factvariables:mount_names→prelim_mount_names, etc. - Removed inconsistent prefixes (
ubtu20cis_,avahi_,snap_, bare names) from registered variables - Updated all cross-file references in defaults, templates, and section tasks
- Added missing Warn Count blocks (
import_tasks: warning_facts.yml+vars: warn_control_id) to 34 manual remediation tasks across 11 files - Fixed
vars: warn_control_idplacement from block-level to task-level (same indentation asansible.builtin.import_tasks:) in 20+ tasks
- Validated all 313 task titles against CIS v3.0.0 benchmark JSON — fixed 150+ stale v2 titles
- Fixed 8 critical logic bugs where tasks implemented the wrong control:
- 2.2.6: Changed from RPC removal (rpcbind) to FTP client removal (ftp) per v3.0.0
- 7.1.10: Fixed file paths from /etc/opasswd to /etc/security/opasswd (4 references)
- 7.2.6: Changed from duplicate username check to duplicate GID check per v3.0.0
- 1.1.2.1.3/1.1.2.1.4: Fixed nosuid/noexec swap in both task logic and systemd template
- 5.3.3.2.7/5.3.3.2.8: Fixed swapped PAM quality titles
- 5.4.2.6: Fixed title to "Ensure root user umask is configured"
- 5.4.2.8: Replaced manual stub with actual logic to find and lock accounts without valid login shells
- 3.1.1: Changed from active IPv6 disable to audit-first approach per v3.0.0
- Fixed remediation 2.4.1.7: replaced manual stub with proper file permission task for /etc/cron.yearly
- Fixed bridge template (
ansible_vars_goss.yml.j2):ubtu20cis_telnet_serverandubtu20cis_telnet_maskwere both mapped fromubtu20cis_telnet_requiredinstead of their actual defaults variables - Fixed bridge template:
ubtu20_varlog_locationrenamed toubtu20cis_varlog_locationto match auditvars/CIS.yml - Added missing
ubtu20cis_remote_log_host,ubtu20cis_remote_log_port,ubtu20cis_remote_log_protocolto defaults/main.yml and bridge template (goss test 6.2.3.6 references these) - Added missing
ubtu20cis_ipv4_requiredto bridge template (was in defaults but not passed to audit)
- Added
ubtu20cis_purge_aptvariable (defaultfalse) to control apt package purging; applied to prelink (1.5.4) and apport (1.5.5) removal tasks - Fixed handler key ordering in
Grub update: movednotifyafterchanged_when/failed_whenper Lockdown conventions - Removed duplicate 1.1.2.2.1 debug stub —
/dev/shmpartition check is already handled in the combined 1.1.2.2.x mount options task - Fixed 1.5.4 prelink: added
when: 'prelink' in ansible_facts.packagesguard onprelink -uacommand; renamed task for clarity - Fixed 2.1.2 avahi: improved task name to specify Stop/Disable action
- Fixed 3.1.1/3.1.2: corrected task name prefixes from
PATCHtoAUDITon read-only commands - Fixed 4.2.x: added inline documentation comments for optional UFW incoming ports task
- Fixed 4.4.2.4: removed
notify: Persistent ip4tablesfrom 3 audit-only tasks wherechanged_when: falseprevented the handler from ever firing - Fixed 5.2.7: removed unused
register: discovered_sugroupfrom group creation task - Fixed 5.4.3.2 TMOUT: replaced deprecated
destparameter withpathinblockinfilemodule; reordered keys to match Lockdown conventions - Fixed 7.1.10: corrected task name prefixes from
PATCHtoAUDITon stat tasks - Fixed 7.1.11/7.1.12: reordered
changed_when/failed_when/check_mode/registerto appear beforewith_items/loop_controlper Lockdown conventions - Fixed 7.2.4: gave all three shadow group tasks unique names instead of duplicate "check users in group"
- Fixed 5.4.2.6: rewrote root umask task — removed pam_umask.so/login.defs/USERGROUPS_ENAB approach, replaced with direct lineinfile to
/root/.bash_profileand/root/.bashrc; added missingautomatedtag; fixedubtu20cis_bash_umaskdefault from027to0027 - Fixed 5.4.2.4: added
whenguard on assert so it doesn't fail when root password isn't set in containers - Fixed 5.4.2.8: added missing
Alert on findingsdebug task before Warn Count - Fixed 2.4.1.9: changed
/etc/cron.allowgroup fromcrontabtorootto match CIS benchmark and goss expectation - Added
deny_usersanddeny_groupstoubtu20cis_sshddefaults (5.1.4 sshd access) - Enabled goss audit in molecule converge (
setup_audit: true,run_audit: true) - Standardized warn count task naming across all sections:
- Debug/alert tasks →
"Alert on findings"suffix (was inconsistent: "Absent", "Output Warning", "Message out...", "Print warning about...") - Warn count tasks →
"Warn Count"suffix (was: "warn_count", "Set warning count", "Capture Warning") - Discovery tasks →
"Discover status"suffix - Applied to 13 task files across sections 1–7
- Debug/alert tasks →
- Fixed 4.2.7: added
whencondition so alert only fires when open ports exist - Fixed 6.2.1.1: added missing
Alert on findingsdebug for journald service status - Fixed 7.2.5: reordered alert debug before warn count import (was warn count before alert)
- Fixed 4.3.3–4.3.10: added
"Alert on findings"suffix to all 8 nftables audit debug tasks - Migrated all
mode:directives from=(absolute) to-(relative) symbolic notation to align with UBUNTU22-CIS conventions (~100 directives across 18 task files). Mapping:u=rw,g=,o=→go-rwx,u=rw,g=r,o=r→u-x,go-wx,u=rwx,g=,o=→u+rwx,go-rwx,u=rwx,g=rx,o=rx→go-w,u=rw,g=r,o=→u-x,g-wx,o-rwx,u=rwx,g=rx,o=→g-w,o-rwx. Also standardized 2 mixed-notation entries incis_2.3.x.yml
- Added missing "Update dconf" handler (14 task references)
- Fixed "Restart timeservice" handler case mismatch
- Added missing when: clause on journald file permissions rule
- Fixed wrong task ID 5.2.3.10 → 5.2.3.20 (audit immutable config)
- Added no_log: true to password task
- Added changed_when to 5 shell/command tasks
- Fixed meta description typo "benmarks" → "benchmarks"
- Removed unused handlers (reload gdm3, reload gdm, persistent ip6tables)
- Created 6 missing dconf templates
- Removed 2 orphaned templates (chrony.conf.j2, ntp.conf.j2)
- Added *.vault, *.pem, *.key to .gitignore
- Complete rewrite of defaults/main.yml with v3.0.0 variable names
- All 313 control toggle variables renamed to v3.0.0 numbering
- Added ubtu20cis_section7_patch toggle
- benchmark_version updated to v3.0.0
- Replaced all scored/not_scored tags with automated/manual
- Updated all rule_X.Y.Z tags to v3.0.0 IDs
- Updated level tags for controls that changed profile
- Faillock (5.3.3.1.1-5.3.3.1.3): Configure deny, unlock_time, and root lockout via lineinfile on
/etc/security/faillock.confwith cleanup of pam-config files - PWQuality (5.3.3.2.1-5.3.3.2.8): Template-based drop-in files in
/etc/security/pwquality.conf.d/with replace to clean duplicate settings from other locations - PWHistory (5.3.3.3.1-5.3.3.3.3): Shell audit + lineinfile with backrefs for remember, enforce_for_root, and use_authtok on pam_pwhistory.so
- PAM Unix (5.3.3.4.1-5.3.3.4.2, 5.3.3.4.4): Shell audit + replace to remove nullok and remember from pam_unix.so; lineinfile for use_authtok
- Updated existing tasks 5.3.3.2.1, 5.3.3.2.2, 5.3.3.2.4, 5.3.3.2.6 from legacy
ubtu20cis_pass.*variables to newubtu20cis_passwd_*variables - Created 8 pwquality drop-in template files in
templates/etc/security/pwquality.conf.d/ - Added 3 prelim tasks:
prelim_pam_conf_files,prelim_pam_pwquality_confsdiscovery, andpwquality.conf.ddirectory creation - Wired up all 25 previously-unused PAM/password defaults variables to tasks and templates
- Fixed
register:ordering in 6 tasks (prelim.yml, cis_5.3.x.yml, handlers/main.yml) —register:now appears afterchanged_when:/failed_when:per Lockdown conventions - Fixed
ubtu20cis_journald_uploadtypo incis_6.2.x.yml:102— should beubtu20cis_journal_upload(would cause undefined variable error at runtime) - Fixed: added
prelim_tmp_mnt_type.stdout is definedguard on tmp mount type conditional to prevent attribute error whenset_factoverwrites command result with string - Moved all inline comments in
defaults/main.ymlto the line above their variable (~70 occurrences) for consistency —pragma: allowlist secretdirectives kept inline - Renamed
prelim_min_int_gidtomin_int_gidin prelim set_facts to matchmin_int_uid/max_int_uidnaming convention
- 1.1.1.6: Overlay kernel module — lineinfile to
/etc/modprobe.d/CIS.conf+ blacklist + modprobe disable (was manual) - 1.1.1.10: Unused filesystem modules — deploys
fs_with_cves.shdiscovery script, warns on loaded modules (was manual) - 1.1.2.2.2:
/dev/shmnodev option — merged into combined 1.1.2.2.x mount options task withansible.posix.mount(was manual) - 2.1.12: rpcbind — package removal or systemd mask via
ubtu20cis_rpc_server/ubtu20cis_rpc_maskvariables (was manual) - 2.1.16: tftp server — package removal or systemd mask via
ubtu20cis_tftp_server/ubtu20cis_tftp_maskvariables (was manual) - 2.1.19: xinetd — package removal or systemd mask via
ubtu20cis_xinetd_server/ubtu20cis_xinetd_maskvariables (was manual) - 4.1.1: Firewall utility — installs ufw package (was manual)
- 5.1.9: SSH GSSAPIAuthentication — lineinfile to set
GSSAPIAuthentication noin sshd_config (was manual) - 5.3.1.1: PAM runtime — installs latest
libpam-runtimepackage (was manual) - 5.3.1.2: PAM modules — installs latest
libpam-modulespackage (was manual) - 5.4.2.1: UID 0 accounts —
passwd -lto lock non-root UID 0 accounts (was manual) - 5.4.2.2: GID 0 accounts — discovers and removes non-root accounts from GID 0 (was manual)
- 5.4.2.5: Root PATH integrity — stat/file module to audit and fix directory ownership and permissions (was manual)
- Added
Systemd daemon reloadhandler for service masking tasks - Added prelim tasks:
prelim_uid_zero_accounts_except_root,prelim_interactive_usernames - Added
files/fs_with_cves.shdiscovery script for unused filesystem modules - Added
failed_when: falseto pwquality replace tasks (5.3.3.2.1-2.7) for missing file resilience - 4.2.2: nftables removal —
package_factscheck +ansible.builtin.packageto remove nftables when ufw is the chosen firewall (was manual) - 5.4.2.3: GID 0 group audit — shell discovers non-root groups with GID 0 and warns with group names (was manual stub with no audit logic)
- 5.4.2.4: Root access controlled —
passwd -S rootcheck with assert to verify root password is set (was manual) - 6.2.3.8: Logrotate configuration —
ansible.builtin.find+ansible.builtin.replaceto set rotation frequency in/etc/logrotate.confand all/etc/logrotate.d/drop-in files using newubtu20cis_logrotatevariable (was manual) - 6.3.2.4: Audit log space warning — lineinfile to set
space_left_actionandadmin_space_left_actionin/etc/audit/auditd.confusing existingubtu20cis_auditdvariables, notifiesRestart auditd(was manual)
- Fixed FQCN connection detection: added
community.docker.dockertoansible_connectionchecks intasks/main.yml(container detection block) andtasks/section_1/cis_1.1.1.x.yml(8 modprobe skip conditions). Molecule uses the FQCNcommunity.docker.dockerconnection plugin, not the short namedocker - Fixed
molecule/default/molecule.yml: changed cgroup volume mount from:roto:rwand addedcgroupns_mode: hostfor full systemd support on cgroups v2 hosts (macOS Docker Desktop) - Created
molecule/default/prepare.yml: installsopenssh-server,libpam-pwquality,sudo,acl,kmod,cron,chrony,rsyslog,aide,aide-common,logrotate; creates/run/sshd; starts cron and rsyslog services - Simplified
molecule/default/converge.yml: removed redundant rule overrides now handled byvars/is_container.ymlauto-loading - Updated
vars/is_container.yml:- Added
ubtu20cis_rule_1_3_1_2: false(AppArmor bootloader config — no grub in containers) - Added
ubtu20cis_ipv6_disable: sysctl(use sysctl instead of grub method) - Added
ubtu20cis_rule_6_3_4_1through6_3_4_10: false(audit log file permissions — no auditd) - Re-enabled AIDE (6.1.x), cron (2.4.x), time sync (2.3.x), rsyslog (6.2.3.x), and logging (6.2.1.x) rules — now functional with prepare.yml packages and working systemd
- Added
- Fixed cron idempotence: added
changed_when: falseto 7 cron access tasks (2.4.1.2–2.4.1.8) that usedstate: touchwhich always reports changed
- issue 148 thanks to @karlg100
- workflow updates for new pipeline
- audit
- updated files and variables
- updated vars/audit.yml
- improved when using local copies or archived
- ability to run goss audit only audit_only variable
- audit vars mainly moved to var/audit.yml
- several control updates
- goss version update to 0.4.4
-
v2.0.1 - refer to change history from official CIS pdf.
- ReWrite of many rules
- Ordering and numbering of rules
- many title updates
-
timesync options increased
- default systemd-timesyncd
- chrony options updated
-
idempotency improvements
-
new discoveries
- interactive users
- uid min value
- is_container discovery and default var
-
pre-commit added to setup
-
README new layout
-
Added test for rule 4.3.4 check user is using sudo has password set before NOPASSWD removed from sudoers
-
grub password check update thanks to @Acenl12 on discord
thanks to ikthomas #84
- update galaxy lint requirements
- license file
- ansible version
- Addressed Bugs
- Added Fixes For Outstanding PR's
- #81 - Thanks @kdebisschop
- Fixed Linting Errors For Yamllint & Ansible-Lint
- Adjusted Builtin to Posix For sysctl module.
- lint files updated
- ansible version updated
- Lots of lint and standardisation changes
- fqcn
- Assertions for root and grub passwords
- Import tasks to allow tags to be used
- Warnings made standard
- warn count feature added
- workflow updates
- wireless interface discovery
- idempotency checks and updates
reboot variable changed from ubtu20_skip_reboot to skip_reboot (still default true)
-
#1 set bootloader pwd - Allowed unrestricted by default but set new variables
- Added extra variable options ubtu20cis_set_grub_password and ubtu20cis_set_root_password (defaults true)
-
#2 Ensure locks for failed attempts
-
#3 root path integrity
-
thanks to @vbotka
- #63 parse_etc_password
-
thanks to @makefu
- #67 UFW incoming firewall ports (optional)
-
thanks to @CFoltin
- #68 logrotate alignment
- #69 stop rule overwrite UFW
-
thanks to @hackery
- #70 TMOUT stops being repeated
Many improvements on multiple controls Remediate and audit version now match. When using remediate will pull in latest version of audit for that release.
- updated goss version used
- aligned new variables with audit
- audit path used now default to /opt from /var/tmp