diff --git a/tasks/main.yml b/tasks/main.yml index 518ca8a..13a6203 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1936,10 +1936,11 @@ - medium_disruption - no_reboot_needed -- name: Enable authselect - Select authselect profile +- name: Enable authselect - Check Current authselect Profile ansible.builtin.command: - cmd: authselect select "{{ var_authselect_profile }}" - register: result_authselect_select + cmd: authselect current + register: result_authselect_current + changed_when: false failed_when: false tags: - CCE-89732-2 @@ -1961,10 +1962,11 @@ - medium_severity | bool - no_reboot_needed | bool -- name: Enable authselect - Verify if PAM has been altered +- name: Enable authselect - Try to Select an authselect Profile ansible.builtin.command: - cmd: rpm -qV pam - register: result_altered_authselect + cmd: authselect select "{{ var_authselect_profile }}" + register: result_authselect_select + changed_when: result_authselect_select.rc == 0 failed_when: false when: - DISA_STIG_needed_rules | bool @@ -1974,6 +1976,34 @@ - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool + - result_authselect_current.rc != 0 + tags: + - CCE-89732-2 + - DISA-STIG-needed_rules + - NIST-800-53-AC-3 + - PCI-DSSv4-8.3.4 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Enable authselect - Verify If pam Has Been Altered + ansible.builtin.command: + cmd: rpm -qV pam + register: result_altered_authselect + changed_when: false + failed_when: false + when: + - DISA_STIG_needed_rules | bool + - configure_strategy | bool + - enable_authselect | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - result_authselect_select is not skipped - result_authselect_select.rc != 0 tags: - CCE-89732-2 @@ -1987,12 +2017,13 @@ - medium_severity - no_reboot_needed -- name: Enable authselect - Informative message based on the authselect integrity check +- name: Enable authselect - Informative Message Based on authselect Integrity Check ansible.builtin.assert: that: - - result_altered_authselect is skipped or result_altered_authselect.rc == 0 + - result_authselect_current.rc == 0 or result_altered_authselect is skipped or result_altered_authselect.rc == 0 fail_msg: - - Files in the 'pam' package have been altered, so the authselect configuration won't be forced. + - authselect is not used but files from the 'pam' package have been altered, so the authselect configuration won't be + forced. tags: - CCE-89732-2 - DISA-STIG-needed_rules @@ -2013,7 +2044,7 @@ - medium_severity | bool - no_reboot_needed | bool -- name: Enable authselect - Force authselect profile select +- name: Enable authselect - Force authselect Profile Selection ansible.builtin.command: cmd: authselect select --force "{{ var_authselect_profile }}" when: @@ -2024,8 +2055,9 @@ - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool + - result_authselect_current.rc != 0 - result_authselect_select.rc != 0 - - result_altered_authselect is skipped or result_altered_authselect.rc == 0 + - result_altered_authselect.rc == 0 tags: - CCE-89732-2 - DISA-STIG-needed_rules @@ -2764,6 +2796,46 @@ - no_reboot_needed - unknown_strategy +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86226-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pam_pwquality_installed + when: + - enable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - package_pam_pwquality_installed | bool + +- name: Ensure libpwquality is installed + package: + name: libpwquality + state: present + when: + - enable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - package_pam_pwquality_installed | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-86226-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pam_pwquality_installed + - name: Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File. - Check if system relies on authselect tool ansible.builtin.stat: @@ -3136,7 +3208,7 @@ - name: 'Limit Password Reuse: password-auth - Collect the available authselect features' ansible.builtin.command: - cmd: authselect list-features minimal + cmd: authselect list-features sssd register: result_authselect_available_features changed_when: false when: @@ -3342,10 +3414,13 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: password-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: '{{ var_password_pam_remember_control_flag.split(",")[0] }}' - name: 'Limit Password Reuse: password-auth - Check if expected PAM module line is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ '{{ var_password_pam_remember_control_flag.split(",")[0] }}' | regex_escape() }}\s+pam_pwhistory.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.* state: absent check_mode: true changed_when: false @@ -3366,7 +3441,7 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) - replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 @@ -3374,7 +3449,7 @@ ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' insertafter: ^password.*requisite.*pam_pwquality\.so - line: password {{ var_password_pam_remember_control_flag.split(",")[0] }} pam_pwhistory.so + line: password {{ pam_module_control }} pam_pwhistory.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -3574,6 +3649,9 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: password-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: '' - name: 'Limit Password Reuse: password-auth - Ensure the "remember" option from "pam_pwhistory.so" is not present in {{ pam_file_path }}' ansible.builtin.replace: @@ -3725,10 +3803,13 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: password-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: requisite - name: 'Limit Password Reuse: password-auth - Check if expected PAM module line is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.* state: absent check_mode: true changed_when: false @@ -3749,14 +3830,14 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) - replace: \1requisite \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 - name: 'Limit Password Reuse: password-auth - Ensure the required PAM module line is included in {{ pam_file_path }}' ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' - line: password requisite pam_pwhistory.so + line: password {{ pam_module_control }} pam_pwhistory.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -3771,35 +3852,38 @@ when: - result_pam_line_present.found is defined - result_pam_line_present.found == 0 + - name: 'Limit Password Reuse: password-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: requisite - name: 'Limit Password Reuse: password-auth - Check if the required PAM module option is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s*.*\sremember\b + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.*\sremember\b state: absent check_mode: true changed_when: false - register: result_pam_module_remember_option_present + register: result_pam_module_accounts_password_pam_pwhistory_remember_password_auth_option_present - name: 'Limit Password Reuse: password-auth - Ensure the "remember" PAM option for "pam_pwhistory.so" is included in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so.*) + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so.*) line: \1 remember={{ var_password_pam_remember }} state: present - register: result_pam_remember_add + register: result_pam_accounts_password_pam_pwhistory_remember_password_auth_add when: - - result_pam_module_remember_option_present.found == 0 + - result_pam_module_accounts_password_pam_pwhistory_remember_password_auth_option_present.found == 0 - name: 'Limit Password Reuse: password-auth - Ensure the required value for "remember" PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) line: \1\2={{ var_password_pam_remember }} \3 - register: result_pam_remember_edit + register: result_pam_accounts_password_pam_pwhistory_remember_password_auth_edit when: - - result_pam_module_remember_option_present.found > 0 + - result_pam_module_accounts_password_pam_pwhistory_remember_password_auth_option_present.found > 0 - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' ansible.builtin.command: cmd: authselect apply-changes -b @@ -3891,7 +3975,7 @@ - name: 'Limit Password Reuse: system-auth - Collect the available authselect features' ansible.builtin.command: - cmd: authselect list-features minimal + cmd: authselect list-features sssd register: result_authselect_available_features changed_when: false when: @@ -4097,10 +4181,13 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: system-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: '{{ var_password_pam_remember_control_flag.split(",")[0] }}' - name: 'Limit Password Reuse: system-auth - Check if expected PAM module line is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ '{{ var_password_pam_remember_control_flag.split(",")[0] }}' | regex_escape() }}\s+pam_pwhistory.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.* state: absent check_mode: true changed_when: false @@ -4121,7 +4208,7 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) - replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 @@ -4129,7 +4216,7 @@ ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' insertafter: ^password.*requisite.*pam_pwquality\.so - line: password {{ var_password_pam_remember_control_flag.split(",")[0] }} pam_pwhistory.so + line: password {{ pam_module_control }} pam_pwhistory.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -4327,6 +4414,9 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: system-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: '' - name: 'Limit Password Reuse: system-auth - Ensure the "remember" option from "pam_pwhistory.so" is not present in {{ pam_file_path }}' ansible.builtin.replace: @@ -4478,10 +4568,13 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: 'Limit Password Reuse: system-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: requisite - name: 'Limit Password Reuse: system-auth - Check if expected PAM module line is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.* state: absent check_mode: true changed_when: false @@ -4502,14 +4595,14 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) - replace: \1requisite \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 - name: 'Limit Password Reuse: system-auth - Ensure the required PAM module line is included in {{ pam_file_path }}' ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' - line: password requisite pam_pwhistory.so + line: password {{ pam_module_control }} pam_pwhistory.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -4524,35 +4617,38 @@ when: - result_pam_line_present.found is defined - result_pam_line_present.found == 0 + - name: 'Limit Password Reuse: system-auth - Define a fact for control already filtered in case filters are used' + ansible.builtin.set_fact: + pam_module_control: requisite - name: 'Limit Password Reuse: system-auth - Check if the required PAM module option is present in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s*.*\sremember\b + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s*.*\sremember\b state: absent check_mode: true changed_when: false - register: result_pam_module_remember_option_present + register: result_pam_module_accounts_password_pam_pwhistory_remember_system_auth_option_present - name: 'Limit Password Reuse: system-auth - Ensure the "remember" PAM option for "pam_pwhistory.so" is included in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so.*) + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so.*) line: \1 remember={{ var_password_pam_remember }} state: present - register: result_pam_remember_add + register: result_pam_accounts_password_pam_pwhistory_remember_system_auth_add when: - - result_pam_module_remember_option_present.found == 0 + - result_pam_module_accounts_password_pam_pwhistory_remember_system_auth_option_present.found == 0 - name: 'Limit Password Reuse: system-auth - Ensure the required value for "remember" PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) line: \1\2={{ var_password_pam_remember }} \3 - register: result_pam_remember_edit + register: result_pam_accounts_password_pam_pwhistory_remember_system_auth_edit when: - - result_pam_module_remember_option_present.found > 0 + - result_pam_module_accounts_password_pam_pwhistory_remember_system_auth_option_present.found > 0 - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' ansible.builtin.command: cmd: authselect apply-changes -b @@ -4957,6 +5053,10 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Lock Accounts After Failed Password Attempts - Define a fact for control already filtered in case filters are + used + ansible.builtin.set_fact: + pam_module_control: '' - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option from "pam_faillock.so" is not present in {{ pam_file_path }} ansible.builtin.replace: @@ -5089,6 +5189,10 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Lock Accounts After Failed Password Attempts - Define a fact for control already filtered in case filters are + used + ansible.builtin.set_fact: + pam_module_control: '' - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option from "pam_faillock.so" is not present in {{ pam_file_path }} ansible.builtin.replace: @@ -5219,6 +5323,611 @@ - no_reboot_needed - restrict_strategy +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Configure the root Account for Failed Password Attempts - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where authselect tool is present + block: + - name: Configure the root Account for Failed Password Attempts - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + failed_when: false + - name: Configure the root Account for Failed Password Attempts - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd.rc == 0 + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not selected or the selected profile is not + intact. + - It is not recommended to manually edit the PAM files when authselect tool is available. + - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + - name: Configure the root Account for Failed Password Attempts - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + - name: Configure the root Account for Failed Password Attempts - Ensure "with-faillock" feature is enabled using authselect + tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where authselect tool is not present + block: + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so preauth editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so authfail editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so account section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Check the presence of /etc/security/faillock.conf file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so even_deny_root parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*even_deny_root + line: even_deny_root + state: present + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so even_deny_root parameter not + in PAM files + block: + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + - name: Configure the root Account for Failed Password Attempts - Check the proper remediation for the system + block: + - name: Configure the root Account for Failed Password Attempts - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + - name: Configure the root Account for Failed Password Attempts - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Configure the root Account for Failed Password Attempts - Ensure authselect custom profile is used if authselect + is present + block: + - name: Configure the root Account for Failed Password Attempts - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + failed_when: false + - name: Configure the root Account for Failed Password Attempts - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd.rc == 0 + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not selected or the selected profile is + not intact. + - It is not recommended to manually edit the PAM files when authselect tool is available. + - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is + recommended. + success_msg: + - authselect integrity check passed + - name: Configure the root Account for Failed Password Attempts - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + - name: Configure the root Account for Failed Password Attempts - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + - name: Configure the root Account for Failed Password Attempts - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Configure the root Account for Failed Password Attempts - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Configure the root Account for Failed Password Attempts - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + - name: Configure the root Account for Failed Password Attempts - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} + when: + - result_authselect_present.stat.exists + - name: Configure the root Account for Failed Password Attempts - Define a fact for control already filtered in case filters + are used + ansible.builtin.set_fact: + pam_module_control: '' + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + - name: Configure the root Account for Failed Password Attempts - Check the proper remediation for the system + block: + - name: Configure the root Account for Failed Password Attempts - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + - name: Configure the root Account for Failed Password Attempts - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Configure the root Account for Failed Password Attempts - Ensure authselect custom profile is used if authselect + is present + block: + - name: Configure the root Account for Failed Password Attempts - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + failed_when: false + - name: Configure the root Account for Failed Password Attempts - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd.rc == 0 + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not selected or the selected profile is + not intact. + - It is not recommended to manually edit the PAM files when authselect tool is available. + - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is + recommended. + success_msg: + - authselect integrity check passed + - name: Configure the root Account for Failed Password Attempts - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + - name: Configure the root Account for Failed Password Attempts - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + - name: Configure the root Account for Failed Password Attempts - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + - name: Configure the root Account for Failed Password Attempts - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Configure the root Account for Failed Password Attempts - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Configure the root Account for Failed Password Attempts - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + - name: Configure the root Account for Failed Password Attempts - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} + when: + - result_authselect_present.stat.exists + - name: Configure the root Account for Failed Password Attempts - Define a fact for control already filtered in case filters + are used + ansible.builtin.set_fact: + pam_module_control: '' + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + - name: Configure the root Account for Failed Password Attempts - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so even_deny_root parameter in PAM + files + block: + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so even_deny_root parameter is already + enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_even_deny_root_parameter_is_present + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion of pam_faillock.so preauth even_deny_root + parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion of pam_faillock.so authfail even_deny_root + parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + when: + - DISA_STIG_RHEL_09_411080 | bool + - accounts_passwords_pam_faillock_deny_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - DISA-STIG-RHEL-09-411080 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - name: Gather the package facts package_facts: manager: auto @@ -5591,6 +6300,10 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Set Lockout Time for Failed Password Attempts - Define a fact for control already filtered in case filters are + used + ansible.builtin.set_fact: + pam_module_control: '' - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" option from "pam_faillock.so" is not present in {{ pam_file_path }} ansible.builtin.replace: @@ -5724,6 +6437,10 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Set Lockout Time for Failed Password Attempts - Define a fact for control already filtered in case filters are + used + ansible.builtin.set_fact: + pam_module_control: '' - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" option from "pam_faillock.so" is not present in {{ pam_file_path }} ansible.builtin.replace: @@ -5854,6 +6571,223 @@ - no_reboot_needed - restrict_strategy +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88413-0 + - DISA-STIG-RHEL-09-611105 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_611105 | bool + - accounts_password_pam_dictcheck | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words - Ensure PAM variable dictcheck is + set accordingly + ansible.builtin.lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*dictcheck + line: dictcheck = {{ var_password_pam_dictcheck }} + when: + - DISA_STIG_RHEL_09_611105 | bool + - accounts_password_pam_dictcheck | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-88413-0 + - DISA-STIG-RHEL-09-611105 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83564-5 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-09-611115 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_611115 | bool + - accounts_password_pam_difok | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Ensure PAM Enforces Password Requirements - Minimum Different Characters - Ensure PAM variable difok is set accordingly + ansible.builtin.lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*difok + line: difok = {{ var_password_pam_difok }} + when: + - DISA_STIG_RHEL_09_611115 | bool + - accounts_password_pam_difok | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-83564-5 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-09-611115 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86356-3 + - DISA-STIG-RHEL-09-611060 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_611060 | bool + - accounts_password_pam_enforce_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Ensure PAM Enforces Password Requirements - Enforce for root User + lineinfile: + path: /etc/security/pwquality.conf + create: true + line: enforce_for_root + state: present + when: + - DISA_STIG_RHEL_09_611060 | bool + - accounts_password_pam_enforce_root | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-86356-3 + - DISA-STIG-RHEL-09-611060 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83567-8 + - DISA-STIG-RHEL-09-611125 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_611125 | bool + - accounts_password_pam_maxrepeat | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Set Password Maximum Consecutive Repeating Characters - Ensure PAM variable maxrepeat is set accordingly + ansible.builtin.lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*maxrepeat + line: maxrepeat = {{ var_password_pam_maxrepeat }} + when: + - DISA_STIG_RHEL_09_611125 | bool + - accounts_password_pam_maxrepeat | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-83567-8 + - DISA-STIG-RHEL-09-611125 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - name: Gather the package facts package_facts: manager: auto @@ -5972,441 +6906,63 @@ package_facts: manager: auto tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) + - CCE-88865-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-09-611135 + - NIST-800-171-3.13.11 - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - PCI-DSSv4-8.3.2 - low_complexity - - medium_disruption + - low_disruption - medium_severity - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool + - DISA_STIG_RHEL_09_611135 | bool - low_complexity | bool - - medium_disruption | bool + - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool + - restrict_strategy | bool + - set_password_hashing_algorithm_libuserconf | bool -- name: Ensure PAM variable retry is set accordingly +- name: Set Password Hashing Algorithm in /etc/libuser.conf - Set Password Hashing Algorithm in /etc/libuser.conf ansible.builtin.lineinfile: + dest: /etc/libuser.conf + insertafter: ^\s*\[defaults] + regexp: ^#?crypt_style + line: crypt_style = {{ var_password_hashing_algorithm_pam }} + state: present create: true - dest: /etc/security/pwquality.conf - regexp: ^\s*retry - line: retry = {{ var_password_pam_retry }} when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool + - DISA_STIG_RHEL_09_611135 | bool - low_complexity | bool - - medium_disruption | bool + - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' + - restrict_strategy | bool + - set_password_hashing_algorithm_libuserconf | bool + - '"libuser" in ansible_facts.packages' tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) + - CCE-88865-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-09-611135 + - NIST-800-171-3.13.11 - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - PCI-DSSv4-8.3.2 - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if /etc/pam.d/password-auth - file is present - ansible.builtin.stat: - path: /etc/pam.d/password-auth - register: result_pam_file_present - when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check the proper - remediation for the system - block: - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the PAM - file to be edited as a local fact - ansible.builtin.set_fact: - pam_file_path: /etc/pam.d/password-auth - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if system - relies on authselect tool - ansible.builtin.stat: - path: /usr/bin/authselect - register: result_authselect_present - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - custom profile is used if authselect is present - block: - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check integrity - of authselect current profile - ansible.builtin.command: - cmd: authselect check - register: result_authselect_check_cmd - changed_when: false - failed_when: false - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Informative message - based on the authselect integrity check result - ansible.builtin.assert: - that: - - result_authselect_check_cmd.rc == 0 - fail_msg: - - authselect integrity check failed. Remediation aborted! - - This remediation could not be applied because an authselect profile was not selected or the selected profile is - not intact. - - It is not recommended to manually edit the PAM files when authselect tool is available. - - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended. - success_msg: - - authselect integrity check passed - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Get authselect - current profile - ansible.builtin.shell: - cmd: authselect current -r | awk '{ print $1 }' - register: result_authselect_profile - changed_when: false - when: - - result_authselect_check_cmd is success - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the current - authselect profile as a local fact - ansible.builtin.set_fact: - authselect_current_profile: '{{ result_authselect_profile.stdout }}' - authselect_custom_profile: '{{ result_authselect_profile.stdout }}' - when: - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the new - authselect custom profile as a local fact - ansible.builtin.set_fact: - authselect_current_profile: '{{ result_authselect_profile.stdout }}' - authselect_custom_profile: custom/hardening - when: - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Get authselect - current features to also enable them in the custom profile - ansible.builtin.shell: - cmd: authselect current | tail -n+3 | awk '{ print $2 }' - register: result_authselect_features - changed_when: false - when: - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if any - custom profile with the same name was already created - ansible.builtin.stat: - path: /etc/authselect/{{ authselect_custom_profile }} - register: result_authselect_custom_profile_present - changed_when: false - when: - - authselect_current_profile is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Create an authselect - custom profile based on the current profile - ansible.builtin.command: - cmd: authselect create-profile hardening -b {{ authselect_current_profile }} - when: - - result_authselect_check_cmd is success - - authselect_current_profile is not match("custom/") - - not result_authselect_custom_profile_present.stat.exists - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=before-hardening-custom-profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - authselect_custom_profile is not match(authselect_current_profile) - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure the authselect - custom profile is selected - ansible.builtin.command: - cmd: authselect select {{ authselect_custom_profile }} - register: result_pam_authselect_select_profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - authselect_custom_profile is not match(authselect_current_profile) - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Restore the authselect - features in the custom profile - ansible.builtin.command: - cmd: authselect enable-feature {{ item }} - loop: '{{ result_authselect_features.stdout_lines }}' - register: result_pam_authselect_restore_features - when: - - result_authselect_profile is not skipped - - result_authselect_features is not skipped - - result_pam_authselect_select_profile is not skipped - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=after-hardening-custom-profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - result_pam_authselect_restore_features is not skipped - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Change the PAM - file to be edited according to the custom authselect profile - ansible.builtin.set_fact: - pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} - when: - - result_authselect_present.stat.exists - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure the "retry" - option from "pam_pwquality.so" is not present in {{ pam_file_path }} - ansible.builtin.replace: - dest: '{{ pam_file_path }}' - regexp: (.*password.*{{ '.*' | regex_escape() }}.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) - replace: \1\2 - register: result_pam_option_removal - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b - when: - - result_authselect_present.stat.exists - - result_pam_option_removal is changed - when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_pam_file_present.stat.exists - tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if /etc/pam.d/system-auth - file is present - ansible.builtin.stat: - path: /etc/pam.d/system-auth - register: result_pam_file_present - when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check the proper - remediation for the system - block: - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the PAM - file to be edited as a local fact - ansible.builtin.set_fact: - pam_file_path: /etc/pam.d/system-auth - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if system - relies on authselect tool - ansible.builtin.stat: - path: /usr/bin/authselect - register: result_authselect_present - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - custom profile is used if authselect is present - block: - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check integrity - of authselect current profile - ansible.builtin.command: - cmd: authselect check - register: result_authselect_check_cmd - changed_when: false - failed_when: false - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Informative message - based on the authselect integrity check result - ansible.builtin.assert: - that: - - result_authselect_check_cmd.rc == 0 - fail_msg: - - authselect integrity check failed. Remediation aborted! - - This remediation could not be applied because an authselect profile was not selected or the selected profile is - not intact. - - It is not recommended to manually edit the PAM files when authselect tool is available. - - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended. - success_msg: - - authselect integrity check passed - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Get authselect - current profile - ansible.builtin.shell: - cmd: authselect current -r | awk '{ print $1 }' - register: result_authselect_profile - changed_when: false - when: - - result_authselect_check_cmd is success - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the current - authselect profile as a local fact - ansible.builtin.set_fact: - authselect_current_profile: '{{ result_authselect_profile.stdout }}' - authselect_custom_profile: '{{ result_authselect_profile.stdout }}' - when: - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Define the new - authselect custom profile as a local fact - ansible.builtin.set_fact: - authselect_current_profile: '{{ result_authselect_profile.stdout }}' - authselect_custom_profile: custom/hardening - when: - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Get authselect - current features to also enable them in the custom profile - ansible.builtin.shell: - cmd: authselect current | tail -n+3 | awk '{ print $2 }' - register: result_authselect_features - changed_when: false - when: - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Check if any - custom profile with the same name was already created - ansible.builtin.stat: - path: /etc/authselect/{{ authselect_custom_profile }} - register: result_authselect_custom_profile_present - changed_when: false - when: - - authselect_current_profile is not match("custom/") - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Create an authselect - custom profile based on the current profile - ansible.builtin.command: - cmd: authselect create-profile hardening -b {{ authselect_current_profile }} - when: - - result_authselect_check_cmd is success - - authselect_current_profile is not match("custom/") - - not result_authselect_custom_profile_present.stat.exists - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=before-hardening-custom-profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - authselect_custom_profile is not match(authselect_current_profile) - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure the authselect - custom profile is selected - ansible.builtin.command: - cmd: authselect select {{ authselect_custom_profile }} - register: result_pam_authselect_select_profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - - authselect_custom_profile is not match(authselect_current_profile) - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Restore the authselect - features in the custom profile - ansible.builtin.command: - cmd: authselect enable-feature {{ item }} - loop: '{{ result_authselect_features.stdout_lines }}' - register: result_pam_authselect_restore_features - when: - - result_authselect_profile is not skipped - - result_authselect_features is not skipped - - result_pam_authselect_select_profile is not skipped - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=after-hardening-custom-profile - when: - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - - result_pam_authselect_restore_features is not skipped - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Change the PAM - file to be edited according to the custom authselect profile - ansible.builtin.set_fact: - pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} - when: - - result_authselect_present.stat.exists - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure the "retry" - option from "pam_pwquality.so" is not present in {{ pam_file_path }} - ansible.builtin.replace: - dest: '{{ pam_file_path }}' - regexp: (.*password.*{{ '.*' | regex_escape() }}.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) - replace: \1\2 - register: result_pam_option_removal - - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session - Ensure authselect - changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b - when: - - result_authselect_present.stat.exists - - result_pam_option_removal is changed - when: - - DISA_STIG_RHEL_09_611010 | bool - - accounts_password_pam_retry | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_pam_file_present.stat.exists - tags: - - CCE-83569-4 - - CJIS-5.5.3 - - DISA-STIG-RHEL-09-611010 - - NIST-800-53-AC-7(a) - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(4) - - accounts_password_pam_retry - - configure_strategy - - low_complexity - - medium_disruption + - low_disruption - medium_severity - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf - name: Gather the package facts package_facts: @@ -6641,11 +7197,15 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - password-auth - Define a fact for control already filtered in case filters + are used + ansible.builtin.set_fact: + pam_module_control: sufficient - name: Set PAM's Password Hashing Algorithm - password-auth - Check if expected PAM module line is present in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so\s*.* state: absent check_mode: true changed_when: false @@ -6667,7 +7227,7 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_unix.so.*) - replace: \1sufficient \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 @@ -6675,7 +7235,7 @@ }} ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' - line: password sufficient pam_unix.so + line: password {{ pam_module_control }} pam_unix.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -6690,32 +7250,231 @@ when: - result_pam_line_present.found is defined - result_pam_line_present.found == 0 + - name: Set PAM's Password Hashing Algorithm - password-auth - Define a fact for control already filtered in case filters + are used + ansible.builtin.set_fact: + pam_module_control: sufficient - name: Set PAM's Password Hashing Algorithm - password-auth - Check if the required PAM module option is present in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so\s*.*\ssha512\b + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so\s*.*\s{{ var_password_hashing_algorithm_pam + }}\b state: absent check_mode: true changed_when: false - register: result_pam_module_sha512_option_present - - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the "sha512" PAM option for "pam_unix.so" is included - in {{ pam_file_path }} + register: result_pam_module_set_password_hashing_algorithm_passwordauth_option_present + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the "{{ var_password_hashing_algorithm_pam }}" PAM + option for "pam_unix.so" is included in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so.*) - line: \1 sha512 + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so.*) + line: \1 {{ var_password_hashing_algorithm_pam }} state: present - register: result_pam_sha512_add + register: result_pam_set_password_hashing_algorithm_passwordauth_add when: - - result_pam_module_sha512_option_present.found == 0 + - result_pam_module_set_password_hashing_algorithm_passwordauth_option_present.found == 0 - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect changes are applied ansible.builtin.command: cmd: authselect apply-changes -b when: - result_authselect_present.stat.exists - - "(result_pam_sha512_add is defined and result_pam_sha512_add.changed)\n or (result_pam_sha512_edit is defined and result_pam_sha512_edit.changed)" + - "(result_pam_set_password_hashing_algorithm_passwordauth_add is defined and result_pam_set_password_hashing_algorithm_passwordauth_add.changed)\n\ + \ or (result_pam_set_password_hashing_algorithm_passwordauth_edit is defined and result_pam_set_password_hashing_algorithm_passwordauth_edit.changed)" + when: + - DISA_STIG_RHEL_09_671025 | bool + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_passwordauth | bool + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85946-2 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-09-671025 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check if /etc/pam.d/password-auth File is Present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: + - DISA_STIG_RHEL_09_671025 | bool + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_passwordauth | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-85946-2 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-09-671025 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check The Proper Remediation For The System + block: + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect custom profile is used if authselect is + present + block: + - name: Set PAM's Password Hashing Algorithm - password-auth - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + failed_when: false + - name: Set PAM's Password Hashing Algorithm - password-auth - Informative message based on the authselect integrity check + result + ansible.builtin.assert: + that: + - result_authselect_check_cmd.rc == 0 + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not selected or the selected profile is + not intact. + - It is not recommended to manually edit the PAM files when authselect tool is available. + - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect current features to also enable them in + the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if any custom profile with the same name was already + created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - password-auth - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Set PAM's Password Hashing Algorithm - password-auth - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + - name: Set PAM's Password Hashing Algorithm - password-auth - Change the PAM file to be edited according to the custom + authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} + when: + - result_authselect_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure That Only the Correct Hashing Algorithm Option For + pam_unix.so Is Used in /etc/pam.d/password-auth + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (^\s*password.*pam_unix\.so.*)\b{{ item }}\b\s*(.*) + replace: \1\2 + when: item != var_password_hashing_algorithm_pam + loop: + - sha512 + - yescrypt + - gost_yescrypt + - blowfish + - sha256 + - md5 + - bigcrypt + register: result_pam_hashing_options_removal + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_hashing_options_removal is changed when: - DISA_STIG_RHEL_09_671025 | bool - configure_strategy | bool @@ -6906,10 +7665,13 @@ pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} when: - result_authselect_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - Define a fact for control already filtered in case filters are used + ansible.builtin.set_fact: + pam_module_control: sufficient - name: Set PAM's Password Hashing Algorithm - Check if expected PAM module line is present in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so\s*.* + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so\s*.* state: absent check_mode: true changed_when: false @@ -6930,14 +7692,14 @@ ansible.builtin.replace: dest: '{{ pam_file_path }}' regexp: ^(\s*password\s+).*(\bpam_unix.so.*) - replace: \1sufficient \2 + replace: \1{{ pam_module_control }} \2 register: result_pam_module_edit when: - result_pam_line_other_control_present.found == 1 - name: Set PAM's Password Hashing Algorithm - Ensure the required PAM module line is included in {{ pam_file_path }} ansible.builtin.lineinfile: dest: '{{ pam_file_path }}' - line: password sufficient pam_unix.so + line: password {{ pam_module_control }} pam_unix.so register: result_pam_module_add when: - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 @@ -6952,31 +7714,221 @@ when: - result_pam_line_present.found is defined - result_pam_line_present.found == 0 + - name: Set PAM's Password Hashing Algorithm - Define a fact for control already filtered in case filters are used + ansible.builtin.set_fact: + pam_module_control: sufficient - name: Set PAM's Password Hashing Algorithm - Check if the required PAM module option is present in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' - regexp: ^\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so\s*.*\ssha512\b + regexp: ^\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so\s*.*\s{{ var_password_hashing_algorithm_pam + }}\b state: absent check_mode: true changed_when: false - register: result_pam_module_sha512_option_present - - name: Set PAM's Password Hashing Algorithm - Ensure the "sha512" PAM option for "pam_unix.so" is included in {{ pam_file_path - }} + register: result_pam_module_set_password_hashing_algorithm_systemauth_option_present + - name: Set PAM's Password Hashing Algorithm - Ensure the "{{ var_password_hashing_algorithm_pam }}" PAM option for "pam_unix.so" + is included in {{ pam_file_path }} ansible.builtin.lineinfile: path: '{{ pam_file_path }}' backrefs: true - regexp: ^(\s*password\s+{{ 'sufficient' | regex_escape() }}\s+pam_unix.so.*) - line: \1 sha512 + regexp: ^(\s*password\s+{{ pam_module_control | regex_escape() }}\s+pam_unix.so.*) + line: \1 {{ var_password_hashing_algorithm_pam }} state: present - register: result_pam_sha512_add + register: result_pam_set_password_hashing_algorithm_systemauth_add when: - - result_pam_module_sha512_option_present.found == 0 + - result_pam_module_set_password_hashing_algorithm_systemauth_option_present.found == 0 - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied ansible.builtin.command: cmd: authselect apply-changes -b when: - result_authselect_present.stat.exists - - "(result_pam_sha512_add is defined and result_pam_sha512_add.changed)\n or (result_pam_sha512_edit is defined and result_pam_sha512_edit.changed)" + - "(result_pam_set_password_hashing_algorithm_systemauth_add is defined and result_pam_set_password_hashing_algorithm_systemauth_add.changed)\n\ + \ or (result_pam_set_password_hashing_algorithm_systemauth_edit is defined and result_pam_set_password_hashing_algorithm_systemauth_edit.changed)" + when: + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_systemauth | bool + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83581-9 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - PCI-DSSv4-8.3.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check if /etc/pam.d/system-auth File is Present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_systemauth | bool + - '"pam" in ansible_facts.packages' + tags: + - CCE-83581-9 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - PCI-DSSv4-8.3.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check The Proper Remediation For The System + block: + - name: Set PAM's Password Hashing Algorithm - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + - name: Set PAM's Password Hashing Algorithm - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Set PAM's Password Hashing Algorithm - Ensure authselect custom profile is used if authselect is present + block: + - name: Set PAM's Password Hashing Algorithm - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + failed_when: false + - name: Set PAM's Password Hashing Algorithm - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd.rc == 0 + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not selected or the selected profile is + not intact. + - It is not recommended to manually edit the PAM files when authselect tool is available. + - In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + - name: Set PAM's Password Hashing Algorithm - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + - name: Set PAM's Password Hashing Algorithm - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + - name: Set PAM's Password Hashing Algorithm - Define the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - Get authselect current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - Check if any custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + - name: Set PAM's Password Hashing Algorithm - Create an authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Set PAM's Password Hashing Algorithm - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + - name: Set PAM's Password Hashing Algorithm - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + - name: Set PAM's Password Hashing Algorithm - Change the PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path | basename }} + when: + - result_authselect_present.stat.exists + - name: Set PAM's Password Hashing Algorithm - Ensure That Only the Correct Hashing Algorithm Option For pam_unix.so Is + Used in /etc/pam.d/system-auth + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (^\s*password.*pam_unix\.so.*)\b{{ item }}\b\s*(.*) + replace: \1\2 + when: item != var_password_hashing_algorithm_pam + loop: + - sha512 + - yescrypt + - gost_yescrypt + - blowfish + - sha256 + - md5 + - bigcrypt + register: result_pam_hashing_options_removal + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_hashing_options_removal is changed when: - configure_strategy | bool - low_complexity | bool @@ -7583,6 +8535,7 @@ ' register: users_nopasswd + changed_when: false when: - DISA_STIG_RHEL_09_611155 | bool - high_severity | bool @@ -8021,6 +8974,185 @@ - no_reboot_needed - restrict_strategy +- name: Ensure interactive local users are the group-owners of their respective initialization files + ansible.builtin.command: + cmd: awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chgrp -f " $4" "$6"/.[^\.]?*") }' /etc/passwd + tags: + - CCE-87037-8 + - accounts_user_dot_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - accounts_user_dot_group_ownership | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: User Initialization Files Must Not Run World-Writable Programs - Initialize variables + set_fact: + home_user_dirs: [] + world_writable_files: [] + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: User Initialization Files Must Not Run World-Writable Programs - Get user's home dir list + ansible.builtin.getent: + database: passwd + register: passwd_database + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: User Initialization Files Must Not Run World-Writable Programs - Fill home_user_dirs + set_fact: + home_user_dirs: '{{ home_user_dirs + [item.data[4]] }}' + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - item.data[4] is defined and item.data[2]|int >= 1000 and item.data[2]|int != 65534 + with_items: '{{ passwd_database.ansible_facts.getent_passwd | dict2items(key_name=''user'', value_name=''data'')}}' + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: User Initialization Files Must Not Run World-Writable Programs - Get world writable files + ansible.builtin.shell: 'find / -xdev -type f -perm -0002 2> /dev/null + + ' + register: world_writable_files + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: User Initialization Files Must Not Run World-Writable Programs - Find referenced_files in init files + ansible.builtin.find: + paths: '{{ home_user_dirs }}' + contains: '{{ item }}' + hidden: true + read_whole_file: true + recurse: true + with_items: '{{ world_writable_files.stdout_lines }}' + register: referenced_files + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: User Initialization Files Must Not Run World-Writable Programs - Remove world writable permissions + ansible.builtin.file: + path: '{{ item.item }}' + mode: o-w + when: + - DISA_STIG_RHEL_09_411115 | bool + - accounts_user_dot_no_world_writable_programs | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - item.matched > 0 + with_items: '{{ referenced_files.results }}' + tags: + - CCE-87451-1 + - DISA-STIG-RHEL-09-411115 + - accounts_user_dot_no_world_writable_programs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the owners of their respective initialization files + ansible.builtin.command: + cmd: awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chown -f " $3" "$6"/.[^\.]?*") }' /etc/passwd + tags: + - CCE-87038-6 + - accounts_user_dot_user_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - accounts_user_dot_user_ownership | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - name: Get all local users from /etc/passwd ansible.builtin.getent: database: passwd @@ -8089,97 +9221,72 @@ - no_reboot_needed - restrict_strategy -- name: Get all local users from /etc/passwd +- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Gather User Info ansible.builtin.getent: database: passwd - split: ':' tags: - - CCE-83629-6 - - DISA-STIG-RHEL-09-411070 - - file_groupownership_home_directories + - CCE-83637-9 + - file_permission_user_init_files - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy when: - - DISA_STIG_RHEL_09_411070 | bool - - file_groupownership_home_directories | bool + - file_permission_user_init_files | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - restrict_strategy | bool -- name: Create local_users variable from the getent output - ansible.builtin.set_fact: - local_users: '{{ ansible_facts.getent_passwd|dict2items }}' - tags: - - CCE-83629-6 - - DISA-STIG-RHEL-09-411070 - - file_groupownership_home_directories - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy +- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Find Init Files + ansible.builtin.find: + paths: '{{ item.value[4] }}' + pattern: '{{ var_user_initialization_files_regex }}' + hidden: true + use_regex: true + with_dict: '{{ ansible_facts.getent_passwd }}' when: - - DISA_STIG_RHEL_09_411070 | bool - - file_groupownership_home_directories | bool + - file_permission_user_init_files | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - restrict_strategy | bool - -- name: Test for existence of home directories to avoid creating them, but only fixing group ownership - ansible.builtin.stat: - path: '{{ item.value[4] }}' - register: path_exists - loop: '{{ local_users }}' - when: - - DISA_STIG_RHEL_09_411070 | bool - - file_groupownership_home_directories | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - item.value[1]|int >= 1000 - - item.value[1]|int != 65534 + - item.value[4] != "/sbin/nologin" + - item.key not in ["nobody", "nfsnobody"] + - item.value[1] | int >= 1000 + register: found_init_files tags: - - CCE-83629-6 - - DISA-STIG-RHEL-09-411070 - - file_groupownership_home_directories + - CCE-83637-9 + - file_permission_user_init_files - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy -- name: Ensure interactive local users are the group-owners of their respective home directories +- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Fix Init Files Permissions ansible.builtin.file: - path: '{{ item.0.value[4] }}' - group: '{{ item.0.value[2] }}' - loop: '{{ local_users|zip(path_exists.results)|list }}' - when: - - DISA_STIG_RHEL_09_411070 | bool - - file_groupownership_home_directories | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - item.1.stat is defined and item.1.stat.exists + path: '{{ item.1.path }}' + mode: u-s,g-wxs,o= + loop: '{{ q(''ansible.builtin.subelements'', found_init_files.results, ''files'', {''skip_missing'': True}) }}' tags: - - CCE-83629-6 - - DISA-STIG-RHEL-09-411070 - - file_groupownership_home_directories + - CCE-83637-9 + - file_permission_user_init_files - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy + when: + - file_permission_user_init_files | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool - name: Get all local users from /etc/passwd ansible.builtin.getent: @@ -9154,206 +10261,6 @@ - medium_severity - no_reboot_needed -- name: Ensure rsyslog is installed - package: - name: rsyslog - state: present - when: - - DISA_STIG_RHEL_09_652010 | bool - - enable_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - package_rsyslog_installed | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-84063-7 - - DISA-STIG-RHEL-09-652010 - - NIST-800-53-CM-6(a) - - enable_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - package_rsyslog_installed - -- name: Enable service rsyslog - block: - - name: Gather the package facts - package_facts: - manager: auto - - name: Enable service rsyslog - systemd: - name: rsyslog - enabled: 'yes' - state: started - masked: 'no' - when: - - '"rsyslog" in ansible_facts.packages' - when: - - DISA_STIG_RHEL_09_652020 | bool - - enable_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - service_rsyslog_enabled | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83989-4 - - DISA-STIG-RHEL-09-652020 - - NIST-800-53-AU-4(1) - - NIST-800-53-CM-6(a) - - enable_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - service_rsyslog_enabled - -- name: Ensure rsyslog Default File Permissions Configured - Search for $FileCreateMode Parameter in rsyslog Main Config File - ansible.builtin.find: - paths: /etc - pattern: rsyslog.conf - contains: ^\s*\$FileCreateMode\s*\d+ - register: rsyslog_main_file_with_filecreatemode - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - -- name: Ensure rsyslog Default File Permissions Configured - Search for $FileCreateMode Parameter in rsyslog Include Files - ansible.builtin.find: - paths: /etc/rsyslog.d/ - pattern: '*.conf' - contains: ^\s*\$FileCreateMode\s*\d+ - register: rsyslog_includes_with_filecreatemode - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - -- name: Ensure rsyslog Default File Permissions Configured - Assemble List of rsyslog Configuration Files with $FileCreateMode - Parameter - ansible.builtin.set_fact: - rsyslog_filecreatemode_files: '{{ rsyslog_main_file_with_filecreatemode.files | map(attribute=''path'') | list + rsyslog_includes_with_filecreatemode.files - | map(attribute=''path'') | list }}' - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - -- name: Ensure rsyslog Default File Permissions Configured - Remove $FileCreateMode Parameter from Multiple Files to Avoid - Conflicts - ansible.builtin.lineinfile: - path: '{{ item }}' - regexp: \$FileCreateMode.* - state: absent - register: result_rsyslog_filecreatemode_removed - loop: '{{ rsyslog_filecreatemode_files }}' - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_filecreatemode_files | length > 1 - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - -- name: Ensure rsyslog Default File Permissions Configured - Add $FileCreateMode Parameter and Expected Value - ansible.builtin.lineinfile: - path: /etc/rsyslog.d/99-rsyslog_filecreatemode.conf - line: $FileCreateMode 0640 - mode: 416 - create: true - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_filecreatemode_files | length == 0 or result_rsyslog_filecreatemode_removed is not skipped - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - -- name: Ensure rsyslog Default File Permissions Configured - Ensure Correct Value of Existing $FileCreateMode Parameter - ansible.builtin.lineinfile: - path: '{{ item }}' - regexp: ^\$FileCreateMode - line: $FileCreateMode 0640 - loop: '{{ rsyslog_filecreatemode_files }}' - when: - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_filecreatemode | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_filecreatemode_files | length == 1 - tags: - - CCE-88322-3 - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_filecreatemode - - name: Ensure Log Files Are Owned By Appropriate Group - Set rsyslog logfile configuration facts ansible.builtin.set_fact: rsyslog_etc_config: /etc/rsyslog.conf @@ -10176,17 +11083,38 @@ - no_reboot_needed - rsyslog_files_permissions -- name: Enable service systemd-journald +- name: Ensure systemd-journal-remote is installed + package: + name: systemd-journal-remote + state: present + when: + - enable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - package_systemd_journal_remote_installed | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86760-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_systemd-journal-remote_installed + +- name: Enable systemd-journald Service - Enable service systemd-journald block: - name: Gather the package facts package_facts: manager: auto - - name: Enable service systemd-journald - systemd: + - name: Enable systemd-journald Service - Enable Service systemd-journald + ansible.builtin.systemd: name: systemd-journald - enabled: 'yes' + enabled: true state: started - masked: 'no' + masked: false when: - '"systemd" in ansible_facts.packages' when: @@ -10209,62 +11137,19 @@ - no_reboot_needed - service_systemd-journald_enabled -- name: Check for duplicate Compress values in master journald configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*Compress= - state: absent - check_mode: true - changed_when: false - register: dupes_master - when: - - journald_compress | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-85931-4 - - journald_compress - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Deduplicate Compress values from journald master configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*Compress= - state: absent - when: - - journald_compress | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - dupes_master.found is defined and dupes_master.found > 1 - tags: - - CCE-85931-4 - - journald_compress - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Collect all config journald files which configure Compress +- name: Ensure journald is configured to compress large log files - Search for a section in files ansible.builtin.find: - paths: /etc/systemd/journald.conf.d - contains: ^[\s]*Compress=.*$ - patterns: '*.conf' - register: journald_Compress_dropin_config_files + paths: '{{item.path}}' + patterns: '{{item.pattern}}' + contains: ^\s*\[Journal\] + read_whole_file: true + use_regex: true + register: systemd_dropin_files_with_section + loop: + - path: '{{ ''/etc/systemd/journald.conf'' | dirname }}' + pattern: '{{ ''/etc/systemd/journald.conf'' | basename | regex_escape }}' + - path: /etc/systemd/journald.conf.d + pattern: .*\.conf when: - journald_compress | bool - low_complexity | bool @@ -10282,13 +11167,10 @@ - no_reboot_needed - restrict_strategy -- name: Deduplicate values from journald Compress dropin configuration - ansible.builtin.lineinfile: - path: '{{ item.path }}' - create: false - regexp: ^\s*Compress= - state: absent - loop: '{{ journald_Compress_dropin_config_files.files }}' +- name: Ensure journald is configured to compress large log files - Count number of files which contain the correct section + ansible.builtin.set_fact: + count_of_systemd_dropin_files_with_section: '{{systemd_dropin_files_with_section.results | map(attribute=''matched'') + | list | map(''int'') | sum}}' when: - journald_compress | bool - low_complexity | bool @@ -10306,15 +11188,14 @@ - no_reboot_needed - restrict_strategy -- name: Insert correct line to journald Compress configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf.d/oscap-remedy.conf - create: true - regexp: ^\s*Compress= - line: Compress=yes +- name: Ensure journald is configured to compress large log files - Add missing configuration to correct section + ini_file: + path: '{{item}}' + section: Journal + option: Compress + value: 'yes' state: present - insertbefore: ^# Compress - validate: bash -n %s + no_extra_spaces: true when: - journald_compress | bool - low_complexity | bool @@ -10323,6 +11204,9 @@ - no_reboot_needed | bool - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - count_of_systemd_dropin_files_with_section | int > 0 + loop: '{{systemd_dropin_files_with_section.results | sum(attribute=''files'', start=[]) | map(attribute=''path'') | list + }}' tags: - CCE-85931-4 - journald_compress @@ -10332,64 +11216,48 @@ - no_reboot_needed - restrict_strategy -- name: Check for duplicate ForwardToSyslog values in master journald configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*ForwardToSyslog= - state: absent - check_mode: true - changed_when: false - register: dupes_master +- name: Ensure journald is configured to compress large log files - Add configuration to new remediation file + ini_file: + path: /etc/systemd/journald.conf.d/complianceascode_hardening.conf + section: Journal + option: Compress + value: 'yes' + state: present + no_extra_spaces: true + create: true when: - - journald_forward_to_syslog | bool + - journald_compress | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - count_of_systemd_dropin_files_with_section | int == 0 tags: - - CCE-85996-7 - - journald_forward_to_syslog + - CCE-85931-4 + - journald_compress - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy -- name: Deduplicate ForwardToSyslog values from journald master configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*ForwardToSyslog= - state: absent - when: - - journald_forward_to_syslog | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - dupes_master.found is defined and dupes_master.found > 1 - tags: - - CCE-85996-7 - - journald_forward_to_syslog - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Collect all config journald files which configure ForwardToSyslog +- name: Ensure journald is configured to write log files to persistent disk - Search for a section in files ansible.builtin.find: - paths: /etc/systemd/journald.conf.d - contains: ^[\s]*ForwardToSyslog=.*$ - patterns: '*.conf' - register: journald_ForwardToSyslog_dropin_config_files + paths: '{{item.path}}' + patterns: '{{item.pattern}}' + contains: ^\s*\[Journal\] + read_whole_file: true + use_regex: true + register: systemd_dropin_files_with_section + loop: + - path: '{{ ''/etc/systemd/journald.conf'' | dirname }}' + pattern: '{{ ''/etc/systemd/journald.conf'' | basename | regex_escape }}' + - path: /etc/systemd/journald.conf.d + pattern: .*\.conf when: - - journald_forward_to_syslog | bool + - journald_storage | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -10397,23 +11265,21 @@ - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] tags: - - CCE-85996-7 - - journald_forward_to_syslog + - CCE-86046-0 + - journald_storage - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy -- name: Deduplicate values from journald ForwardToSyslog dropin configuration - ansible.builtin.lineinfile: - path: '{{ item.path }}' - create: false - regexp: ^\s*ForwardToSyslog= - state: absent - loop: '{{ journald_ForwardToSyslog_dropin_config_files.files }}' +- name: Ensure journald is configured to write log files to persistent disk - Count number of files which contain the correct + section + ansible.builtin.set_fact: + count_of_systemd_dropin_files_with_section: '{{systemd_dropin_files_with_section.results | map(attribute=''matched'') + | list | map(''int'') | sum}}' when: - - journald_forward_to_syslog | bool + - journald_storage | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -10421,49 +11287,22 @@ - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] tags: - - CCE-85996-7 - - journald_forward_to_syslog + - CCE-86046-0 + - journald_storage - low_complexity - low_disruption - medium_severity - no_reboot_needed - restrict_strategy -- name: Insert correct line to journald ForwardToSyslog configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf.d/oscap-remedy.conf - create: true - regexp: ^\s*ForwardToSyslog= - line: ForwardToSyslog=yes +- name: Ensure journald is configured to write log files to persistent disk - Add missing configuration to correct section + ini_file: + path: '{{item}}' + section: Journal + option: Storage + value: persistent state: present - insertbefore: ^# ForwardToSyslog - validate: bash -n %s - when: - - journald_forward_to_syslog | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-85996-7 - - journald_forward_to_syslog - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Check for duplicate Storage values in master journald configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*Storage= - state: absent - check_mode: true - changed_when: false - register: dupes_master + no_extra_spaces: true when: - journald_storage | bool - low_complexity | bool @@ -10472,6 +11311,9 @@ - no_reboot_needed | bool - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - count_of_systemd_dropin_files_with_section | int > 0 + loop: '{{systemd_dropin_files_with_section.results | sum(attribute=''files'', start=[]) | map(attribute=''path'') | list + }}' tags: - CCE-86046-0 - journald_storage @@ -10481,86 +11323,15 @@ - no_reboot_needed - restrict_strategy -- name: Deduplicate Storage values from journald master configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf - create: false - regexp: ^\s*Storage= - state: absent - when: - - journald_storage | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - dupes_master.found is defined and dupes_master.found > 1 - tags: - - CCE-86046-0 - - journald_storage - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Collect all config journald files which configure Storage - ansible.builtin.find: - paths: /etc/systemd/journald.conf.d - contains: ^[\s]*Storage=.*$ - patterns: '*.conf' - register: journald_Storage_dropin_config_files - when: - - journald_storage | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-86046-0 - - journald_storage - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Deduplicate values from journald Storage dropin configuration - ansible.builtin.lineinfile: - path: '{{ item.path }}' - create: false - regexp: ^\s*Storage= - state: absent - loop: '{{ journald_Storage_dropin_config_files.files }}' - when: - - journald_storage | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-86046-0 - - journald_storage - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - -- name: Insert correct line to journald Storage configuration - ansible.builtin.lineinfile: - path: /etc/systemd/journald.conf.d/oscap-remedy.conf - create: true - regexp: ^\s*Storage= - line: Storage=persistent +- name: Ensure journald is configured to write log files to persistent disk - Add configuration to new remediation file + ini_file: + path: /etc/systemd/journald.conf.d/complianceascode_hardening.conf + section: Journal + option: Storage + value: persistent state: present - insertbefore: ^# Storage - validate: bash -n %s + no_extra_spaces: true + create: true when: - journald_storage | bool - low_complexity | bool @@ -10569,6 +11340,7 @@ - no_reboot_needed | bool - restrict_strategy | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - count_of_systemd_dropin_files_with_section | int == 0 tags: - CCE-86046-0 - journald_storage @@ -10622,318 +11394,6 @@ - no_reboot_needed - socket_systemd-journal-remote_disabled -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Define Rsyslog Config Lines Regex in - Legacy Syntax - ansible.builtin.set_fact: - rsyslog_listen_legacy_regex: ^\s*\$(((Input(TCP|RELP)|UDP)ServerRun)|ModLoad\s+(imtcp|imudp|imrelp)) - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Search for Legacy Config Lines in Rsyslog - Main Config File - ansible.builtin.find: - paths: /etc - pattern: rsyslog.conf - contains: '{{ rsyslog_listen_legacy_regex }}' - register: rsyslog_listen_legacy_main_file - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Search for Legacy Config Lines in Rsyslog - Include Files - ansible.builtin.find: - paths: /etc/rsyslog.d/ - pattern: '*.conf' - contains: '{{ rsyslog_listen_legacy_regex }}' - register: rsyslog_listen_legacy_include_files - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Assemble List of Config Files With Listen - Lines in Legacy Syntax - ansible.builtin.set_fact: - rsyslog_legacy_remote_listen_files: '{{ rsyslog_listen_legacy_main_file.files | map(attribute=''path'') | list + rsyslog_listen_legacy_include_files.files - | map(attribute=''path'') | list }}' - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Comment Listen Config Lines Wherever - Defined Using Legacy Syntax - ansible.builtin.replace: - path: '{{ item }}' - regexp: '{{ rsyslog_listen_legacy_regex }}' - replace: '# \1' - loop: '{{ rsyslog_legacy_remote_listen_files }}' - register: rsyslog_listen_legacy_comment - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_legacy_remote_listen_files | length > 0 - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Define Rsyslog Config Lines Regex in - RainerScript Syntax - ansible.builtin.set_fact: - rsyslog_listen_rainer_regex: ^\s*(module|input)\((load|type)="(imtcp|imudp)".*$ - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Search for RainerScript Config Lines - in Rsyslog Main Config File - ansible.builtin.find: - paths: /etc - pattern: rsyslog.conf - contains: '{{ rsyslog_listen_rainer_regex }}' - register: rsyslog_rainer_remote_main_file - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Search for RainerScript Config Lines - in Rsyslog Include Files - ansible.builtin.find: - paths: /etc/rsyslog.d/ - pattern: '*.conf' - contains: '{{ rsyslog_listen_rainer_regex }}' - register: rsyslog_rainer_remote_include_files - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Assemble List of Config Files With Listen - Lines in RainerScript - ansible.builtin.set_fact: - rsyslog_rainer_remote_listen_files: '{{ rsyslog_rainer_remote_main_file.files | map(attribute=''path'') | list + rsyslog_rainer_remote_include_files.files - | map(attribute=''path'') | list }}' - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Comment Listen Config Lines Wherever - Defined Using RainerScript - ansible.builtin.replace: - path: '{{ item }}' - regexp: '{{ rsyslog_listen_rainer_regex }}' - replace: '# \1' - loop: '{{ rsyslog_rainer_remote_listen_files }}' - register: rsyslog_listen_rainer_comment - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_rainer_remote_listen_files | length > 0 - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - -- name: Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server - Restart Rsyslog if Any Line Were Commented - Out - ansible.builtin.service: - name: rsyslog - state: restarted - when: - - DISA_STIG_RHEL_09_652025 | bool - - configure_strategy | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - rsyslog_nolisten | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - rsyslog_listen_legacy_comment is changed or rsyslog_listen_rainer_comment is changed - tags: - - CCE-83995-1 - - DISA-STIG-RHEL-09-652025 - - NIST-800-53-CM-6(a) - - NIST-800-53-CM-7(a) - - NIST-800-53-CM-7(b) - - configure_strategy - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - rsyslog_nolisten - - name: Ensure firewalld is installed package: name: firewalld @@ -10988,17 +11448,17 @@ - no_reboot_needed | bool - service_firewalld_enabled | bool -- name: Enable service firewalld +- name: Verify firewalld Enabled - Enable service firewalld block: - name: Gather the package facts package_facts: manager: auto - - name: Enable service firewalld - systemd: + - name: Verify firewalld Enabled - Enable Service firewalld + ansible.builtin.systemd: name: firewalld - enabled: 'yes' + enabled: true state: started - masked: 'no' + masked: false when: - '"firewalld" in ansible_facts.packages' when: @@ -13561,7 +14021,7 @@ - no_reboot_needed - service_nftables_disabled -- name: Verify nftables Service is Disabled - Ensure "nftables.service" is Masked +- name: Verify nftables Service is Disabled - Ensure nftables.service is Masked ansible.builtin.systemd: name: nftables.service state: stopped @@ -13576,7 +14036,7 @@ - service_nftables_disabled | bool - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "firewalld" in ansible_facts.packages and "nftables" in ansible_facts.packages ) - - service_exists.stdout_lines is search("nftables.service",multiline=True) + - service_exists.stdout_lines is search("nftables.service", multiline=True) tags: - CCE-88429-6 - PCI-DSSv4-1.2.1 @@ -13612,12 +14072,12 @@ - no_reboot_needed - service_nftables_disabled -- name: Disable socket nftables +- name: Verify nftables Service is Disabled - Disable Socket nftables ansible.builtin.systemd: name: nftables.socket - enabled: 'no' + enabled: false state: stopped - masked: 'yes' + masked: true when: - disable_strategy | bool - low_complexity | bool @@ -13627,7 +14087,7 @@ - service_nftables_disabled | bool - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "firewalld" in ansible_facts.packages and "nftables" in ansible_facts.packages ) - - socket_file_exists.stdout_lines is search("nftables.socket",multiline=True) + - socket_file_exists.stdout_lines is search("nftables.socket", multiline=True) tags: - CCE-88429-6 - PCI-DSSv4-1.2.1 @@ -13638,65 +14098,181 @@ - no_reboot_needed - service_nftables_disabled -- name: Gather the package facts - package_facts: - manager: auto - tags: - - CCE-86163-3 - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - - set_nftables_table +- name: Ensure kernel module 'dccp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: install\s+dccp + line: install dccp /bin/false when: + - disable_strategy | bool + - kernel_module_dccp_disabled | bool - low_complexity | bool - - low_disruption | bool + - medium_disruption | bool - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - set_nftables_table | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84136-1 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSS-Req-1.4.2 + - PCI-DSSv4-1.4.2 + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required -- name: Collect Existing Nftables - ansible.builtin.command: nft list tables - register: existing_nftables +- name: Ensure kernel module 'dccp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: ^blacklist dccp$ + line: blacklist dccp when: + - disable_strategy | bool + - kernel_module_dccp_disabled | bool - low_complexity | bool - - low_disruption | bool + - medium_disruption | bool - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - set_nftables_table | bool - - '"nftables" in ansible_facts.packages' + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] tags: - - CCE-86163-3 + - CCE-84136-1 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSS-Req-1.4.2 + - PCI-DSSv4-1.4.2 + - disable_strategy + - kernel_module_dccp_disabled - low_complexity - - low_disruption + - medium_disruption - medium_severity - - no_reboot_needed - - restrict_strategy - - set_nftables_table + - reboot_required -- name: Set Nftable Table - ansible.builtin.command: nft create table {{ var_nftables_family }} {{ var_nftables_table }} +- name: Ensure kernel module 'rds' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: install\s+rds + line: install rds /bin/false when: + - disable_strategy | bool + - kernel_module_rds_disabled | bool - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - set_nftables_table | bool - - '"nftables" in ansible_facts.packages' - - existing_nftables is not skipped - - existing_nftables.stdout_lines | length == 0 + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] tags: - - CCE-86163-3 + - CCE-84064-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled - low_complexity - - low_disruption + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'rds' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: ^blacklist rds$ + line: blacklist rds + when: + - disable_strategy | bool + - kernel_module_rds_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84064-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'sctp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: install\s+sctp + line: install sctp /bin/false + when: + - DISA_STIG_RHEL_09_213060 | bool + - disable_strategy | bool + - kernel_module_sctp_disabled | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84139-5 + - CJIS-5.10.1 + - DISA-STIG-RHEL-09-213060 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSS-Req-1.4.2 + - PCI-DSSv4-1.4.2 + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption - medium_severity - - no_reboot_needed - - restrict_strategy - - set_nftables_table + - reboot_required + +- name: Ensure kernel module 'sctp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: ^blacklist sctp$ + line: blacklist sctp + when: + - DISA_STIG_RHEL_09_213060 | bool + - disable_strategy | bool + - kernel_module_sctp_disabled | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84139-5 + - CJIS-5.10.1 + - DISA-STIG-RHEL-09-213060 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSS-Req-1.4.2 + - PCI-DSSv4-1.4.2 + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required - name: Ensure kernel module 'tipc' is disabled lineinfile: @@ -13754,6 +14330,128 @@ - medium_disruption - reboot_required +- name: Disable Bluetooth Service - Collect systemd Services Present in the System + ansible.builtin.command: systemctl -q list-unit-files --type service + register: service_exists + changed_when: false + failed_when: service_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_bluetooth_disabled | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86761-4 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + +- name: Disable Bluetooth Service - Ensure bluetooth.service is Masked + ansible.builtin.systemd: + name: bluetooth.service + state: stopped + enabled: false + masked: true + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_bluetooth_disabled | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - service_exists.stdout_lines is search("bluetooth.service", multiline=True) + tags: + - CCE-86761-4 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + +- name: Unit Socket Exists - bluetooth.socket + ansible.builtin.command: systemctl -q list-unit-files bluetooth.socket + register: socket_file_exists + changed_when: false + failed_when: socket_file_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_bluetooth_disabled | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86761-4 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + +- name: Disable Bluetooth Service - Disable Socket bluetooth + ansible.builtin.systemd: + name: bluetooth.socket + enabled: false + state: stopped + masked: true + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_bluetooth_disabled | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - socket_file_exists.stdout_lines is search("bluetooth.socket", multiline=True) + tags: + - CCE-86761-4 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + - name: Gather the package facts package_facts: manager: auto @@ -14556,6 +15254,51 @@ - medium_severity - no_reboot_needed +- name: Test for existence /etc/shells + stat: + path: /etc/shells + register: file_exists + tags: + - CCE-90434-2 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_groupowner_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + when: + - configure_strategy | bool + - file_groupowner_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + +- name: Ensure group owner 0 on /etc/shells + file: + path: /etc/shells + group: '0' + when: + - configure_strategy | bool + - file_groupowner_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90434-2 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_groupowner_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - name: Test for existence /etc/group- stat: path: /etc/group- @@ -14972,6 +15715,51 @@ - medium_severity - no_reboot_needed +- name: Test for existence /etc/shells + stat: + path: /etc/shells + register: file_exists + tags: + - CCE-90435-9 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_owner_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + when: + - configure_strategy | bool + - file_owner_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + +- name: Ensure owner 0 on /etc/shells + file: + path: /etc/shells + owner: '0' + when: + - configure_strategy | bool + - file_owner_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90435-9 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_owner_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - name: Test for existence /etc/group- stat: path: /etc/group- @@ -15386,6 +16174,51 @@ - medium_severity - no_reboot_needed +- name: Test for existence /etc/shells + stat: + path: /etc/shells + register: file_exists + tags: + - CCE-90432-6 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_permissions_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + when: + - configure_strategy | bool + - file_permissions_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/shells + file: + path: /etc/shells + mode: u-xs,g-xws,o-xwt + when: + - configure_strategy | bool + - file_permissions_etc_shells | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90432-6 + - NIST-800-53-AC-3 + - NIST-800-53-MP-2 + - configure_strategy + - file_permissions_etc_shells + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - name: Test for existence /sbin/auditctl stat: path: /sbin/auditctl @@ -16247,6 +17080,432 @@ - medium_severity - no_reboot_needed +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83850-8 + - DISA-STIG-RHEL-09-231040 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + when: + - DISA_STIG_RHEL_09_231040 | bool + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_autofs_disabled | bool + +- name: Disable the Automounter - Collect systemd Services Present in the System + ansible.builtin.command: systemctl -q list-unit-files --type service + register: service_exists + changed_when: false + failed_when: service_exists.rc not in [0, 1] + check_mode: false + when: + - DISA_STIG_RHEL_09_231040 | bool + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_autofs_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "autofs" in ansible_facts.packages + ) + tags: + - CCE-83850-8 + - DISA-STIG-RHEL-09-231040 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Disable the Automounter - Ensure autofs.service is Masked + ansible.builtin.systemd: + name: autofs.service + state: stopped + enabled: false + masked: true + when: + - DISA_STIG_RHEL_09_231040 | bool + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_autofs_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "autofs" in ansible_facts.packages + ) + - service_exists.stdout_lines is search("autofs.service", multiline=True) + tags: + - CCE-83850-8 + - DISA-STIG-RHEL-09-231040 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Unit Socket Exists - autofs.socket + ansible.builtin.command: systemctl -q list-unit-files autofs.socket + register: socket_file_exists + changed_when: false + failed_when: socket_file_exists.rc not in [0, 1] + check_mode: false + when: + - DISA_STIG_RHEL_09_231040 | bool + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_autofs_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "autofs" in ansible_facts.packages + ) + tags: + - CCE-83850-8 + - DISA-STIG-RHEL-09-231040 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Disable the Automounter - Disable Socket autofs + ansible.builtin.systemd: + name: autofs.socket + enabled: false + state: stopped + masked: true + when: + - DISA_STIG_RHEL_09_231040 | bool + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_autofs_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "autofs" in ansible_facts.packages + ) + - socket_file_exists.stdout_lines is search("autofs.socket", multiline=True) + tags: + - CCE-83850-8 + - DISA-STIG-RHEL-09-231040 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Ensure kernel module 'cramfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: install\s+cramfs + line: install cramfs /bin/false + when: + - DISA_STIG_RHEL_09_231195 | bool + - disable_strategy | bool + - kernel_module_cramfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83853-2 + - DISA-STIG-RHEL-09-231195 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'cramfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: ^blacklist cramfs$ + line: blacklist cramfs + when: + - DISA_STIG_RHEL_09_231195 | bool + - disable_strategy | bool + - kernel_module_cramfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83853-2 + - DISA-STIG-RHEL-09-231195 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'freevxfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: install\s+freevxfs + line: install freevxfs /bin/false + when: + - disable_strategy | bool + - kernel_module_freevxfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86763-0 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'freevxfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: ^blacklist freevxfs$ + line: blacklist freevxfs + when: + - disable_strategy | bool + - kernel_module_freevxfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86763-0 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: install\s+hfs + line: install hfs /bin/false + when: + - disable_strategy | bool + - kernel_module_hfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86764-8 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: ^blacklist hfs$ + line: blacklist hfs + when: + - disable_strategy | bool + - kernel_module_hfs_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86764-8 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfsplus' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: install\s+hfsplus + line: install hfsplus /bin/false + when: + - disable_strategy | bool + - kernel_module_hfsplus_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86765-5 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfsplus' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: ^blacklist hfsplus$ + line: blacklist hfsplus + when: + - disable_strategy | bool + - kernel_module_hfsplus_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86765-5 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'jffs2' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: install\s+jffs2 + line: install jffs2 /bin/false + when: + - disable_strategy | bool + - kernel_module_jffs2_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86766-3 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'jffs2' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: ^blacklist jffs2$ + line: blacklist jffs2 + when: + - disable_strategy | bool + - kernel_module_jffs2_disabled | bool + - low_complexity | bool + - low_severity | bool + - medium_disruption | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86766-3 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - name: Ensure kernel module 'squashfs' is disabled lineinfile: create: true @@ -19489,6 +20748,89 @@ - mount_option_var_tmp_nosuid - no_reboot_needed +- name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + - /usr/local/lib/sysctl.d/ + contains: ^[\s]*kernel.yama.ptrace_scope.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: + - DISA_STIG_RHEL_09_213080 | bool + - disable_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - sysctl_kernel_yama_ptrace_scope | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - DISA-STIG-RHEL-09-213080 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Comment out any occurrences of kernel.yama.ptrace_scope from config files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.yama.ptrace_scope + replace: '#kernel.yama.ptrace_scope' + loop: '{{ find_sysctl_d.files }}' + when: + - DISA_STIG_RHEL_09_213080 | bool + - disable_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - sysctl_kernel_yama_ptrace_scope | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - DISA-STIG-RHEL-09-213080 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Ensure sysctl kernel.yama.ptrace_scope is set to 1 + sysctl: + name: kernel.yama.ptrace_scope + value: '1' + sysctl_file: /etc/sysctl.conf + state: present + reload: true + when: + - DISA_STIG_RHEL_09_213080 | bool + - disable_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - sysctl_kernel_yama_ptrace_scope | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - DISA-STIG-RHEL-09-213080 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + - name: Gather the package facts package_facts: manager: auto @@ -19513,31 +20855,14 @@ - no_reboot_needed | bool - restrict_strategy | bool -- name: Disable core dump backtraces - block: - - name: Check for duplicate values - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*ProcessSizeMax\s*=\s* - state: absent - check_mode: true - changed_when: false - register: dupes - - name: Deduplicate values from /etc/systemd/coredump.conf - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*ProcessSizeMax\s*=\s* - state: absent - when: dupes.found is defined and dupes.found > 1 - - name: Insert correct line to /etc/systemd/coredump.conf - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*ProcessSizeMax\s*=\s* - line: ProcessSizeMax=0 - state: present +- name: Set 'ProcessSizeMax' to '0' in the [Coredump] section of '/etc/systemd/coredump.conf' + ini_file: + path: /etc/systemd/coredump.conf + section: Coredump + option: ProcessSizeMax + value: '0' + create: true + mode: 420 when: - DISA_STIG_RHEL_09_213085 | bool - coredump_disable_backtraces | bool @@ -19584,31 +20909,14 @@ - no_reboot_needed | bool - restrict_strategy | bool -- name: Disable storing core dump - block: - - name: Check for duplicate values - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*Storage\s*=\s* - state: absent - check_mode: true - changed_when: false - register: dupes - - name: Deduplicate values from /etc/systemd/coredump.conf - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*Storage\s*=\s* - state: absent - when: dupes.found is defined and dupes.found > 1 - - name: Insert correct line to /etc/systemd/coredump.conf - lineinfile: - path: /etc/systemd/coredump.conf - create: false - regexp: ^\s*Storage\s*=\s* - line: Storage=none - state: present +- name: Set 'Storage' to 'none' in the [Coredump] section of '/etc/systemd/coredump.conf' + ini_file: + path: /etc/systemd/coredump.conf + section: Coredump + option: Storage + value: none + create: true + mode: 420 when: - DISA_STIG_RHEL_09_213090 | bool - coredump_disable_storage | bool @@ -20129,40 +21437,154 @@ - restrict_strategy - selinux_state -- name: Ensure avahi is removed - package: - name: avahi - state: absent +- name: Gather the package facts + package_facts: + manager: auto tags: - - CCE-86513-9 + - CCE-90824-4 - NIST-800-53-CM-6(a) - NIST-800-53-CM-7(a) - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 - disable_strategy - low_complexity - low_disruption - medium_severity - no_reboot_needed - - package_avahi_removed + - service_avahi-daemon_disabled when: - disable_strategy | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - package_avahi_removed | bool + - service_avahi_daemon_disabled | bool -- name: Enable service crond +- name: Disable Avahi Server Software - Collect systemd Services Present in the System + ansible.builtin.command: systemctl -q list-unit-files --type service + register: service_exists + changed_when: false + failed_when: service_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_avahi_daemon_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "avahi" in ansible_facts.packages + ) + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Disable Avahi Server Software - Ensure avahi-daemon.service is Masked + ansible.builtin.systemd: + name: avahi-daemon.service + state: stopped + enabled: false + masked: true + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_avahi_daemon_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "avahi" in ansible_facts.packages + ) + - service_exists.stdout_lines is search("avahi-daemon.service", multiline=True) + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Unit Socket Exists - avahi-daemon.socket + ansible.builtin.command: systemctl -q list-unit-files avahi-daemon.socket + register: socket_file_exists + changed_when: false + failed_when: socket_file_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_avahi_daemon_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "avahi" in ansible_facts.packages + ) + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Disable Avahi Server Software - Disable Socket avahi-daemon + ansible.builtin.systemd: + name: avahi-daemon.socket + enabled: false + state: stopped + masked: true + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - service_avahi_daemon_disabled | bool + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] and "avahi" in ansible_facts.packages + ) + - socket_file_exists.stdout_lines is search("avahi-daemon.socket", multiline=True) + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Enable cron Service - Enable service crond block: - name: Gather the package facts package_facts: manager: auto - - name: Enable service crond - systemd: + - name: Enable cron Service - Enable Service crond + ansible.builtin.systemd: name: crond - enabled: 'yes' + enabled: true state: started - masked: 'no' + masked: false when: - '"cronie" in ansible_facts.packages' when: @@ -21500,7 +22922,7 @@ - no_reboot_needed - service_rpcbind_disabled -- name: Disable rpcbind Service - Ensure "rpcbind.service" is Masked +- name: Disable rpcbind Service - Ensure rpcbind.service is Masked ansible.builtin.systemd: name: rpcbind.service state: stopped @@ -21514,7 +22936,7 @@ - no_reboot_needed | bool - service_rpcbind_disabled | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - service_exists.stdout_lines is search("rpcbind.service",multiline=True) + - service_exists.stdout_lines is search("rpcbind.service", multiline=True) tags: - CCE-84245-0 - PCI-DSSv4-2.2.4 @@ -21549,12 +22971,12 @@ - no_reboot_needed - service_rpcbind_disabled -- name: Disable socket rpcbind +- name: Disable rpcbind Service - Disable Socket rpcbind ansible.builtin.systemd: name: rpcbind.socket - enabled: 'no' + enabled: false state: stopped - masked: 'yes' + masked: true when: - disable_strategy | bool - low_complexity | bool @@ -21563,7 +22985,7 @@ - no_reboot_needed | bool - service_rpcbind_disabled | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - socket_file_exists.stdout_lines is search("rpcbind.socket",multiline=True) + - socket_file_exists.stdout_lines is search("rpcbind.socket", multiline=True) tags: - CCE-84245-0 - PCI-DSSv4-2.2.4 @@ -21600,7 +23022,7 @@ - service_nfs_disabled - unknown_severity -- name: Disable Network File System (nfs) - Ensure "nfs-server.service" is Masked +- name: Disable Network File System (nfs) - Ensure nfs-server.service is Masked ansible.builtin.systemd: name: nfs-server.service state: stopped @@ -21614,7 +23036,7 @@ - service_nfs_disabled | bool - unknown_severity | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - service_exists.stdout_lines is search("nfs-server.service",multiline=True) + - service_exists.stdout_lines is search("nfs-server.service", multiline=True) tags: - CCE-90850-9 - NIST-800-53-CM-6(a) @@ -21653,12 +23075,12 @@ - service_nfs_disabled - unknown_severity -- name: Disable socket nfs-server +- name: Disable Network File System (nfs) - Disable Socket nfs-server ansible.builtin.systemd: name: nfs-server.socket - enabled: 'no' + enabled: false state: stopped - masked: 'yes' + masked: true when: - disable_strategy | bool - low_complexity | bool @@ -21667,7 +23089,7 @@ - service_nfs_disabled | bool - unknown_severity | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - socket_file_exists.stdout_lines is search("nfs-server.socket",multiline=True) + - socket_file_exists.stdout_lines is search("nfs-server.socket", multiline=True) tags: - CCE-90850-9 - NIST-800-53-CM-6(a) @@ -21767,6 +23189,76 @@ - medium_severity - no_reboot_needed +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84108-0 + - PCI-DSSv4-10.6.3 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + when: + - chronyd_run_as_chrony_user | bool + - configure_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + +- name: Detect if file /etc/sysconfig/chronyd is not empty or missing + find: + path: /etc/sysconfig/ + patterns: chronyd + contains: ^([\s]*OPTIONS=["]?[^"]*)("?) + register: chronyd_file + when: + - chronyd_run_as_chrony_user | bool + - configure_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + tags: + - CCE-84108-0 + - PCI-DSSv4-10.6.3 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Remove any previous configuration of user used to run chronyd process + replace: + path: /etc/sysconfig/chronyd + regexp: \s*-u\s*\w+\s* + replace: ' ' + when: + - chronyd_run_as_chrony_user | bool + - configure_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + - chronyd_file is defined and chronyd_file.matched > 0 + tags: + - CCE-84108-0 + - PCI-DSSv4-10.6.3 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - name: Ensure rsync-daemon is removed package: name: rsync-daemon @@ -21787,6 +23279,80 @@ - no_reboot_needed | bool - package_rsync_removed | bool +- name: Ensure xinetd is removed + package: + name: xinetd + state: absent + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - low_severity | bool + - no_reboot_needed | bool + - package_xinetd_removed | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84155-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_xinetd_removed + +- name: Ensure ypbind is removed + package: + name: ypbind + state: absent + tags: + - CCE-84151-0 + - PCI-DSSv4-2.2.4 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypbind_removed + - unknown_severity + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - no_reboot_needed | bool + - package_ypbind_removed | bool + - unknown_severity | bool + +- name: Ensure ypserv is removed + package: + name: ypserv + state: absent + tags: + - CCE-84152-8 + - DISA-STIG-RHEL-09-215030 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - PCI-DSS-Req-2.2.2 + - PCI-DSSv4-2.2.4 + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypserv_removed + when: + - DISA_STIG_RHEL_09_215030 | bool + - disable_strategy | bool + - high_severity | bool + - low_complexity | bool + - low_disruption | bool + - no_reboot_needed | bool + - package_ypserv_removed | bool + - name: Gather the package facts package_facts: manager: auto @@ -21986,12 +23552,22 @@ - no_reboot_needed | bool - package_tftp_removed | bool -- name: Ensure cups is removed - package: - name: cups - state: absent +- name: Disable the CUPS Service - Collect systemd Services Present in the System + ansible.builtin.command: systemctl -q list-unit-files --type service + register: service_exists + changed_when: false + failed_when: service_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - no_reboot_needed | bool + - service_cups_disabled | bool + - unknown_severity | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] tags: - - CCE-86300-1 + - CCE-90795-6 - NIST-800-53-CM-6(a) - NIST-800-53-CM-7(a) - NIST-800-53-CM-7(b) @@ -21999,15 +23575,88 @@ - low_complexity - low_disruption - no_reboot_needed - - package_cups_removed + - service_cups_disabled - unknown_severity + +- name: Disable the CUPS Service - Ensure cups.service is Masked + ansible.builtin.systemd: + name: cups.service + state: stopped + enabled: false + masked: true when: - disable_strategy | bool - low_complexity | bool - low_disruption | bool - no_reboot_needed | bool - - package_cups_removed | bool + - service_cups_disabled | bool - unknown_severity | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - service_exists.stdout_lines is search("cups.service", multiline=True) + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Unit Socket Exists - cups.socket + ansible.builtin.command: systemctl -q list-unit-files cups.socket + register: socket_file_exists + changed_when: false + failed_when: socket_file_exists.rc not in [0, 1] + check_mode: false + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - no_reboot_needed | bool + - service_cups_disabled | bool + - unknown_severity | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Disable the CUPS Service - Disable Socket cups + ansible.builtin.systemd: + name: cups.socket + enabled: false + state: stopped + masked: true + when: + - disable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - no_reboot_needed | bool + - service_cups_disabled | bool + - unknown_severity | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - socket_file_exists.stdout_lines is search("cups.socket", multiline=True) + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity - name: Ensure squid is removed package: @@ -22124,7 +23773,7 @@ - no_reboot_needed - name: Find /etc/ssh/ file(s) matching ^.*_key$ - command: find -H /etc/ssh/ -maxdepth 1 -type f ! -group ssh_keys -regex "^.*_key$" + command: find -H /etc/ssh/ -maxdepth 1 -type f ! -group ssh_keys -regextype posix-extended -regex "^.*_key$" register: files_found changed_when: false failed_when: false @@ -22171,7 +23820,7 @@ - no_reboot_needed - name: Find /etc/ssh/ file(s) matching ^.*\.pub$ - command: find -H /etc/ssh/ -maxdepth 1 -type f ! -group 0 -regex "^.*\.pub$" + command: find -H /etc/ssh/ -maxdepth 1 -type f ! -group 0 -regextype posix-extended -regex "^.*\.pub$" register: files_found changed_when: false failed_when: false @@ -22271,7 +23920,7 @@ - no_reboot_needed - name: Find /etc/ssh/ file(s) matching ^.*_key$ - command: find -H /etc/ssh/ -maxdepth 1 -type f ! -uid 0 -regex "^.*_key$" + command: find -H /etc/ssh/ -maxdepth 1 -type f ! -uid 0 -regextype posix-extended -regex "^.*_key$" register: files_found changed_when: false failed_when: false @@ -22318,7 +23967,7 @@ - no_reboot_needed - name: Find /etc/ssh/ file(s) matching ^.*\.pub$ - command: find -H /etc/ssh/ -maxdepth 1 -type f ! -uid 0 -regex "^.*\.pub$" + command: find -H /etc/ssh/ -maxdepth 1 -type f ! -uid 0 -regextype posix-extended -regex "^.*\.pub$" register: files_found changed_when: false failed_when: false @@ -22550,7 +24199,7 @@ - no_reboot_needed - name: Find /etc/ssh/ file(s) - command: find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regex "^.*\.pub$" + command: find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regextype posix-extended -regex "^.*\.pub$" register: files_found changed_when: false failed_when: false @@ -22855,6 +24504,67 @@ - restrict_strategy - sshd_disable_empty_passwords +- name: Disable GSSAPI Authentication + block: + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + - name: Check if the parameter GSSAPIAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + line: GSSAPIAuthentication no + state: present + insertbefore: BOF + validate: /usr/sbin/sshd -t -f %s + when: + - DISA_STIG_RHEL_09_255135 | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - sshd_disable_gssapi_auth | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90808-7 + - DISA-STIG-RHEL-09-255135 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_gssapi_auth + - name: Disable SSH Support for .rhosts Files block: - name: Deduplicate values from /etc/ssh/sshd_config @@ -22986,119 +24696,6 @@ - restrict_strategy - sshd_disable_root_login -- name: Disable SSH TCP Forwarding - block: - - name: Deduplicate values from /etc/ssh/sshd_config - lineinfile: - path: /etc/ssh/sshd_config - create: false - regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ - state: absent - - name: Check if /etc/ssh/sshd_config.d exists - stat: - path: /etc/ssh/sshd_config.d - register: _etc_ssh_sshd_config_d_exists - - name: Check if the parameter AllowTcpForwarding is present in /etc/ssh/sshd_config.d - find: - paths: /etc/ssh/sshd_config.d - recurse: 'yes' - follow: 'no' - contains: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ - register: _etc_ssh_sshd_config_d_has_parameter - when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir - - name: Remove parameter from files in /etc/ssh/sshd_config.d - lineinfile: - path: '{{ item.path }}' - create: false - regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ - state: absent - with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' - when: _etc_ssh_sshd_config_d_has_parameter.matched - - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf - lineinfile: - path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf - create: true - regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ - line: AllowTcpForwarding no - state: present - insertbefore: BOF - validate: /usr/sbin/sshd -t -f %s - when: - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - sshd_disable_tcp_forwarding | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-90806-1 - - PCI-DSSv4-2.2.6 - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - - sshd_disable_tcp_forwarding - -- name: Disable X11 Forwarding - block: - - name: Deduplicate values from /etc/ssh/sshd_config - lineinfile: - path: /etc/ssh/sshd_config - create: false - regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ - state: absent - - name: Check if /etc/ssh/sshd_config.d exists - stat: - path: /etc/ssh/sshd_config.d - register: _etc_ssh_sshd_config_d_exists - - name: Check if the parameter X11Forwarding is present in /etc/ssh/sshd_config.d - find: - paths: /etc/ssh/sshd_config.d - recurse: 'yes' - follow: 'no' - contains: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ - register: _etc_ssh_sshd_config_d_has_parameter - when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir - - name: Remove parameter from files in /etc/ssh/sshd_config.d - lineinfile: - path: '{{ item.path }}' - create: false - regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ - state: absent - with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' - when: _etc_ssh_sshd_config_d_has_parameter.matched - - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf - lineinfile: - path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf - create: true - regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ - line: X11Forwarding no - state: present - insertbefore: BOF - validate: /usr/sbin/sshd -t -f %s - when: - - DISA_STIG_RHEL_09_255155 | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - sshd_disable_x11_forwarding | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - tags: - - CCE-90798-0 - - DISA-STIG-RHEL-09-255155 - - NIST-800-53-CM-6(b) - - PCI-DSSv4-2.2.6 - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - - sshd_disable_x11_forwarding - - name: Do Not Allow SSH Environment Options block: - name: Deduplicate values from /etc/ssh/sshd_config @@ -23521,6 +25118,96 @@ - restrict_strategy - sshd_set_maxstartups +- name: Use Only Strong Key Exchange algorithms + block: + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*KexAlgorithms\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*KexAlgorithms\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*KexAlgorithms\s+ + line: KexAlgorithms {{ sshd_strong_kex }} + state: present + insertbefore: BOF + validate: /usr/sbin/sshd -t -f %s + when: + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - sshd_use_strong_kex | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86768-9 + - PCI-DSS-Req-2.3 + - PCI-DSSv4-2.2.7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_strong_kex + +- name: Use Only Strong MACs + block: + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MACs\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MACs\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MACs\s+ + line: MACs {{ sshd_strong_macs }} + state: present + insertbefore: BOF + validate: /usr/sbin/sshd -t -f %s + when: + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - sshd_use_strong_macs | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86769-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_strong_macs + - name: Ensure xorg-x11-server-common is removed package: name: xorg-x11-server-common @@ -23544,6 +25231,63 @@ - no_reboot_needed | bool - package_xorg_x11_server_common_removed | bool +- name: Switch to multi-user runlevel + file: + src: /usr/lib/systemd/system/multi-user.target + dest: /etc/systemd/system/default.target + state: link + force: true + when: + - DISA_STIG_RHEL_09_211030 | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - reboot_required | bool + - restrict_strategy | bool + - xwindows_runlevel_target | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84105-6 + - DISA-STIG-RHEL-09-211030 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - xwindows_runlevel_target + +- name: Ensure audit-libs is installed + package: + name: audit-libs + state: present + when: + - enable_strategy | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - package_audit_libs_installed | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86772-1 + - NIST-800-53-AC-7(a) + - NIST-800-53-AU-12(2) + - NIST-800-53-AU-14 + - NIST-800-53-AU-2(a) + - NIST-800-53-AU-7(1) + - NIST-800-53-AU-7(2) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audit-libs_installed + - name: Ensure audit is installed package: name: audit @@ -23612,17 +25356,17 @@ - no_reboot_needed | bool - service_auditd_enabled | bool -- name: Enable service auditd +- name: Enable auditd Service - Enable service auditd block: - name: Gather the package facts package_facts: manager: auto - - name: Enable service auditd - systemd: + - name: Enable auditd Service - Enable Service auditd + ansible.builtin.systemd: name: auditd - enabled: 'yes' + enabled: true state: started - masked: 'no' + masked: false when: - '"audit" in ansible_facts.packages' when: @@ -29064,8 +30808,8 @@ - medium_severity | bool - no_reboot_needed | bool -- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ - command: find -H /etc/audit/ -maxdepth 1 -type f ! -group 0 -regex "^audit(\.rules|d\.conf)$" +- name: Find /etc/audit/ file(s) matching ^.*audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -group 0 -regextype posix-extended -regex "^.*audit(\.rules|d\.conf)$" register: files_found changed_when: false failed_when: false @@ -29088,7 +30832,7 @@ - medium_severity - no_reboot_needed -- name: Ensure group owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ +- name: Ensure group owner on /etc/audit/ file(s) matching ^.*audit(\.rules|d\.conf)$ file: path: '{{ item }}' group: '0' @@ -29114,7 +30858,7 @@ - no_reboot_needed - name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ - command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -group 0 -regex "^.*\.rules$" + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -group 0 -regextype posix-extended -regex "^.*\.rules$" register: files_found changed_when: false failed_when: false @@ -29181,8 +30925,8 @@ - medium_severity | bool - no_reboot_needed | bool -- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ - command: find -H /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regex "^audit(\.rules|d\.conf)$" +- name: Find /etc/audit/ file(s) matching ^.*audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regextype posix-extended -regex "^.*audit(\.rules|d\.conf)$" register: files_found changed_when: false failed_when: false @@ -29205,7 +30949,7 @@ - medium_severity - no_reboot_needed -- name: Ensure owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ +- name: Ensure owner on /etc/audit/ file(s) matching ^.*audit(\.rules|d\.conf)$ file: path: '{{ item }}' owner: '0' @@ -29231,7 +30975,7 @@ - no_reboot_needed - name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ - command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regex "^.*\.rules$" + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regextype posix-extended -regex "^.*\.rules$" register: files_found changed_when: false failed_when: false @@ -29299,7 +31043,7 @@ - no_reboot_needed | bool - name: Find /etc/audit/ file(s) - command: find -H /etc/audit/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex ".*audit\(\.rules\|d\.conf\)$" + command: find -H /etc/audit/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regextype posix-extended -regex "^.*audit(\.rules|d\.conf)$" register: files_found changed_when: false failed_when: false @@ -29348,7 +31092,7 @@ - no_reboot_needed - name: Find /etc/audit/rules.d/ file(s) - command: find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex ".*\.rules$" + command: find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regextype posix-extended -regex "^.*\.rules$" register: files_found changed_when: false failed_when: false @@ -42652,155 +44396,6 @@ - no_reboot_needed - restrict_strategy -- name: Gather the package facts - package_facts: - manager: auto - tags: - - CCE-83835-9 - - CJIS-5.4.1.1 - - NIST-800-171-3.1.7 - - NIST-800-53-AC-6(9) - - NIST-800-53-AU-12(c) - - NIST-800-53-AU-2(d) - - NIST-800-53-CM-6(a) - - PCI-DSS-Req-10.4.2.b - - PCI-DSSv4-10.6.3 - - audit_rules_time_stime - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - when: - - audit_rules_time_stime | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - -- name: Perform remediation of Audit rules for stime syscall for x86 platform - block: - - name: Declare list of syscalls - set_fact: - syscalls: - - stime - syscall_grouping: - - adjtimex - - settimeofday - - stime - - name: Check existence of stime in /etc/audit/rules.d/ - find: - paths: /etc/audit/rules.d - contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ - patterns: '*.rules' - register: find_command - loop: '{{ (syscall_grouping + syscalls) | unique }}' - - name: Reset syscalls found per file - set_fact: - syscalls_per_file: {} - found_paths_dict: {} - - name: Declare syscalls found per file - set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path :[item.item] + syscalls_per_file.get(item.files[0].path, - []) } ) }}" - loop: '{{ find_command.results | selectattr(''matched'') | list }}' - - name: Declare files where syscalls were found - set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten | map(attribute='path') | list }}" - - name: Count occurrences of syscalls in paths - set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, 0) }) }}" - loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') | list }}' - - name: Get path with most syscalls - set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') | last).key }}" - when: found_paths | length >= 1 - - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules - set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" - when: found_paths | length == 0 - - name: Declare found syscalls - set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') | list }}" - - name: Declare missing syscalls - set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" - - name: Replace the audit rule in {{ audit_file }} - lineinfile: - path: '{{ audit_file }}' - regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] | join("|") }}))\b)((?:( -S - |,)\w+)+)( (?:-k |-F key=)\w+) - line: \1\2\3{{ missing_syscalls | join("\3") }}\4 - backrefs: true - state: present - when: syscalls_found | length > 0 and missing_syscalls | length > 0 - - name: Add the audit rule to {{ audit_file }} - lineinfile: - path: '{{ audit_file }}' - line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules - create: true - mode: o-rwx - state: present - when: syscalls_found | length == 0 - - name: Declare list of syscalls - set_fact: - syscalls: - - stime - syscall_grouping: - - adjtimex - - settimeofday - - stime - - name: Check existence of stime in /etc/audit/audit.rules - find: - paths: /etc/audit - contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ - patterns: audit.rules - register: find_command - loop: '{{ (syscall_grouping + syscalls) | unique }}' - - name: Set path to /etc/audit/audit.rules - set_fact: audit_file="/etc/audit/audit.rules" - - name: Declare found syscalls - set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') | list }}" - - name: Declare missing syscalls - set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" - - name: Replace the audit rule in {{ audit_file }} - lineinfile: - path: '{{ audit_file }}' - regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k - |-F key=)\w+) - line: \1\2\3{{ missing_syscalls | join("\3") }}\4 - backrefs: true - state: present - when: syscalls_found | length > 0 and missing_syscalls | length > 0 - - name: Add the audit rule to {{ audit_file }} - lineinfile: - path: '{{ audit_file }}' - line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules - create: true - mode: o-rwx - state: present - when: syscalls_found | length == 0 - when: - - audit_rules_time_stime | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - '"audit" in ansible_facts.packages' - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == "s390x" ) ) - tags: - - CCE-83835-9 - - CJIS-5.4.1.1 - - NIST-800-171-3.1.7 - - NIST-800-53-AC-6(9) - - NIST-800-53-AU-12(c) - - NIST-800-53-AU-2(d) - - NIST-800-53-CM-6(a) - - PCI-DSS-Req-10.4.2.b - - PCI-DSSv4-10.6.3 - - audit_rules_time_stime - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - - name: Gather the package facts package_facts: manager: auto @@ -43056,6 +44651,114 @@ - no_reboot_needed - restrict_strategy +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83690-8 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - auditd_data_disk_error_action | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Configure auditd Disk Error Action on Disk Error + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_error_action = {{ var_auditd_disk_error_action.split('|')[0] }} + regexp: ^\s*disk_error_action\s*=\s*.*$ + state: present + create: true + when: + - auditd_data_disk_error_action | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83690-8 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83684-1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + when: + - auditd_data_disk_full_action | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + +- name: Configure auditd Disk Full Action when Disk Space Is Full + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_full_action = {{ var_auditd_disk_full_action.split('|')[0] }} + regexp: ^\s*disk_full_action\s*=\s*.*$ + state: present + create: true + when: + - auditd_data_disk_full_action | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83684-1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - name: Gather the package facts package_facts: manager: auto