Guide to the Secure Configuration of Red Hat Enterprise Linux 10

with profile DRAFT - Protection Profile for General Purpose Operating Systems
This profile is part of Red Hat Enterprise Linux 9 Common Criteria Guidance documentation for Target of Evaluation based on Protection Profile for General Purpose Operating Systems (OSPP) version 4.3 and Functional Package for SSH version 1.0. Where appropriate, CNSSI 1253 or DoD-specific values are used for configuration, based on Configuration Annex to the OSPP.
This guide presents a catalog of security-relevant configuration settings for Red Hat Enterprise Linux 10. It is a rendering of content structured in the eXtensible Configuration Checklist Description Format (XCCDF) in order to support security automation. The SCAP content is is available in the scap-security-guide package which is developed at https://www.open-scap.org/security-policies/scap-security-guide.

Providing system administrators with such guidance informs them how to securely configure systems under their control in a variety of network roles. Policy makers and baseline creators can use this catalog of settings, with its associated references to higher-level security control catalogs, in order to assist them in security baseline creation. This guide is a catalog, not a checklist, and satisfaction of every item is not likely to be possible or sensible in many operational scenarios. However, the XCCDF format enables granular selection and adjustment of settings, and their association with OVAL and OCIL content provides an automated checking capability. Transformations of this document, and its associated automated checking content, are capable of providing baselines that meet a diverse set of policy objectives. Some example XCCDF Profiles, which are selections of items that form checklists and can be used as baselines, are available with this guide. They can be processed, in an automated fashion, with tools that support the Security Content Automation Protocol (SCAP). The DISA STIG, which provides required settings for US Department of Defense systems, is one example of a baseline created from this guidance.
Do not attempt to implement any of the settings in this guide without first testing them in a non-operational environment. The creators of this guidance assume no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic.

Profile Information

Profile TitleDRAFT - Protection Profile for General Purpose Operating Systems
Profile IDxccdf_org.ssgproject.content_profile_ospp

CPE Platforms

  • cpe:/o:redhat:enterprise_linux:10

Revision History

Current version: 0.1.74

  • draft (as of 2024-06-20)

Table of Contents

  1. System Settings
    1. Installing and Maintaining Software
    2. Account and Access Control
    3. GRUB2 bootloader configuration
    4. zIPL bootloader configuration
    5. Configure Syslog
    6. Network Configuration and Firewalls
    7. File Permissions and Masks
  2. Services
    1. Base Services
    2. Application Whitelisting Daemon
    3. Kerberos
    4. Network Time Protocol
    5. Hardware RNG Entropy Gatherer Daemon
    6. SSH Server
    7. System Security Services Daemon
    8. USBGuard daemon
  3. System Accounting with auditd
    1. Configure auditd Rules for Comprehensive Auditing
    2. Configure auditd Data Retention
    3. System Accounting with auditd

Checklist

Group   Guide to the Secure Configuration of Red Hat Enterprise Linux 10   Group contains 64 groups and 285 rules
Group   System Settings   Group contains 41 groups and 110 rules
[ref]   Contains rules that check correct system settings.
Group   Installing and Maintaining Software   Group contains 11 groups and 41 rules
[ref]   The following sections contain information on security-relevant choices during the initial operating system installation process and the setup of software updates.
Group   System and Software Integrity   Group contains 2 groups and 10 rules
[ref]   System and software integrity can be gained by installing antivirus, increasing system encryption strength with FIPS, verifying installed software, enabling SELinux, installing an Intrusion Prevention System, etc. However, installing or enabling integrity checking tools cannot prevent intrusions, but they can detect that an intrusion may have occurred. Requirements for integrity checking may be highly dependent on the environment in which the system will be used. Snapshot-based approaches such as AIDE may induce considerable overhead in the presence of frequent software updates.
Group   Federal Information Processing Standard (FIPS)   Group contains 2 rules
[ref]   The Federal Information Processing Standard (FIPS) is a computer security standard which is developed by the U.S. Government and industry working groups to validate the quality of cryptographic modules. The FIPS standard provides four security levels to ensure adequate coverage of different industries, implementation of cryptographic modules, and organizational sizes and requirements.

FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules utilize authentication that meets industry and government requirements. For government systems, this allows Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux 10.

See http://csrc.nist.gov/publications/PubsFIPS.html for more information.

Rule   Enable Dracut FIPS Module   [ref]

To enable FIPS mode, run the following command:
fips-mode-setup --enable
To enable FIPS, the system requires that the fips module is added in dracut configuration. Check if /etc/dracut.conf.d/40-fips.conf contain add_dracutmodules+=" fips "
Warning:  The system needs to be rebooted for these changes to take effect.
Warning:  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.
Rationale:
Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_enable_dracut_fips_module
References:
disaCCI-000068, CCI-000803, CCI-002450
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-12(2), SC-12(3), IA-7, SC-13, CM-6(a), SC-12
osppFCS_RBG_EXT.1
os-srgSRG-OS-000478-GPOS-00223

Complexity:medium
Disruption:medium
Reboot:true
Strategy:restrict
- name: Check to see the current status of FIPS mode
  command: /usr/bin/fips-mode-setup --check
  register: is_fips_enabled
  changed_when: false
  failed_when: false
  when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman",
    "container"] and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_dracut_fips_module
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

- name: Enable FIPS mode
  command: /usr/bin/fips-mode-setup --enable
  when:
  - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
    and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  - is_fips_enabled.stdout.find('FIPS mode is enabled.') == -1
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_dracut_fips_module
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

- name: Enable Dracut FIPS Module
  lineinfile:
    path: /etc/dracut.conf.d/40-fips.conf
    line: add_dracutmodules+=" fips "
  when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman",
    "container"] and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_dracut_fips_module
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && ! ( [ "${container:-}" == "bwrap-osbuild" ] ) ); then

fips-mode-setup --enable
FIPS_CONF="/etc/dracut.conf.d/40-fips.conf"
if ! grep "^add_dracutmodules+=\" fips \"" $FIPS_CONF; then
    echo "add_dracutmodules+=\" fips \"" >> $FIPS_CONF
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Enable FIPS Mode   [ref]

To enable FIPS mode, run the following command:
fips-mode-setup --enable

The fips-mode-setup command will configure the system in FIPS mode by automatically configuring the following:
  • Setting the kernel FIPS mode flag (/proc/sys/crypto/fips_enabled) to 1
  • Creating /etc/system-fips
  • Setting the system crypto policy in /etc/crypto-policies/config to DEFAULT
  • Loading the Dracut fips module
Warning:  The system needs to be rebooted for these changes to take effect.
Warning:  This rule DOES NOT CHECK if the components of the operating system are FIPS certified. You can find the list of FIPS certified modules at https://csrc.nist.gov/projects/cryptographic-module-validation-program/validated-modules/search. This rule checks if the system is running in FIPS mode. See the rule description for more information about what it means.
Rationale:
Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_enable_fips_mode
References:
disaCCI-000068, CCI-000803, CCI-002450
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistCM-3(6), SC-12(2), SC-12(3), IA-7, SC-13, CM-6(a), SC-12
osppFCS_COP.1(1), FCS_COP.1(2), FCS_COP.1(3), FCS_COP.1(4), FCS_CKM.1, FCS_CKM.2, FCS_TLSC_EXT.1, FCS_RBG_EXT.1
os-srgSRG-OS-000478-GPOS-00223, SRG-OS-000396-GPOS-00176


[customizations]
fips = true

Complexity:medium
Disruption:medium
Reboot:true
Strategy:restrict
- name: XCCDF Value var_system_crypto_policy # promote to variable
  set_fact:
    var_system_crypto_policy: !!str DEFAULT
  tags:
    - always

- name: Enable FIPS Mode - Check to See the Current Status of FIPS Mode
  ansible.builtin.command: /usr/bin/fips-mode-setup --check
  register: is_fips_enabled
  failed_when: false
  changed_when: false
  when:
  - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
    and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-3(6)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_fips_mode
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

- name: Enable FIPS Mode - Enable FIPS Mode
  ansible.builtin.command: /usr/bin/fips-mode-setup --enable
  when:
  - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
    and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - is_fips_enabled.stdout.find('FIPS mode is enabled.') == -1
  tags:
  - NIST-800-53-CM-3(6)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_fips_mode
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

- name: Enable FIPS Mode - Configure Crypto Policy
  ansible.builtin.lineinfile:
    path: /etc/crypto-policies/config
    regexp: ^(?!#)(\S+)$
    line: '{{ var_system_crypto_policy }}'
    create: true
  when:
  - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
    and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-3(6)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_fips_mode
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

- name: Enable FIPS Mode - Verify that Crypto Policy is Set (runtime)
  ansible.builtin.command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy
    }}
  when:
  - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
    and not ( lookup("env", "container") == "bwrap-osbuild" ) )
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-3(6)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-7
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - enable_fips_mode
  - high_severity
  - medium_complexity
  - medium_disruption
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && ! ( [ "${container:-}" == "bwrap-osbuild" ] ) ) && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

var_system_crypto_policy='DEFAULT'


fips-mode-setup --enable

stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null)
rc=$?

if test "$rc" = 127; then
	echo "$stderr_of_call" >&2
	echo "Make sure that the script is installed on the remediated system." >&2
	echo "See output of the 'dnf provides update-crypto-policies' command" >&2
	echo "to see what package to (re)install" >&2

	false  # end with an error code
elif test "$rc" != 0; then
	echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2
	false  # end with an error code
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   System Cryptographic Policies   Group contains 8 rules
[ref]   Linux has the capability to centrally configure cryptographic polices. The command update-crypto-policies is used to set the policy applicable for the various cryptographic back-ends, such as SSL/TLS libraries. The configured cryptographic policies will be the default policy used by these backends unless the application user configures them otherwise. When the system has been configured to use the centralized cryptographic policies, the administrator is assured that any application that utilizes the supported backends will follow a policy that adheres to the configured profile. Currently the supported backends are:
  • GnuTLS library
  • OpenSSL library
  • NSS library
  • OpenJDK
  • Libkrb5
  • BIND
  • OpenSSH
Applications and languages which rely on any of these backends will follow the system policies as well. Examples are apache httpd, nginx, php, and others.

Rule   Install crypto-policies package   [ref]

The crypto-policies package can be installed with the following command:
$ sudo dnf install crypto-policies
Rationale:
Centralized cryptographic policies simplify applying secure ciphers across an operating system and the applications that run on that operating system. Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_crypto-policies_installed
References:
osppFCS_COP.1(1), FCS_COP.1(2), FCS_COP.1(3), FCS_COP.1(4), FCS_CKM.1, FCS_CKM.2, FCS_TLSC_EXT.1
os-srgSRG-OS-000396-GPOS-00176, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174


[[packages]]
name = "crypto-policies"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure crypto-policies is installed
  package:
    name: crypto-policies
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_crypto-policies_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=crypto-policies

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_crypto-policies

class install_crypto-policies {
  package { 'crypto-policies':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "crypto-policies" ; then
    dnf install -y "crypto-policies"
fi

Rule   Configure System Cryptography Policy   [ref]

To configure the system cryptography policy to use ciphers only from the DEFAULT policy, run the following command:
$ sudo update-crypto-policies --set DEFAULT
         
The rule checks if settings for selected crypto policy are configured as expected. Configuration files in the /etc/crypto-policies/back-ends are either symlinks to correct files provided by Crypto-policies package or they are regular files in case crypto policy customizations are applied. Crypto policies may be customized by crypto policy modules, in which case it is delimited from the base policy using a colon.
Warning:  The system needs to be rebooted for these changes to take effect.
Warning:  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.
Rationale:
Centralized cryptographic policies simplify applying secure ciphers across an operating system and the applications that run on that operating system. Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_configure_crypto_policy
References:
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.312(e)(1), 164.312(e)(2)(ii)
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
osppFCS_COP.1(1), FCS_COP.1(2), FCS_COP.1(3), FCS_COP.1(4), FCS_CKM.1, FCS_CKM.2, FCS_TLSC_EXT.1
os-srgSRG-OS-000396-GPOS-00176, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174
cis1.6.1
pcidss42.2.7

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_system_crypto_policy # promote to variable
  set_fact:
    var_system_crypto_policy: !!str DEFAULT
  tags:
    - always

- name: Configure System Cryptography Policy
  lineinfile:
    path: /etc/crypto-policies/config
    regexp: ^(?!#)(\S+)$
    line: '{{ var_system_crypto_policy }}'
    create: true
  tags:
  - NIST-800-53-AC-17(2)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - PCI-DSSv4-2.2.7
  - configure_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Verify that Crypto Policy is Set (runtime)
  command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy }}
  tags:
  - NIST-800-53-AC-17(2)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - PCI-DSSv4-2.2.7
  - configure_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
        - name: configure-crypto-policy.service
          enabled: true
          contents: |
            [Unit]
            Before=kubelet.service
            [Service]
            Type=oneshot
            ExecStart=update-crypto-policies --set {{.var_system_crypto_policy}}
            RemainAfterExit=yes
            [Install]
            WantedBy=multi-user.target


var_system_crypto_policy='DEFAULT'


stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null)
rc=$?

if test "$rc" = 127; then
	echo "$stderr_of_call" >&2
	echo "Make sure that the script is installed on the remediated system." >&2
	echo "See output of the 'dnf provides update-crypto-policies' command" >&2
	echo "to see what package to (re)install" >&2

	false  # end with an error code
elif test "$rc" != 0; then
	echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2
	false  # end with an error code
fi

Rule   Configure Libreswan to use System Crypto Policy   [ref]

Crypto Policies provide a centralized control over crypto algorithms usage of many packages. Libreswan is supported by system crypto policy, but the Libreswan configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that the /etc/ipsec.conf includes the appropriate configuration file. In /etc/ipsec.conf, make sure that the following line is not commented out or superseded by later includes: include /etc/crypto-policies/back-ends/libreswan.config
Rationale:
Overriding the system crypto policy makes the behavior of the Libreswan service violate expectations, and makes system configuration more fragmented.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_configure_libreswan_crypto_policy
References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistCM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
osppFCS_IPSEC_EXT.1.4, FCS_IPSEC_EXT.1.6
pcidssReq-2.2
os-srgSRG-OS-000033-GPOS-00014

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Configure Libreswan to use System Crypto Policy
  lineinfile:
    path: /etc/ipsec.conf
    line: include /etc/crypto-policies/back-ends/libreswan.config
    create: true
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - PCI-DSS-Req-2.2
  - configure_libreswan_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy


function remediate_libreswan_crypto_policy() {
    CONFIG_FILE="/etc/ipsec.conf"
    if ! grep -qP "^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$" "$CONFIG_FILE" ; then
        # the file might not end with a new line
        echo -e '\ninclude /etc/crypto-policies/back-ends/libreswan.config' >> "$CONFIG_FILE"
    fi
    return 0
}

remediate_libreswan_crypto_policy

Rule   Configure SSH to use System Crypto Policy   [ref]

Crypto Policies provide a centralized control over crypto algorithms usage of many packages. SSH is supported by crypto policy, but the SSH configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that the CRYPTO_POLICY variable is either commented or not set at all in the /etc/sysconfig/sshd.
Rationale:
Overriding the system crypto policy makes the behavior of the SSH service violate expectations, and makes system configuration more fragmented.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy
References:
disaCCI-001453
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.312(e)(1), 164.312(e)(2)(ii)
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13
osppFCS_SSH_EXT.1, FCS_SSHS_EXT.1, FCS_SSHC_EXT.1
pcidssReq-2.2
os-srgSRG-OS-000250-GPOS-00093
cis1.6.2
pcidss42.2.7

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Configure SSH to use System Crypto Policy
  lineinfile:
    dest: /etc/sysconfig/sshd
    state: absent
    regexp: (?i)^\s*CRYPTO_POLICY.*$
  tags:
  - NIST-800-53-AC-17(2)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-13
  - PCI-DSS-Req-2.2
  - PCI-DSSv4-2.2.7
  - configure_ssh_crypto_policy
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required


SSH_CONF="/etc/sysconfig/sshd"

sed -i "/^\s*CRYPTO_POLICY.*$/Id" $SSH_CONF

Rule   Harden OpenSSL Crypto Policy   [ref]

Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSL. OpenSSL is by default configured to modify its configuration based on currently configured Crypto Policy. However, in certain cases it might be needed to override the Crypto Policy specific to OpenSSL and leave rest of the Crypto Policy intact. This can be done by dropping a file named opensslcnf-xxx.config, replacing xxx with arbitrary identifier, into /etc/crypto-policies/local.d. This has to be followed by running update-crypto-policies so that changes are applied. Changes are propagated into /etc/crypto-policies/back-ends/opensslcnf.config. This rule checks if this file contains predefined Ciphersuites variable configured with predefined value.
Rationale:
The Common Criteria requirements specify that certain parameters for OpenSSL are configured e.g. cipher suites. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_harden_openssl_crypto_policy
References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-8(1), SC-13
osppFCS_TLSC_EXT.1.1
os-srgSRG-OS-000396-GPOS-00176, SRG-OS-000424-GPOS-00188, SRG-OS-000478-GPOS-00223

Rule   Harden SSH client Crypto Policy   [ref]

Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSH client. To override the system wide crypto policy for Openssh client, place a file in the /etc/ssh/ssh_config.d/ so that it is loaded before the 05-redhat.conf. In this case it is file named 02-ospp.conf containing parameters which need to be changed with respect to the crypto policy. This rule checks if the file exists and if it contains required parameters and values which modify the Crypto Policy. During the parsing process, as soon as Openssh client parses some configuration option and its value, it remembers it and ignores any subsequent overrides. The customization mechanism provided by crypto policies appends eventual customizations at the end of the system wide crypto policy. Therefore, if the crypto policy customization overrides some parameter which is already configured in the system wide crypto policy, the SSH client will not honor that customized parameter.
Rationale:
The Common Criteria requirements specify how certain parameters for OpenSSH Client are configured. Particular parameters are RekeyLimit, GSSAPIAuthentication, Ciphers, PubkeyAcceptedKeyTypes, MACs and KexAlgorithms. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_harden_ssh_client_crypto_policy
References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13
osppFCS_SSHC_EXT.1
os-srgSRG-OS-000033-GPOS-00014, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174


#the file starts with 02 so that it is loaded before the 05-redhat.conf which activates configuration provided by system vide crypto policy
file="/etc/ssh/ssh_config.d/02-ospp.conf"
echo -e "Match final all\n\
RekeyLimit 512M 1h\n\
GSSAPIAuthentication no\n\
Ciphers aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc\n\
PubkeyAcceptedKeyTypes ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256\n\
MACs hmac-sha2-512,hmac-sha2-256\n\
KexAlgorithms ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1\n" > "$file"

Rule   Harden SSHD Crypto Policy   [ref]

Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSH server. The SSHD service is by default configured to modify its configuration based on currently configured Crypto-Policy. However, in certain cases it might be needed to override the Crypto Policy specific to OpenSSH Server and leave rest of the Crypto Policy intact. This can be done by dropping a file named opensshserver-xxx.config, replacing xxx with arbitrary identifier, into /etc/crypto-policies/local.d. This has to be followed by running update-crypto-policies so that changes are applied. Changes are propagated into /etc/crypto-policies/back-ends/opensshserver.config. This rule checks if this file contains predefined CRYPTO_POLICY environment variable configured with predefined value.
Rationale:
The Common Criteria requirements specify that certain parameters for OpenSSH Server are configured e.g. supported ciphers, accepted host key algorithms, public key types, key exchange algorithms, HMACs and GSSAPI key exchange is disabled. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_harden_sshd_crypto_policy
References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
osppFCS_SSHS_EXT.1
os-srgSRG-OS-000250-GPOS-00093, SRG-OS-000033-GPOS-00014, SRG-OS-000120-GPOS-00061

Rule   OpenSSL uses strong entropy source   [ref]

By default, OpenSSL doesn't always use a SP800-90A compliant random number generator. A way to configure OpenSSL to always use a strong source is to setup a wrapper that defines a shell function that shadows the actual openssl binary, and that ensures that the -rand /dev/random option is added to every openssl invocation. To do so, place the following shell snippet exactly as-is to /etc/profile.d/openssl-rand.sh:
# provide a default -rand /dev/random option to openssl commands that
# support it

# written inefficiently for maximum shell compatibility
openssl()
(
  openssl_bin=/usr/bin/openssl

  case "$*" in
    # if user specified -rand, honor it
    *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;;
  esac

  cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '`
  for i in `$openssl_bin list -commands`; do
    if $openssl_bin list -options "$i" | grep -q '^rand '; then
      cmds=" $i $cmds"
    fi
  done

  case "$cmds" in
    *\ "$1"\ *)
      cmd="$1"; shift
      exec $openssl_bin "$cmd" -rand /dev/random "$@" ;;
  esac

  exec $openssl_bin "$@"
)
Warning:  This setting can cause problems on computers without the hardware random generator, because insufficient entropy blocks the program until enough entropy is available.
Rationale:
This rule ensures that openssl invocations always uses SP800-90A compliant random number generator as a default behavior.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_openssl_use_strong_entropy
References:
ism1277, 1552
osppFCS_RBG_EXT.1.2
os-srgSRG-OS-000480-GPOS-00227

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Put a file with shell wrapper to configure OpenSSL to always use strong entropy
  copy:
    dest: /etc/profile.d/openssl-rand.sh
    content: |
      # provide a default -rand /dev/random option to openssl commands that
      # support it

      # written inefficiently for maximum shell compatibility
      openssl()
      (
        openssl_bin=/usr/bin/openssl

        case "$*" in
          # if user specified -rand, honor it
          *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;;
        esac

        cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '`
        for i in `$openssl_bin list -commands`; do
          if $openssl_bin list -options "$i" | grep -q '^rand '; then
            cmds=" $i $cmds"
          fi
        done

        case "$cmds" in
          *\ "$1"\ *)
            cmd="$1"; shift
            exec $openssl_bin "$cmd" -rand /dev/random "$@" ;;
        esac

        exec $openssl_bin "$@"
      )
  tags:
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - openssl_use_strong_entropy
  - restrict_strategy
Group   Disk Partitioning   Group contains 1 rule
[ref]   To ensure separation and protection of data, there are top-level system directories which should be placed on their own physical partition or logical volume. The installer's default partitioning scheme creates separate logical volumes for /, /boot, and swap.
  • If starting with any of the default layouts, check the box to \"Review and modify partitioning.\" This allows for the easy creation of additional logical volumes inside the volume group already created, though it may require making /'s logical volume smaller to create space. In general, using logical volumes is preferable to using partitions because they can be more easily adjusted later.
  • If creating a custom layout, create the partitions mentioned in the previous paragraph (which the installer will require anyway), as well as separate ones described in the following sections.
If a system has already been installed, and the default partitioning scheme was used, it is possible but nontrivial to modify it to create separate logical volumes for the directories listed above. The Logical Volume Manager (LVM) makes this possible. See the LVM HOWTO at http://tldp.org/HOWTO/LVM-HOWTO/ for more detailed information on LVM.

Rule   Ensure /var/log/audit Located On Separate Partition   [ref]

Audit logs are stored in the /var/log/audit directory. Ensure that /var/log/audit has its own partition or logical volume at installation time, or migrate it using LVM. Make absolutely certain that it is large enough to store all audit logs that will be created by the auditing daemon.
Rationale:
Placing /var/log/audit in its own partition enables better separation between audit files and other files, and helps ensure that auditing cannot be halted due to the partition running out of space.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_var_log_audit
References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 8
cobit5APO11.04, APO13.01, BAI03.05, BAI04.04, DSS05.02, DSS05.04, DSS05.07, MEA02.01
disaCCI-000366, CCI-001849
hipaa164.312(a)(2)(ii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.17.2.1
nerc-cipCIP-007-3 R6.5
nistCM-6(a), AU-4, SC-5(2)
nist-csfPR.DS-4, PR.PT-1, PR.PT-4
osppFMT_SMF_EXT.1
os-srgSRG-OS-000341-GPOS-00132, SRG-OS-000480-GPOS-00227
app-srg-ctrSRG-APP-000357-CTR-000800
anssiR71
cis1.1.2.7.1


[[customizations.filesystem]]
mountpoint = "/var/log/audit"
size = 10737418240

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /var/log/audit
Group   GNOME Desktop Environment   Group contains 3 groups and 14 rules
[ref]   GNOME is a graphical desktop environment bundled with many Linux distributions that allow users to easily interact with the operating system graphically rather than textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user switching contexts as well as display server management.

GNOME is developed by the GNOME Project and is considered the default Red Hat Graphical environment.

For more information on GNOME and the GNOME Project, see https://www.gnome.org.
Group   Configure GNOME Login Screen   Group contains 3 rules
Group   Configure GNOME Screen Locking   Group contains 10 rules
[ref]   In the default GNOME3 desktop, the screen can be locked by selecting the user name in the far right corner of the main panel and selecting Lock.

The following sections detail commands to enforce idle activation of the screensaver, screen locking, a blank-screen screensaver, and an idle activation time.

Because users should be trained to lock the screen when they step away from the computer, the automatic locking feature is only meant as a backup.

The root account can be screen-locked; however, the root account should never be used to log into an X Windows environment and should only be used to for direct login via console in emergency circumstances.

For more information about enforcing preferences in the GNOME3 environment using the DConf configuration system, see http://wiki.gnome.org/dconf and the man page dconf(1).

Rule   Enable GNOME3 Screensaver Idle Activation   [ref]

To activate the screensaver in the GNOME3 desktop after a period of inactivity, add or set idle-activation-enabled to true in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
idle-activation-enabled=true
Once the setting has been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/idle-activation-enabled
After the settings have been set, run dconf update.
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock.

Enabling idle activation of the screensaver ensures the screensaver will be activated after the idle delay. Applications requiring continuous, real-time screen display (such as network management products) require the login session does not have administrator rights and the display station is located in a controlled-access area.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_activation_enabled
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-11(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_activation_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Screensaver Idle Activation
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: idle-activation-enabled
    value: 'true'
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_activation_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME idle-activation-enabled
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$
    line: /org/gnome/desktop/screensaver/idle-activation-enabled
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_activation_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_activation_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*idle-activation-enabled\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)idle-activation-enabled(\s*=)/#\1idle-activation-enabled\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*idle-activation-enabled\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*idle-activation-enabled\\s*=\\s*.*/idle-activation-enabled=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\idle-activation-enabled=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/idle-activation-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/idle-activation-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure Users Cannot Change GNOME3 Screensaver Idle Activation   [ref]

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding
/org/gnome/desktop/screensaver/idle-activation-enabled
to /etc/dconf/db/local.d/00-security-settings. For example:
/org/gnome/desktop/screensaver/idle-activation-enabled
After the settings have been set, run dconf update.
Rationale:
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_activation_locked
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_idle_activation_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME Screensaver idle-activation-enabled
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$
    line: /org/gnome/desktop/screensaver/idle-activation-enabled
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_idle_activation_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_idle_activation_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/idle-activation-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/idle-activation-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Set GNOME3 Screensaver Inactivity Timeout   [ref]

The idle time-out value for inactivity in the GNOME3 desktop is configured via the idle-delay setting must be set under an appropriate configuration file(s) in the /etc/dconf/db/local.d directory and locked in /etc/dconf/db/local.d/locks directory to prevent user modification.

For example, to configure the system for a 15 minute delay, add the following to /etc/dconf/db/local.d/00-security-settings:
[org/gnome/desktop/session]
idle-delay=uint32 900
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME3 can be configured to identify when a user's session has idled and take action to initiate a session lock.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057, CCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-11(a), CM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.4
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
- name: XCCDF Value inactivity_timeout_value # promote to variable
  set_fact:
    inactivity_timeout_value: !!str 900
  tags:
    - always

- name: Set GNOME3 Screensaver Inactivity Timeout
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/session
    option: idle-delay
    value: uint32 {{ inactivity_timeout_value }}
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

inactivity_timeout_value='900'


# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)idle-delay(\s*=)/#\1idle-delay\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/session\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")"
if grep -q "^\\s*idle-delay\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${DCONFFILE}"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Set GNOME3 Screensaver Lock Delay After Activation Period   [ref]

To activate the locking delay of the screensaver in the GNOME3 desktop when the screensaver is activated, add or set lock-delay to uint32 0 in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
lock-delay=uint32 0
         
After the settings have been set, run dconf update.
Rationale:
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000056, CCI-000057, CCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-11(a), CM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.4
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
- name: XCCDF Value var_screensaver_lock_delay # promote to variable
  set_fact:
    var_screensaver_lock_delay: !!str 0
  tags:
    - always

- name: Set GNOME3 Screensaver Lock Delay After Activation Period
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: lock-delay
    value: uint32 {{ var_screensaver_lock_delay }}
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

var_screensaver_lock_delay='0'


# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*lock-delay\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)lock-delay(\s*=)/#\1lock-delay\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${var_screensaver_lock_delay}")"
if grep -q "^\\s*lock-delay\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*lock-delay\\s*=\\s*.*/lock-delay=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-delay=${escaped_value}" "${DCONFFILE}"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Enable GNOME3 Screensaver Lock After Idle Period   [ref]

To activate locking of the screensaver in the GNOME3 desktop when it is activated, add or set lock-enabled to true in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
lock-enabled=true
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.
Rationale:
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000056, CCI-000058, CCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution == 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Screensaver Lock After Idle Period
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: lock-enabled
    value: 'true'
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution != 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME lock-enabled
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-enabled$
    line: /org/gnome/desktop/screensaver/lock-enabled
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution != 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Screensaver Lock After Idle Period
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/lockdown
    option: disable-lock-screen
    value: 'false'
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution == 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME disable-lock-screen
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/lockdown/disable-lock-screen$
    line: /org/gnome/desktop/lockdown/disable-lock-screen
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution == 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Check GNOME3 screenserver disable-lock-screen false
  command: gsettings get org.gnome.desktop.lockdown disable-lock-screen
  register: cmd_out
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution == 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Update GNOME3 screenserver disable-lock-screen false
  command: gsettings set org.gnome.desktop.lockdown disable-lock-screen false
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ansible_distribution == 'SLES'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)lock-enabled(\s*=)/#\1lock-enabled\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*lock-enabled\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period   [ref]

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding
/org/gnome/desktop/screensaver/lock-enabled
to /etc/dconf/db/local.d/locks/00-security-settings. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.
Rationale:
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_locked
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000056, CCI-000057
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME Screensaver lock-enabled
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-enabled$
    line: /org/gnome/desktop/screensaver/lock-enabled
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Implement Blank Screensaver   [ref]

To set the screensaver mode in the GNOME3 desktop to a blank screen, add or set picture-uri to string '' in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
picture-uri=string ''
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/picture-uri
After the settings have been set, run dconf update.
Rationale:
Setting the screensaver mode to blank-only conceals the contents of the display from passersby.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_mode_blank
References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-11(1), CM-6(a), AC-11(1).1
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000031-GPOS-00012
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(1)
  - NIST-800-53-AC-11(1).1
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_mode_blank
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Implement Blank Screensaver
  ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: picture-uri
    value: string ''
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(1)
  - NIST-800-53-AC-11(1).1
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_mode_blank
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME picture-uri
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/picture-uri$
    line: /org/gnome/desktop/screensaver/picture-uri
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(1)
  - NIST-800-53-AC-11(1).1
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_mode_blank
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.5
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(1)
  - NIST-800-53-AC-11(1).1
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_mode_blank
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*picture-uri\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)picture-uri(\s*=)/#\1picture-uri\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "string ''")"
if grep -q "^\\s*picture-uri\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*picture-uri\\s*=\\s*.*/picture-uri=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\picture-uri=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/picture-uri$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/picture-uri$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/picture-uri$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/picture-uri" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure Users Cannot Change GNOME3 Screensaver Settings   [ref]

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding /org/gnome/desktop/screensaver/lock-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-delay
After the settings have been set, run dconf update.
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_user_locks
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057, CCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.5

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME lock-delay
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-delay$
    line: /org/gnome/desktop/screensaver/lock-delay
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-delay$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-delay$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-delay$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure Users Cannot Change GNOME3 Session Idle Settings   [ref]

If not already configured, ensure that users cannot change GNOME3 session idle settings by adding /org/gnome/desktop/session/idle-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/session/idle-delay
After the settings have been set, run dconf update.
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057, CCI-000060
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.5
pcidss48.2.8

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME Session idle-delay
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/session/idle-delay$
    line: /org/gnome/desktop/session/idle-delay
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/session/idle-delay$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/session/idle-delay$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   GNOME System Settings   Group contains 1 rule
[ref]   GNOME provides configuration and functionality to a graphical desktop environment that changes grahical configurations or allow a user to perform actions that users normally would not be able to do in non-graphical mode such as remote access configuration, power policies, Geo-location, etc. Configuring such settings in GNOME will prevent accidential graphical configuration changes by users from taking place.

Rule   Disable User Administration in GNOME3   [ref]

By default, GNOME will allow all users to have some administratrion capability. This should be disabled so that non-administrative users are not making configuration changes. To configure the system to disable user administration capability in the Graphical User Interface (GUI), add or set user-administration-disabled to true in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/lockdown]
user-administration-disabled=true
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/lockdown/user-administration-disabled
After the settings have been set, run dconf update.
Rationale:
Allowing all users to have some administratrive capabilities to the system through the Graphical User Interface (GUI) when they would not have them otherwise could allow unintended configuration changes as well as a nefarious user the capability to make system changes such as adding new accounts, etc.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_disable_user_admin
References:
cui3.1.5
osppFMT_MOD_EXT.1

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Detect if user-administration-disabled can be found on /etc/dconf/db/local.d/
  find:
    path: /etc/dconf/db/local.d/
    contains: ^\s*user-administration-disabled
  register: dconf_gnome_disable_user_admin_config_files
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Configure user-administration-disabled - default file
  ini_file:
    dest: /etc/dconf/db/local.d//00-security-settings
    section: org/gnome/desktop/lockdown
    option: user-administration-disabled
    value: 'true'
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched
    == 0
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Configure user-administration-disabled - existing files
  ini_file:
    dest: '{{ item.path }}'
    section: org/gnome/desktop/lockdown
    option: user-administration-disabled
    value: 'true'
    create: true
  with_items: '{{ dconf_gnome_disable_user_admin_config_files.files }}'
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched
    > 0
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Detect if lock for user-administration-disabled can be found on /etc/dconf/db/local.d/
  find:
    path: /etc/dconf/db/local.d/locks
    contains: ^\s*user-administration-disabled
  register: dconf_gnome_disable_user_admin_lock_files
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification user-administration-disabled - default file
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$
    line: /org/gnome/desktop/lockdown/user-administration-disabled
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched
    == 0
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification user-administration-disabled - existing files
  lineinfile:
    path: '{{ item.path }}'
    regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$
    line: /org/gnome/desktop/lockdown/user-administration-disabled
    create: true
  with_items: '{{ dconf_gnome_disable_user_admin_lock_files.files }}'
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched
    > 0
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update - user-administration-disabled
  command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.5
  - dconf_gnome_disable_user_admin
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/lockdown\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*user-administration-disabled\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)user-administration-disabled(\s*=)/#\1user-administration-disabled\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/lockdown\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/lockdown]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*user-administration-disabled\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*user-administration-disabled\\s*=\\s*.*/user-administration-disabled=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/lockdown\\]|a\\user-administration-disabled=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/lockdown/user-administration-disabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/lockdown/user-administration-disabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/lockdown/user-administration-disabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/lockdown/user-administration-disabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Sudo   Group contains 1 rule
[ref]   Sudo, which stands for "su 'do'", provides the ability to delegate authority to certain users, groups of users, or system administrators. When configured for system users and/or groups, Sudo can allow a user or group to execute privileged commands that normally only root is allowed to execute.

For more information on Sudo and addition Sudo configuration options, see https://www.sudo.ws.

Rule   Install sudo Package   [ref]

The sudo package can be installed with the following command:
$ sudo dnf install sudo
Rationale:
sudo is a program designed to allow a system administrator to give limited root privileges to users and log root activity. The basic philosophy is to give as few privileges as possible but still allow system users to get their work done.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_sudo_installed
Identifiers:

CCE-87100-4

References:
ism1382, 1384, 1386
nistCM-6(a)
osppFMT_MOF_EXT.1
os-srgSRG-OS-000324-GPOS-00125
anssiR33
cis5.2.1
pcidss42.2.6


[[packages]]
name = "sudo"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure sudo is installed
  package:
    name: sudo
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CCE-87100-4
  - NIST-800-53-CM-6(a)
  - PCI-DSSv4-2.2.6
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_sudo_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=sudo

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_sudo

class install_sudo {
  package { 'sudo':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "sudo" ; then
    dnf install -y "sudo"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   System Tooling / Utilities   Group contains 6 rules
[ref]   The following checks evaluate the system for recommended base packages -- both for installation and removal.

Rule   Install dnf-plugin-subscription-manager Package   [ref]

The dnf-plugin-subscription-manager package can be installed with the following command:
$ sudo dnf install dnf-plugin-subscription-manager
Rationale:
This package provides plugins to interact with repositories and subscriptions from the Red Hat entitlement platform; contains subscription-manager and product-id plugins.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_dnf-plugin-subscription-manager_installed
References:
ism0940, 1144, 1467, 1472, 1483, 1493, 1494, 1495
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
os-srgSRG-OS-000366-GPOS-00153


[[packages]]
name = "dnf-plugin-subscription-manager"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure dnf-plugin-subscription-manager is installed
  package:
    name: dnf-plugin-subscription-manager
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_dnf-plugin-subscription-manager_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=dnf-plugin-subscription-manager

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_dnf-plugin-subscription-manager

class install_dnf-plugin-subscription-manager {
  package { 'dnf-plugin-subscription-manager':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "dnf-plugin-subscription-manager" ; then
    dnf install -y "dnf-plugin-subscription-manager"
fi

Rule   Ensure gnutls-utils is installed   [ref]

The gnutls-utils package can be installed with the following command:
$ sudo dnf install gnutls-utils
Rationale:
GnuTLS is a secure communications library implementing the SSL, TLS and DTLS protocols and technologies around them. It provides a simple C language application programming interface (API) to access the secure communications protocols as well as APIs to parse and write X.509, PKCS #12, OpenPGP and other required structures. This package contains command line TLS client and server and certificate manipulation tools.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_gnutls-utils_installed
References:
osppFIA_X509_EXT.1, FIA_X509_EXT.2
os-srgSRG-OS-000480-GPOS-00227


[[packages]]
name = "gnutls-utils"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure gnutls-utils is installed
  package:
    name: gnutls-utils
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_gnutls-utils_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=gnutls-utils

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_gnutls-utils

class install_gnutls-utils {
  package { 'gnutls-utils':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "gnutls-utils" ; then
    dnf install -y "gnutls-utils"
fi

Rule   Ensure nss-tools is installed   [ref]

The nss-tools package can be installed with the following command:
$ sudo dnf install nss-tools
Rationale:
Network Security Services (NSS) is a set of libraries designed to support cross-platform development of security-enabled client and server applications. Install the nss-tools package to install command-line tools to manipulate the NSS certificate and key database.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_nss-tools_installed
References:
osppFMT_SMF_EXT.1
os-srgSRG-OS-000480-GPOS-00227


[[packages]]
name = "nss-tools"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure nss-tools is installed
  package:
    name: nss-tools
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_nss-tools_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=nss-tools

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_nss-tools

class install_nss-tools {
  package { 'nss-tools':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "nss-tools" ; then
    dnf install -y "nss-tools"
fi

Rule   Install openscap-scanner Package   [ref]

The openscap-scanner package can be installed with the following command:
$ sudo dnf install openscap-scanner
Rationale:
openscap-scanner contains the oscap command line tool. This tool is a configuration and vulnerability scanner, capable of performing compliance checking using SCAP content.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_openscap-scanner_installed
References:
osppAGD_PRE.1, AGD_OPE.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000191-GPOS-00080


[[packages]]
name = "openscap-scanner"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure openscap-scanner is installed
  package:
    name: openscap-scanner
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_openscap-scanner_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=openscap-scanner

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_openscap-scanner

class install_openscap-scanner {
  package { 'openscap-scanner':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "openscap-scanner" ; then
    dnf install -y "openscap-scanner"
fi

Rule   Install scap-security-guide Package   [ref]

The scap-security-guide package can be installed with the following command:
$ sudo dnf install scap-security-guide
Rationale:
The scap-security-guide package provides a guide for configuration of the system from the final system's security point of view. The guidance is specified in the Security Content Automation Protocol (SCAP) format and constitutes a catalog of practical hardening advice, linked to government requirements where applicable. The SCAP Security Guide project bridges the gap between generalized policy requirements and specific implementation guidelines. A system administrator can use the oscap CLI tool from the openscap-scanner package, or the SCAP Workbench GUI tool from the scap-workbench package, to verify that the system conforms to provided guidelines. Refer to the scap-security-guide(8) manual page for futher information.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_scap-security-guide_installed
References:
osppAGD_PRE.1, AGD_OPE.1
os-srgSRG-OS-000480-GPOS-00227


[[packages]]
name = "scap-security-guide"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure scap-security-guide is installed
  package:
    name: scap-security-guide
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_scap-security-guide_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=scap-security-guide

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_scap-security-guide

class install_scap-security-guide {
  package { 'scap-security-guide':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "scap-security-guide" ; then
    dnf install -y "scap-security-guide"
fi

Rule   Install subscription-manager Package   [ref]

The subscription-manager package can be installed with the following command:
$ sudo dnf install subscription-manager
Rationale:
Red Hat Subscription Manager is a local service which tracks installed products and subscriptions on a local system to help manage subscription assignments. It communicates with the backend subscription service (the Customer Portal or an on-premise server such as Subscription Asset Manager) and works with content management tools such as .
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_subscription-manager_installed
References:
ism0940, 1144, 1467, 1472, 1483, 1493, 1494, 1495
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
os-srgSRG-OS-000366-GPOS-00153


[[packages]]
name = "subscription-manager"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure subscription-manager is installed
  package:
    name: subscription-manager
    state: present
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_subscription-manager_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=subscription-manager

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_subscription-manager

class install_subscription-manager {
  package { 'subscription-manager':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

if ! rpm -q --quiet "subscription-manager" ; then
    dnf install -y "subscription-manager"
fi
Group   Updating Software   Group contains 9 rules
[ref]   The dnf command line tool is used to install and update software packages. The system also provides a graphical software update tool in the System menu, in the Administration submenu, called Software Update.

Red Hat Enterprise Linux 10 systems contain an installed software catalog called the RPM database, which records metadata of installed packages. Consistently using dnf or the graphical Software Update for all software installation allows for insight into the current inventory of installed software on the system.

Rule   Configure dnf-automatic to Install Available Updates Automatically   [ref]

To ensure that the packages comprising the available updates will be automatically installed by dnf-automatic, set apply_updates to yes under [commands] section in /etc/dnf/automatic.conf.
Rationale:
Installing software updates is a fundamental mitigation against the exploitation of publicly-known vulnerabilities. If the most recent security patches and updates are not installed, unauthorized users may take advantage of weaknesses in the unpatched software. The lack of prompt attention to patching could result in a system compromise. The automated installation of updates ensures that recent security patches are applied in a timely manner.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dnf-automatic_apply_updates
References:
ism0940, 1144, 1467, 1472, 1483, 1493, 1494, 1495
nistSI-2(5), CM-6(a), SI-2(c)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000191-GPOS-00080
anssiR61

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Configure dnf-automatic to Install Available Updates Automatically
  ini_file:
    dest: /etc/dnf/automatic.conf
    section: commands
    option: apply_updates
    value: 'yes'
    create: true
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-2(5)
  - NIST-800-53-SI-2(c)
  - dnf-automatic_apply_updates
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy


found=false

# set value in all files if they contain section or key
for f in $(echo -n "/etc/dnf/automatic.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*apply_updates" "$f"; then
            sed -i "s/apply_updates[^(\n)]*/apply_updates = yes/" "$f"
            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[commands\]" "$f"; then
            sed -i "/[[:space:]]*\[commands\]/a apply_updates = yes" "$f"
            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"
    echo -e "[commands]\napply_updates = yes" >> "$file"
fi

Rule   Configure dnf-automatic to Install Only Security Updates   [ref]

To configure dnf-automatic to install only security updates automatically, set upgrade_type to security under [commands] section in /etc/dnf/automatic.conf.
Rationale:
By default, dnf-automatic installs all available updates. Reducing the amount of updated packages only to updates that were issued as a part of a security advisory increases the system stability.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_dnf-automatic_security_updates_only
References:
nistSI-2(5), CM-6(a), SI-2(c)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000191-GPOS-00080
anssiR61

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Configure dnf-automatic to Install Only Security Updates
  ini_file:
    dest: /etc/dnf/automatic.conf
    section: commands
    option: upgrade_type
    value: security
    create: true
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-2(5)
  - NIST-800-53-SI-2(c)
  - dnf-automatic_security_updates_only
  - low_complexity
  - low_severity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy


found=false

# set value in all files if they contain section or key
for f in $(echo -n "/etc/dnf/automatic.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*upgrade_type" "$f"; then
            sed -i "s/upgrade_type[^(\n)]*/upgrade_type = security/" "$f"
            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[commands\]" "$f"; then
            sed -i "/[[:space:]]*\[commands\]/a upgrade_type = security" "$f"
            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"
    echo -e "[commands]\nupgrade_type = security" >> "$file"
fi

Rule   Ensure gpgcheck Enabled In Main dnf Configuration   [ref]

The gpgcheck option controls whether RPM packages' signatures are always checked prior to installation. To configure dnf to check package signatures before installing them, ensure the following line appears in /etc/dnf/dnf.conf in the [main] section:
gpgcheck=1
Rationale:
Changes to any software components can have significant effects on the overall security of the operating system. This requirement ensures the software has not been tampered with and that it has been provided by a trusted vendor.
Accordingly, patches, service packs, device drivers, or operating system components must be signed with a certificate recognized and approved by the organization.
Verifying the authenticity of the software prior to installation validates the integrity of the patch or upgrade received from a vendor. This ensures the software has not been tampered with and that it has been provided by a trusted vendor. Self-signed certificates are disallowed by this requirement. Certificates used to verify the software must be from an approved Certificate Authority (CA).
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated
References:
cis-csc11, 2, 3, 9
cjis5.10.4.1
cobit5APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02
cui3.4.8
disaCCI-001749
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4
nistCM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b)
nist-csfPR.DS-6, PR.DS-8, PR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
pcidssReq-6.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59
cis1.2.1.2
pcidss46.3.3

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - configure_strategy
  - ensure_gpgcheck_globally_activated
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed

- name: Ensure GPG check is globally activated
  ini_file:
    dest: /etc/dnf/dnf.conf
    section: main
    option: gpgcheck
    value: 1
    no_extra_spaces: true
    create: false
  when: '"dnf" in ansible_facts.packages'
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - configure_strategy
  - ensure_gpgcheck_globally_activated
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed

# Remediation is applicable only in certain platforms
if rpm --quiet -q dnf; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^gpgcheck")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^gpgcheck\\>" "/etc/dnf/dnf.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/dnf/dnf.conf"
else
    if [[ -s "/etc/dnf/dnf.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/dnf/dnf.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/dnf/dnf.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/dnf/dnf.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure gpgcheck Enabled for Local Packages   [ref]

dnf should be configured to verify the signature(s) of local packages prior to installation. To configure dnf to verify signatures of local packages, set the localpkg_gpgcheck to 1 in /etc/dnf/dnf.conf.
Rationale:
Changes to any software components can have significant effects to the overall security of the operating system. This requirement ensures the software has not been tampered and has been provided by a trusted vendor.

Accordingly, patches, service packs, device drivers, or operating system components must be signed with a certificate recognized and approved by the organization.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages
References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.4.8
disaCCI-001749
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistCM-11(a), CM-11(b), CM-6(a), CM-5(3), SA-12, SA-12(10)
nist-csfPR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - ensure_gpgcheck_local_packages
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Ensure GPG check Enabled for Local Packages (dnf)
  block:

  - name: Check stats of dnf
    stat:
      path: /etc/dnf/dnf.conf
    register: pkg

  - name: Check if config file of dnf is a symlink
    ansible.builtin.set_fact:
      pkg_config_file_symlink: '{{ pkg.stat.lnk_target if pkg.stat.lnk_target is match("^/.*")
        else "/etc/dnf/dnf.conf" | dirname ~ "/" ~ pkg.stat.lnk_target }}'
    when: pkg.stat.lnk_target is defined

  - name: Ensure GPG check Enabled for Local Packages (dnf)
    ini_file:
      dest: '{{ pkg_config_file_symlink |  default("/etc/dnf/dnf.conf") }}'
      section: main
      option: localpkg_gpgcheck
      value: 1
      no_extra_spaces: true
      create: true
  when: '"dnf" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - ensure_gpgcheck_local_packages
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q dnf; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^localpkg_gpgcheck")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^localpkg_gpgcheck\\>" "/etc/dnf/dnf.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^localpkg_gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/dnf/dnf.conf"
else
    if [[ -s "/etc/dnf/dnf.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/dnf/dnf.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/dnf/dnf.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/dnf/dnf.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure gpgcheck Enabled for All dnf Package Repositories   [ref]

To ensure signature checking is not disabled for any repos, remove any lines from files in /etc/yum.repos.d of the form:
gpgcheck=0
Rationale:
Verifying the authenticity of the software prior to installation validates the integrity of the patch or upgrade received from a vendor. This ensures the software has not been tampered with and that it has been provided by a trusted vendor. Self-signed certificates are disallowed by this requirement. Certificates used to verify the software must be from an approved Certificate Authority (CA)."
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_ensure_gpgcheck_never_disabled
References:
cis-csc11, 2, 3, 9
cjis5.10.4.1
cobit5APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02
cui3.4.8
disaCCI-001749
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4
nistCM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b)
nist-csfPR.DS-6, PR.DS-8, PR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
pcidssReq-6.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59
pcidss46.3.3

Complexity:low
Disruption:medium
Reboot:false
Strategy:enable
- name: Grep for dnf repo section names
  shell: |
    set -o pipefail
    grep -HEr '^\[.+\]' -r /etc/yum.repos.d/
  register: repo_grep_results
  failed_when: repo_grep_results.rc not in [0, 1]
  changed_when: false
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - enable_strategy
  - ensure_gpgcheck_never_disabled
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed

- name: Set gpgcheck=1 for each dnf repo
  ini_file:
    path: '{{ item[0] }}'
    section: '{{ item[1] }}'
    option: gpgcheck
    value: '1'
    no_extra_spaces: true
  loop: '{{ repo_grep_results.stdout |regex_findall( ''(.+\.repo):\[(.+)\]\n?'' )
    if repo_grep_results is not skipped else [] }}'
  when: repo_grep_results is not skipped
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - enable_strategy
  - ensure_gpgcheck_never_disabled
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed


sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/*

Rule   Ensure Red Hat GPG Key Installed   [ref]

To ensure the system can cryptographically verify base software packages come from Red Hat (and to connect to the Red Hat Network to receive them), the Red Hat GPG key must properly be installed. To install the Red Hat GPG key, run:
$ sudo subscription-manager register
If the system is not connected to the Internet or an RHN Satellite, then install the Red Hat GPG key from trusted media such as the Red Hat installation CD-ROM or DVD. Assuming the disc is mounted in /media/cdrom, use the following command as the root user to import it into the keyring:
$ sudo rpm --import /media/cdrom/RPM-GPG-KEY
Alternatively, the key may be pre-loaded during the RHEL installation. In such cases, the key can be installed by running the following command:
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
Rationale:
Changes to software components can have significant effects on the overall security of the operating system. This requirement ensures the software has not been tampered with and that it has been provided by a trusted vendor. The Red Hat GPG key is necessary to cryptographically verify packages are from Red Hat.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed
References:
cis-csc11, 2, 3, 9
cjis5.10.4.1
cobit5APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02
cui3.4.8
disaCCI-001749
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4
nerc-cipCIP-003-8 R4.2, CIP-003-8 R6, CIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-5(3), SI-7, SC-12, SC-12(3), CM-6(a)
nist-csfPR.DS-6, PR.DS-8, PR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
pcidssReq-6.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59
pcidss46.3.3

Complexity:medium
Disruption:medium
Reboot:false
Strategy:restrict
- name: Read permission of GPG key directory
  stat:
    path: /etc/pki/rpm-gpg/
  register: gpg_key_directory_permission
  check_mode: false
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - ensure_redhat_gpgkey_installed
  - high_severity
  - medium_complexity
  - medium_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Read signatures in GPG key
  command: gpg --with-fingerprint --with-colons "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
  changed_when: false
  register: gpg_fingerprints
  check_mode: false
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - ensure_redhat_gpgkey_installed
  - high_severity
  - medium_complexity
  - medium_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Set Fact - Installed GPG Fingerprints
  set_fact:
    gpg_installed_fingerprints: |-
      {{ gpg_fingerprints.stdout | regex_findall('^pub.*
      (?:^fpr[:]*)([0-9A-Fa-f]*)', '\1') | list }}
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - ensure_redhat_gpgkey_installed
  - high_severity
  - medium_complexity
  - medium_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Set Fact - Valid fingerprints
  set_fact:
    gpg_valid_fingerprints:
    - ''
    - ''
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - ensure_redhat_gpgkey_installed
  - high_severity
  - medium_complexity
  - medium_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Import RedHat GPG key
  rpm_key:
    state: present
    key: /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
  when:
  - gpg_key_directory_permission.stat.mode <= '0755'
  - (gpg_installed_fingerprints | difference(gpg_valid_fingerprints)) | length ==
    0
  - gpg_installed_fingerprints | length > 0
  - ansible_distribution == "RedHat"
  tags:
  - CJIS-5.10.4.1
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-12
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SI-7
  - PCI-DSS-Req-6.2
  - PCI-DSSv4-6.3.3
  - ensure_redhat_gpgkey_installed
  - high_severity
  - medium_complexity
  - medium_disruption
  - no_reboot_needed
  - restrict_strategy

# The two fingerprints below are retrieved from https://access.redhat.com/security/team/key
readonly REDHAT_RELEASE_FINGERPRINT=""
readonly REDHAT_AUXILIARY_FINGERPRINT=""

# Location of the key we would like to import (once it's integrity verified)
readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"

RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")")

# Verify /etc/pki/rpm-gpg directory permissions are safe
if [ "${RPM_GPG_DIR_PERMS}" -le "755" ]
then
  # If they are safe, try to obtain fingerprints from the key file
  # (to ensure there won't be e.g. CRC error).

  readarray -t GPG_OUT < <(gpg --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep "^fpr" | cut -d ":" -f 10)

  GPG_RESULT=$?
  # No CRC error, safe to proceed
  if [ "${GPG_RESULT}" -eq "0" ]
  then
    echo "${GPG_OUT[*]}" | grep -vE "${REDHAT_RELEASE_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || {
      # If $REDHAT_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it
      rpm --import "${REDHAT_RELEASE_KEY}"
    }
  fi
fi

Rule   Ensure SUSE GPG Key Installed   [ref]

To ensure the system can cryptographically verify base software packages come from SUSE (and to connect to the SUSE to receive them), the SUSE GPG key must properly be installed. To install the SUSE GPG key, run:
$ sudo zypper install suse-build-key
If the system is not connected to the Internet or an RHN Satellite, then install the SUSE GPG key from trusted media such as the SUSE installation CD-ROM or DVD. Assuming the disc is mounted in /media/cdrom, use the following command as the root user to import it into the keyring:
$ sudo rpm --import /media/cdrom/content.key
or
$ sudo rpm --import /media/cdrom/repodata/repomd.xml.key
Alternatively, the key may be pre-loaded during the SUSE installation. In such cases, one can use the repository cache files to install the key, for example by running the following command:
sudo rpm --import /var/cache/zypp/raw/Basesystem_Module_15_SP2_x86_64:SLE-Module-Basesystem15-SP2-Pool/repodata/repomd.xml.key
Rationale:
Changes to software components can have significant effects on the overall security of the operating system. This requirement ensures the software has not been tampered with and that it has been provided by a trusted vendor. The SUSE GPG key is necessary to cryptographically verify packages are from SUSE.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_ensure_suse_gpgkey_installed
References:
cis-csc11, 2, 3, 9
cjis5.10.4.1
cobit5APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02
cui3.4.8
disaCCI-001749
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4
nerc-cipCIP-003-8 R4.2, CIP-003-8 R6, CIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-5(3), SI-7, SC-12, SC-12(3), CM-6(a)
nist-csfPR.DS-6, PR.DS-8, PR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
pcidssReq-6.2
os-srgSRG-OS-000366-GPOS-00153
pcidss46.3.3

Rule   Ensure Software Patches Installed   [ref]



NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy dictates.
Warning:  Red Hat Enterprise Linux 10 does not have a corresponding OVAL CVE Feed. Therefore, this will result in a "not checked" result during a scan.
Rationale:
Installing software updates is a fundamental mitigation against the exploitation of publicly-known vulnerabilities. If the most recent security patches and updates are not installed, unauthorized users may take advantage of weaknesses in the unpatched software. The lack of prompt attention to patching could result in a system compromise.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_security_patches_up_to_date
References:
cis-csc18, 20, 4
cjis5.10.4.1
cobit5APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02
disaCCI-000366, CCI-001227
isa-62443-20094.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9
iso27001-2013A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3
nistSI-2(5), SI-2(c), CM-6(a)
nist-csfID.RA-1, PR.IP-12
osppFMT_MOF_EXT.1
pcidssReq-6.2
os-srgSRG-OS-000480-GPOS-00227
anssiR61
pcidss46.3.3

Rule   Enable dnf-automatic Timer   [ref]

The dnf-automatic timer can be enabled with the following command:
$ sudo systemctl enable dnf-automatic.timer
Rationale:
The dnf-automatic is an alternative command line interface (CLI) to dnf upgrade with specific facilities to make it suitable to be executed automatically and regularly from systemd timers, cron jobs and similar. The tool is controlled by dnf-automatic.timer SystemD timer.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_timer_dnf-automatic_enabled
References:
nistSI-2(5), CM-6(a), SI-2(c)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000191-GPOS-00080
anssiR61

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable timer dnf-automatic
  block:

  - name: Gather the package facts
    package_facts:
      manager: auto

  - name: Enable timer dnf-automatic
    systemd:
      name: dnf-automatic.timer
      enabled: 'yes'
      state: started
    when:
    - '"dnf-automatic" in ansible_facts.packages'
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-2(5)
  - NIST-800-53-SI-2(c)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - timer_dnf-automatic_enabled

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'dnf-automatic.timer'
"$SYSTEMCTL_EXEC" enable 'dnf-automatic.timer'
Group   Account and Access Control   Group contains 13 groups and 33 rules
[ref]   In traditional Unix security, if an attacker gains shell access to a certain login account, they can perform any action or access any file to which that account has access. Therefore, making it more difficult for unauthorized people to gain shell access to accounts, particularly to privileged accounts, is a necessary part of securing a system. This section introduces mechanisms for restricting access to accounts under Red Hat Enterprise Linux 10.
Group   Warning Banners for System Accesses   Group contains 1 group and 3 rules
[ref]   Each system should expose as little information about itself as possible.

System banners, which are typically displayed just before a login prompt, give out information about the service or the host's operating system. This might include the distribution name and the system kernel version, and the particular version of a network service. This information can assist intruders in gaining access to the system as it can reveal whether the system is running vulnerable software. Most network services can be configured to limit what information is displayed.

Many organizations implement security policies that require a system banner provide notice of the system's ownership, provide warning to unauthorized users, and remind authorized users of their consent to monitoring.
Group   Implement a GUI Warning Banner   Group contains 2 rules

Rule   Enable GNOME3 Login Warning Banner   [ref]

In the default graphical environment, displaying a login warning banner in the GNOME Display Manager's login screen can be enabled on the login screen by setting banner-message-enable to true.

To enable, add or edit banner-message-enable to /etc/dconf/db/distro.d/00-security-settings. For example:
[org/gnome/login-screen]
banner-message-enable=true
Once the setting has been added, add a lock to /etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/login-screen/banner-message-enable
After the settings have been set, run dconf update. The banner text must also be set.
Rationale:
Display of a standardized and approved use notification before granting access to the operating system ensures privacy and security notification verbiage used is consistent with applicable federal laws, Executive Orders, directives, policies, regulations, standards, and guidance.

For U.S. Government systems, system use notifications are required only for access via login interfaces with human users and are not required when such human interfaces do not exist.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dconf_gnome_banner_enabled
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.9
disaCCI-000048, CCI-000050, CCI-001384, CCI-001385, CCI-001386, CCI-001387, CCI-001388
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-8(a), AC-8(b), AC-8(c)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000023-GPOS-00006, SRG-OS-000228-GPOS-00088
cis1.8.2

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Login Warning Banner
  ini_file:
    dest: /etc/dconf/db/distro.d/00-security-settings
    section: org/gnome/login-screen
    option: banner-message-enable
    value: 'true'
    create: true
    no_extra_spaces: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME banner-message-enabled
  lineinfile:
    path: /etc/dconf/db/distro.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/login-screen/banner-message-enable$
    line: /org/gnome/login-screen/banner-message-enable
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|distro.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/distro.d/00-security-settings"
DBDIR="/etc/dconf/db/distro.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*banner-message-enable\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)banner-message-enable(\s*=)/#\1banner-message-enable\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/login-screen\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*banner-message-enable\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*banner-message-enable\\s*=\\s*.*/banner-message-enable=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-enable=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-enable$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|distro.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/distro.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/login-screen/banner-message-enable$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/login-screen/banner-message-enable$" /etc/dconf/db/distro.d/
then
    echo "/org/gnome/login-screen/banner-message-enable" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Modify the System Login Banner   [ref]

To configure the system login banner edit /etc/issue. Replace the default text with a message compliant with the local site policy or a legal disclaimer. The DoD required text is either:

You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions:
-The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations.
-At any time, the USG may inspect and seize data stored on this IS.
-Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose.
-This IS includes security measures (e.g., authentication and access controls) to protect USG interests -- not for your personal benefit or privacy.
-Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details.


OR:

I've read & consent to terms in IS user agreem't.
Rationale:
Display of a standardized and approved use notification before granting access to the operating system ensures privacy and security notification verbiage used is consistent with applicable federal laws, Executive Orders, directives, policies, regulations, standards, and guidance.

System use notifications are required only for access via login interfaces with human users and are not required when such human interfaces do not exist.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_banner_etc_issue
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.9
disaCCI-000048, CCI-000050, CCI-001384, CCI-001385, CCI-001386, CCI-001387, CCI-001388
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-8(a), AC-8(c)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000023-GPOS-00006, SRG-OS-000228-GPOS-00088
cis1.7.2

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: XCCDF Value login_banner_text # promote to variable
  set_fact:
    login_banner_text: !!str ^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$
  tags:
    - always

- name: Modify the System Login Banner - Ensure Correct Banner
  copy:
    dest: /etc/issue
    content: '{{ login_banner_text | regex_replace("^\^(.*)\$$", "\1") | regex_replace("^\((.*\.)\|.*\)$",
      "\1") | regex_replace("\[\\s\\n\]\+"," ") | regex_replace("\(\?:\[\\n\]\+\|\(\?:\\\\n\)\+\)",
      "\n") | regex_replace("\\", "") | wordwrap() }}'
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - banner_etc_issue
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: master
    machineconfiguration.openshift.io/role: worker
  name: 75-banner-etc-issue
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,You%20are%20accessing%20a%20U.S.%20Government%20%28USG%29%20Information%20System%20%28IS%29%20that%20is%20%0Aprovided%20for%20USG-authorized%20use%20only.%20By%20using%20this%20IS%20%28which%20includes%20any%20%0Adevice%20attached%20to%20this%20IS%29%2C%20you%20consent%20to%20the%20following%20conditions%3A%0A%0A-The%20USG%20routinely%20intercepts%20and%20monitors%20communications%20on%20this%20IS%20for%20%0Apurposes%20including%2C%20but%20not%20limited%20to%2C%20penetration%20testing%2C%20COMSEC%20monitoring%2C%20%0Anetwork%20operations%20and%20defense%2C%20personnel%20misconduct%20%28PM%29%2C%20law%20enforcement%20%0A%28LE%29%2C%20and%20counterintelligence%20%28CI%29%20investigations.%0A%0A-At%20any%20time%2C%20the%20USG%20may%20inspect%20and%20seize%20data%20stored%20on%20this%20IS.%0A%0A-Communications%20using%2C%20or%20data%20stored%20on%2C%20this%20IS%20are%20not%20private%2C%20are%20subject%20%0Ato%20routine%20monitoring%2C%20interception%2C%20and%20search%2C%20and%20may%20be%20disclosed%20or%20used%20%0Afor%20any%20USG-authorized%20purpose.%0A%0A-This%20IS%20includes%20security%20measures%20%28e.g.%2C%20authentication%20and%20access%20controls%29%20%0Ato%20protect%20USG%20interests--not%20for%20your%20personal%20benefit%20or%20privacy.%0A%0A-Notwithstanding%20the%20above%2C%20using%20this%20IS%20does%20not%20constitute%20consent%20to%20PM%2C%20LE%20%0Aor%20CI%20investigative%20searching%20or%20monitoring%20of%20the%20content%20of%20privileged%20%0Acommunications%2C%20or%20work%20product%2C%20related%20to%20personal%20representation%20or%20services%20%0Aby%20attorneys%2C%20psychotherapists%2C%20or%20clergy%2C%20and%20their%20assistants.%20Such%20%0Acommunications%20and%20work%20product%20are%20private%20and%20confidential.%20See%20User%20%0AAgreement%20for%20details.
        mode: 0644
        path: /etc/issue.d/legal-notice
        overwrite: true

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

login_banner_text='^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$'


# Multiple regexes transform the banner regex into a usable banner
# 0 - Remove anchors around the banner text
login_banner_text=$(echo "$login_banner_text" | sed 's/^\^\(.*\)\$$/\1/g')
# 1 - Keep only the first banners if there are multiple
#    (dod_banners contains the long and short banner)
login_banner_text=$(echo "$login_banner_text" | sed 's/^(\(.*\.\)|.*)$/\1/g')
# 2 - Add spaces ' '. (Transforms regex for "space or newline" into a " ")
login_banner_text=$(echo "$login_banner_text" | sed 's/\[\\s\\n\]+/ /g')
# 3 - Adds newlines. (Transforms "(?:\[\\n\]+|(?:\\n)+)" into "\n")
login_banner_text=$(echo "$login_banner_text" | sed 's/(?:\[\\n\]+|(?:\\\\n)+)/\n/g')
# 4 - Remove any leftover backslash. (From any parethesis in the banner, for example).
login_banner_text=$(echo "$login_banner_text" | sed 's/\\//g')
formatted=$(echo "$login_banner_text" | fold -sw 80)
cat <<EOF >/etc/issue
$formatted
EOF

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Protect Accounts by Configuring PAM   Group contains 3 groups and 12 rules
[ref]   PAM, or Pluggable Authentication Modules, is a system which implements modular authentication for Linux programs. PAM provides a flexible and configurable architecture for authentication, and it should be configured to minimize exposure to unnecessary risk. This section contains guidance on how to accomplish that.

PAM is implemented as a set of shared objects which are loaded and invoked whenever an application wishes to authenticate a user. Typically, the application must be running as root in order to take advantage of PAM, because PAM's modules often need to be able to access sensitive stores of account information, such as /etc/shadow. Traditional privileged network listeners (e.g. sshd) or SUID programs (e.g. sudo) already meet this requirement. An SUID root application, userhelper, is provided so that programs which are not SUID or privileged themselves can still take advantage of PAM.

PAM looks in the directory /etc/pam.d for application-specific configuration information. For instance, if the program login attempts to authenticate a user, then PAM's libraries follow the instructions in the file /etc/pam.d/login to determine what actions should be taken.

One very important file in /etc/pam.d is /etc/pam.d/system-auth. This file, which is included by many other PAM configuration files, defines 'default' system authentication measures. Modifying this file is a good way to make far-reaching authentication changes, for instance when implementing a centralized authentication service.
Warning:  Be careful when making changes to PAM's configuration files. The syntax for these files is complex, and modifications can have unexpected consequences. The default configurations shipped with applications should be sufficient for most users.
Warning:  Running authconfig or system-config-authentication will re-write the PAM configuration files, destroying any manually made changes and replacing them with a series of system defaults. One reference to the configuration file syntax can be found at https://fossies.org/linux/Linux-PAM-docs/doc/sag/Linux-PAM_SAG.pdf.
Group   Set Lockouts for Failed Password Attempts   Group contains 6 rules
[ref]   The pam_faillock PAM module provides the capability to lock out user accounts after a number of failed login attempts. Its documentation is available in /usr/share/doc/pam-VERSION/txts/README.pam_faillock.

Warning:  Locking out user accounts presents the risk of a denial-of-service attack. The lockout policy must weigh whether the risk of such a denial-of-service attack outweighs the benefits of thwarting password guessing attacks.

Rule   Lock Accounts After Failed Password Attempts   [ref]

This rule configures the system to lock out accounts after a number of incorrect login attempts using pam_faillock.so. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. Ensure that the file /etc/security/faillock.conf contains the following entry: deny = <count> Where count should be less than or equal to 3 and greater than 0. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Warning:  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.
Rationale:
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny
References:
cis-csc1, 12, 15, 16
cjis5.5.3
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.8
disaCCI-000044, CCI-002236, CCI-002237, CCI-002238
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a)
nist-csfPR.AC-7
osppFIA_AFL.1
pcidssReq-8.1.6
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis5.3.3.1.1
pcidss48.3.4

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Check if system relies on authselect
    tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Remediation where authselect
    tool is present
  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
    failed_when: false

  - name: Lock Accounts After Failed Password Attempts - Informative message based
      on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Lock Accounts After Failed Password Attempts - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    when:
    - result_authselect_check_cmd is success

  - name: 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_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:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so preauth
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so authfail
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so account
      section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_deny # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_deny: !!str 3
  tags:
    - always

- name: Lock Accounts After Failed Password Attempts - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - 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 in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*deny\s*=
    line: deny = {{ var_accounts_passwords_pam_faillock_deny }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - 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 tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      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
        failed_when: false

      - name: Lock Accounts After Failed Password Attempts - Informative message based
          on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: 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 tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      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
        failed_when: false

      - name: Lock Accounts After Failed Password Attempts - Informative message based
          on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: 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:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - 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 in PAM files
  block:

  - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so
      deny parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*deny
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_deny_parameter_is_present

  - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so
      preauth deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found == 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so
      authfail deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found == 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the desired value
      for pam_faillock.so preauth deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(deny)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found > 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the desired value
      for pam_faillock.so authfail deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(deny)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_deny='3'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
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."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
    regex="^\s*deny\s*="
    line="deny = $var_accounts_passwords_pam_faillock_deny"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF
    fi
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                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."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b' "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*deny' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure the root Account for Failed Password Attempts   [ref]

This rule configures the system to lock out the root account after a number of incorrect login attempts using pam_faillock.so. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Warning:  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.
Rationale:
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny_root
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
disaCCI-002238, CCI-000044
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b), IA-5(c)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis5.3.3.1.3

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Check if system
    relies on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Remediation where
    authselect tool is present
  block:

  - name: Configure the root Account for Failed Password Attempts - Check integrity
      of authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    failed_when: false

  - name: Configure the root Account for Failed Password Attempts - Informative message
      based on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Configure the root Account for Failed Password Attempts - Get authselect
      current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    when:
    - result_authselect_check_cmd is success

  - name: Configure the root Account for Failed Password Attempts - Ensure "with-faillock"
      feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Configure the root Account for Failed Password Attempts - Ensure authselect
      changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Remediation where
    authselect tool is not present
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      preauth editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      authfail editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      account section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Check the presence
    of /etc/security/faillock.conf file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*even_deny_root
    line: even_deny_root
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter not in PAM files
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Configure the root Account for Failed Password Attempts - Check the proper
      remediation for the system
    block:

    - name: Configure the root Account for Failed Password Attempts - Define the PAM
        file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Configure the root Account for Failed Password Attempts - Check if system
        relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Configure the root Account for Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Configure the root Account for Failed Password Attempts - Informative
          message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Configure the root Account for Failed Password Attempts - Define the
          current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Define the
          new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Check if any
          custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("custom/")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Ensure the
          authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Restore the
          authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Configure the root Account for Failed Password Attempts - Change the
          PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
      when:
      - result_authselect_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Configure the root Account for Failed Password Attempts - Check the proper
      remediation for the system
    block:

    - name: Configure the root Account for Failed Password Attempts - Define the PAM
        file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Configure the root Account for Failed Password Attempts - Check if system
        relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Configure the root Account for Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Configure the root Account for Failed Password Attempts - Informative
          message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Configure the root Account for Failed Password Attempts - Define the
          current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Define the
          new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Check if any
          custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("custom/")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Ensure the
          authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Restore the
          authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Configure the root Account for Failed Password Attempts - Change the
          PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
      when:
      - result_authselect_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter in PAM files
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so
      even_deny_root parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_even_deny_root_parameter_is_present

  - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so preauth even_deny_root parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 even_deny_root
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_even_deny_root_parameter_is_present.found == 0

  - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so authfail even_deny_root parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 even_deny_root
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_even_deny_root_parameter_is_present.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
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."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
    regex="^\s*even_deny_root"
    line="even_deny_root"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    fi
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                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."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\beven_deny_root\b' "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ even_deny_root/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ even_deny_root/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Set Interval For Counting Failed Password Attempts   [ref]

Utilizing pam_faillock.so, the fail_interval directive configures the system to lock out an account after a number of incorrect login attempts within a specified time period. Ensure that the file /etc/security/faillock.conf contains the following entry: fail_interval = <interval-in-seconds> where interval-in-seconds is 900 or greater. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Warning:  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.
Rationale:
By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
disaCCI-000044, CCI-002236, CCI-002237, CCI-002238
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a)
nist-csfPR.AC-7
osppFIA_AFL.1
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Check if system relies
    on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect
    tool is present
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check integrity of
      authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    failed_when: false

  - name: Set Interval For Counting Failed Password Attempts - Informative message
      based on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Set Interval For Counting Failed Password Attempts - Get authselect current
      features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    when:
    - result_authselect_check_cmd is success

  - name: Set Interval For Counting Failed Password Attempts - Ensure "with-faillock"
      feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Set Interval For Counting 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:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      preauth editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      authfail editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      account section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_fail_interval: !!str 900
  tags:
    - always

- name: Set Interval For Counting Failed Password Attempts - Check the presence of
    /etc/security/faillock.conf file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*fail_interval\s*=
    line: fail_interval = {{ var_accounts_passwords_pam_faillock_fail_interval }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter not in PAM files
  block:

  - name: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Set Interval For Counting Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Set Interval For Counting Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Set Interval For Counting Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Set Interval For Counting Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting 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: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Set Interval For Counting 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:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter in PAM files
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so
      fail_interval parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*fail_interval
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_fail_interval_parameter_is_present

  - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so preauth fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so authfail fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the desired
      value for pam_faillock.so preauth fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(fail_interval)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found > 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the desired
      value for pam_faillock.so authfail fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(fail_interval)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_fail_interval='900'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
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."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
    regex="^\s*fail_interval\s*="
    line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF
    fi
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                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."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b' "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*fail_interval' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Set Lockout Time for Failed Password Attempts   [ref]

This rule configures the system to lock out accounts during a specified time period after a number of incorrect login attempts using pam_faillock.so. Ensure that the file /etc/security/faillock.conf contains the following entry: unlock_time=<interval-in-seconds> where interval-in-seconds is 0 or greater. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid any errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version. If unlock_time is set to 0, manual intervention by an administrator is required to unlock a user. This should be done using the faillock tool.
Warning:  If the system supports the new /etc/security/faillock.conf file but the pam_faillock.so parameters are defined directly in /etc/pam.d/system-auth and /etc/pam.d/password-auth, the remediation will migrate the unlock_time parameter to /etc/security/faillock.conf to ensure compatibility with authselect tool. The parameters deny and fail_interval, if used, also have to be migrated by their respective remediation.
Warning:  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.
Rationale:
By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time
References:
cis-csc1, 12, 15, 16
cjis5.5.3
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.8
disaCCI-000044, CCI-002236, CCI-002237, CCI-002238
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b)
nist-csfPR.AC-7
osppFIA_AFL.1
pcidssReq-8.1.7
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis5.3.3.1.2
pcidss48.3.4

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Check if system relies on
    authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect
    tool is present
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
      current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    failed_when: false

  - name: Set Lockout Time for Failed Password Attempts - Informative message based
      on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Set Lockout Time for Failed Password Attempts - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    when:
    - result_authselect_check_cmd is success

  - name: Set Lockout Time for Failed Password Attempts - Ensure "with-faillock" feature
      is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
      are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so preauth
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so authfail
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so account
      section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_unlock_time: !!str 0
  tags:
    - always

- name: Set Lockout Time for Failed Password Attempts - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*unlock_time\s*=
    line: unlock_time = {{ var_accounts_passwords_pam_faillock_unlock_time }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter not in PAM files
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Set Lockout Time for Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Set Lockout Time for Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Lockout Time for Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("custom/")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Change the PAM file
          to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
      when:
      - result_authselect_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Set Lockout Time for Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        failed_when: false

      - name: Set Lockout Time for Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Lockout Time for Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("custom/")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Change the PAM file
          to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
      when:
      - result_authselect_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter in PAM files
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so
      unlock_time parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*unlock_time
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_unlock_time_parameter_is_present

  - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of
      pam_faillock.so preauth unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of
      pam_faillock.so authfail unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value
      for pam_faillock.so preauth unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(unlock_time)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found > 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value
      for pam_faillock.so authfail unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(unlock_time)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CJIS-5.5.3
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_unlock_time='0'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
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."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
    regex="^\s*unlock_time\s*="
    line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF
    fi
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                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."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b' "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*unlock_time' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure the root Account lock for Failed Password Attempts via pam_tally2   [ref]

This rule configures the system to lock out the root account after a number of incorrect login attempts using pam_tally2.so.
Rationale:
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_tally2_deny_root
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
disaCCI-002238, CCI-000044
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b), IA-5(c)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31

Rule   Set Lockout Time for Failed Password Attempts using pam_tally2   [ref]

This rule configures the system to lock out accounts during a specified time period after a number of incorrect login attempts using pam_tally2.so.
Rationale:
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_tally2_unlock_time
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
disaCCI-002238, CCI-000044
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b), IA-5(c)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
pcidssReq-8.1.7
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
pcidss48.3.4
Group   Set Password Quality Requirements   Group contains 1 group and 6 rules
[ref]   The default pam_pwquality PAM module provides strength checking for passwords. It performs a number of checks, such as making sure passwords are not similar to dictionary words, are of at least a certain length, are not the previous password reversed, and are not simply a change of case from the previous password. It can also require passwords to be in certain character classes. The pam_pwquality module is the preferred way of configuring password requirements.

The man pages pam_pwquality(8) provide information on the capabilities and configuration of each.
Group   Set Password Quality Requirements with pam_pwquality   Group contains 6 rules
[ref]   The pam_pwquality PAM module can be configured to meet requirements for a variety of policies.

For example, to configure pam_pwquality to require at least one uppercase character, lowercase character, digit, and other (special) character, make sure that pam_pwquality exists in /etc/pam.d/system-auth:
password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. Next, modify the settings in /etc/security/pwquality.conf to match the following:
difok = 4
minlen = 14
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
maxrepeat = 3
The arguments can be modified to ensure compliance with your organization's security policy. Discussion of each parameter follows.

Rule   Ensure PAM Enforces Password Requirements - Minimum Digit Characters   [ref]

The pam_pwquality module's dcredit parameter controls requirements for usage of digits in a password. When set to a negative number, any password will be required to contain that many digits. When set to a positive number, pam_pwquality will grant +1 additional length credit for each digit. Modify the dcredit setting in /etc/security/pwquality.conf to require the use of a digit in passwords.
Rationale:
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring digits makes password guessing attacks more difficult by ensuring a larger search space.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit
References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-000194
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000071-GPOS-00039
anssiR31
pcidss48.3.6

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_dcredit # promote to variable
  set_fact:
    var_password_pam_dcredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Digit Characters - Ensure
    PAM variable dcredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*dcredit
    line: dcredit = {{ var_password_pam_dcredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_dcredit='-1'






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dcredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dcredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^dcredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^dcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters   [ref]

The pam_pwquality module's lcredit parameter controls requirements for usage of lowercase letters in a password. When set to a negative number, any password will be required to contain that many lowercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each lowercase character. Modify the lcredit setting in /etc/security/pwquality.conf to require the use of a lowercase character in passwords.
Rationale:
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possble combinations that need to be tested before the password is compromised. Requiring a minimum number of lowercase characters makes password guessing attacks more difficult by ensuring a larger search space.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit
References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-000193
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000070-GPOS-00038
anssiR31
pcidss48.3.6

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_lcredit # promote to variable
  set_fact:
    var_password_pam_lcredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters -
    Ensure PAM variable lcredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*lcredit
    line: lcredit = {{ var_password_pam_lcredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_lcredit='-1'






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^lcredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_lcredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^lcredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^lcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure PAM Enforces Password Requirements - Minimum Length   [ref]

The pam_pwquality module's minlen parameter controls requirements for minimum characters required in a password. Add minlen=15 after pam_pwquality to set minimum password length requirements.
Rationale:
The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.
Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. Password length is one factor of several that helps to determine strength and how long it takes to crack a password. Use of more characters in a password helps to exponentially increase the time and/or resources required to compromise the password.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen
References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.1.1
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-000205
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000078-GPOS-00046
anssiR31, R68
cis5.3.3.2.2
pcidss48.3.6

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.6.2.1.1
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_minlen # promote to variable
  set_fact:
    var_password_pam_minlen: !!str 15
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Length - Ensure PAM variable
    minlen is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*minlen
    line: minlen = {{ var_password_pam_minlen }}
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.6.2.1.1
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_minlen='15'






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minlen")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minlen"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^minlen\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^minlen\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure PAM Enforces Password Requirements - Minimum Special Characters   [ref]

The pam_pwquality module's ocredit= parameter controls requirements for usage of special (or "other") characters in a password. When set to a negative number, any password will be required to contain that many special characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each special character. Modify the ocredit setting in /etc/security/pwquality.conf to equal -1 to require use of a special character in passwords.
Rationale:
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring a minimum number of special characters makes password guessing attacks more difficult by ensuring a larger search space.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_ocredit
References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-001619
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
os-srgSRG-OS-000266-GPOS-00101
anssiR31

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_ocredit # promote to variable
  set_fact:
    var_password_pam_ocredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Special Characters - Ensure
    PAM variable ocredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ocredit
    line: ocredit = {{ var_password_pam_ocredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_ocredit='-1'






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ocredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ocredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^ocredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^ocredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session   [ref]

To configure the number of retry prompts that are permitted per-session: Edit the pam_pwquality.so statement in /etc/pam.d/system-auth to show retry=3 , or a lower value if site policy is more restrictive. The DoD requirement is a maximum of 3 prompts per session.
Rationale:
Setting the password retry prompts that are permitted on a per-session basis to a low value requires some software, such as SSH, to re-connect. This can slow down and draw additional attention to some types of password-guessing attacks. Note that this is different from account lockout, which is provided by the pam_faillock module.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_retry
References:
cis-csc1, 11, 12, 15, 16, 3, 5, 9
cjis5.5.3
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-000192, CCI-000366
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7, PR.IP-1
osppFMT_MOF_EXT.1
os-srgSRG-OS-000069-GPOS-00037, SRG-OS-000480-GPOS-00227
anssiR68

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
- name: XCCDF Value var_password_pam_retry # promote to variable
  set_fact:
    var_password_pam_retry: !!str 3
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check if expected PAM module line is present in /etc/pam.d/system-auth
  ansible.builtin.lineinfile:
    path: /etc/pam.d/system-auth
    regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s*.*
    state: absent
  check_mode: true
  changed_when: false
  register: result_pam_line_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Include or update the PAM module line in /etc/pam.d/system-auth
  block:

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Check if required PAM module line is present in /etc/pam.d/system-auth
      with different control
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: ^\s*password\s+.*\s+pam_pwquality.so\s*
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_line_other_control_present

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure the correct control for the required PAM module
      line in /etc/pam.d/system-auth
    ansible.builtin.replace:
      dest: /etc/pam.d/system-auth
      regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*)
      replace: \1requisite \2
    register: result_pam_module_edit
    when:
    - result_pam_line_other_control_present.found == 1

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure the required PAM module line is included in /etc/pam.d/system-auth
    ansible.builtin.lineinfile:
      dest: /etc/pam.d/system-auth
      insertafter: ^\s*account
      line: password    requisite    pam_pwquality.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: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_present is defined
    - 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:
  - '"pam" in ansible_facts.packages'
  - result_pam_line_present.found is defined
  - result_pam_line_present.found == 0
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check if the required PAM module option is present in /etc/pam.d/system-auth
  ansible.builtin.lineinfile:
    path: /etc/pam.d/system-auth
    regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s*.*\sretry\b
    state: absent
  check_mode: true
  changed_when: false
  register: result_pam_module_retry_option_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Ensure the "retry" PAM option for "pam_pwquality.so" is included
    in /etc/pam.d/system-auth
  ansible.builtin.lineinfile:
    path: /etc/pam.d/system-auth
    backrefs: true
    regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so.*)
    line: \1 retry={{ var_password_pam_retry }}
    state: present
  register: result_pam_retry_add
  when:
  - '"pam" in ansible_facts.packages'
  - result_pam_module_retry_option_present.found == 0
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Ensure the required value for "retry" PAM option from "pam_pwquality.so"
    in /etc/pam.d/system-auth
  ansible.builtin.lineinfile:
    path: /etc/pam.d/system-auth
    backrefs: true
    regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s+.*)(retry)=[0-9a-zA-Z]+\s*(.*)
    line: \1\2={{ var_password_pam_retry }} \3
  register: result_pam_retry_edit
  when:
  - '"pam" in ansible_facts.packages'
  - result_pam_module_retry_option_present.found > 0
  tags:
  - CJIS-5.5.3
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_retry='3'



	
		if [ -e "/etc/pam.d/system-auth" ] ; then
    PAM_FILE_PATH="/etc/pam.d/system-auth"
    if [ -f /usr/bin/authselect ]; then
        
        if ! authselect check; then
        echo "
        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."
        exit 1
        fi

        CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
        # If not already in use, a custom profile is created preserving the enabled features.
        if [[ ! $CURRENT_PROFILE == custom/* ]]; then
            ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
            authselect create-profile hardening -b $CURRENT_PROFILE
            CURRENT_PROFILE="custom/hardening"
            
            authselect apply-changes -b --backup=before-hardening-custom-profile
            authselect select $CURRENT_PROFILE
            for feature in $ENABLED_FEATURES; do
                authselect enable-feature $feature;
            done
            
            authselect apply-changes -b --backup=after-hardening-custom-profile
        fi
        PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth")
        PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

        authselect apply-changes -b
    fi
    if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then
            # Line matching group + control + module was not found. Check group + module.
            if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then
                # The control is updated only if one single line matches.
                sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH"
            else
                LAST_MATCH_LINE=$(grep -nP "^\s*account" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1)
                if [ ! -z $LAST_MATCH_LINE ]; then
                    sed -i --follow-symlinks $LAST_MATCH_LINE' a password     '"requisite"'    pam_pwquality.so' "$PAM_FILE_PATH"
                else
                    echo 'password    '"requisite"'    pam_pwquality.so' >> "$PAM_FILE_PATH"
                fi
            fi
        fi
        # Check the option
        if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*\sretry\b' "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwquality.so.*/ s/$/ retry='"$var_password_pam_retry"'/' "$PAM_FILE_PATH"
        else
            sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwquality.so\s+.*)('"retry"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_retry"' \3/' "$PAM_FILE_PATH"
        fi
    if [ -f /usr/bin/authselect ]; then
        
        authselect apply-changes -b
    fi
else
    echo "/etc/pam.d/system-auth was not found" >&2
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters   [ref]

The pam_pwquality module's ucredit= parameter controls requirements for usage of uppercase letters in a password. When set to a negative number, any password will be required to contain that many uppercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each uppercase character. Modify the ucredit setting in /etc/security/pwquality.conf to require the use of an uppercase character in passwords.
Rationale:
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_ucredit
References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disaCCI-000192, CCI-000193
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000069-GPOS-00037, SRG-OS-000070-GPOS-00038
anssiR31

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_ucredit # promote to variable
  set_fact:
    var_password_pam_ucredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters -
    Ensure PAM variable ucredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ucredit
    line: ucredit = {{ var_password_pam_ucredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_password_pam_ucredit='-1'






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ucredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ucredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^ucredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^ucredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Protect Physical Console Access   Group contains 2 groups and 14 rules
[ref]   It is impossible to fully protect a system from an attacker with physical access, so securing the space in which the system is located should be considered a necessary step. However, there are some steps which, if taken, make it more difficult for an attacker to quickly or undetectably modify a system from its console.
Group   Configure Screen Locking   Group contains 1 group and 6 rules
[ref]   When a user must temporarily leave an account logged-in, screen locking should be employed to prevent passersby from abusing the account. User education and training is particularly important for screen locking to be effective, and policies can be implemented to reinforce this.

Automatic screen locking is only meant as a safeguard for those cases where a user forgot to lock the screen.
Group   Configure Console Screen Locking   Group contains 6 rules
[ref]   A console screen locking mechanism is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, operating systems need to be able to identify when a user's session has idled and take action to initiate the session lock.

Rule   Install the screen Package   [ref]

To enable console screen locking, install the screen package. The screen package can be installed with the following command:
$ sudo dnf install screen
Instruct users to begin new terminal sessions with the following command:
$ screen
The console can now be locked with the following key combination:
ctrl+a x
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, operating systems need to be able to identify when a user's session has idled and take action to initiate the session lock.

The screen package allows for a session lock to be implemented and configured.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_screen_installed
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000057, CCI-000058
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000029-GPOS-00010


[[packages]]
name = "screen"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure screen is installed
  package:
    name: screen
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_screen_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=screen

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_screen

class install_screen {
  package { 'screen':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "screen" ; then
    dnf install -y "screen"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Install the tmux Package   [ref]

To enable console screen locking, install the tmux package. A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to log out because of the temporary nature of the absence. The session lock is implemented at the point where session activity can be determined. Rather than be forced to wait for a period of time to expire before the user session can be locked, Red Hat Enterprise Linux 10 needs to provide users with the ability to manually invoke a session lock so users can secure their session if it is necessary to temporarily vacate the immediate physical vicinity. Instruct users to begin new terminal sessions with the following command:
$ tmux
The console can now be locked with the following key combination:
ctrl+b :lock-session
Rationale:
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, operating systems need to be able to identify when a user's session has idled and take action to initiate the session lock.

The tmux package allows for a session lock to be implemented and configured.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_tmux_installed
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
disaCCI-000058, CCI-000056
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
osppFMT_SMF_EXT.1, FMT_MOF_EXT.1, FTA_SSL.1
os-srgSRG-OS-000030-GPOS-00011, SRG-OS-000028-GPOS-00009


[[packages]]
name = "tmux"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure tmux is installed
  package:
    name: tmux
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_tmux_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=tmux

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_tmux

class install_tmux {
  package { 'tmux':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "tmux" ; then
    dnf install -y "tmux"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Support session locking with tmux   [ref]

The tmux terminal multiplexer is used to implement automatic session locking. It should be started from /etc/bashrc or drop-in files within /etc/profile.d/.
Rationale:
Unlike bash itself, the tmux terminal multiplexer provides a mechanism to lock sessions after period of inactivity. A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to log out because of the temporary nature of the absence.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_configure_bashrc_exec_tmux
References:
disaCCI-000056, CCI-000058
osppFMT_SMF_EXT.1, FMT_MOF_EXT.1, FTA_SSL.1
os-srgSRG-OS-000031-GPOS-00012, SRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - configure_bashrc_exec_tmux
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: 'Support session locking with tmux: Determine If the Tmux Launch Script Is
    Present in /etc/bashrc'
  ansible.builtin.find:
    paths: /etc
    patterns: bashrc
    contains: .*case "$name" in sshd|login\) exec tmux ;; esac.*
  register: tmux_in_bashrc
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"tmux" in ansible_facts.packages'
  tags:
  - configure_bashrc_exec_tmux
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: 'Support session locking with tmux: Determine If the Tmux Launch Script Is
    Present in /etc/profile.d/*.sh'
  ansible.builtin.find:
    paths: /etc/profile.d
    patterns: '*.sh'
    contains: .*case "$name" in sshd|login\) exec tmux ;; esac.*
  register: tmux_in_profile_d
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"tmux" in ansible_facts.packages'
  tags:
  - configure_bashrc_exec_tmux
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: 'Support session locking with tmux: Insert the Correct Script into /etc/profile.d/tmux.sh'
  ansible.builtin.blockinfile:
    path: /etc/profile.d/tmux.sh
    block: |
      if [ "$PS1" ]; then
        parent=$(ps -o ppid= -p $$)
        name=$(ps -o comm= -p $parent)
        case "$name" in sshd|login) exec tmux ;; esac
      fi
    create: true
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"tmux" in ansible_facts.packages'
  - tmux_in_bashrc is defined and tmux_in_bashrc.matched == 0
  - tmux_in_profile_d is defined and tmux_in_profile_d.matched == 0
  tags:
  - configure_bashrc_exec_tmux
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

Complexity:low
Disruption:low
Reboot:true
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q tmux; }; then

if ! grep -x '  case "$name" in (sshd|login) exec tmux ;; esac' /etc/bashrc; then
    cat >> /etc/profile.d/tmux.sh <<'EOF'
if [ "$PS1" ]; then
  parent=$(ps -o ppid= -p $$)
  name=$(ps -o comm= -p $parent)
  case "$name" in (sshd|login) exec tmux ;; esac
fi
EOF
    chmod 0644 /etc/profile.d/tmux.sh
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure tmux to lock session after inactivity   [ref]

To enable console screen locking in tmux terminal multiplexer after a period of inactivity, the lock-after-time option has to be set to a value greater than 0 and less than or equal to 900 in /etc/tmux.conf.
Rationale:
Locking the session after a period of inactivity limits the potential exposure if the session is left unattended.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_configure_tmux_lock_after_time
References:
disaCCI-000057, CCI-000060
osppFMT_SMF_EXT.1, FMT_MOF_EXT.1, FTA_SSL.1
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - configure_tmux_lock_after_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure tmux to lock session after inactivity
  block:

  - name: Check for duplicate values
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-after-time\s+
      mode: '0644'
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/tmux.conf
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-after-time\s+
      mode: '0644'
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/tmux.conf
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-after-time\s+
      mode: '0644'
      line: set -g lock-after-time 900
      state: present
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"tmux" in ansible_facts.packages'
  tags:
  - configure_tmux_lock_after_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q tmux; }; then

tmux_conf="/etc/tmux.conf"

if grep -qP '^\s*set\s+-g\s+lock-after-time' "$tmux_conf" ; then
    sed -i 's/^\s*set\s\+-g\s\+lock-after-time.*$/set -g lock-after-time 900/' "$tmux_conf"
else
    echo "set -g lock-after-time 900" >> "$tmux_conf"
fi
chmod 0644 "$tmux_conf"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure the tmux Lock Command   [ref]

To enable console screen locking in tmux terminal multiplexer, the vlock command must be configured to be used as a locking mechanism. Add the following line to /etc/tmux.conf:
set -g lock-command vlock
. The console can now be locked with the following key combination:
ctrl+b :lock-session
Rationale:
The tmux package allows for a session lock to be implemented and configured. However, the session lock is implemented by an external command. The tmux default configuration does not contain an effective session lock.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_configure_tmux_lock_command
References:
disaCCI-000056, CCI-000058
nistAC-11(a), AC-11(b), CM-6(a)
osppFMT_SMF_EXT.1, FMT_MOF_EXT.1, FTA_SSL.1
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-AC-11(a)
  - NIST-800-53-AC-11(b)
  - NIST-800-53-CM-6(a)
  - configure_tmux_lock_command
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the tmux Lock Command
  block:

  - name: Check for duplicate values
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-command\s+
      mode: '0644'
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/tmux.conf
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-command\s+
      mode: '0644'
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/tmux.conf
    lineinfile:
      path: /etc/tmux.conf
      create: true
      regexp: ^\s*set -g lock-command\s+
      mode: '0644'
      line: set -g lock-command vlock
      state: present
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"tmux" in ansible_facts.packages'
  tags:
  - NIST-800-53-AC-11(a)
  - NIST-800-53-AC-11(b)
  - NIST-800-53-CM-6(a)
  - configure_tmux_lock_command
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q tmux; }; then

tmux_conf="/etc/tmux.conf"

if grep -qP '^\s*set\s+-g\s+lock-command' "$tmux_conf" ; then
    sed -i 's/^\s*set\s\+-g\s\+lock-command.*$/set -g lock-command vlock/' "$tmux_conf"
else
    echo "set -g lock-command vlock" >> "$tmux_conf"
fi
chmod 0644 "$tmux_conf"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Prevent user from disabling the screen lock   [ref]

The tmux terminal multiplexer is used to implement automatic session locking. It should not be listed in /etc/shells.
Rationale:
Not listing tmux among permitted shells prevents malicious program running as user from lowering security by disabling the screen lock.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_no_tmux_in_shells
References:
disaCCI-000056, CCI-000058
nistCM-6
osppFMT_SMF_EXT.1, FMT_MOF_EXT.1, FTA_SSL.1
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,/bin/sh%0A/bin/bash%0A/usr/bin/sh%0A/usr/bin/bash%0A
        mode: 0644
        path: /etc/shells
        overwrite: true

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if grep -q 'tmux\s*$' /etc/shells ; then
	sed -i '/tmux\s*$/d' /etc/shells
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Disable debug-shell SystemD Service   [ref]

SystemD's debug-shell service is intended to diagnose SystemD related boot issues with various systemctl commands. Once enabled and following a system reboot, the root shell will be available on tty9 which is access by pressing CTRL-ALT-F9. The debug-shell service should only be used for SystemD related issues and should otherwise be disabled.

By default, the debug-shell SystemD service is already disabled. The debug-shell service can be disabled with the following command:
$ sudo systemctl mask --now debug-shell.service
Rationale:
This prevents attackers with physical access from trivially bypassing security on the machine through valid troubleshooting configurations and gaining root access when the system is rebooted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_service_debug-shell_disabled
References:
cui3.4.5
disaCCI-000366
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
nistCM-6
osppFIA_UAU.1
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227


[customizations.services]
masked = ["debug-shell"]

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Disable debug-shell SystemD Service - Collect systemd Services Present in
    the System
  ansible.builtin.command: systemctl -q list-unit-files --type service
  register: service_exists
  changed_when: false
  failed_when: service_exists.rc not in [0, 1]
  check_mode: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled

- name: Disable debug-shell SystemD Service - Ensure debug-shell.service is Masked
  ansible.builtin.systemd:
    name: debug-shell.service
    state: stopped
    enabled: false
    masked: true
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - service_exists.stdout_lines is search("debug-shell.service", multiline=True)
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled

- name: Unit Socket Exists - debug-shell.socket
  ansible.builtin.command: systemctl -q list-unit-files debug-shell.socket
  register: socket_file_exists
  changed_when: false
  failed_when: socket_file_exists.rc not in [0, 1]
  check_mode: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled

- name: Disable debug-shell SystemD Service - Disable Socket debug-shell
  ansible.builtin.systemd:
    name: debug-shell.socket
    enabled: false
    state: stopped
    masked: true
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - socket_file_exists.stdout_lines is search("debug-shell.socket", multiline=True)
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: debug-shell.service
        enabled: false
        mask: true
      - name: debug-shell.socket
        enabled: false
        mask: true

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include disable_debug-shell

class disable_debug-shell {
  service {'debug-shell':
    enable => false,
    ensure => 'stopped',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" stop 'debug-shell.service'
"$SYSTEMCTL_EXEC" disable 'debug-shell.service'
"$SYSTEMCTL_EXEC" mask 'debug-shell.service'
# Disable socket activation if we have a unit file for it
if "$SYSTEMCTL_EXEC" -q list-unit-files debug-shell.socket; then
    "$SYSTEMCTL_EXEC" stop 'debug-shell.socket'
    "$SYSTEMCTL_EXEC" mask 'debug-shell.socket'
fi
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
"$SYSTEMCTL_EXEC" reset-failed 'debug-shell.service' || true

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Verify that Interactive Boot is Disabled   [ref]

Red Hat Enterprise Linux 10 systems support an "interactive boot" option that can be used to prevent services from being started. On a Red Hat Enterprise Linux 10 system, interactive boot can be enabled by providing a 1, yes, true, or on value to the systemd.confirm_spawn kernel argument.
Rationale:
Using interactive boot, the console user could disable auditing, firewalls, or other services, weakening system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_coreos_disable_interactive_boot
References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.1.2, 3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistSC-2(1), CM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000480-GPOS-00227

Rule   Disable Ctrl-Alt-Del Burst Action   [ref]

By default, SystemD will reboot the system if the Ctrl-Alt-Del key sequence is pressed Ctrl-Alt-Delete more than 7 times in 2 seconds.

To configure the system to ignore the CtrlAltDelBurstAction setting, add or modify the following to /etc/systemd/system.conf:
CtrlAltDelBurstAction=none
Warning:  Disabling the Ctrl-Alt-Del key sequence in /etc/init/control-alt-delete.conf DOES NOT disable the Ctrl-Alt-Del key sequence if running in runlevel 6 (e.g. in GNOME, KDE, etc.)! The Ctrl-Alt-Del key sequence will only be disabled if running in the non-graphical runlevel 3.
Rationale:
A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_disable_ctrlaltdel_burstaction
References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.4.5
disaCCI-000366
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1), CM-6(a)
nist-csfPR.AC-4, PR.DS-5
osppFAU_GEN.1.2
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_burstaction
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

- name: Disable Ctrl-Alt-Del Burst Action
  lineinfile:
    dest: /etc/systemd/system.conf
    state: present
    regexp: ^CtrlAltDelBurstAction
    line: CtrlAltDelBurstAction=none
    create: true
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"systemd" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_burstaction
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,CtrlAltDelBurstAction%3Dnone
        mode: 0644
        path: /etc/systemd/system.conf.d/disable_ctrlaltdelete_burstaction.conf
        overwrite: true

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q systemd; }; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^CtrlAltDelBurstAction=")

# shellcheck disable=SC2059
printf -v formatted_output "%s=%s" "$stripped_key" "none"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^CtrlAltDelBurstAction=\\>" "/etc/systemd/system.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^CtrlAltDelBurstAction=\\>.*/$escaped_formatted_output/gi" "/etc/systemd/system.conf"
else
    if [[ -s "/etc/systemd/system.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/systemd/system.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/systemd/system.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/systemd/system.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Disable Ctrl-Alt-Del Reboot Activation   [ref]

By default, SystemD will reboot the system if the Ctrl-Alt-Del key sequence is pressed.

To configure the system to ignore the Ctrl-Alt-Del key sequence from the command line instead of rebooting the system, do either of the following:
ln -sf /dev/null /etc/systemd/system/ctrl-alt-del.target
or
systemctl mask ctrl-alt-del.target


Do not simply delete the /usr/lib/systemd/system/ctrl-alt-del.service file, as this file may be restored during future system updates.
Rationale:
A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_disable_ctrlaltdel_reboot
References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.4.5
disaCCI-000366
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
osppFAU_GEN.1.2
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Disable Ctrl-Alt-Del Reboot Activation
  systemd:
    name: ctrl-alt-del.target
    force: true
    masked: true
    state: stopped
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_reboot
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: ctrl-alt-del.target
        mask: true

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

systemctl disable --now ctrl-alt-del.target
systemctl mask --now ctrl-alt-del.target

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Verify that Interactive Boot is Disabled   [ref]

Red Hat Enterprise Linux 10 systems support an "interactive boot" option that can be used to prevent services from being started. On a Red Hat Enterprise Linux 10 system, interactive boot can be enabled by providing a 1, yes, true, or on value to the systemd.confirm_spawn kernel argument in /etc/default/grub. Remove any instance of
systemd.confirm_spawn=(1|yes|true|on)
from the kernel arguments in that file to disable interactive boot. Recovery booting must also be disabled. Confirm that GRUB_DISABLE_RECOVERY=true is set in /etc/default/grub. It is also required to change the runtime configuration, run:
/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn"
grub2-mkconfig -o /boot/grub2/grub.cfg
Rationale:
Using interactive or recovery boot, the console user could disable auditing, firewalls, or other services, weakening system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_disable_interactive_boot
References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.1.2, 3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistSC-2(1), CM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000480-GPOS-00227

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.2
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-2(1)
  - grub2_disable_interactive_boot
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Verify GRUB_DISABLE_RECOVERY=true
  lineinfile:
    path: /etc/default/grub
    regexp: ^GRUB_DISABLE_RECOVERY=.*
    line: GRUB_DISABLE_RECOVERY=true
    state: present
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.2
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-2(1)
  - grub2_disable_interactive_boot
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Verify that Interactive Boot is Disabled in /etc/default/grub
  replace:
    dest: /etc/default/grub
    regexp: systemd.confirm_spawn(=(1|yes|true|on)|\b)
    replace: systemd.confirm_spawn=no
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.2
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-2(1)
  - grub2_disable_interactive_boot
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Verify that Interactive Boot is Disabled (runtime)
  command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn"
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.2
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-2(1)
  - grub2_disable_interactive_boot
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn
  command: grub2-mkconfig -o  /boot/grub2/grub.cfg
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.2
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-2(1)
  - grub2_disable_interactive_boot
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then

# Verify that Interactive Boot is Disabled in /etc/default/grub
CONFIRM_SPAWN_YES="systemd.confirm_spawn\(=\(1\|yes\|true\|on\)\|\b\)"
CONFIRM_SPAWN_NO="systemd.confirm_spawn=no"

if grep -q "\(GRUB_CMDLINE_LINUX\|GRUB_CMDLINE_LINUX_DEFAULT\)" /etc/default/grub
then
	sed -i "s/${CONFIRM_SPAWN_YES}/${CONFIRM_SPAWN_NO}/" /etc/default/grub
fi

# make sure GRUB_DISABLE_RECOVERY=true
if grep -q '^GRUB_DISABLE_RECOVERY=.*'  '/etc/default/grub' ; then
       # modify the GRUB command-line if an GRUB_DISABLE_RECOVERY= arg already exists
       sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' /etc/default/grub
else
       # no GRUB_DISABLE_RECOVERY=arg is present, append it to file
       echo "GRUB_DISABLE_RECOVERY=true"  >> '/etc/default/grub'
fi



# Remove 'systemd.confirm_spawn' kernel argument also from runtime settings
/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn"


#Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn
grub2-mkconfig -o /boot/grub2/grub.cfg

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure Logind to terminate idle sessions after certain time of inactivity   [ref]

To configure logind service to terminate inactive user sessions after 300 seconds, edit the file /etc/systemd/logind.conf. Ensure that there is a section
[Login]
which contains the configuration
StopIdleSessionSec=300
        
.
Rationale:
Terminating an idle session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been let unattended.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_logind_session_timeout
References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8
cjis5.5.6
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.1.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.3, CIP-007-3 R5.1, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistCM-6(a), AC-17(a), AC-2(5), AC-12, AC-17(a), SC-10, CM-6(a)
nist-csfDE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2
osppFMT_SMF_EXT.1.1
pcidssReq-8.1.8
os-srgSRG-OS-000163-GPOS-00072
anssiR32

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: XCCDF Value var_logind_session_timeout # promote to variable
  set_fact:
    var_logind_session_timeout: !!str 300
  tags:
    - always

- name: Set 'StopIdleSessionSec' to '{{ var_logind_session_timeout }}' in the [Login]
    section of '/etc/systemd/logind.conf'
  ini_file:
    path: /etc/systemd/logind.conf
    section: Login
    option: StopIdleSessionSec
    value: '{{ var_logind_session_timeout }}'
    create: true
    mode: 420
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - ( ansible_distribution == 'RedHat' and ansible_distribution_version is version('8.7',
    '>=') and ansible_distribution == 'RedHat' and ansible_distribution_version is
    version('9.0', '!=') ) or ansible_distribution == 'OracleLinux' and ansible_distribution_version
    is version('8.7', '>=')
  tags:
  - CJIS-5.5.6
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - logind_session_timeout
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.7"; printf "%s\n%s" "$expected" "$real" | sort -VC; } && grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="9.0"; [[ "$real" != "$expected" ]]; } ) || grep -qP "^ID=[\"']?ol[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.7"; printf "%s\n%s" "$expected" "$real" | sort -VC; }; }; then

var_logind_session_timeout='300'



# Try find '[Login]' and 'StopIdleSessionSec' in '/etc/systemd/logind.conf', if it exists, set
# to '$var_logind_session_timeout', if it isn't here, add it, if '[Login]' doesn't exist, add it there
if grep -qzosP '[[:space:]]*\[Login]([^\n\[]*\n+)+?[[:space:]]*StopIdleSessionSec' '/etc/systemd/logind.conf'; then
    
    sed -i "s/StopIdleSessionSec[^(\n)]*/StopIdleSessionSec=$var_logind_session_timeout/" '/etc/systemd/logind.conf'
elif grep -qs '[[:space:]]*\[Login]' '/etc/systemd/logind.conf'; then
    sed -i "/[[:space:]]*\[Login]/a StopIdleSessionSec=$var_logind_session_timeout" '/etc/systemd/logind.conf'
else
    if test -d "/etc/systemd"; then
        printf '%s\n' '[Login]' "StopIdleSessionSec=$var_logind_session_timeout" >> '/etc/systemd/logind.conf'
    else
        echo "Config file directory '/etc/systemd' doesnt exist, not remediating, assuming non-applicability." >&2
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Require Authentication for Emergency Systemd Target   [ref]

Emergency mode is intended as a system recovery method, providing a single user root access to the system during a failed boot sequence.

By default, Emergency mode is protected by requiring a password and is set in /usr/lib/systemd/system/emergency.service.
Rationale:
This prevents attackers with physical access from trivially bypassing security on the machine and gaining root access. Such accesses are further prevented by configuring the bootloader password.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_require_emergency_target_auth
References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.1.1, 3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistIA-2, AC-3, CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Require emergency mode password
  lineinfile:
    create: true
    dest: /usr/lib/systemd/system/emergency.service
    regexp: ^#?ExecStart=
    line: ExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block
      default"
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.1
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-3
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-2
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - require_emergency_target_auth
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

service_file="/usr/lib/systemd/system/emergency.service"


sulogin='/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"'


if grep "^ExecStart=.*" "$service_file" ; then
    sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file"
else
    echo "ExecStart=-$sulogin" >> "$service_file"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Require Authentication for Single User Mode   [ref]

Single-user mode is intended as a system recovery method, providing a single user root access to the system by providing a boot option at startup.

By default, single-user mode is protected by requiring a password and is set in /usr/lib/systemd/system/rescue.service.
Rationale:
This prevents attackers with physical access from trivially bypassing security on the machine and gaining root access. Such accesses are further prevented by configuring the bootloader password.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_require_singleuser_auth
References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.1.1, 3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistIA-2, AC-3, CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Require single user mode password
  lineinfile:
    create: true
    dest: /usr/lib/systemd/system/rescue.service
    regexp: ^#?ExecStart=
    line: ExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block
      default"
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.1
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-3
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-2
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - require_singleuser_auth
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

service_file="/usr/lib/systemd/system/rescue.service"

sulogin='/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"'

if grep "^ExecStart=.*" "$service_file" ; then
    sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file"
else
    echo "ExecStart=-$sulogin" >> "$service_file"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Protect Accounts by Restricting Password-Based Login   Group contains 2 groups and 2 rules
[ref]   Conventionally, Unix shell accounts are accessed by providing a username and password to a login program, which tests these values for correctness using the /etc/passwd and /etc/shadow files. Password-based login is vulnerable to guessing of weak passwords, and to sniffing and man-in-the-middle attacks against passwords entered over a network or at an insecure console. Therefore, mechanisms for accessing accounts by entering usernames and passwords should be restricted to those which are operationally necessary.
Group   Verify Proper Storage and Existence of Password Hashes   Group contains 1 rule
[ref]   By default, password hashes for local accounts are stored in the second field (colon-separated) in /etc/shadow. This file should be readable only by processes running with root credentials, preventing users from casually accessing others' password hashes and attempting to crack them. However, it remains possible to misconfigure the system and store password hashes in world-readable files such as /etc/passwd, or to even store passwords themselves in plaintext on the system. Using system-provided tools for password change/creation should allow administrators to avoid such misconfiguration.

Rule   Prevent Login to Accounts With Empty Password   [ref]

If an account is configured for password authentication but does not have an assigned password, it may be possible to log into the account without authentication. Remove any instances of the nullok in /etc/pam.d/system-auth and /etc/pam.d/password-auth to prevent logins with empty passwords.
Warning:  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. Note that this rule is not applicable for systems running within a container. Having user with empty password within a container is not considered a risk, because it should not be possible to directly login into a container anyway.
Rationale:
If an account has an empty password, anyone could log in and run commands with the privileges of that account. Accounts with empty passwords should never be used in operational environments.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_no_empty_passwords
References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5
cjis5.5.2
cobit5APO01.06, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.10
cui3.1.1, 3.1.5
disaCCI-000366
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistIA-5(1)(a), IA-5(c), CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5
osppFIA_UAU.1
pcidssReq-8.2.3
os-srgSRG-OS-000480-GPOS-00227
cis5.3.3.4.1
pcidss48.3.1

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- 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: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - CJIS-5.5.2
  - 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
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

- 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
    failed_when: false

  - 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.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: 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

  - 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:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.2
  - 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
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

- 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:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - not result_authselect_present.stat.exists
  tags:
  - CJIS-5.5.2
  - 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
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
        mode: 0644
        path: /etc/pam.d/password-auth
        overwrite: true
      - contents:
          source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
        mode: 0644
        path: /etc/pam.d/system-auth
        overwrite: true

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
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."
exit 1
fi
authselect enable-feature without-nullok

authselect apply-changes -b
else
    
if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then
    sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth"
fi
    
if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then
    sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth"
fi
    
if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then
    sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth"
fi
    
if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then
    sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth"
fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Restrict Root Logins   Group contains 1 rule
[ref]   Direct root logins should be allowed only for emergency use. In normal situations, the administrator should access the system via a unique unprivileged account, and then use su or sudo to execute privileged commands. Discouraging administrators from accessing the root account directly ensures an audit trail in organizations with multiple administrators. Locking down the channels through which root can connect directly also reduces opportunities for password-guessing against the root account. The login program uses the file /etc/securetty to determine which interfaces should allow root logins. The virtual devices /dev/console and /dev/tty* represent the system consoles (accessible via the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default installation). The default securetty file also contains /dev/vc/*. These are likely to be deprecated in most environments, but may be retained for compatibility. Root should also be prohibited from connecting via network protocols. Other sections of this document include guidance describing how to prevent root from logging in via SSH.

Rule   Enforce usage of pam_wheel for su authentication   [ref]

To ensure that only users who are members of the wheel group can run commands with altered privileges through the su command, make sure that the following line exists in the file /etc/pam.d/su:
auth required pam_wheel.so use_uid
Warning:  Members of "wheel" or GID 0 groups are checked by default if the group option is not set for pam_wheel.so module. Therefore, members of these groups should be manually checked or a different group should be informed according to the site policy.
Rationale:
The su program allows to run commands with a substitute user and group ID. It is commonly used to run commands as the root user. Limiting access to such command is considered a good security practice.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_use_pam_wheel_for_su
References:
osppFMT_SMF_EXT.1.1
os-srgSRG-OS-000373-GPOS-00156, SRG-OS-000312-GPOS-00123

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - use_pam_wheel_for_su

- name: Restrict usage of su command only to members of wheel group
  replace:
    path: /etc/pam.d/su
    regexp: ^[\s]*#[\s]*auth[\s]+required[\s]+pam_wheel\.so[\s]+use_uid$
    replace: auth             required        pam_wheel.so use_uid
  when: '"pam" in ansible_facts.packages'
  tags:
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - use_pam_wheel_for_su

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

# uncomment the option if commented
sed '/^[[:space:]]*#[[:space:]]*auth[[:space:]]\+required[[:space:]]\+pam_wheel\.so[[:space:]]\+use_uid$/s/^[[:space:]]*#//' -i /etc/pam.d/su

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Secure Session Configuration Files for Login Accounts   Group contains 1 rule
[ref]   When a user logs into a Unix account, the system configures the user's session by reading a number of files. Many of these files are located in the user's home directory, and may have weak permissions as a result of user error or misconfiguration. If an attacker can modify or even read certain types of account configuration information, they can often gain full access to the affected user's account. Therefore, it is important to test and correct configuration file permissions for interactive accounts, particularly those of privileged users such as root or system administrators.

Rule   Set Interactive Session Timeout   [ref]

Setting the TMOUT option in /etc/profile ensures that all user sessions will terminate based on inactivity. The value of TMOUT should be exported and read only. The TMOUT setting in a file loaded by /etc/profile, e.g. /etc/profile.d/tmout.sh should read as follows:
typeset -xr TMOUT=600
        
or
declare -xr TMOUT=600
        
Using the typeset keyword is preferred for wider compatibility with ksh and other shells.
Rationale:
Terminating an idle session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been left unattended.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_tmout
References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.11
disaCCI-000057, CCI-001133, CCI-002361
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nerc-cipCIP-004-6 R2.2.3, CIP-007-3 R5.1, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistAC-12, SC-10, AC-2(5), CM-6(a)
nist-csfPR.AC-7
osppFMT_MOF_EXT.1
os-srgSRG-OS-000163-GPOS-00072, SRG-OS-000029-GPOS-00010
anssiR32
cis5.4.3.2
pcidss48.6.1

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_accounts_tmout # promote to variable
  set_fact:
    var_accounts_tmout: !!str 600
  tags:
    - always

- name: Correct any occurrence of TMOUT in /etc/profile
  replace:
    path: /etc/profile
    regexp: ^[^#].*TMOUT=.*
    replace: typeset -xr TMOUT={{ var_accounts_tmout }}
  register: profile_replaced
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSSv4-8.6.1
  - accounts_tmout
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interactive Session Timeout
  lineinfile:
    path: /etc/profile.d/tmout.sh
    create: true
    regexp: TMOUT=
    line: typeset -xr TMOUT={{ var_accounts_tmout }}
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSSv4-8.6.1
  - accounts_tmout
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

var_accounts_tmout='600'


# if 0, no occurence of tmout found, if 1, occurence found
tmout_found=0


for f in /etc/profile /etc/profile.d/*.sh; do

    if grep --silent '^[^#].*TMOUT' $f; then
        sed -i -E "s/^(.*)TMOUT\s*=\s*(\w|\$)*(.*)$/typeset -xr TMOUT=$var_accounts_tmout\3/g" $f
        tmout_found=1
    fi
done

if [ $tmout_found -eq 0 ]; then
        echo -e "\n# Set TMOUT to $var_accounts_tmout per security requirements" >> /etc/profile.d/tmout.sh
        echo "typeset -xr TMOUT=$var_accounts_tmout" >> /etc/profile.d/tmout.sh
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Enable authselect   [ref]

Configure user authentication setup to use the authselect tool. If authselect profile is selected, the rule will enable the minimal profile.
Warning:  If the sudo authselect select command returns an error informing that the chosen profile cannot be selected, it is probably because PAM files have already been modified by the administrator. If this is the case, in order to not overwrite the desired changes made by the administrator, the current PAM settings should be investigated before forcing the selection of the chosen authselect profile.
Rationale:
Authselect is a successor to authconfig. It is a tool to select system authentication and identity sources from a list of supported profiles instead of letting the administrator manually build the PAM stack. That way, it avoids potential breakage of configuration, as it ships several tested profiles that are well tested and supported to solve different use-cases.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_enable_authselect
References:
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
nistAC-3
osppFIA_UAU.1, FIA_AFL.1
os-srgSRG-OS-000480-GPOS-00227
anssiR31
cisenable_authselect
pcidss48.3.4


var_authselect_profile='minimal'


authselect current

if test "$?" -ne 0; then
    authselect select "$var_authselect_profile"

    if test "$?" -ne 0; then
        if rpm --quiet --verify pam; then
            authselect select --force "$var_authselect_profile"
        else
	        echo "authselect is not used but files from the 'pam' package have been altered, so the authselect configuration won't be forced." >&2
        fi
    fi
fi
Group   GRUB2 bootloader configuration   Group contains 2 groups and 8 rules
[ref]   During the boot process, the boot loader is responsible for starting the execution of the kernel and passing options to it. The boot loader allows for the selection of different kernels - possibly on different partitions or media. The default Red Hat Enterprise Linux 10 boot loader for x86 systems is called GRUB2. Options it can pass to the kernel include single-user mode, which provides root access without any authentication, and the ability to disable SELinux. To prevent local users from modifying the boot parameters and endangering security, protect the boot loader configuration with a password and ensure its configuration file's permissions are set properly.
Group   Non-UEFI GRUB2 bootloader configuration   Group contains 2 rules
[ref]   Non-UEFI GRUB2 bootloader configuration

Rule   Set the Boot Loader Admin Username to a Non-Default Value   [ref]

The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

To maximize the protection, select a password-protected superuser account with unique name, and modify the /etc/grub.d/01_users configuration file to reflect the account name change.

Do not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users


Once the superuser account has been added, update the grub.cfg file by running:
grubby --update-kernel=ALL
Warning:  To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
Rationale:
Having a non-default grub superuser username makes password-guessing attacks less effective.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_grub2_admin_username
References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048

Rule   Set Boot Loader Password in grub2   [ref]

The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

Since plaintext passwords are a security risk, generate a hash for the password by running the following command:
# grub2-setpassword
When prompted, enter the password that was selected.

Warning:  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
Rationale:
Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_grub2_password
References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048
anssiR5
cis1.4.1
Group   UEFI GRUB2 bootloader configuration   Group contains 2 rules
[ref]   UEFI GRUB2 bootloader configuration
Warning:  UEFI generally uses vfat file systems, which does not support Unix-style permissions managed by chmod command. In this case, in order to change file permissions for files within /boot/efi it is necessary to update the mount options in /etc/fstab file and reboot the system.

Rule   Set the UEFI Boot Loader Admin Username to a Non-Default Value   [ref]

The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

To maximize the protection, select a password-protected superuser account with unique name, and modify the /etc/grub.d/01_users configuration file to reflect the account name change.

It is highly suggested not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users


Once the superuser account has been added, update the grub.cfg file by running:
grubby --update-kernel=ALL
Warning:  To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
Rationale:
Having a non-default grub superuser username makes password-guessing attacks less effective.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_uefi_admin_username
References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048

Rule   Set the UEFI Boot Loader Password   [ref]

The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

Since plaintext passwords are a security risk, generate a hash for the password by running the following command:
# grub2-setpassword
When prompted, enter the password that was selected.

Warning:  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
Rationale:
Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_grub2_uefi_password
References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.4.5
disaCCI-000213
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048
anssiR5

Rule   Disable Recovery Booting   [ref]

Red Hat Enterprise Linux 10 systems support an "recovery boot" option that can be used to prevent services from being started. The GRUB_DISABLE_RECOVERY configuration option in /etc/default/grub should be set to true to disable the generation of recovery mode menu entries. It is also required to change the runtime configuration, run:
$ sudo grubby --update-kernel=ALL
Rationale:
Using recovery boot, the console user could disable auditing, firewalls, or other services, weakening system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_disable_recovery
References:
osppFIA_UAU.1

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - grub2_disable_recovery
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Verify GRUB_DISABLE_RECOVERY=true
  lineinfile:
    path: /etc/default/grub
    regexp: ^GRUB_DISABLE_RECOVERY=.*
    line: GRUB_DISABLE_RECOVERY=true
    state: present
  when: '"grub2-common" in ansible_facts.packages'
  tags:
  - grub2_disable_recovery
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  command: /sbin/grubby --update-kernel=ALL
  when: '"grub2-common" in ansible_facts.packages'
  tags:
  - grub2_disable_recovery
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q grub2-common; then

if grep -q '^GRUB_DISABLE_RECOVERY=.*'  '/etc/default/grub' ; then
    sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' "/etc/default/grub"
else
    echo "GRUB_DISABLE_RECOVERY=true" >> '/etc/default/grub'
fi

grubby --update-kernel=ALL

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure kernel to trust the CPU random number generator   [ref]

There exist two ways how to ensure that the Linux kernel trusts the CPU hardware random number generator. If the option is configured during kernel compilation, e.g. the option CONFIG_RANDOM_TRUST_CPU is set to Y, make sure that it is not overridden with the boot parameter. There must not exist the boot parameter random.trust_cpu=off. If the option is not compiled in, make sure that random.trust_cpu=on is configured as a boot parameter. Configure the default Grub2 kernel command line to contain random.trust_cpu=on as follows:
# grub2-editenv - set "$(grub2-editenv - list | grep kernelopts) random.trust_cpu=on"
Rationale:
The Linux kernel offers an option which signifies if the kernel should trust data provided by CPU hardware random number generator. Hardware random number generators can provide random data very quickly and are used to generate random cryptographic keys. They can be useful during boot time when other means of getting random data can be slow because there is not yet enough entropy in the system.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_kernel_trust_cpu_rng
References:
osppFCS_RBG_EXT.1.1
os-srgSRG-OS-000480-GPOS-00227

[customizations.kernel]
append = "random.trust_cpu=on"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - grub2_kernel_trust_cpu_rng
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  command: /sbin/grubby --update-kernel=ALL --args="random.trust_cpu=on"
  when:
  - '"grub2-common" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - grub2_kernel_trust_cpu_rng
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

grubby --update-kernel=ALL --args=random.trust_cpu=on

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure debug-shell service is not enabled during boot   [ref]

systemd's debug-shell service is intended to diagnose systemd related boot issues with various systemctl commands. Once enabled and following a system reboot, the root shell will be available on tty9 which is access by pressing CTRL-ALT-F9. The debug-shell service should only be used for systemd related issues and should otherwise be disabled.

By default, the debug-shell systemd service is already disabled. Ensure the debug-shell is not enabled by the systemd.debug-shel=1 boot paramenter option. Check that the line
GRUB_CMDLINE_LINUX="..."
within /etc/default/grub doesn't contain the argument systemd.debug-shell=1. Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --remove-args="systemd.debug-shell"
Rationale:
This prevents attackers with physical access from trivially bypassing security on the machine through valid troubleshooting configurations and gaining root access when the system is rebooted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_systemd_debug-shell_argument_absent
References:
osppFIA_UAU.1

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - grub2_systemd_debug-shell_argument_absent
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.debug-shell"
  when:
  - '"grub2-common" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - grub2_systemd_debug-shell_argument_absent
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

grubby --update-kernel=ALL --remove-args=systemd.debug-shell

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Disable vsyscalls   [ref]

To disable use of virtual syscalls, add the argument vsyscall=none to the default GRUB 2 command line for the Linux operating system. Configure the default Grub2 kernel command line to contain vsyscall=none as follows:
# grub2-editenv - set "$(grub2-editenv - list | grep kernelopts) vsyscall=none"
Rationale:
Virtual Syscalls provide an opportunity of attack for a user who has control of the return instruction pointer.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_grub2_vsyscall_argument
References:
disaCCI-001084
nistCM-7(a)
osppFPT_ASLR_EXT.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000134-GPOS-00068

[customizations.kernel]
append = "vsyscall=none"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-53-CM-7(a)
  - grub2_vsyscall_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  command: /sbin/grubby --update-kernel=ALL --args="vsyscall=none"
  when:
  - '"grub2-common" in ansible_facts.packages'
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-7(a)
  - grub2_vsyscall_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

# Remediation is applicable only in certain platforms
if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then

grubby --update-kernel=ALL --args=vsyscall=none

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   zIPL bootloader configuration   Group contains 4 rules
[ref]   During the boot process, the bootloader is responsible for starting the execution of the kernel and passing options to it. The default Red Hat Enterprise Linux 10 boot loader for s390x systems is called zIPL.

Rule   Enable Auditing to Start Prior to the Audit Daemon in zIPL   [ref]

To ensure all processes can be audited, even those which start prior to the audit daemon, check that all boot entries in /boot/loader/entries/*.conf have audit=1 included in its options.
To ensure that new kernels and boot entries continue to enable audit, add audit=1 to /etc/kernel/cmdline.
Rationale:
Each process on the system carries an "auditable" flag which indicates whether its activities can be audited. Although auditd takes care of enabling this for all processes which launch after it does, adding the kernel argument ensures it is set for every process during boot.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_zipl_audit_argument
References:
osppFAU_GEN.1

Rule   Extend Audit Backlog Limit for the Audit Daemon in zIPL   [ref]

To improve the kernel capacity to queue all log events, even those which start prior to the audit daemon, check that all boot entries in /boot/loader/entries/*.conf have audit_backlog_limit=8192 included in its options.
To ensure that new kernels and boot entries continue to extend the audit log events queue, add audit_backlog_limit=8192 to /etc/kernel/cmdline.
Rationale:
audit_backlog_limit sets the queue length for audit events awaiting transfer to the audit daemon. Until the audit daemon is up and running, all log messages are stored in this queue. If the queue is overrun during boot process, the action defined by audit failure flag is taken.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_zipl_audit_backlog_limit_argument
References:
osppFAU_STG.1, FAU_STG.3

Rule   Ensure debug-shell service is not enabled in zIPL   [ref]

systemd's debug-shell service is intended to diagnose systemd related boot issues with various systemctl commands. Once enabled and following a system reboot, the root shell will be available on tty9 which is access by pressing CTRL-ALT-F9. The debug-shell service should only be used for systemd related issues and should otherwise be disabled.

By default, the debug-shell systemd service is already disabled. Ensure the debug-shell is not enabled by the systemd.debug-shel=1 boot paramenter option. Check that not boot entries in /boot/loader/entries/*.conf have systemd.debug-shell=1 included in its options.
To ensure that new kernels and boot entries don't enable the debug-shell, check that systemd.debug-shell=1 is not present in /etc/kernel/cmdline.
Rationale:
This prevents attackers with physical access from trivially bypassing security on the machine through valid troubleshooting configurations and gaining root access when the system is rebooted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_zipl_systemd_debug-shell_argument_absent
References:
osppFIA_UAU.1

Rule   Disable vsyscalls in zIPL   [ref]

To disable use of virtual syscalls, check that all boot entries in /boot/loader/entries/*.conf have vsyscall=none included in its options.
To ensure that new kernels and boot entries continue to disable virtual syscalls, add vsyscall=none to /etc/kernel/cmdline.
Rationale:
Virtual Syscalls provide an opportunity of attack for a user who has control of the return instruction pointer.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_zipl_vsyscall_argument
References:
osppFPT_ASLR_EXT.1
Group   Configure Syslog   Group contains 2 groups and 6 rules
[ref]   The syslog service has been the default Unix logging mechanism for many years. It has a number of downsides, including inconsistent log format, lack of authentication for received messages, and lack of authentication, encryption, or reliable transport for messages sent over a network. However, due to its long history, syslog is a de facto standard which is supported by almost all Unix applications.

In Red Hat Enterprise Linux 10, rsyslog has replaced ksyslogd as the syslog daemon of choice, and it includes some additional security features such as reliable, connection-oriented (i.e. TCP) transmission of logs, the option to log to database formats, and the encryption of log data en route to a central logging server. This section discusses how to configure rsyslog for best effect, and how to use tools provided with the system to maintain and monitor logs.
Group   Ensure Proper Configuration of Log Files   Group contains 1 rule
[ref]   The file /etc/rsyslog.conf controls where log message are written. These are controlled by lines called rules, which consist of a selector and an action. These rules are often customized depending on the role of the system, the requirements of the environment, and whatever may enable the administrator to most effectively make use of log data. The default rules in Red Hat Enterprise Linux 10 are:
*.info;mail.none;authpriv.none;cron.none                /var/log/messages
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log
See the man page rsyslog.conf(5) for more information. Note that the rsyslog daemon can be configured to use a timestamp format that some log processing programs may not understand. If this occurs, edit the file /etc/rsyslog.conf and add or edit the following line:
$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat

Rule   Ensure cron Is Logging To Rsyslog   [ref]

Cron logging must be implemented to spot intrusions or trace cron job status. If cron is not logging to rsyslog, it can be implemented by adding the following to the RULES section of /etc/rsyslog.conf: If the legacy syntax is used:
cron.*                                                  /var/log/cron
If the modern syntax (RainerScript) is used:
cron.* action(type="omfile" file="/var/log/cron")
Rationale:
Cron logging can be used to trace the successful or unsuccessful execution of cron jobs. It can also be used to spot intrusions into the use of the cron facility by unauthorized and malicious users.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_rsyslog_cron_logging
References:
cis-csc1, 14, 15, 16, 3, 5, 6
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS05.04, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
disaCCI-000366
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1
ism0988, 1405
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.15.2.1, A.15.2.2
nistCM-6(a)
nist-csfID.SC-4, PR.PT-1
osppFAU_GEN.1.1.c
os-srgSRG-OS-000480-GPOS-00227

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! grep -s "^\s*cron\.\*\s*/var/log/cron$" /etc/rsyslog.conf /etc/rsyslog.d/*.conf; then
	mkdir -p /etc/rsyslog.d
	echo "cron.*	/var/log/cron" >> /etc/rsyslog.d/cron.conf
fi

systemctl restart rsyslog.service

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Rsyslog Logs Sent To Remote Host   Group contains 3 rules
[ref]   If system logs are to be useful in detecting malicious activities, it is necessary to send logs to a remote server. An intruder who has compromised the root account on a system may delete the log entries which indicate that the system was attacked before they are seen by an administrator.

However, it is recommended that logs be stored on the local host in addition to being sent to the loghost, especially if rsyslog has been configured to use the UDP protocol to send messages over a network. UDP does not guarantee reliable delivery, and moderately busy sites will lose log messages occasionally, especially in periods of high traffic which may be the result of an attack. In addition, remote rsyslog messages are not authenticated in any way by default, so it is easy for an attacker to introduce spurious messages to the central log server. Also, some problems cause loss of network connectivity, which will prevent the sending of messages to the central server. For all of these reasons, it is better to store log messages both centrally and on each host, so that they can be correlated if necessary.

Rule   Ensure Logs Sent To Remote Host   [ref]

To configure rsyslog to send logs to a remote log server, open /etc/rsyslog.conf and read and understand the last section of the file, which describes the multiple directives necessary to activate remote logging. Along with these other directives, the system can be configured to forward its logs to a particular log server by adding or correcting one of the following lines, substituting logcollector appropriately. The choice of protocol depends on the environment of the system; although TCP and RELP provide more reliable message delivery, they may not be supported in all environments.
To use UDP for log message delivery:
*.* @logcollector
        

To use TCP for log message delivery:
*.* @@logcollector
        

To use RELP for log message delivery:
*.* :omrelp:logcollector
        

There must be a resolvable DNS CNAME or Alias record set to "logcollector" for logs to be sent correctly to the centralized logging utility.
Warning:  It is important to configure queues in case the client is sending log messages to a remote server. If queues are not configured, the system will stop functioning when the connection to the remote server is not available. Please consult Rsyslog documentation for more information about configuration of queues. The example configuration which should go into /etc/rsyslog.conf can look like the following lines:
$ActionQueueType LinkedList
$ActionQueueFileName queuefilename
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionResumeRetryCount -1
Rationale:
A log server (loghost) receives syslog messages from one or more systems. This data can be used as an additional log source in the event a system is compromised and its local logs are suspect. Forwarding log messages to a remote loghost also provides system administrators with a centralized place to view the status of multiple hosts within the enterprise.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_rsyslog_remote_loghost
References:
cis-csc1, 13, 14, 15, 16, 2, 3, 5, 6
cobit5APO11.04, APO13.01, BAI03.05, BAI04.04, DSS05.04, DSS05.07, MEA02.01
disaCCI-000366, CCI-001348, CCI-000136, CCI-001851
hipaa164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(B), 164.308(a)(5)(ii)(C), 164.308(a)(6)(ii), 164.308(a)(8), 164.310(d)(2)(iii), 164.312(b), 164.314(a)(2)(i)(C), 164.314(a)(2)(iii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 7.1, SR 7.2
ism0988, 1405
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.17.2.1
nerc-cipCIP-003-8 R5.2, CIP-004-6 R3.3
nistCM-6(a), AU-4(1), AU-9(2)
nist-csfPR.DS-4, PR.PT-1
osppFAU_GEN.1.1.c
os-srgSRG-OS-000479-GPOS-00224, SRG-OS-000480-GPOS-00227, SRG-OS-000342-GPOS-00133
anssiR71

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value rsyslog_remote_loghost_address # promote to variable
  set_fact:
    rsyslog_remote_loghost_address: !!str logcollector
  tags:
    - always

- name: Set rsyslog remote loghost
  lineinfile:
    dest: /etc/rsyslog.conf
    regexp: ^\*\.\*
    line: '*.* @@{{ rsyslog_remote_loghost_address }}'
    create: true
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-4(1)
  - NIST-800-53-AU-9(2)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - rsyslog_remote_loghost

# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

rsyslog_remote_loghost_address='logcollector'


# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^\*\.\*")

# shellcheck disable=SC2059
printf -v formatted_output "%s %s" "$stripped_key" "@@$rsyslog_remote_loghost_address"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^\*\.\*\\>" "/etc/rsyslog.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^\*\.\*\\>.*/$escaped_formatted_output/gi" "/etc/rsyslog.conf"
else
    if [[ -s "/etc/rsyslog.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/rsyslog.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/rsyslog.conf"
    fi
    printf '%s\n' "$formatted_output" >> "/etc/rsyslog.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure TLS for rsyslog remote logging   [ref]

Configure rsyslog to use Transport Layer Security (TLS) support for logging to remote server for the Forwarding Output Module in /etc/rsyslog.conf using action. You can use the following command:
echo 'action(type="omfwd" protocol="tcp" Target="<remote system>" port="6514"
    StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" streamdriver.CheckExtendedKeyPurpose="on")' >> /etc/rsyslog.conf
Replace the <remote system> in the above command with an IP address or a host name of the remote logging server.
Rationale:
For protection of data being logged, the connection to the remote logging server needs to be authenticated and encrypted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_rsyslog_remote_tls
References:
ism0988, 1405
nistAU-9(3), CM-6(a)
osppFCS_TLSC_EXT.1, FTP_ITC_EXT.1.1, FIA_X509_EXT.1.1, FMT_SMF_EXT.1.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000120-GPOS-00061
anssiR71

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: XCCDF Value rsyslog_remote_loghost_address # promote to variable
  set_fact:
    rsyslog_remote_loghost_address: !!str logcollector
  tags:
    - always

- name: 'Configure TLS for rsyslog remote logging: search for omfwd action directive
    in rsyslog include files'
  ansible.builtin.find:
    paths: /etc/rsyslog.d/
    pattern: '*.conf'
    contains: ^\s*action\s*\(\s*type\s*=\s*"omfwd".*
  register: rsyslog_includes_with_directive
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: search for omfwd action directive
    in rsyslog main config file'
  ansible.builtin.find:
    paths: /etc
    pattern: rsyslog.conf
    contains: ^\s*action\s*\(\s*type\s*=\s*"omfwd".*
  register: rsyslog_main_file_with_directive
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: declare Rsyslog option parameters
    to be inserted if entirely missing'
  ansible.builtin.set_fact:
    rsyslog_parameters_to_add_if_missing:
    - protocol
    - target
    - port
    - StreamDriver
    - StreamDriverMode
    - StreamDriverAuthMode
    - streamdriver.CheckExtendedKeyPurpose
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: declare Rsyslog option values to
    be inserted if entirely missing'
  ansible.builtin.set_fact:
    rsyslog_values_to_add_if_missing:
    - tcp
    - '{{ rsyslog_remote_loghost_address }}'
    - '6514'
    - gtls
    - '1'
    - x509/name
    - 'on'
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: declare Rsyslog option parameters
    to be replaced if defined with wrong values'
  ansible.builtin.set_fact:
    rsyslog_parameters_to_replace_if_wrong_value:
    - protocol
    - StreamDriver
    - StreamDriverMode
    - StreamDriverAuthMode
    - streamdriver.CheckExtendedKeyPurpose
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: declare Rsyslog option values to
    be replaced when having wrong value'
  ansible.builtin.set_fact:
    rsyslog_values_to_replace_if_wrong_value:
    - tcp
    - gtls
    - '1'
    - x509/name
    - 'on'
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: assemble list of files with existing
    directives'
  ansible.builtin.set_fact:
    rsyslog_files: '{{ rsyslog_includes_with_directive.files | map(attribute=''path'')
      | list + rsyslog_main_file_with_directive.files | map(attribute=''path'') |
      list }}'
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: try to fix existing directives'
  block:

  - name: 'Configure TLS for rsyslog remote logging: Fix existing omfwd directives
      by adjusting the value'
    ansible.builtin.replace:
      path: '{{ item[0] }}'
      regexp: (?i)^(\s*action\s*\(\s*type\s*=\s*"omfwd"[\s\S]*)({{ item[1][0] | regex_escape()
        }}\s*=\s*"\S*")([\s\S]*\))$
      replace: \1{{ item[1][0] }}="{{ item[1][1] }}"\3
    loop: '{{ rsyslog_files | product (rsyslog_parameters_to_replace_if_wrong_value
      | zip(rsyslog_values_to_replace_if_wrong_value)) | list }}'

  - name: 'Configure TLS for rsyslog remote logging: Fix existing omfwd directives
      by adding parameter and value'
    ansible.builtin.replace:
      path: '{{ item[0] }}'
      regexp: (?i)^(\s*action\s*\(\s*type\s*=\s*"omfwd"(?:[\s\S](?!{{ item[1][0] |
        regex_escape() }}))*.)(\))$
      replace: \1 {{ item[1][0] }}="{{ item[1][1] }}" \2
    loop: '{{ rsyslog_files | product (rsyslog_parameters_to_add_if_missing | zip(rsyslog_values_to_add_if_missing))
      | list }}'
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - rsyslog_includes_with_directive.matched or rsyslog_main_file_with_directive.matched
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

- name: 'Configure TLS for rsyslog remote logging: Add missing rsyslog directive'
  ansible.builtin.lineinfile:
    dest: /etc/rsyslog.conf
    line: action(type="omfwd" protocol="tcp" Target="{{ rsyslog_remote_loghost_address
      }}" port="6514" StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name"
      streamdriver.CheckExtendedKeyPurpose="on")
    create: true
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - not rsyslog_includes_with_directive.matched and not rsyslog_main_file_with_directive.matched
  tags:
  - NIST-800-53-AU-9(3)
  - NIST-800-53-CM-6(a)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_tls

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

rsyslog_remote_loghost_address='logcollector'

params_to_add_if_missing=("protocol" "target" "port" "StreamDriver" "StreamDriverMode" "StreamDriverAuthMode" "streamdriver.CheckExtendedKeyPurpose")
values_to_add_if_missing=("tcp" "$rsyslog_remote_loghost_address" "6514" "gtls" "1" "x509/name" "on")
params_to_replace_if_wrong_value=("protocol" "StreamDriver" "StreamDriverMode" "StreamDriverAuthMode" "streamdriver.CheckExtendedKeyPurpose")
values_to_replace_if_wrong_value=("tcp" "gtls" "1" "x509/name" "on")

files_containing_omfwd=("$(grep -ilE '^[^#]*\s*action\s*\(\s*type\s*=\s*"omfwd".*' /etc/rsyslog.conf /etc/rsyslog.d/*.conf)")
if [ -n "${files_containing_omfwd[*]}" ]; then
    for file in "${files_containing_omfwd[@]}"; do
        for ((i=0; i<${#params_to_replace_if_wrong_value[@]}; i++)); do
            sed -i -E -e 'H;$!d;x;s/^\n//' -e "s|(\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"].*?)${params_to_replace_if_wrong_value[$i]}\s*=\s*[\"]\S*[\"](.*\))|\1${params_to_replace_if_wrong_value[$i]}=\"${values_to_replace_if_wrong_value[$i]}\"\2|gI" "$file"
        done
        for ((i=0; i<${#params_to_add_if_missing[@]}; i++)); do
            if ! grep -qPzi "(?s)\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"].*?${params_to_add_if_missing[$i]}.*?\).*" "$file"; then
                sed -i -E -e 'H;$!d;x;s/^\n//' -e "s|(\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"])|\1\n${params_to_add_if_missing[$i]}=\"${values_to_add_if_missing[$i]}\"|gI" "$file"
            fi
        done
    done
else
    echo "action(type=\"omfwd\" protocol=\"tcp\" Target=\"$rsyslog_remote_loghost_address\" port=\"6514\" StreamDriver=\"gtls\" StreamDriverMode=\"1\" StreamDriverAuthMode=\"x509/name\" streamdriver.CheckExtendedKeyPurpose=\"on\")"  >> /etc/rsyslog.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Configure CA certificate for rsyslog remote logging   [ref]

Configure CA certificate for rsyslog logging to remote server using Transport Layer Security (TLS) using correct path for the DefaultNetstreamDriverCAFile global option in /etc/rsyslog.conf, for example with the following command:
echo 'global(DefaultNetstreamDriverCAFile="/etc/pki/tls/cert.pem")' >> /etc/rsyslog.conf
Replace the /etc/pki/tls/cert.pem in the above command with the path to the file with CA certificate generated for the purpose of remote logging.
Warning:  Automatic remediation is not available as each organization has unique requirements.
Rationale:
The CA certificate needs to be set or rsyslog.service fails to start with
error: ca certificate is not set, cannot continue
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_rsyslog_remote_tls_cacert
References:
ism0988, 1405
osppFCS_TLSC_EXT.1
os-srgSRG-OS-000480-GPOS-00227
anssiR71

Rule   Ensure rsyslog-gnutls is installed   [ref]

TLS protocol support for rsyslog is installed. The rsyslog-gnutls package can be installed with the following command:
$ sudo dnf install rsyslog-gnutls
Rationale:
The rsyslog-gnutls package provides Transport Layer Security (TLS) support for the rsyslog daemon, which enables secure remote logging.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_rsyslog-gnutls_installed
References:
disaCCI-000366
osppFTP_ITC_EXT.1.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000120-GPOS-00061
anssiR71


[[packages]]
name = "rsyslog-gnutls"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure rsyslog-gnutls is installed
  package:
    name: rsyslog-gnutls
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_rsyslog-gnutls_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=rsyslog-gnutls

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_rsyslog-gnutls

class install_rsyslog-gnutls {
  package { 'rsyslog-gnutls':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "rsyslog-gnutls" ; then
    dnf install -y "rsyslog-gnutls"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Ensure rsyslog is Installed   [ref]

Rsyslog is installed by default. The rsyslog package can be installed with the following command:
 $ sudo dnf install rsyslog
Rationale:
The rsyslog package provides the rsyslog daemon, which provides system logging services.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_rsyslog_installed
References:
cis-csc1, 14, 15, 16, 3, 5, 6
cobit5APO11.04, BAI03.05, DSS05.04, DSS05.07, MEA02.01
disaCCI-001311, CCI-001312, CCI-000366
hipaa164.312(a)(2)(ii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1
nistCM-6(a)
nist-csfPR.PT-1
osppFTP_ITC_EXT.1.1
os-srgSRG-OS-000479-GPOS-00224, SRG-OS-000051-GPOS-00024, SRG-OS-000480-GPOS-00227


[[packages]]
name = "rsyslog"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure rsyslog is installed
  package:
    name: rsyslog
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-6(a)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_rsyslog_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=rsyslog

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_rsyslog

class install_rsyslog {
  package { 'rsyslog':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "rsyslog" ; then
    dnf install -y "rsyslog"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Network Configuration and Firewalls   Group contains 4 groups and 7 rules
[ref]   Most systems must be connected to a network of some sort, and this brings with it the substantial risk of network attack. This section discusses the security impact of decisions about networking which must be made when configuring a system.

This section also discusses firewalls, network access controls, and other network security frameworks, which allow system-level rules to be written that can limit an attackers' ability to connect to your system. These rules can specify that network traffic should be allowed or denied from certain IP addresses, hosts, and networks. The rules can also specify which of the system's network services are available to particular hosts or networks.
Group   firewalld   Group contains 2 groups and 3 rules
[ref]   The dynamic firewall daemon firewalld provides a dynamically managed firewall with support for network “zones” to assign a level of trust to a network and its associated connections and interfaces. It has support for IPv4 and IPv6 firewall settings. It supports Ethernet bridges and has a separation of runtime and permanent configuration options. It also has an interface for services or applications to add firewall rules directly.
A graphical configuration tool, firewall-config, is used to configure firewalld, which in turn uses iptables tool to communicate with Netfilter in the kernel which implements packet filtering.
The firewall service provided by firewalld is dynamic rather than static because changes to the configuration can be made at anytime and are immediately implemented. There is no need to save or apply the changes. No unintended disruption of existing network connections occurs as no part of the firewall has to be reloaded.
Group   Inspect and Activate Default firewalld Rules   Group contains 2 rules
[ref]   Firewalls can be used to separate networks into different zones based on the level of trust the user has decided to place on the devices and traffic within that network. NetworkManager informs firewalld to which zone an interface belongs. An interface's assigned zone can be changed by NetworkManager or via the firewall-config tool.
The zone settings in /etc/firewalld/ are a range of preset settings which can be quickly applied to a network interface. These are the zones provided by firewalld sorted according to the default trust level of the zones from untrusted to trusted:
  • drop

    Any incoming network packets are dropped, there is no reply. Only outgoing network connections are possible.

  • block

    Any incoming network connections are rejected with an icmp-host-prohibited message for IPv4 and icmp6-adm-prohibited for IPv6. Only network connections initiated from within the system are possible.

  • public

    For use in public areas. You do not trust the other computers on the network to not harm your computer. Only selected incoming connections are accepted.

  • external

    For use on external networks with masquerading enabled especially for routers. You do not trust the other computers on the network to not harm your computer. Only selected incoming connections are accepted.

  • dmz

    For computers in your demilitarized zone that are publicly-accessible with limited access to your internal network. Only selected incoming connections are accepted.

  • work

    For use in work areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.

  • home

    For use in home areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.

  • internal

    For use on internal networks. You mostly trust the other computers on the networks to not harm your computer. Only selected incoming connections are accepted.

  • trusted

    All network connections are accepted.


It is possible to designate one of these zones to be the default zone. When interface connections are added to NetworkManager, they are assigned to the default zone. On installation, the default zone in firewalld is set to be the public zone.
To find out all the settings of a zone, for example the public zone, enter the following command as root:
# firewall-cmd --zone=public --list-all
Example output of this command might look like the following:
# firewall-cmd --zone=public --list-all
public
  interfaces:
  services: mdns dhcpv6-client ssh
  ports:
  forward-ports:
  icmp-blocks: source-quench
To view the network zones currently active, enter the following command as root:
# firewall-cmd --get-service
The following listing displays the result of this command on common Red Hat Enterprise Linux 10 system:
# firewall-cmd --get-service
amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp
high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd
ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn
pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind
samba samba-client smtp ssh telnet tftp tftp-client transmission-client
vnc-server wbem-https
Finally to view the network zones that will be active after the next firewalld service reload, enter the following command as root:
# firewall-cmd --get-service --permanent

Rule   Install firewalld Package   [ref]

The firewalld package can be installed with the following command:
$ sudo dnf install firewalld
Rationale:
"Firewalld" provides an easy and effective way to block/limit remote access to the system via ports, services, and protocols. Remote access services, such as those providing remote access to network devices and information systems, which lack automated control capabilities, increase risk and make remote user access management difficult at best. Remote access is access to DoD nonpublic information systems by an authorized user (or an information system) communicating through an external, non-organization-controlled network. Remote access methods include, for example, dial-up, broadband, and wireless. Red Hat Enterprise Linux 10 functionality (e.g., SSH) must be capable of taking enforcement action if the audit reveals unauthorized activity. Automated control of remote access sessions allows organizations to ensure ongoing compliance with remote access policies by enforcing connection rules of remote access applications on a variety of information system components (e.g., servers, workstations, notebook computers, smartphones, and tablets)."
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_firewalld_installed
References:
disaCCI-002314
nistCM-6(a)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000297-GPOS-00115, SRG-OS-000298-GPOS-00116, SRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00232
cis4.1.2
pcidss41.2.1


[[packages]]
name = "firewalld"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure firewalld is installed
  package:
    name: firewalld
    state: present
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
  - NIST-800-53-CM-6(a)
  - PCI-DSSv4-1.2.1
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_firewalld_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=firewalld

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_firewalld

class install_firewalld {
  package { 'firewalld':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then

if ! rpm -q --quiet "firewalld" ; then
    dnf install -y "firewalld"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Rule   Verify firewalld Enabled   [ref]

The firewalld service can be enabled with the following command:
$ sudo systemctl enable firewalld.service
Rationale:
Access control methods provide the ability to enhance system security posture by restricting services and known good IP addresses and address ranges. This prevents connections from unknown hosts and protocols.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_service_firewalld_enabled
References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.3, 3.4.7
disaCCI-000366, CCI-000382, CCI-002314
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nerc-cipCIP-003-8 R4, CIP-003-8 R5, CIP-004-6 R3
nistAC-4, CM-7(b), CA-3(5), SC-7(21), CM-6(a)
nist-csfPR.IP-1
osppFMT_SMF_EXT.1
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000297-GPOS-00115, SRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00231, SRG-OS-000480-GPOS-00232
cis4.1.2
pcidss41.2.1


[customizations.services]
enabled = ["firewalld"]

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - NIST-800-171-3.1.3
  - NIST-800-171-3.4.7
  - NIST-800-53-AC-4
  - NIST-800-53-CA-3(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(21)
  - PCI-DSSv4-1.2.1
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_firewalld_enabled

- name: Verify firewalld Enabled - Enable service firewalld
  block:

  - name: Gather the package facts
    package_facts:
      manager: auto

  - name: Verify firewalld Enabled - Enable Service firewalld
    ansible.builtin.systemd:
      name: firewalld
      enabled: true
      state: started
      masked: false
    when:
    - '"firewalld" in ansible_facts.packages'
  when:
  - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  - '"firewalld" in ansible_facts.packages'
  tags:
  - NIST-800-171-3.1.3
  - NIST-800-171-3.4.7
  - NIST-800-53-AC-4
  - NIST-800-53-CA-3(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(21)
  - PCI-DSSv4-1.2.1
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_firewalld_enabled

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include enable_firewalld

class enable_firewalld {
  service {'firewalld':
    enable => true,
    ensure => 'running',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q firewalld; }; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" unmask 'firewalld.service'
"$SYSTEMCTL_EXEC" start 'firewalld.service'
"$SYSTEMCTL_EXEC" enable 'firewalld.service'

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
Group   Strengthen the Default Ruleset   Group contains 1 rule
[ref]   The default rules can be strengthened. The system scripts that activate the firewall rules expect them to be defined in configuration files under the /etc/firewalld/services and /etc/firewalld/zones directories.

The following recommendations describe how to strengthen the default ruleset configuration file. An alternative to editing this configuration file is to create a shell script that makes calls to the firewall-cmd program to load in rules under the /etc/firewalld/services and /etc/firewalld/zones directories.

Instructions apply to both unless otherwise noted. Language and address conventions for regular firewalld rules are used throughout this section.
Warning:  The program firewall-config allows additional services to penetrate the default firewall rules and automatically adjusts the firewalld ruleset(s).

Rule   Set Default firewalld Zone for Incoming Packets   [ref]

To set the default zone to drop for the built-in default zone which processes incoming IPv4 and IPv6 packets, modify the following line in /etc/firewalld/firewalld.conf to be:
DefaultZone=drop
Warning:  To prevent denying any access to the system, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above.
Rationale:
In firewalld the default zone is applied only after all the applicable rules in the table are examined for a match. Setting the default zone to drop implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_set_firewalld_default_zone
References:
cis-csc11, 14, 3, 9
cjis5.10.1
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.3, 3.4.7, 3.13.6
disaCCI-000366
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
ism1416
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCA-3(5), CM-7(b), SC-7(23), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
osppFMT_MOF_EXT.1
pcidssReq-1.4