diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8af784ff..3014d8aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: # Safety - id: detect-aws-credentials diff --git a/Changelog.md b/Changelog.md index 0fe314ff..04969f0f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,21 @@ # Changes to rhel9CIS +## 1.1.5 - Based on CIS v1.0.0 + +- added new interactive user discoveries + - updated controls 6.2.10-6.2.14 +- audit + - steps moved to prelim + - update to coipy and archive logic and variables +- removed vars not used +- updated quotes used in mode tasks +- pre-commit update +- issues addressed + - #190 thanks to @ipruteanu-sie + - aligned logic for user shadow suite params (aligned with other repos) + - new variables to force changes to existing users added 5.6.1.1 - 5.6.1.2 + - #198 thanks to @brakkio86 + ## 1.1.4 - Based on CIS v1.0.0 - 1.2.1 new option for a new system to import gpg key for 1.2.1 to pass redhat only diff --git a/defaults/main.yml b/defaults/main.yml index f5838c0a..d48728a6 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -67,74 +67,56 @@ skip_reboot: true # default value will change to true but wont reboot if not enabled but will error change_requires_reboot: false -########################################## +########################################### ### Goss is required on the remote host ### -## Refer to vars/auditd.yml for any other settings ## -#### Basic external goss audit enablement settings #### -#### Precise details - per setting can be found at the bottom of this file #### - -## Audit setup -# Audits are carried out using Goss. This variable -# determines whether execution of the role prepares for auditing -# by installing the required binary. +### vars/auditd.yml for other settings ### + +# Allow audit to setup the requirements including installing git (if option chosen and downloading and adding goss binary to system) setup_audit: false -## Enable audits to run - this runs the audit and get the latest content -# This variable governs whether the audit using the -# separately maintained audit role using Goss -# is carried out. +# enable audits to run - this runs the audit and get the latest content run_audit: false +# Run heavy tests - some tests can have more impact on a system enabling these can have greater impact on a system +audit_run_heavy_tests: true -# Only run Audit do not remediate +## Only run Audit do not remediate audit_only: false -# This will enable files to be copied back to control node(part of audit_only) +### As part of audit_only ### +# This will enable files to be copied back to control node in audit_only mode fetch_audit_files: false -# Path to copy the files to will create dir structure(part of audit_only) +# Path to copy the files to will create dir structure in audit_only mode audit_capture_files_dir: /some/location to copy to on control node +############################# -## How to retrieve audit binary(Goss) -# Options are 'copy' or 'download' - detailed settings at the bottom of this file -# - if 'copy': -# - the filepath mentioned via the below 'audit_bin_copy_location' var will be used to access already downloaded Goss -# - if 'download': -# - the GitHub Goss-releases URL will be used for a fresh-download, via 'audit_bin_url' and 'audit_pkg_arch_name' vars +# How to retrieve audit binary +# Options are copy or download - detailed settings at the bottom of this file +# you will need to access to either github or the file already dowmloaded get_audit_binary_method: download -## if get_audit_binary_method is 'copy', the following var needs to be updated for your environment +## if get_audit_binary_method - copy the following needs to be updated for your environment ## it is expected that it will be copied from somewhere accessible to the control node ## e.g copy from ansible control node to remote host audit_bin_copy_location: /some/accessible/path -## How to retrieve the audit role -# The role for auditing is maintained separately. -# This variable specifies the method of how to get the audit role -# options are git/copy/get_url other e.g. if you wish to run from already downloaded conf -# onto the system. The options are as follows: -# - 'git': clone audit content from GitHub REPOSITORY, set up via `audit_file_git` var, and -# VERSION(e.g. branch, tag name), set up via `audit_git_version` var. -# - 'copy': copy from path as specified in variable `audit_conf_copy`. -# - 'archive': same as 'copy', only that the specified filepath needs to be unpacked. -# - 'get_url': Download from url as specified in variable `audit_files_url` +# how to get audit files onto host options +# options are git/copy/archive/get_url other e.g. if you wish to run from already downloaded conf audit_content: git -# This variable(only used when 'audit_content' is 'copy' or 'archive') should -# contain the filepath with audit-content to be copied/unarchived on server: -audit_conf_copy: "some path to copy from" +# If using either archive, copy, get_url: +## Note will work with .tar files - zip will require extra configuration +### If using get_url this is expecting github url in tar.gz format e.g. +### https://github.com/ansible-lockdown/UBUNTU22-CIS-Audit/archive/refs/heads/benchmark-v1.0.0.tar.gz +audit_conf_source: "some path or url to copy from" -# This variable(only used when 'audit_content' is 'get_url') should -# contain the URL from where the audit-content must be downloaded on server: -audit_files_url: "some url maybe s3?" - -# Run heavy tests - some tests can have more impact on a system enabling these can have greater impact on a system -audit_run_heavy_tests: true +# Destination for the audit content to be placed on managed node +# note may not need full path e.g. /opt with the directory being the {{ benchmark }}-Audit directory +audit_conf_dest: "/opt" -# Timeout for those cmds that take longer to run where timeout set -# This variable specifies the timeout (in ms) for audit commands that -# take a very long time: if a command takes too long to complete, -# it will be forcefully terminated after the specified duration. -audit_cmd_timeout: 120000 +# Where the audit logs are stored +audit_log_dir: '/opt' -### End Goss enablements #### +### Goss Settings ## +####### END ######## # These variables correspond with the CIS rule IDs or paragraph numbers defined in # the CIS benchmark documents. @@ -171,10 +153,6 @@ rhel9cis_rule_1_1_8_1: true rhel9cis_rule_1_1_8_2: true rhel9cis_rule_1_1_8_3: true rhel9cis_rule_1_1_8_4: true -rhel9cis_rule_1_1_18: true -rhel9cis_rule_1_1_19: true -rhel9cis_rule_1_1_20: true -rhel9cis_rule_1_1_21: true rhel9cis_rule_1_1_9: true rhel9cis_rule_1_2_1: true rhel9cis_rule_1_2_2: true @@ -371,7 +349,6 @@ rhel9cis_rule_5_5_1: true rhel9cis_rule_5_5_2: true rhel9cis_rule_5_5_3: true rhel9cis_rule_5_5_4: true -rhel9cis_rule_5_5_5: true rhel9cis_rule_5_6_1_1: true rhel9cis_rule_5_6_1_2: true rhel9cis_rule_5_6_1_3: true @@ -821,7 +798,7 @@ rhel9cis_auditd: max_log_file: 10 # This variable determines what action the audit system should take when the maximum # size of a log file is reached. - # The options for setting this variable are as follows: + # The options for setting this variable are as follows: # - `ignore`: the system does nothing when the size of a log file is full; # - `syslog`: a message is sent to the system log indicating the problem; # - `suspend`: the system suspends recording audit events until the log file is cleared or rotated; @@ -837,14 +814,12 @@ rhel9cis_auditd_extra_conf_usage: false # Example: # rhel9cis_auditd_extra_conf: # admin_space_left: '10%' + +# These variables governs the threshold(MegaBytes) under which the audit daemon should perform a +# specific action to alert that the system is running low on disk space. rhel9cis_auditd_extra_conf: - # This variable governs the threshold(MegaBytes) under which the audit daemon should perform a - # specific action to alert that the system is running low on disk space. Must be lower than - # the 'space_left' variable. + # Must be lower than the 'space_left' variable. admin_space_left: 50 - # This variable governs the threshold(MegaBytes) under which the audit daemon should perform a - # specific action to alert that the system is running low on disk space(last chance to do something - # before running out of disk space). Must be lower than the 'space_left' variable. space_left: 75 ## Control 4.1.1.4 - Ensure rhel9cis_audit_back_log_limit is sufficient @@ -855,12 +830,6 @@ rhel9cis_auditd_extra_conf: # This variable should be set to a sufficient value. The CIS baseline recommends at least `8192` as value. rhel9cis_audit_back_log_limit: 8192 -## Control 4.1.2.1 - Ensure audit log storage size is configured -# This variable specifies the maximum size in MB that an audit log file can reach -# before it is archived or deleted to make space for the new audit data. -# This should be set based on your sites policy. CIS does not provide a specific value. -rhel9cis_max_log_file_size: 10 - ## Control 4.1.3.x - Audit template # This variable governs if the auditd logic should be executed(if value is true). # NOTE: The current default value is likely to be overriden(via 'set_fact') by other further tasks(in sub-section 'Auditd rules'). @@ -1015,30 +984,22 @@ rhel9cis_sshd: # access for users whose user name matches one of the patterns. This is done # by setting the value of `AllowUsers` option in `/etc/ssh/sshd_config` file. # If an USER@HOST format will be used, the specified user will be allowed only on that particular host. - # The allow/deny directives process order: DenyUsers, AllowUsers, DenyGroups, AllowGroups. - # For more info, see https://linux.die.net/man/5/sshd_config # allowusers: "" # (String) This variable, if specified, configures a list of GROUP name patterns, separated by spaces, to allow SSH access # for users whose primary group or supplementary group list matches one of the patterns. This is done # by setting the value of `AllowGroups` option in `/etc/ssh/sshd_config` file. - # The allow/deny directives process order: DenyUsers, AllowUsers, DenyGroups, AllowGroups. - # For more info, https://linux.die.net/man/5/sshd_config # allowgroups: "wheel" # This variable, if specified, configures a list of USER name patterns, separated by spaces, to prevent SSH access # for users whose user name matches one of the patterns. This is done # by setting the value of `DenyUsers` option in `/etc/ssh/sshd_config` file. # If an USER@HOST format will be used, the specified user will be restricted only on that particular host. - # The allow/deny directives process order: DenyUsers, AllowUsers, DenyGroups, AllowGroups. - # For more info, see https://linux.die.net/man/5/sshd_config denyusers: "nobody" - # This variable, if specified, configures a list of GROUP name patterns, separated by spaces, to prevent SSH access - # for users whose primary group or supplementary group list matches one of the patterns. This is done + # This variable, if specified, configures a list of GROUP name patterns, separated by spaces, + # to prevent SSH access for users whose primary group or supplementary group list matches one of the patterns. This is done # by setting the value of `DenyGroups` option in `/etc/ssh/sshd_config` file. - # The allow/deny directives process order: DenyUsers, AllowUsers, DenyGroups, AllowGroups. - # For more info, see https://linux.die.net/man/5/sshd_config denygroups: "" ## Control 5.2.5 - Ensure SSH LogLevel is appropriate @@ -1088,21 +1049,6 @@ rhel9cis_authselect_custom_profile_create: false # to the PAM templates and meta files in the original profile will be reflected in your custom profile, too.) rhel9cis_authselect_custom_profile_select: false -## Section 5.6.1.x: Shadow Password Suite Parameters -rhel9cis_pass: - ## Control 5.6.1.1 - Ensure password expiration is 365 days or less - # This variable governs after how many days a password expires. - # CIS requires a value of 365 or less. - max_days: 365 - ## Control 5.6.1.2 - Ensure minimum days between password changes is 7 or more - # This variable specifies the minimum number of days allowed between changing - # passwords. CIS requires a value of at least 1. - min_days: 7 - ## Control 5.6.1.3 - Ensure password expiration warning days is 7 or more - # This variable governs, how many days before a password expires, the user will be warned. - # CIS requires a value of at least 7. - warn_age: 7 - ## Control 5.5.1 - Ensure password creation requirements are configured - PAM rhel9cis_pam_password: # This variable sets the minimum chars a password needs to be set. @@ -1171,6 +1117,33 @@ rhel9cis_add_faillock_without_authselect: false # to 'true', in order to include the 'with-failock' option to the current authselect profile. rhel9cis_5_4_2_risks: NEVER +## Section 5.6.1.x: Shadow Password Suite Parameters +rhel9cis_pass: + ## Control 5.6.1.1 - Ensure password expiration is 365 days or less + # This variable governs after how many days a password expires. + # CIS requires a value of 365 or less. + max_days: 365 + ## Control 5.6.1.2 - Ensure minimum days between password changes is 7 or more + # This variable specifies the minimum number of days allowed between changing + # passwords. CIS requires a value of at least 1. + min_days: 7 + ## Control 5.6.1.3 - Ensure password expiration warning days is 7 or more + # This variable governs, how many days before a password expires, the user will be warned. + # CIS requires a value of at least 7. + warn_age: 7 + +## Allow the forcing of setting user_max_days for logins. +# This can break current connecting user access +rhel9cis_force_user_maxdays: false + +## Allow the force setting of minimum days between changing the password +# This can break current connecting user access +rhel9cis_force_user_mindays: false + +## Allow the forcing of of number of days before warning users of password expiry +# This can break current connecting user access +rhel9cis_force_user_warnage: false + ## Control 5.6.3 - Ensure default user shell timeout is 900 seconds or less # Session timeout setting file (TMOUT setting can be set in multiple files) # Timeout value is in seconds. (60 seconds * 10 = 600) diff --git a/tasks/main.yml b/tasks/main.yml index 114c806b..509ae273 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -151,23 +151,6 @@ - prelim_tasks - always -- name: Include audit specific variables - when: - - run_audit or audit_only - - setup_audit - tags: - - setup_audit - - run_audit - ansible.builtin.include_vars: audit.yml - -- name: Include pre-remediation audit tasks - when: - - run_audit or audit_only - - setup_audit - tags: - - run_audit - ansible.builtin.import_tasks: pre_remediation_audit.yml - - name: Run Section 1 tasks ansible.builtin.import_tasks: file: section_1/main.yml diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index 6bc50861..b3111c80 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,11 +1,11 @@ --- - name: Post Audit | Run post_remediation {{ benchmark }} audit - ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ post_audit_outfile }} -g \"{{ group_names }}\"" changed_when: true environment: AUDIT_BIN: "{{ audit_bin }}" - AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" AUDIT_FILE: goss.yml - name: Post Audit | ensure audit files readable by users @@ -22,13 +22,13 @@ - audit_format == "json" block: - name: capture data {{ post_audit_outfile }} - ansible.builtin.shell: cat {{ post_audit_outfile }} + ansible.builtin.shell: "cat {{ post_audit_outfile }}" register: post_audit changed_when: false - name: Capture post-audit result ansible.builtin.set_fact: - post_audit_summary: "{{ post_audit.stdout | from_json | community.general.json_query(summary) }}" + post_audit_summary: "{{ post_audit.stdout | from_json | json_query(summary) }}" vars: summary: summary."summary-line" @@ -37,7 +37,7 @@ - audit_format == "documentation" block: - name: Post Audit | capture data {{ post_audit_outfile }} - ansible.builtin.shell: tail -2 {{ post_audit_outfile }} + ansible.builtin.shell: "tail -2 {{ post_audit_outfile }}" register: post_audit changed_when: false diff --git a/tasks/pre_remediation_audit.yml b/tasks/pre_remediation_audit.yml index 158c053b..d0137e81 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -5,7 +5,8 @@ - setup_audit tags: - setup_audit - ansible.builtin.include_tasks: LE_audit_setup.yml + ansible.builtin.include_tasks: + file: LE_audit_setup.yml - name: Pre Audit Setup | Ensure {{ audit_conf_dir }} exists ansible.builtin.file: @@ -32,23 +33,25 @@ when: - audit_content == 'copy' ansible.builtin.copy: - src: "{{ audit_conf_copy }}" - dest: "{{ audit_conf_dir }}" + src: "{{ audit_conf_source }}" + dest: "{{ audit_conf_dest }}" mode: preserve - name: Pre Audit Setup | Unarchive audit content files on server when: - - audit_content == 'archived' + - audit_content == 'archive' ansible.builtin.unarchive: - src: "{{ audit_conf_copy }}" - dest: "{{ audit_conf_dir }}" + src: "{{ audit_conf_source }}" + dest: "{{ audit_conf_dest }}" - name: Pre Audit Setup | Get audit content from url when: - audit_content == 'get_url' - ansible.builtin.get_url: - url: "{{ audit_files_url }}" - dest: "{{ audit_conf_dir }}" + ansible.builtin.unarchive: + src: "{{ audit_conf_source }}" + dest: "{{ audit_conf_dest }}/{{ benchmark }}-Audit" + remote_src: "{{ ( audit_conf_source is contains ('http'))| ternary(true, false ) }}" + extra_opts: "{{ (audit_conf_source is contains ('github')) | ternary('--strip-components=1', [] ) }}" - name: Pre Audit Setup | Check Goss is available when: @@ -77,25 +80,25 @@ mode: '0600' - name: Pre Audit | Run pre_remediation {{ benchmark }} audit - ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ pre_audit_outfile }} -g \"{{ group_names }}\"" changed_when: true environment: AUDIT_BIN: "{{ audit_bin }}" - AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" AUDIT_FILE: goss.yml - name: Pre Audit | Capture audit data if json format when: - audit_format == "json" block: - - name: capture data {{ pre_audit_outfile }} - ansible.builtin.shell: cat {{ pre_audit_outfile }} + - name: Pre Audit | Capture data {{ pre_audit_outfile }} + ansible.builtin.shell: "cat {{ pre_audit_outfile }}" register: pre_audit changed_when: false - name: Pre Audit | Capture pre-audit result ansible.builtin.set_fact: - pre_audit_summary: "{{ pre_audit.stdout | from_json | community.general.json_query(summary) }}" + pre_audit_summary: "{{ pre_audit.stdout | from_json | json_query(summary) }}" vars: summary: summary."summary-line" @@ -103,8 +106,8 @@ when: - audit_format == "documentation" block: - - name: Pre Audit | capture data {{ pre_audit_outfile }} | documentation format - ansible.builtin.shell: tail -2 {{ pre_audit_outfile }} + - name: Pre Audit | Capture data {{ pre_audit_outfile }} | documentation format + ansible.builtin.shell: "tail -2 {{ pre_audit_outfile }}" register: pre_audit changed_when: false diff --git a/tasks/prelim.yml b/tasks/prelim.yml index f58ad014..6ffc298f 100644 --- a/tasks/prelim.yml +++ b/tasks/prelim.yml @@ -2,6 +2,54 @@ # Preliminary tasks that should always be run # List users in order to look files inside each home directory + +- name: PRELIM | Include audit specific variables + when: + - run_audit or audit_only + - setup_audit + tags: + - setup_audit + - run_audit + ansible.builtin.include_vars: audit.yml + +- name: PRELIM | Include pre-remediation audit tasks + when: + - run_audit or audit_only + - setup_audit + tags: + - run_audit + ansible.builtin.import_tasks: pre_remediation_audit.yml + +- name: "PRELIM | AUDIT | Interactive Users" + tags: + - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $1 }' + changed_when: false + register: discovered_interactive_usernames + +- name: "PRELIM | AUDIT | Interactive User accounts home directories" + tags: + - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $6 }' + changed_when: false + register: discovered_interactive_users_home + +- name: "PRELIM | AUDIT | Interactive UIDs" + tags: + - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $3 }' + changed_when: false + register: discovered_interactive_uids + +- name: "PRELIM | capture /etc/password variables" + ansible.builtin.include_tasks: + file: parse_etc_password.yml + tags: + - always + - name: "PRELIM | List users accounts" ansible.builtin.shell: "awk -F: '{print $1}' /etc/passwd" changed_when: false @@ -12,25 +60,6 @@ - level1-workstation - users -- name: "PRELIM | capture /etc/password variables" - ansible.builtin.include_tasks: parse_etc_password.yml - tags: - - rule_5.5.2 - - rule_5.6.2 - - rule_6.2.9 - - rule_6.2.10 - - rule_6.2.11 - - rhel9cis_section5 - - rhel9cis_section6 - - level1-server - -- name: "PRELIM | Interactive User accounts" - ansible.builtin.shell: 'cat /etc/passwd | grep -Ev "nologin|/sbin" | cut -d: -f6' - changed_when: false - register: interactive_users_home - tags: - - always - - name: "PRELIM | Gather accounts with empty password fields" ansible.builtin.shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" changed_when: false diff --git a/tasks/section_1/cis_1.3.x.yml b/tasks/section_1/cis_1.3.x.yml index 3010b5aa..fa2d6a52 100644 --- a/tasks/section_1/cis_1.3.x.yml +++ b/tasks/section_1/cis_1.3.x.yml @@ -9,14 +9,14 @@ - name: "1.3.1 | PATCH | Ensure AIDE is installed | Build AIDE DB" ansible.builtin.shell: /usr/sbin/aide --init - changed_when: false - failed_when: false - async: 45 - poll: 0 args: creates: /var/lib/aide/aide.db.new.gz when: not ansible_check_mode + - name: "1.3.1 | PATCH | Ensure AIDE is installed | Wait for file before continuing" + ansible.builtin.wait_for: + path: /var/lib/aide/aide.db.new.gz + - name: "1.3.1 | PATCH | Ensure AIDE is installed | copy AIDE DB" ansible.builtin.copy: src: /var/lib/aide/aide.db.new.gz @@ -59,12 +59,12 @@ path: /etc/aide.conf marker: "# {mark} Audit tools - CIS benchmark - Ansible-lockdown" block: | - /sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 - /sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 - /sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 - /sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 - /sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 - /sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 + /usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 validate: aide -D --config %s when: - rhel9cis_rule_1_3_3 diff --git a/tasks/section_3/cis_3.1.x.yml b/tasks/section_3/cis_3.1.x.yml index 2a135745..3f938582 100644 --- a/tasks/section_3/cis_3.1.x.yml +++ b/tasks/section_3/cis_3.1.x.yml @@ -60,7 +60,7 @@ ansible.builtin.template: src: "etc/modprobe.d/modprobe.conf.j2" dest: "/etc/modprobe.d/{{ item }}.conf" - mode: "0600" + mode: '0600' owner: root group: root loop: diff --git a/tasks/section_5/cis_5.6.1.x.yml b/tasks/section_5/cis_5.6.1.x.yml index 8d082bc9..6ad3dc06 100644 --- a/tasks/section_5/cis_5.6.1.x.yml +++ b/tasks/section_5/cis_5.6.1.x.yml @@ -1,10 +1,28 @@ --- - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_MAX_DAYS' - line: "PASS_MAX_DAYS {{ rhel9cis_pass['max_days'] }}" + block: + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MAX_DAYS' + line: "PASS_MAX_DAYS {{ rhel9cis_pass['max_days'] }}" + + - name: "5.6.1.1 | AUDIT | Ensure password expiration is 365 days or less | Get existing users PASS_MAX_DAYS" + ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($5> {{ rhel9cis_pass['max_days'] }} || $5< {{ rhel9cis_pass['max_days'] }} || $5 == -1)){print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_max_days + + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | Set existing users PASS_MAX_DAYS" + ansible.builtin.user: + name: "{{ item }}" + password_expire_max: "{{ rhel9cis_pass['max_days'] }}" + loop: "{{ discovered_max_days.stdout_lines }}" + when: + - discovered_max_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - rhel9cis_force_user_maxdays when: - rhel9cis_rule_5_6_1_1 tags: @@ -15,10 +33,28 @@ - rule_5.6.1.1 - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_MIN_DAYS' - line: "PASS_MIN_DAYS {{ rhel9cis_pass['min_days'] }}" + block: + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is configured | set login.defs" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MIN_DAYS' + line: "PASS_MIN_DAYS {{ rhel9cis_pass['min_days'] }}" + + - name: "5.6.1.2 | AUDIT | Ensure minimum days between password changes is configured | Get existing users PASS_MIN_DAYS" + ansible.builtin.shell: "awk -F: '/^[^:]+:[^!*]/ && $4< {{ rhel9cis_pass['min_days'] }} {print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_min_days + + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is configured | Set existing users PASS_MIN_DAYS" + ansible.builtin.user: + name: "{{ item }}" + password_expire_max: "{{ rhel9cis_pass['min_days'] }}" + loop: "{{ discovered_min_days.stdout_lines }}" + when: + - discovered_min_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - rhel9cis_force_user_mindays when: - rhel9cis_rule_5_6_1_2 tags: @@ -29,10 +65,26 @@ - rule_5.6.1.2 - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_WARN_AGE' - line: "PASS_WARN_AGE {{ rhel9cis_pass['warn_age'] }}" + block: + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | set login.defs" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_WARN_AGE' + line: "PASS_WARN_AGE {{ rhel9cis_pass['warn_age'] }}" + + - name: "5.6.1.3 | AUDIT | Ensure password expiration warning days is 7 or more | Get existing users WARN_DAYS" + ansible.builtin.shell: "awk -F: '/^[^:]+:[^!*]/ && $6< {{ rhel9cis_pass['warn_age'] }} {print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_warn_days + + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Set existing users WARN_DAYS" + ansible.builtin.shell: "chage --warndays {{ rhel9cis_pass['warn_age'] }} {{ item }}" + loop: "{{ discovered_warn_days.stdout_lines }}" + when: + - discovered_warn_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - rhel9cis_force_user_warnage when: - rhel9cis_rule_5_6_1_3 tags: diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index e2d03e5e..7be9ae98 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -235,7 +235,7 @@ state: directory owner: root group: root - mode: "0755" + mode: '0755' follow: false loop: "{{ root_path_perms.results }}" loop_control: @@ -278,7 +278,7 @@ owner: "{{ item.id }}" group: "{{ item.gid }}" register: rhel_09_6_2_10_home_dir - loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" + loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" loop_control: label: "{{ item.id }}" @@ -290,7 +290,7 @@ etype: group permissions: rx state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Set other ACL" @@ -300,7 +300,7 @@ etype: other permissions: 0 state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container when: - rhel9cis_rule_6_2_10 @@ -320,10 +320,7 @@ loop_control: label: "{{ item.id }}" when: - - item.uid >= min_int_uid | int - - item.id != 'nobody' - - (item.id != 'tss' and item.dir != '/dev/null') - - item.shell != '/sbin/nologin' + - item.id in discovered_interactive_usernames.stdout - rhel9cis_rule_6_2_11 tags: - level1-server @@ -338,13 +335,13 @@ ansible.builtin.stat: path: "{{ item }}" register: rhel_09_6_2_12_home_dir_perms - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | amend if needed" ansible.builtin.file: path: "{{ item.stat.path }}" state: directory - mode: "0750" + mode: '0750' loop: "{{ rhel_09_6_2_12_home_dir_perms.results }}" loop_control: label: "{{ item }}" @@ -359,7 +356,7 @@ etype: group permissions: rx state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | Set other ACL" @@ -369,7 +366,7 @@ etype: other permissions: 0 state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container when: - rhel9cis_rule_6_2_12 @@ -385,7 +382,7 @@ ansible.builtin.file: path: "{{ item }}/.netrc" state: absent - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_13 tags: @@ -400,7 +397,7 @@ ansible.builtin.file: path: "{{ item }}/.forward" state: absent - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_14 tags: @@ -415,7 +412,7 @@ ansible.builtin.file: path: "~{{ item }}/.rhosts" state: absent - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_15 tags: diff --git a/vars/audit.yml b/vars/audit.yml index 26e2b879..bb50f6d3 100644 --- a/vars/audit.yml +++ b/vars/audit.yml @@ -2,6 +2,9 @@ #### Audit Configuration Settings #### +# Timeout for those cmds that take longer to run where timeout set +audit_cmd_timeout: 120000 + # if get_audit_binary_method == download change accordingly audit_bin_url: "https://github.com/goss-org/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-" @@ -12,14 +15,12 @@ audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" audit_git_version: "benchmark_{{ benchmark_version }}" ## Goss configuration information -# Where the goss configs and outputs are stored -audit_out_dir: '/opt' -# Where the goss audit configuration will be stored -audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit" +# Where the goss audit configuration will be stored - NOTE benchmark-audit is expected +audit_conf_dir: "{{ audit_conf_dest | default('/opt') }}/{{ benchmark }}-Audit" # If changed these can affect other products -pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" -post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" +pre_audit_outfile: "{{ audit_log_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" +post_audit_outfile: "{{ audit_log_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" ## The following should not need changing @@ -33,6 +34,7 @@ audit_format: json audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_facts.hostname }}.yml" audit_results: | - The pre remediation results are: {{ pre_audit_summary }}. - The post remediation results are: {{ post_audit_summary }}. - Full breakdown can be found in {{ audit_out_dir }} + The audit results are: {{ pre_audit_summary }} + {% if not audit_only %}The post remediation audit results are: {{ post_audit_summary }}{% endif %} + + Full breakdown can be found in {{ audit_log_dir }}