From ed14f7826e8a9720bce3420e4db638a56c870c81 Mon Sep 17 00:00:00 2001 From: ComplianceAsCode development team Date: Tue, 9 Aug 2022 11:24:41 -0400 Subject: [PATCH] Updated tasks/main.yml --- tasks/main.yml | 2035 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 1439 insertions(+), 596 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index fd6a69b..bc0d663 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -3,6 +3,7 @@ name: aide state: present when: + - DISA_STIG_RHEL_08_010359 | bool - enable_strategy | bool - low_complexity | bool - low_disruption | bool @@ -46,6 +47,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_010020 | bool - configure_crypto_policy | bool - high_severity | bool - low_complexity | bool @@ -72,6 +74,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_010020 | bool - configure_crypto_policy | bool - high_severity | bool - low_complexity | bool @@ -99,6 +102,7 @@ - medium_severity - reboot_required when: + - DISA_STIG_RHEL_08_010287 | bool - configure_ssh_crypto_policy | bool - disable_strategy | bool - low_complexity | bool @@ -131,6 +135,7 @@ - medium_disruption - no_reboot_needed when: + - DISA_STIG_RHEL_08_010370 | bool - configure_strategy | bool - ensure_gpgcheck_globally_activated | bool - high_severity | bool @@ -147,6 +152,7 @@ no_extra_spaces: true create: false when: + - DISA_STIG_RHEL_08_010370 | bool - configure_strategy | bool - ensure_gpgcheck_globally_activated | bool - high_severity | bool @@ -188,6 +194,7 @@ tags: - CCE-80792-5 - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 - NIST-800-171-3.4.8 - NIST-800-53-CM-11(a) - NIST-800-53-CM-11(b) @@ -206,6 +213,7 @@ - medium_disruption - no_reboot_needed when: + - DISA_STIG_RHEL_08_010370 | bool - enable_strategy | bool - ensure_gpgcheck_never_disabled | bool - high_severity | bool @@ -224,6 +232,7 @@ tags: - CCE-80792-5 - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 - NIST-800-171-3.4.8 - NIST-800-53-CM-11(a) - NIST-800-53-CM-11(b) @@ -242,6 +251,7 @@ - medium_disruption - no_reboot_needed when: + - DISA_STIG_RHEL_08_010370 | bool - enable_strategy | bool - ensure_gpgcheck_never_disabled | bool - high_severity | bool @@ -417,6 +427,7 @@ - security_patches_up_to_date - skip_ansible_lint when: + - DISA_STIG_RHEL_08_010010 | bool - high_disruption | bool - low_complexity | bool - medium_severity | bool @@ -425,431 +436,101 @@ - security_patches_up_to_date | bool - skip_ansible_lint | bool -- name: Gather the package facts - package_facts: - manager: auto +- name: Select authselect profile + ansible.builtin.command: + cmd: authselect select "{{ var_authselect_profile }}" + ignore_errors: true + register: result_authselect_select tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember + - CCE-88248-0 + - NIST-800-53-AC-3 - configure_strategy + - enable_authselect - low_complexity - medium_disruption - medium_severity - no_reboot_needed when: - - accounts_password_pam_unix_remember | bool - configure_strategy | bool + - enable_authselect | bool - low_complexity | bool - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool -- name: Check if system relies on authselect - ansible.builtin.stat: - path: /usr/bin/authselect - register: result_authselect_present - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Check the integrity of the current authselect profile +- name: Verify if PAM has been altered ansible.builtin.command: - cmd: authselect check - register: result_authselect_check_cmd - changed_when: false + cmd: rpm -qV pam + register: result_altered_authselect ignore_errors: true + args: + warn: false when: - - accounts_password_pam_unix_remember | bool - configure_strategy | bool + - enable_authselect | bool - low_complexity | bool - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_present.stat.exists + - result_authselect_select is failed tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember + - CCE-88248-0 + - NIST-800-53-AC-3 - configure_strategy + - enable_authselect - low_complexity - medium_disruption - medium_severity - no_reboot_needed -- name: Informative message based on the authselect integrity check result +- name: Informative message based on the authselect integrity check ansible.builtin.assert: that: - - result_authselect_check_cmd is success + - result_altered_authselect is success fail_msg: - - authselect integrity check failed. Remediation aborted! - - This remediation could not be applied because the authselect profile is not intact. - - It is not recommended to manually edit the PAM files when authselect 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 - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Get authselect current profile - ansible.builtin.shell: - cmd: authselect current -r | awk '{ print $1 }' - register: result_authselect_profile - changed_when: false - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_present.stat.exists - - result_authselect_check_cmd is success - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: 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: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is match("custom/") + - Files in the 'pam' package have been altered, so the authselect configuration won't be forced. tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: 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: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - result_authselect_profile.stdout is not match("custom/") - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: 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: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - authselect_current_profile is not match("custom/") - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Check if any custom profile with the same name was already created in the past - ansible.builtin.stat: - path: /etc/authselect/{{ authselect_custom_profile }} - register: result_authselect_custom_profile_present - changed_when: false - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_present.stat.exists - - authselect_current_profile is not match("custom/") - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Create a custom profile based on the current profile - ansible.builtin.command: - cmd: authselect create-profile hardening -b sssd - register: result_authselect_create_profile - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_present.stat.exists - - result_authselect_check_cmd is success - - authselect_current_profile is not match("custom/") - - not result_authselect_custom_profile_present.stat.exists - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure the desired configuration is updated in the custom profile - ansible.builtin.replace: - dest: '{{ item }}' - regexp: (.*pam_pwhistory.so.*remember=)(\S+)(.*)$ - replace: \g<1>{{ var_password_pam_unix_remember }}\g<3> - loop: - - /etc/authselect/{{ authselect_custom_profile }}/system-auth - - /etc/authselect/{{ authselect_custom_profile }}/password-auth - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - authselect_custom_profile is match("custom/") - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed - -- name: Ensure the desired configuration in present in the custom profile - ansible.builtin.lineinfile: - dest: '{{ item }}' - insertafter: ^password.*requisite.*pam_pwquality.so.* - line: password requisite pam_pwhistory.so remember={{ var_password_pam_unix_remember }} use_authtok - loop: - - /etc/authselect/{{ authselect_custom_profile }}/system-auth - - /etc/authselect/{{ authselect_custom_profile }}/password-auth - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - authselect_custom_profile is match("custom/") - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember + - CCE-88248-0 + - NIST-800-53-AC-3 - configure_strategy + - enable_authselect - low_complexity - medium_disruption - medium_severity - no_reboot_needed - -- name: Ensure a backup of current authselect profile before select the custom profile - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=before-pwhistory-hardening.backup - register: result_authselect_backup when: - - accounts_password_pam_unix_remember | bool - configure_strategy | bool + - enable_authselect | bool - low_complexity | bool - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - 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) - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed -- name: Ensure the custom profile is selected +- name: Force authselect profile select ansible.builtin.command: - cmd: authselect select {{ authselect_custom_profile }} --force - register: result_pam_authselect_select_profile + cmd: authselect select --force "{{ var_authselect_profile }}" when: - - accounts_password_pam_unix_remember | bool - configure_strategy | bool + - enable_authselect | bool - low_complexity | bool - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - 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) + - result_altered_authselect is success + - result_authselect_select is failed tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember + - CCE-88248-0 + - NIST-800-53-AC-3 - configure_strategy + - enable_authselect - low_complexity - medium_disruption - medium_severity - no_reboot_needed -- name: Restore the authselect features in the custom profile - ansible.builtin.command: - cmd: authselect enable-feature {{ item }} - register: result_pam_authselect_select_features - loop: '{{ result_authselect_features.stdout_lines }}' - when: - - accounts_password_pam_unix_remember | bool - - configure_strategy | bool - - low_complexity | bool - - medium_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_profile is not skipped - - result_authselect_features is not skipped - - result_pam_authselect_select_profile is not skipped +- name: Gather the package facts + package_facts: + manager: auto tags: - CCE-80666-1 - CJIS-5.6.2.1.1 @@ -863,10 +544,6 @@ - medium_disruption - medium_severity - no_reboot_needed - -- name: Ensure the custom profile changes are applied - ansible.builtin.command: - cmd: authselect apply-changes -b --backup=after-pwhistory-hardening.backup when: - accounts_password_pam_unix_remember | bool - configure_strategy | bool @@ -874,28 +551,11 @@ - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - '"pam" in ansible_facts.packages' - - result_authselect_check_cmd is success - - result_authselect_profile is not skipped - tags: - - CCE-80666-1 - - CJIS-5.6.2.1.1 - - NIST-800-171-3.5.8 - - NIST-800-53-IA-5(1)(e) - - NIST-800-53-IA-5(f) - - PCI-DSS-Req-8.2.5 - - accounts_password_pam_unix_remember - - configure_strategy - - low_complexity - - medium_disruption - - medium_severity - - no_reboot_needed -- name: Do not allow users to reuse recent passwords - system-auth (change) - replace: - dest: /etc/pam.d/system-auth - regexp: ^(password\s+sufficient\s+pam_unix\.so\s.*remember\s*=\s*)(\S+)(.*)$ - replace: \g<1>{{ var_password_pam_unix_remember }}\g<3> +- name: Limit Password Reuse - 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: - accounts_password_pam_unix_remember | bool - configure_strategy | bool @@ -904,7 +564,6 @@ - medium_severity | bool - no_reboot_needed | bool - '"pam" in ansible_facts.packages' - - not result_authselect_present.stat.exists tags: - CCE-80666-1 - CJIS-5.6.2.1.1 @@ -919,11 +578,194 @@ - medium_severity - no_reboot_needed -- name: Do not allow users to reuse recent passwords - system-auth (add) - replace: - dest: /etc/pam.d/system-auth - regexp: ^password\s+sufficient\s+pam_unix\.so\s(?!.*remember\s*=\s*).*$ - replace: \g<0> remember={{ var_password_pam_unix_remember }} +- name: Limit Password Reuse - Check the proper remediation for the system + block: + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Limit Password Reuse - Remediate using authselect + block: + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + - name: Limit Password Reuse - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - Check if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path }} + block: + - name: Limit Password Reuse - Check if required PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + - name: Limit Password Reuse - Ensure the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + - name: Limit Password Reuse - Ensure the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality.so + line: password requisite 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 + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + - name: Limit Password Reuse - 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\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + - name: Limit Password Reuse - 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\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + - name: Limit Password Reuse - 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\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or (result_pam_remember_edit is defined and + result_pam_remember_edit.changed) when: - accounts_password_pam_unix_remember | bool - configure_strategy | bool @@ -932,7 +774,7 @@ - medium_severity | bool - no_reboot_needed | bool - '"pam" in ansible_facts.packages' - - not result_authselect_present.stat.exists + - result_pam_file_present.stat.exists tags: - CCE-80666-1 - CJIS-5.6.2.1.1 @@ -947,11 +789,10 @@ - medium_severity - no_reboot_needed -- name: Do not allow users to reuse recent passwords - system-auth (change) - replace: - dest: /etc/pam.d/system-auth - regexp: ^(password\s+(?:(?:requisite)|(?:required))\s+pam_pwhistory\.so\s.*remember\s*=\s*)(\S+)(.*)$ - replace: \g<1>{{ var_password_pam_unix_remember }}\g<3> +- name: Limit Password Reuse - 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: - accounts_password_pam_unix_remember | bool - configure_strategy | bool @@ -960,7 +801,6 @@ - medium_severity | bool - no_reboot_needed | bool - '"pam" in ansible_facts.packages' - - not result_authselect_present.stat.exists tags: - CCE-80666-1 - CJIS-5.6.2.1.1 @@ -975,11 +815,194 @@ - medium_severity - no_reboot_needed -- name: Do not allow users to reuse recent passwords - system-auth (add) - replace: - dest: /etc/pam.d/system-auth - regexp: ^password\s+(?:(?:requisite)|(?:required))\s+pam_pwhistory\.so\s(?!.*remember\s*=\s*).*$ - replace: \g<0> remember={{ var_password_pam_unix_remember }} +- name: Limit Password Reuse - Check the proper remediation for the system + block: + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Limit Password Reuse - Remediate using authselect + block: + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + - name: Limit Password Reuse - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - 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: Limit Password Reuse - Check if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path }} + block: + - name: Limit Password Reuse - Check if required PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + - name: Limit Password Reuse - Ensure the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + - name: Limit Password Reuse - Ensure the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality.so + line: password requisite 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 + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + - name: Limit Password Reuse - 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\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + - name: Limit Password Reuse - 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\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + - name: Limit Password Reuse - 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\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or (result_pam_remember_edit is defined and + result_pam_remember_edit.changed) when: - accounts_password_pam_unix_remember | bool - configure_strategy | bool @@ -988,7 +1011,7 @@ - medium_severity | bool - no_reboot_needed | bool - '"pam" in ansible_facts.packages' - - not result_authselect_present.stat.exists + - result_pam_file_present.stat.exists tags: - CCE-80666-1 - CJIS-5.6.2.1.1 @@ -1021,6 +1044,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1033,6 +1057,7 @@ path: /usr/bin/authselect register: result_authselect_present when: + - DISA_STIG_RHEL_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1082,14 +1107,21 @@ changed_when: false when: - result_authselect_check_cmd is success - - name: Lock Accounts After Failed Password Attempts - Ensure with-faillock feature is enabled using authselect tool + - name: Lock Accounts After Failed Password Attempts - Ensure "with-faillock" feature is enabled using authselect tool ansible.builtin.command: cmd: authselect enable-feature with-faillock - register: result_authselect_cmd + register: result_authselect_enable_feature_cmd when: - result_authselect_check_cmd is success - result_authselect_features.stdout is not search("with-faillock") + - name: Lock Accounts After 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_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1138,7 +1170,7 @@ ansible.builtin.lineinfile: path: '{{ item }}' line: auth required pam_faillock.so authfail - insertafter: ^auth.*sufficient.*pam_unix.so.* + insertbefore: ^auth.*required.*pam_deny.so.* state: present loop: - /etc/pam.d/system-auth @@ -1157,6 +1189,7 @@ when: - result_pam_faillock_is_enabled.found == 0 when: + - DISA_STIG_RHEL_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1185,6 +1218,7 @@ path: /etc/security/faillock.conf register: result_faillock_conf_check when: + - DISA_STIG_RHEL_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1214,6 +1248,298 @@ line: deny = {{ var_accounts_passwords_pam_faillock_deny }} state: present when: + - DISA_STIG_RHEL_08_020010 | bool + - accounts_passwords_pam_faillock_deny | 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-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so deny parameter not in PAM files + block: + - name: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Check the proper remediation for the system + block: + - name: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + - name: Lock Accounts After Failed Password Attempts - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Ensure the "deny" option from "pam_faillock.so" is not present + in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + - name: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Check the proper remediation for the system + block: + - name: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + - name: Lock Accounts After Failed Password Attempts - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After 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: Lock Accounts After Failed Password Attempts - Ensure the "deny" option from "pam_faillock.so" is not present + in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + - name: Lock Accounts After 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_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1301,6 +1627,7 @@ when: - result_pam_faillock_deny_parameter_is_present.found > 0 when: + - DISA_STIG_RHEL_08_020010 | bool - accounts_passwords_pam_faillock_deny | bool - low_complexity | bool - low_disruption | bool @@ -1342,6 +1669,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020130 | bool - accounts_password_pam_dcredit | bool - low_complexity | bool - low_disruption | bool @@ -1356,6 +1684,7 @@ regexp: ^#?\s*dcredit line: dcredit = {{ var_password_pam_dcredit }} when: + - DISA_STIG_RHEL_08_020130 | bool - accounts_password_pam_dcredit | bool - low_complexity | bool - low_disruption | bool @@ -1396,6 +1725,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020170 | bool - accounts_password_pam_difok | bool - low_complexity | bool - low_disruption | bool @@ -1410,6 +1740,7 @@ regexp: ^#?\s*difok line: difok = {{ var_password_pam_difok }} when: + - DISA_STIG_RHEL_08_020170 | bool - accounts_password_pam_difok | bool - low_complexity | bool - low_disruption | bool @@ -1450,6 +1781,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020120 | bool - accounts_password_pam_lcredit | bool - low_complexity | bool - low_disruption | bool @@ -1464,6 +1796,7 @@ regexp: ^#?\s*lcredit line: lcredit = {{ var_password_pam_lcredit }} when: + - DISA_STIG_RHEL_08_020120 | bool - accounts_password_pam_lcredit | bool - low_complexity | bool - low_disruption | bool @@ -1503,6 +1836,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020280 | bool - accounts_password_pam_ocredit | bool - low_complexity | bool - low_disruption | bool @@ -1517,6 +1851,7 @@ regexp: ^#?\s*ocredit line: ocredit = {{ var_password_pam_ocredit }} when: + - DISA_STIG_RHEL_08_020280 | bool - accounts_password_pam_ocredit | bool - low_complexity | bool - low_disruption | bool @@ -1555,6 +1890,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_020104 | bool - accounts_password_pam_retry | bool - configure_strategy | bool - low_complexity | bool @@ -1569,6 +1905,7 @@ regexp: ^#?\s*retry line: retry = {{ var_password_pam_retry }} when: + - DISA_STIG_RHEL_08_020104 | bool - accounts_password_pam_retry | bool - configure_strategy | bool - low_complexity | bool @@ -1608,6 +1945,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020110 | bool - accounts_password_pam_ucredit | bool - low_complexity | bool - low_disruption | bool @@ -1622,6 +1960,7 @@ regexp: ^#?\s*ucredit line: ucredit = {{ var_password_pam_ucredit }} when: + - DISA_STIG_RHEL_08_020110 | bool - accounts_password_pam_ucredit | bool - low_complexity | bool - low_disruption | bool @@ -1719,43 +2058,576 @@ - restrict_strategy - set_password_hashing_algorithm_logindefs when: + - DISA_STIG_RHEL_08_010110 | bool + - low_complexity | bool + - low_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - restrict_strategy | bool + - set_password_hashing_algorithm_logindefs | bool + +- name: Set Password Hashing Algorithm in /etc/login.defs + lineinfile: + dest: /etc/login.defs + regexp: ^#?ENCRYPT_METHOD + line: ENCRYPT_METHOD {{ var_password_hashing_algorithm }} + state: present + create: true + when: + - DISA_STIG_RHEL_08_010110 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool - no_reboot_needed | bool - restrict_strategy | bool - set_password_hashing_algorithm_logindefs | bool + - '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80892-3 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010110 + - 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 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_logindefs + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - 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 + when: + - DISA_STIG_RHEL_08_010160 | bool + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_passwordauth | bool + +- 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_08_010160 | 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-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - 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 + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Set PAM's Password Hashing Algorithm - password-auth - Remediate using authselect + 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 + ignore_errors: true + - 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 is success + 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 - Check if expected PAM module line is present in {{ pam_file_path + }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + - name: Set PAM's Password Hashing Algorithm - password-auth - Include or update the PAM module line in {{ pam_file_path + }} + block: + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if required PAM module line is present in {{ pam_file_path + }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the correct control for the required PAM module + line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + - name: Set PAM's Password Hashing Algorithm - password-auth - 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 + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 + - 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_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + - 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\s+pam_unix.so\s*.*\ssha512\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 }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_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) or (result_pam_sha512_edit is defined and result_pam_sha512_edit.changed) + when: + - DISA_STIG_RHEL_08_010160 | 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-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - 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: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80893-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010159 + - 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_systemauth + when: + - DISA_STIG_RHEL_08_010159 | bool + - configure_strategy | bool + - low_complexity | bool + - medium_disruption | bool + - medium_severity | bool + - no_reboot_needed | bool + - set_password_hashing_algorithm_systemauth | bool + +- 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: + - DISA_STIG_RHEL_08_010159 | bool + - 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-80893-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010159 + - 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_systemauth -- name: Set Password Hashing Algorithm in /etc/login.defs - lineinfile: - dest: /etc/login.defs - regexp: ^#?ENCRYPT_METHOD - line: ENCRYPT_METHOD {{ var_password_hashing_algorithm }} - state: present - create: true +- 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 + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + - name: Set PAM's Password Hashing Algorithm - Remediate using authselect + 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 + ignore_errors: true + - name: Set PAM's Password Hashing Algorithm - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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 - Check if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + - name: Set PAM's Password Hashing Algorithm - Include or update the PAM module line in {{ pam_file_path }} + block: + - name: Set PAM's Password Hashing Algorithm - Check if required PAM module line is present in {{ pam_file_path }} with + different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + - name: Set PAM's Password Hashing Algorithm - Ensure the correct control for the required PAM module line in {{ pam_file_path + }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \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 + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found > 1 + - 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_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + - 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\s+pam_unix.so\s*.*\ssha512\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 + }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_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) or (result_pam_sha512_edit is defined and result_pam_sha512_edit.changed) when: + - DISA_STIG_RHEL_08_010159 | bool + - configure_strategy | bool - low_complexity | bool - - low_disruption | bool + - medium_disruption | bool - medium_severity | bool - no_reboot_needed | bool - - restrict_strategy | bool - - set_password_hashing_algorithm_logindefs | bool - - '"shadow-utils" in ansible_facts.packages' + - set_password_hashing_algorithm_systemauth | bool + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists tags: - - CCE-80892-3 + - CCE-80893-1 - CJIS-5.6.2.2 - - DISA-STIG-RHEL-08-010110 + - DISA-STIG-RHEL-08-010159 - 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 - - low_disruption + - medium_disruption - medium_severity - no_reboot_needed - - restrict_strategy - - set_password_hashing_algorithm_logindefs + - set_password_hashing_algorithm_systemauth - name: require single user mode password lineinfile: @@ -1764,6 +2636,7 @@ regexp: ^#?ExecStart= line: ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue when: + - DISA_STIG_RHEL_08_010151 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -1804,6 +2677,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_020190 | bool - accounts_minimum_age_login_defs | bool - low_complexity | bool - low_disruption | bool @@ -1818,6 +2692,7 @@ regexp: ^#?PASS_MIN_DAYS line: PASS_MIN_DAYS {{ var_accounts_minimum_age_login_defs }} when: + - DISA_STIG_RHEL_08_020190 | bool - accounts_minimum_age_login_defs | bool - low_complexity | bool - low_disruption | bool @@ -1840,61 +2715,6 @@ - no_reboot_needed - restrict_strategy -- name: Gather the package facts - package_facts: - manager: auto - tags: - - CCE-80652-1 - - CJIS-5.6.2.1 - - DISA-STIG-RHEL-08-020231 - - NIST-800-171-3.5.7 - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(1)(a) - - NIST-800-53-IA-5(f) - - accounts_password_minlen_login_defs - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - when: - - accounts_password_minlen_login_defs | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - -- name: Set Password Minimum Length in login.defs - lineinfile: - dest: /etc/login.defs - regexp: ^PASS_MIN_LEN *[0-9]* - state: present - line: PASS_MIN_LEN {{ var_accounts_password_minlen_login_defs }} - create: true - when: - - accounts_password_minlen_login_defs | bool - - low_complexity | bool - - low_disruption | bool - - medium_severity | bool - - no_reboot_needed | bool - - restrict_strategy | bool - - '"shadow-utils" in ansible_facts.packages' - tags: - - CCE-80652-1 - - CJIS-5.6.2.1 - - DISA-STIG-RHEL-08-020231 - - NIST-800-171-3.5.7 - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(1)(a) - - NIST-800-53-IA-5(f) - - accounts_password_minlen_login_defs - - low_complexity - - low_disruption - - medium_severity - - no_reboot_needed - - restrict_strategy - - name: Gather the package facts package_facts: manager: auto @@ -1946,11 +2766,12 @@ - no_reboot_needed - restrict_strategy -- name: Check if system relies on authselect +- name: Prevent Login to Accounts With Empty Password - Check if system relies on authselect ansible.builtin.stat: path: /usr/bin/authselect register: result_authselect_present when: + - DISA_STIG_RHEL_08_020331 | bool - configure_strategy | bool - high_severity | bool - low_complexity | bool @@ -1975,114 +2796,48 @@ - no_empty_passwords - no_reboot_needed -- name: Check integrity of authselect current profile - ansible.builtin.command: - cmd: authselect check - register: result_authselect_check_cmd - changed_when: false - ignore_errors: true - when: - - configure_strategy | bool - - high_severity | bool - - low_complexity | bool - - medium_disruption | bool - - no_empty_passwords | bool - - no_reboot_needed | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - result_authselect_present.stat.exists - tags: - - CCE-80841-0 - - CJIS-5.5.2 - - DISA-STIG-RHEL-08-020331 - - NIST-800-171-3.1.1 - - NIST-800-171-3.1.5 - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(1)(a) - - NIST-800-53-IA-5(c) - - PCI-DSS-Req-8.2.3 - - configure_strategy - - high_severity - - low_complexity - - medium_disruption - - no_empty_passwords - - no_reboot_needed - -- name: Get authselect current features - ansible.builtin.shell: - cmd: authselect current | tail -n+3 | awk '{ print $2 }' - register: result_authselect_features - changed_when: false - when: - - configure_strategy | bool - - high_severity | bool - - low_complexity | bool - - medium_disruption | bool - - no_empty_passwords | bool - - no_reboot_needed | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - result_authselect_present.stat.exists - - result_authselect_check_cmd is success - tags: - - CCE-80841-0 - - CJIS-5.5.2 - - DISA-STIG-RHEL-08-020331 - - NIST-800-171-3.1.1 - - NIST-800-171-3.1.5 - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(1)(a) - - NIST-800-53-IA-5(c) - - PCI-DSS-Req-8.2.3 - - configure_strategy - - high_severity - - low_complexity - - medium_disruption - - no_empty_passwords - - no_reboot_needed - -- name: Ensure nullok property is absent via authselect tool - ansible.builtin.command: - cmd: authselect enable-feature without-nullok - register: result_authselect_cmd - when: - - configure_strategy | bool - - high_severity | bool - - low_complexity | bool - - medium_disruption | bool - - no_empty_passwords | bool - - no_reboot_needed | bool - - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] - - result_authselect_present.stat.exists - - result_authselect_check_cmd is success - - result_authselect_features.stdout is not search("without-nullok") - tags: - - CCE-80841-0 - - CJIS-5.5.2 - - DISA-STIG-RHEL-08-020331 - - NIST-800-171-3.1.1 - - NIST-800-171-3.1.5 - - NIST-800-53-CM-6(a) - - NIST-800-53-IA-5(1)(a) - - NIST-800-53-IA-5(c) - - PCI-DSS-Req-8.2.3 - - configure_strategy - - high_severity - - low_complexity - - medium_disruption - - no_empty_passwords - - no_reboot_needed - -- name: Informative message based on the authselect integrity check result - ansible.builtin.assert: - that: +- name: Prevent Login to Accounts With Empty Password - Remediate using authselect + block: + - name: Prevent Login to Accounts With Empty Password - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + - name: Prevent Login to Accounts With Empty Password - Informative message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + 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: Prevent Login to Accounts With Empty Password - 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 - fail_msg: - - authselect integrity check failed. Remediation aborted! - - This remediation could not be applied because the authselect profile is not intact. - - It is not recommended to manually edit the PAM files when authselect 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: Prevent Login to Accounts With Empty Password - Ensure "without-nullok" feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature without-nullok + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("without-nullok") + - name: Prevent Login to Accounts With Empty Password - 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_08_020331 | bool - configure_strategy | bool - high_severity | bool - low_complexity | bool @@ -2090,6 +2845,7 @@ - no_empty_passwords | bool - no_reboot_needed | bool - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_authselect_present.stat.exists tags: - CCE-80841-0 - CJIS-5.5.2 @@ -2107,14 +2863,15 @@ - no_empty_passwords - no_reboot_needed -- name: Ensure nullok property is absent by directly editing pam files - replace: +- name: Prevent Login to Accounts With Empty Password - Remediate directly editing PAM files + ansible.builtin.replace: dest: '{{ item }}' regexp: nullok loop: - /etc/pam.d/system-auth - /etc/pam.d/password-auth when: + - DISA_STIG_RHEL_08_020331 | bool - configure_strategy | bool - high_severity | bool - low_complexity | bool @@ -2309,6 +3066,7 @@ when: - '"firewalld" in ansible_facts.packages' when: + - DISA_STIG_RHEL_08_040101 | bool - enable_strategy | bool - low_complexity | bool - low_disruption | bool @@ -2361,6 +3119,34 @@ - medium_severity - reboot_required +- 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 + - medium_disruption | bool + - medium_severity | bool + - reboot_required | bool + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80833-7 + - 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) + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - name: Ensure kernel module 'sctp' is disabled lineinfile: create: true @@ -2368,6 +3154,37 @@ regexp: sctp line: install sctp /bin/true when: + - DISA_STIG_RHEL_08_040023 | 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-80834-5 + - CJIS-5.10.1 + - DISA-STIG-RHEL-08-040023 + - 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_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - 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_08_040023 | bool - disable_strategy | bool - kernel_module_sctp_disabled | bool - low_complexity | bool @@ -2986,6 +3803,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_010310 | bool - file_ownership_binary_dirs | bool - medium_complexity | bool - medium_disruption | bool @@ -2999,6 +3817,7 @@ owner: root with_items: '{{ no_root_system_executables.stdout_lines }}' when: + - DISA_STIG_RHEL_08_010310 | bool - file_ownership_binary_dirs | bool - medium_complexity | bool - medium_disruption | bool @@ -3040,6 +3859,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3068,6 +3888,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3095,6 +3916,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3123,6 +3945,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3150,6 +3973,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3178,6 +4002,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3205,6 +4030,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3233,6 +4059,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010340 | bool - configure_strategy | bool - file_ownership_library_dirs | bool - low_complexity | bool @@ -3260,6 +4087,7 @@ - no_reboot_needed - restrict_strategy when: + - DISA_STIG_RHEL_08_010300 | bool - file_permissions_binary_dirs | bool - medium_complexity | bool - medium_disruption | bool @@ -3273,6 +4101,7 @@ mode: go-w with_items: '{{ world_writable_library_files.stdout_lines }}' when: + - DISA_STIG_RHEL_08_010300 | bool - file_permissions_binary_dirs | bool - medium_complexity | bool - medium_disruption | bool @@ -3314,6 +4143,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3342,6 +4172,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3369,6 +4200,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3397,6 +4229,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3424,6 +4257,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3452,6 +4286,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3479,6 +4314,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3507,6 +4343,7 @@ - medium_severity - no_reboot_needed when: + - DISA_STIG_RHEL_08_010330 | bool - configure_strategy | bool - file_permissions_library_dirs | bool - low_complexity | bool @@ -3540,6 +4377,7 @@ line: SELINUXTYPE={{ var_selinux_policy_name }} state: present when: + - DISA_STIG_RHEL_08_010450 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -3589,6 +4427,7 @@ line: SELINUX={{ var_selinux_state }} state: present when: + - DISA_STIG_RHEL_08_010170 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -3710,11 +4549,13 @@ - no_reboot_needed - package_telnet-server_removed when: + - DISA_STIG_RHEL_08_040000 | bool - disable_strategy | bool - high_severity | bool - low_complexity | bool - low_disruption | bool - no_reboot_needed | bool + - package_telnet_server_removed | bool - name: Ensure telnet is removed package: @@ -3868,7 +4709,6 @@ tags: - CCE-83405-1 - CJIS-5.5.6 - - DISA-STIG-RHEL-08-010200 - NIST-800-171-3.1.11 - NIST-800-53-AC-12 - NIST-800-53-AC-17(a) @@ -3921,7 +4761,6 @@ tags: - CCE-80906-1 - CJIS-5.5.6 - - DISA-STIG-RHEL-08-010201 - NIST-800-171-3.1.11 - NIST-800-53-AC-12 - NIST-800-53-AC-17(a) @@ -4154,6 +4993,7 @@ insertbefore: ^[#\s]*Match validate: /usr/sbin/sshd -t -f %s when: + - DISA_STIG_RHEL_08_020330 | bool - high_severity | bool - low_complexity | bool - low_disruption | bool @@ -4206,6 +5046,7 @@ insertbefore: ^[#\s]*Match validate: /usr/sbin/sshd -t -f %s when: + - DISA_STIG_RHEL_08_010550 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -4261,6 +5102,7 @@ insertbefore: ^[#\s]*Match validate: /usr/sbin/sshd -t -f %s when: + - DISA_STIG_RHEL_08_010830 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool @@ -4312,6 +5154,7 @@ insertbefore: ^[#\s]*Match validate: /usr/sbin/sshd -t -f %s when: + - DISA_STIG_RHEL_08_010040 | bool - low_complexity | bool - low_disruption | bool - medium_severity | bool