Guide to the Secure Configuration of Red Hat Enterprise Linux 8

with profile ANSSI-BP-028 (enhanced)
This profile contains configurations that align to ANSSI-BP-028 at the enhanced hardening level. ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. A copy of the ANSSI-BP-028 can be found at the ANSSI website: https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/
This guide presents a catalog of security-relevant configuration settings for Red Hat Enterprise Linux 8. 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 TitleANSSI-BP-028 (enhanced)
Profile IDxccdf_org.ssgproject.content_profile_anssi_bp28_enhanced

CPE Platforms

  • cpe:/o:redhat:enterprise_linux:8

Revision History

Current version: 0.1.55

  • draft (as of 2021-03-19)

Table of Contents

  1. System Settings
    1. Installing and Maintaining Software
    2. Account and Access Control
    3. System Accounting with auditd
    4. File Permissions and Masks
    5. GRUB2 bootloader configuration
    6. Network Configuration and Firewalls
    7. Configure Syslog
    8. SELinux
  2. Services
    1. Deprecated services
    2. Mail Server Software
    3. DHCP
    4. Network Time Protocol
    5. SSH Server

Checklist

Group   Guide to the Secure Configuration of Red Hat Enterprise Linux 8   Group contains 52 groups and 156 rules
Group   System Settings   Group contains 42 groups and 145 rules
[ref]   Contains rules that check correct system settings.
Group   Installing and Maintaining Software   Group contains 3 groups and 33 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   Sudo   Group contains 13 rules
[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 yum 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 and References

Identifiers:  CCE-82214-8

References:  CM-6(a), SRG-OS-000324-GPOS-00125, 1.3.1, 1382, 1384, 1386, BP28(R19)


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

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

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

Complexity:low
Disruption:low
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-82214-8
    - NIST-800-53-CM-6(a)
    - enable_strategy
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - package_sudo_installed

Complexity:low
Disruption:low
Strategy:enable
include install_sudo

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

Complexity:low
Disruption:low
Strategy:enable

package --add=sudo

Rule   Don't target root user in the sudoers file   [ref]

The targeted users of a user specification should be, as much as possible, non privileged users (i.e.: non-root). User specifications have to explicitly list the runas spec (i.e. the list of target users that can be impersonated), and ALL or root should not be used.
Warning:  This rule doesn't come with a remediation, as the exact requirement allows exceptions, and removing lines from the sudoers file can make the system non-administrable.
Rationale:
It is common that the command to be executed does not require superuser rights (editing a file whose the owner is not root, sending a signal to an unprivileged process,etc.). In order to limit any attempt of privilege escalation through a command, it is better to apply normal user rights.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudoers_no_root_target
Identifiers and References

Identifiers:  CCE-83598-3

References:  BP28(R60)

Rule   Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty   [ref]

The sudo requiretty tag, when specified, will only execute sudo commands from users logged in to a real tty. This should be enabled by making sure that the requiretty tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Restricting the use cases in which a user is allowed to execute sudo commands reduces the attack surface.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_requiretty
Identifiers and References

Identifiers:  CCE-83790-6

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\brequiretty\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option requiretty
        echo "Defaults requiretty" >> /etc/sudoers
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Ensure requiretty is enabled in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults.*\brequiretty\b.*$
    line: Defaults requiretty
    validate: /usr/sbin/visudo -cf %s
  tags:
    - CCE-83790-6
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_requiretty

Rule   Ensure a dedicated group owns sudo   [ref]

Restrict the execution of privilege escalated commands to a dedicated group of users. Ensure the group owner of /usr/bin/sudo is sudogrp.
Warning:  Changing group owner of /usr/bin/sudo to a group with no member users will prevent any and all escalatation of privileges. Additionally, the system may become unmanageable if root logins are not allowed.
Warning:  This rule doesn't come with a remediation, before remediating the sysadmin needs to add users to the dedicated sudo group.
Rationale:
Restricting the set of users able to execute commands as privileged user reduces the attack surface.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_dedicated_group
Identifiers and References

Identifiers:  CCE-83982-9

References:  BP28(R57)

Rule   Explicit arguments in sudo specifications   [ref]

All commands in the sudoers file must strictly specify the arguments allowed to be used for a given user. If the command is supposed to be executed only without arguments, pass "" as an argument in the corresponding user specification.
Warning:  This rule doesn't come with a remediation, as absence of arguments in the user spec doesn't mean that the command is intended to be executed with no arguments.
Warning:  The rule can produce false findings when an argument contains a comma - sudoers syntax allows comma escaping using backslash, but the check doesn't support that. For example, root ALL=(ALL) echo 1\,2 allows root to execute echo 1,2, but the check would interpret it as two commands echo 1\ and 2.
Rationale:
Any argument can modify quite significantly the behavior of a program, whether regarding the realized operation (read, write, delete, etc.) or accessed resources (path in a file system tree). To avoid any possibility of misuse of a command by a user, the ambiguities must be removed at the level of its specification. For example, on some systems, the kernel messages are only accessible by root. If a user nevertheless must have the privileges to read them, the argument of the dmesg command has to be restricted in order to prevent the user from flushing the buffer through the -c option:
user ALL = dmesg ""
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudoers_explicit_command_args
Identifiers and References

Identifiers:  CCE-83632-0

References:  BP28(R63)

Rule   Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate   [ref]

The sudo !authenticate option, when specified, allows a user to execute commands using sudo without having to authenticate. This should be disabled by making sure that the !authenticate option does not exist in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Without re-authentication, users may access resources or perform tasks for which they do not have authorization.

When operating systems provide the capability to escalate a functional capability, it is critical that the user re-authenticate.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
Identifiers and References

Identifiers:  CCE-82202-3

References:  BP28(R5), BP28(R59), CCI-002038, IA-11, CM-6(a), PR.AC-1, PR.AC-7, SRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158, SRG-OS-000373-VMM-001470, SRG-OS-000373-VMM-001480, SRG-OS-000373-VMM-001490, SR 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, 4.3.3.5.1, 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, DSS05.04, DSS05.10, DSS06.03, DSS06.10, A.18.1.4, 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, 1, 12, 15, 16, 5, RHEL-08-010381, SV-230272r599732_rule


Complexity:low
Disruption:low
Strategy:restrict

for f in $( ls /etc/sudoers /etc/sudoers.d/* 2> /dev/null ) ; do
  matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      # comment out "!authenticate" matches to preserve user data
      sed -i "s/^${entry}$/# &/g" $f
    done <<< "$matching_list"

    /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
  fi
done

Complexity:low
Disruption:low
Strategy:restrict
- name: Find /etc/sudoers.d/ files
  find:
    paths:
      - /etc/sudoers.d/
  register: sudoers
  tags:
    - CCE-82202-3
    - DISA-STIG-RHEL-08-010381
    - NIST-800-53-CM-6(a)
    - NIST-800-53-IA-11
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_remove_no_authenticate

- name: Remove lines containing !authenticate from sudoers files
  replace:
    regexp: (^(?!#).*[\s]+\!authenticate.*$)
    replace: '# \g<1>'
    path: '{{ item.path }}'
    validate: /usr/sbin/visudo -cf %s
  with_items:
    - path: /etc/sudoers
    - '{{ sudoers.files }}'
  tags:
    - CCE-82202-3
    - DISA-STIG-RHEL-08-010381
    - NIST-800-53-CM-6(a)
    - NIST-800-53-IA-11
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_remove_no_authenticate

Rule   Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC   [ref]

The sudo NOEXEC tag, when specified, prevents user executed commands from executing other commands, like a shell for example. This should be enabled by making sure that the NOEXEC tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Restricting the capability of sudo allowed commands to execute sub-commands prevents users from running programs with privileges they wouldn't have otherwise.
Severity: 
high
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_noexec
Identifiers and References

Identifiers:  CCE-83747-6

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\bnoexec\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option noexec
        echo "Defaults noexec" >> /etc/sudoers
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Ensure noexec is enabled in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults.*\bnoexec\b.*$
    line: Defaults noexec
    validate: /usr/sbin/visudo -cf %s
  tags:
    - CCE-83747-6
    - high_severity
    - low_complexity
    - low_disruption
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_noexec

Rule   Ensure sudo Runs In A Minimal Environment - sudo env_reset   [ref]

The sudo env_reset tag, when specified, will run the command in a minimal environment, containing the TERM, PATH, HOME, MAIL, SHELL, LOGNAME, USER and SUDO_* variables. On Red Hat Enterprise Linux 8, env_reset is enabled by default This should be enabled by making sure that the env_reset tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Forcing sudo to reset the environment ensures that environment variables are not passed on to the command accidentaly, preventing leak of potentially sensitive information.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_env_reset
Identifiers and References

Identifiers:  CCE-83820-1

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\benv_reset\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option env_reset
        echo "Defaults env_reset" >> /etc/sudoers
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Ensure env_reset is enabled in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults.*\benv_reset\b.*$
    line: Defaults env_reset
    validate: /usr/sbin/visudo -cf %s
  tags:
    - CCE-83820-1
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_env_reset

Rule   Ensure sudo umask is appropriate - sudo umask   [ref]

The sudo umask tag, when specified, will be added the to the user's umask in the command environment. On Red Hat Enterprise Linux 8, the default umask value is 0022. The umask should be configured by making sure that the umask=sub_var_value("var_sudo_umask") tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_umask
Identifiers and References

Identifiers:  CCE-83860-7

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict


var_sudo_umask="0027"



if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\bumask=\w+\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option umask
        echo "Defaults umask=${var_sudo_umask}" >> /etc/sudoers
    else
        # sudoers file defines Option umask, remediate if appropriate value is not set
        if ! grep -P "^[\s]*Defaults.*\bumask=${var_sudo_umask}\b.*$" /etc/sudoers; then
            sed -Ei "s/(^[\s]*Defaults.*\bumask=)\w+(\b.*$)/\1${var_sudo_umask}\2/" /etc/sudoers
        fi
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_sudo_umask # promote to variable
  set_fact:
    var_sudo_umask: !!str 0027
  tags:
    - always

- name: Ensure umask is enabled with the appropriate value in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults\s(.*)\bumask=\w+\b(.*)$
    line: Defaults \1umask={{ var_sudo_umask }}\2
    validate: /usr/sbin/visudo -cf %s
    backrefs: true
  register: edit_sudoers_umask_option
  tags:
    - CCE-83860-7
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_umask

- name: Enable umask option with appropriate value in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    line: Defaults umask={{ var_sudo_umask }}
    validate: /usr/sbin/visudo -cf %s
  when: edit_sudoers_umask_option is defined and not edit_sudoers_umask_option.changed
  tags:
    - CCE-83860-7
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_umask

Rule   Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty   [ref]

The sudo use_pty tag, when specified, will only execute sudo commands from users logged in to a real tty. This should be enabled by making sure that the use_pty tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Requiring that sudo commands be run in a pseudo-terminal can prevent an attacker from retaining access to the user's terminal after the main program has finished executing.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_use_pty
Identifiers and References

Identifiers:  CCE-83798-9

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\buse_pty\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option use_pty
        echo "Defaults use_pty" >> /etc/sudoers
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Ensure use_pty is enabled in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults.*\buse_pty\b.*$
    line: Defaults use_pty
    validate: /usr/sbin/visudo -cf %s
  tags:
    - CCE-83798-9
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_use_pty

Rule   Ensure sudo Ignores Commands In Current Dir - sudo ignore_dot   [ref]

The sudo ignore_dot tag, when specified, will ignore the current directory in the PATH environment variable. On Red Hat Enterprise Linux 8, env_reset is enabled by default This should be enabled by making sure that the ignore_dot tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Ignoring the commands in the user's current directory prevents an attacker from executing commands downloaded locally.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_add_ignore_dot
Identifiers and References

Identifiers:  CCE-83810-2

References:  BP28(R58)


Complexity:low
Disruption:low
Strategy:restrict

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P "^[\s]*Defaults.*\bignore_dot\b.*$" /etc/sudoers; then
        # sudoers file doesn't define Option ignore_dot
        echo "Defaults ignore_dot" >> /etc/sudoers
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Ensure ignore_dot is enabled in /etc/sudoers
  lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults.*\bignore_dot\b.*$
    line: Defaults ignore_dot
    validate: /usr/sbin/visudo -cf %s
  tags:
    - CCE-83810-2
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_add_ignore_dot

Rule   Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD   [ref]

The sudo NOPASSWD tag, when specified, allows a user to execute commands using sudo without having to authenticate. This should be disabled by making sure that the NOPASSWD tag does not exist in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale:
Without re-authentication, users may access resources or perform tasks for which they do not have authorization.

When operating systems provide the capability to escalate a functional capability, it is critical that the user re-authenticate.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
Identifiers and References

Identifiers:  CCE-82197-5

References:  BP28(R5), BP28(R59), CCI-002038, IA-11, CM-6(a), PR.AC-1, PR.AC-7, SRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158, SRG-OS-000373-VMM-001470, SRG-OS-000373-VMM-001480, SRG-OS-000373-VMM-001490, SR 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, 4.3.3.5.1, 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, DSS05.04, DSS05.10, DSS06.03, DSS06.10, A.18.1.4, 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, 1, 12, 15, 16, 5, RHEL-08-010380, SV-230271r599732_rule


Complexity:low
Disruption:low
Strategy:restrict

for f in $( ls /etc/sudoers /etc/sudoers.d/* 2> /dev/null ) ; do
  matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      # comment out "NOPASSWD" matches to preserve user data
      sed -i "s/^${entry}$/# &/g" $f
    done <<< "$matching_list"

    /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
  fi
done

Complexity:low
Disruption:low
Strategy:restrict
- name: Find /etc/sudoers.d/ files
  find:
    paths:
      - /etc/sudoers.d/
  register: sudoers
  tags:
    - CCE-82197-5
    - DISA-STIG-RHEL-08-010380
    - NIST-800-53-CM-6(a)
    - NIST-800-53-IA-11
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_remove_nopasswd

- name: Remove lines containing NOPASSWD from sudoers files
  replace:
    regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$)
    replace: '# \g<1>'
    path: '{{ item.path }}'
    validate: /usr/sbin/visudo -cf %s
  with_items:
    - path: /etc/sudoers
    - '{{ sudoers.files }}'
  tags:
    - CCE-82197-5
    - DISA-STIG-RHEL-08-010380
    - NIST-800-53-CM-6(a)
    - NIST-800-53-IA-11
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
    - sudo_remove_nopasswd

Rule   Don't define allowed commands in sudoers by means of exclusion   [ref]

Policies applied by sudo through the sudoers file should not involve negation. Each user specification in the sudoers file contains a comma-delimited list of command specifications. The definition can make use glob patterns, as well as of negations. Indirect definition of those commands by means of exclusion of a set of commands is trivial to bypass, so it is not allowed to use such constructs.
Warning:  This rule doesn't come with a remediation, as negations indicate design issues with the sudoers user specifications design. Just removing negations doesn't increase the security - you typically have to rethink the definition of allowed commands to fix the issue.
Rationale:
Specifying access right using negation is inefficient and can be easily circumvented. For example, it is expected that a specification like
# To avoid absolutely , this rule can be easily circumvented!
user ALL = ALL ,!/ bin/sh
prevents the execution of the shell but that’s not the case: just copy the binary /bin/sh to a different name to make it executable again through the rule keyword ALL.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sudoers_no_command_negation
Identifiers and References

Identifiers:  CCE-83518-1

References:  BP28(R61)

Group   Updating Software   Group contains 9 rules
[ref]   The yum 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 8 systems contain an installed software catalog called the RPM database, which records metadata of installed packages. Consistently using yum or the graphical Software Update for all software installation allows for insight into the current inventory of installed software on the system.

Rule   Install dnf-automatic Package   [ref]

The dnf-automatic package can be installed with the following command:
$ sudo yum install dnf-automatic
Rationale:
dnf-automatic is an alternative command line interface (CLI) to dnf upgrade suitable for automatic, regular execution.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_package_dnf-automatic_installed
Identifiers and References

Identifiers:  CCE-82985-3

References:  SRG-OS-000191-GPOS-00080, BP28(R8)


Complexity:low
Disruption:low
Strategy:enable

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

Complexity:low
Disruption:low
Strategy:enable
- name: Ensure dnf-automatic is installed
  package:
    name: dnf-automatic
    state: present
  tags:
    - CCE-82985-3
    - enable_strategy
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - package_dnf-automatic_installed

Complexity:low
Disruption:low
Strategy:enable
include install_dnf-automatic

class install_dnf-automatic {
  package { 'dnf-automatic':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Strategy:enable

package --add=dnf-automatic

Rule   Ensure gpgcheck Enabled In Main yum Configuration   [ref]

The gpgcheck option controls whether RPM packages' signatures are always checked prior to installation. To configure yum to check package signatures before installing them, ensure the following line appears in /etc/yum.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
Identifiers and References

Identifiers:  CCE-80790-9

References:  1.2.4, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, A.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, 11, 2, 3, 9, BP28(R15), RHEL-08-010370, SV-230264r599732_rule


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

# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append "/etc/yum.conf" '^gpgcheck' '1' 'CCE-80790-9'

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

Complexity:low
Disruption:medium
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80790-9
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-08-010370
    - NIST-800-171-3.4.8
    - NIST-800-53-CM-11(a)
    - NIST-800-53-CM-11(b)
    - 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
    - ensure_gpgcheck_globally_activated
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed
    - unknown_strategy

- name: Check existence of yum on Fedora
  stat:
    path: /etc/yum.conf
  register: yum_config_file
  check_mode: false
  when:
    - ansible_distribution == "Fedora"
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80790-9
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-08-010370
    - NIST-800-171-3.4.8
    - NIST-800-53-CM-11(a)
    - NIST-800-53-CM-11(b)
    - 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
    - ensure_gpgcheck_globally_activated
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed
    - unknown_strategy

- name: Ensure GPG check is globally activated (yum)
  ini_file:
    dest: /etc/yum.conf
    section: main
    option: gpgcheck
    value: 1
    no_extra_spaces: true
    create: false
  when:
    - (ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or ansible_distribution
      == "Scientific" or yum_config_file.stat.exists)
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80790-9
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-08-010370
    - NIST-800-171-3.4.8
    - NIST-800-53-CM-11(a)
    - NIST-800-53-CM-11(b)
    - 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
    - ensure_gpgcheck_globally_activated
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed
    - unknown_strategy

- name: Ensure GPG check is globally activated (dnf)
  ini_file:
    dest: /etc/dnf/dnf.conf
    section: main
    option: gpgcheck
    value: 1
    no_extra_spaces: true
    create: false
  when:
    - ansible_distribution == "Fedora"
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80790-9
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-08-010370
    - NIST-800-171-3.4.8
    - NIST-800-53-CM-11(a)
    - NIST-800-53-CM-11(b)
    - 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
    - ensure_gpgcheck_globally_activated
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed
    - unknown_strategy

Rule   Ensure gpgcheck Enabled for All yum 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
Identifiers and References

Identifiers:  CCE-80792-5

References:  SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, A.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, 11, 2, 3, 9, BP28(R15)


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

Complexity:low
Disruption:medium
Strategy:enable
- name: Grep for yum repo section names
  shell: grep -HEr '^\[.+\]' -r /etc/yum.repos.d/
  register: repo_grep_results
  ignore_errors: true
  changed_when: false
  tags:
    - CCE-80792-5
    - 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
    - enable_strategy
    - ensure_gpgcheck_never_disabled
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed

- name: Set gpgcheck=1 for each yum 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?'' )
    }}'
  tags:
    - CCE-80792-5
    - 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
    - enable_strategy
    - ensure_gpgcheck_never_disabled
    - high_severity
    - low_complexity
    - medium_disruption
    - no_reboot_needed

Rule   Ensure Software Patches Installed   [ref]

If the system is joined to the Red Hat Network, a Red Hat Satellite Server, or a yum server, run the following command to install updates:
$ sudo yum update
If the system is not configured to use one of these sources, updates (in the form of RPM packages) can be manually downloaded from the Red Hat Network and installed using rpm.

NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy dictates.
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: 
high
Rule ID:xccdf_org.ssgproject.content_rule_security_patches_up_to_date
Identifiers and References

Identifiers:  CCE-80865-9

References:  1.9, 5.10.4.1, CCI-000366, SI-2(5), SI-2(c), CM-6(a), ID.RA-1, PR.IP-12, FMT_MOF_EXT.1, Req-6.2, SRG-OS-000480-GPOS-00227, SRG-OS-000480-VMM-002000, 4.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9, APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02, A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3, 18, 20, 4, BP28(R08), RHEL-08-010010, SV-230222r599732_rule


Complexity:low
Disruption:high
Reboot:true
Strategy:patch


yum -y update

Complexity:low
Disruption:high
Reboot:true
Strategy:patch
- name: Security patches are up to date
  package:
    name: '*'
    state: latest
  tags:
    - CCE-80865-9
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-08-010010
    - NIST-800-53-CM-6(a)
    - NIST-800-53-SI-2(5)
    - NIST-800-53-SI-2(c)
    - PCI-DSS-Req-6.2
    - high_disruption
    - high_severity
    - low_complexity
    - patch_strategy
    - reboot_required
    - security_patches_up_to_date
    - skip_ansible_lint

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
Identifiers and References

Identifiers:  CCE-82267-6

References:  FMT_SMF_EXT.1, SI-2(5), CM-6(a), SI-2(c), SRG-OS-000191-GPOS-00080, BP28(R8)



CONF="/etc/dnf/automatic.conf"
APPLY_UPDATES_REGEX="[[:space:]]*\[commands]([^\n\[]*\n+)+?[[:space:]]*upgrade_type"
COMMANDS_REGEX="[[:space:]]*\[commands]"

# Try find [commands] and upgrade_type in automatic.conf, if it exists, set
# it to security, if it isn't here, add it, if [commands] doesn't exist,
# add it there
if grep -qzosP $APPLY_UPDATES_REGEX $CONF; then
    sed -i "s/upgrade_type[^(\n)]*/upgrade_type = security/" $CONF
elif grep -qs $COMMANDS_REGEX $CONF; then
    sed -i "/$COMMANDS_REGEX/a upgrade_type = security" $CONF
else
    mkdir -p /etc/dnf
    echo -e "[commands]\nupgrade_type = security" >> $CONF
fi

Complexity:low
Disruption:medium
- 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:
    - CCE-82267-6
    - 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

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
Identifiers and References

Identifiers:  CCE-82494-6

References:  FMT_SMF_EXT.1, SI-2(5), CM-6(a), SI-2(c), SRG-OS-000191-GPOS-00080, 0940, 1144, 1467, 1472, 1483, 1493, 1494, 1495, BP28(R8)



CONF="/etc/dnf/automatic.conf"
APPLY_UPDATES_REGEX="[[:space:]]*\[commands]([^\n\[]*\n+)+?[[:space:]]*apply_updates"
COMMANDS_REGEX="[[:space:]]*\[commands]"

# Try find [commands] and apply_updates in automatic.conf, if it exists, set
# to yes, if it isn't here, add it, if [commands] doesn't exist, add it there
if grep -qzosP $APPLY_UPDATES_REGEX $CONF; then
    sed -i "s/apply_updates[^(\n)]*/apply_updates = yes/" $CONF
elif grep -qs $COMMANDS_REGEX $CONF; then
    sed -i "/$COMMANDS_REGEX/a apply_updates = yes" $CONF
else
    mkdir -p /etc/dnf
    echo -e "[commands]\napply_updates = yes" >> $CONF
fi

Complexity:low
Disruption:medium
- 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:
    - CCE-82494-6
    - 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

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
Identifiers and References

Identifiers:  CCE-80795-8

References:  SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650, 1.2.3, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, A.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, 11, 2, 3, 9, BP28(R15)


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

# 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 --show-keys --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep -A1 "^pub" | 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

Complexity:medium
Disruption:medium
Strategy:restrict
- name: Read permission of GPG key directory
  stat:
    path: /etc/pki/rpm-gpg/
  register: gpg_key_directory_permission
  check_mode: false
  tags:
    - CCE-80795-8
    - 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
    - ensure_redhat_gpgkey_installed
    - high_severity
    - medium_complexity
    - medium_disruption
    - no_reboot_needed
    - restrict_strategy

- name: Read signatures in GPG key
  command: gpg --show-keys --with-fingerprint --with-colons "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
  args:
    warn: false
  changed_when: false
  register: gpg_fingerprints
  check_mode: false
  tags:
    - CCE-80795-8
    - 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
    - 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:
    - CCE-80795-8
    - 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
    - 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: ("567E347AD0044ADE55BA8A5F199E2F91FD431D51" "6A6AA7C97C8890AEC6AEBFE2F76F66C3D4082792")
  tags:
    - CCE-80795-8
    - 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
    - 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:
    - CCE-80795-8
    - 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
    - ensure_redhat_gpgkey_installed
    - high_severity
    - medium_complexity
    - medium_disruption
    - no_reboot_needed
    - restrict_strategy

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
Identifiers and References

Identifiers:  CCE-82360-9

References:  FMT_SMF_EXT.1, SI-2(5), CM-6(a), SI-2(c), SRG-OS-000191-GPOS-00080, BP28(R8)


Complexity:low
Disruption:low
Strategy:enable

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'dnf-automatic.timer'
"$SYSTEMCTL_EXEC" enable 'dnf-automatic.timer'

Complexity:low
Disruption:low
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:
    - CCE-82360-9
    - 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

Rule   Ensure gpgcheck Enabled for Local Packages   [ref]

yum should be configured to verify the signature(s) of local packages prior to installation. To configure yum to verify signatures of local packages, set the localpkg_gpgcheck to 1 in /etc/yum.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
Identifiers and References

Identifiers:  CCE-80791-7

References:  3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-11(a), CM-11(b), CM-6(a), CM-5(3), SA-12, SA-12(10), PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650, SR 7.6, 4.3.4.3.2, 4.3.4.3.3, BAI10.01, BAI10.02, BAI10.03, BAI10.05, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, 11, 3, 9, BP28(R15), RHEL-08-010371, SV-230265r599732_rule


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

# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/yum.conf' '^localpkg_gpgcheck' '1' 'CCE-80791-7'

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

Complexity:low
Disruption:medium
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80791-7
    - DISA-STIG-RHEL-08-010371
    - 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: Check existence of yum on Fedora
  stat:
    path: /etc/yum.conf
  register: yum_config_file
  check_mode: false
  when:
    - ansible_distribution == "Fedora"
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80791-7
    - DISA-STIG-RHEL-08-010371
    - 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 (Yum)
  ini_file:
    dest: /etc/yum.conf
    section: main
    option: localpkg_gpgcheck
    value: 1
    create: true
  when:
    - (ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or ansible_distribution
      == "Scientific" or yum_config_file.stat.exists)
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80791-7
    - DISA-STIG-RHEL-08-010371
    - 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)
  ini_file:
    dest: /etc/dnf/dnf.conf
    section: main
    option: localpkg_gpgcheck
    value: 1
    create: true
  when:
    - ansible_distribution == "Fedora"
    - '"yum" in ansible_facts.packages'
  tags:
    - CCE-80791-7
    - DISA-STIG-RHEL-08-010371
    - 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
Group   Disk Partitioning   Group contains 10 rules
[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 /home Located On Separate Partition   [ref]

If user home directories will be stored locally, create a separate partition for /home at installation time (or migrate it later using LVM). If /home will be mounted from another system such as an NFS server, then creating a separate partition is not necessary at installation time, and the mountpoint can instead be configured later.
Rationale:
Ensuring that /home is mounted on its own partition enables the setting of more restrictive mount options, and also helps ensure that users cannot trivially fill partitions used for log or audit data storage.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_home
Identifiers and References

Identifiers:  CCE-81044-0

References:  BP28(R12), 1.1.13, CCI-000366, CCI-001208, CM-6(a), SC-5(2), PR.PT-4, SRG-OS-000480-GPOS-00227, 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.6, APO13.01, DSS05.02, A.13.1.1, A.13.2.1, A.14.1.3, 12, 15, 8, RHEL-08-010800, SV-230328r599732_rule


Complexity:low
Disruption:high
Strategy:enable

part /home

Rule   Ensure /srv Located On Separate Partition   [ref]

If a file server (FTP, TFTP...) is hosted locally, create a separate partition for /srv at installation time (or migrate it later using LVM). If /srv will be mounted from another system such as an NFS server, then creating a separate partition is not necessary at installation time, and the mountpoint can instead be configured later.
Rationale:
Srv deserves files for local network file server such as FTP. Ensuring that /srv is mounted on its own partition enables the setting of more restrictive mount options, and also helps ensure that users cannot trivially fill partitions used for log or audit data storage.
Severity: 
unknown
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_srv
Identifiers and References

Identifiers:  CCE-83387-1

References:  BP28(R12)


Complexity:low
Disruption:high
Strategy:enable

part /srv

Rule   Ensure /tmp Located On Separate Partition   [ref]

The /tmp directory is a world-writable directory used for temporary file storage. Ensure it has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale:
The /tmp partition is used as temporary storage by many programs. Placing /tmp in its own partition enables the setting of more restrictive mount options, which can help protect programs which use it.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_tmp
Identifiers and References

Identifiers:  CCE-80851-9

References:  BP28(R12), 1.1.2, CCI-000366, CM-6(a), SC-5(2), PR.PT-4, SRG-OS-000480-GPOS-00227, 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.6, APO13.01, DSS05.02, A.13.1.1, A.13.2.1, A.14.1.3, 12, 15, 8, RHEL-08-010543, SV-230295r599732_rule


Complexity:low
Disruption:high
Strategy:enable

part /tmp

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

Audit logs are stored in the /var/log/audit directory. Ensure that it has its own partition or logical volume at installation time, or migrate it later 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
Identifiers and References

Identifiers:  CCE-80854-3

References:  1.1.12, CCI-000366, CCI-001849, 164.312(a)(2)(ii), A.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, CM-6(a), AU-4, SC-5(2), PR.DS-4, PR.PT-1, PR.PT-4, SRG-OS-000341-GPOS-00132, SRG-OS-000480-GPOS-00227, SRG-OS-000341-VMM-001220, SR 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, 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, APO11.04, APO13.01, BAI03.05, BAI04.04, DSS05.02, DSS05.04, DSS05.07, MEA02.01, 1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 8, BP28(R43), RHEL-08-010542, SV-230294r599732_rule


Complexity:low
Disruption:high
Strategy:enable

part /var/log/audit

Rule   Ensure /boot Located On Separate Partition   [ref]

It is recommended that the /boot directory resides on a separate partition. This makes it easier to apply restrictions e.g. through the noexec mount option. Eventually, the /boot partition can be configured not to be mounted automatically with the noauto mount option.
Rationale:
The /boot partition contains the kernel and bootloader files. Access to this partition should be restricted.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_boot
Identifiers and References

Identifiers:  CCE-83336-8

References:  BP28(R12)


Complexity:low
Disruption:high
Strategy:enable

part /boot

Rule   Ensure /var Located On Separate Partition   [ref]

The /var directory is used by daemons and other system services to store frequently-changing data. Ensure that /var has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale:
Ensuring that /var is mounted on its own partition enables the setting of more restrictive mount options. This helps protect system services such as daemons or other programs which use it. It is not uncommon for the /var directory to contain world-writable directories installed by other software packages.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_var
Identifiers and References

Identifiers:  CCE-80852-7

References:  BP28(R12), 1.1.6, CCI-000366, CM-6(a), SC-5(2), PR.PT-4, SRG-OS-000480-GPOS-00227, SRG-OS-000341-VMM-001220, 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.6, APO13.01, DSS05.02, A.13.1.1, A.13.2.1, A.14.1.3, 12, 15, 8, RHEL-08-010540, SV-230292r599732_rule


Complexity:low
Disruption:high
Strategy:enable

part /var

Rule   Ensure /opt Located On Separate Partition   [ref]

It is recommended that the /opt directory resides on a separate partition.
Rationale:
The /opt partition contains additional software, usually installed outside the packaging system. Putting this directory on a separate partition makes it easier to apply restrictions e.g. through the nosuid mount option.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_opt
Identifiers and References

Identifiers:  CCE-83340-0

References:  BP28(R12)


Complexity:low
Disruption:high
Strategy:enable

part /opt

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

System logs are stored in the /var/log directory. Ensure that it has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale:
Placing /var/log in its own partition enables better separation between log files and other files in /var/.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_var_log
Identifiers and References

Identifiers:  CCE-80853-5

References:  BP28(R12), BP28(R47), 1.1.11, CM-6(a), AU-4, SC-5(2), PR.PT-1, PR.PT-4, SR 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.6, 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, APO11.04, APO13.01, BAI03.05, DSS05.02, DSS05.04, DSS05.07, MEA02.01, 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, 1, 12, 14, 15, 16, 3, 5, 6, 8, SRG-OS-000480-GPOS-00227, RHEL-08-010541, SV-230293r599732_rule


Complexity:low
Disruption:high
Strategy:enable

part /var/log

Rule   Ensure /usr Located On Separate Partition   [ref]

It is recommended that the /usr directory resides on a separate partition.
Rationale:
The /usr partition contains system software, utilities and files. Putting it on a separate partition allows limiting its size and applying restrictions through mount options.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_usr
Identifiers and References

Identifiers:  CCE-83343-4

References:  BP28(R12)


Complexity:low
Disruption:high
Strategy:enable

part /usr

Rule   Ensure /var/tmp Located On Separate Partition   [ref]

The /var/tmp directory is a world-writable directory used for temporary file storage. Ensure it has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale:
The /var/tmp partition is used as temporary storage by many programs. Placing /var/tmp in its own partition enables the setting of more restrictive mount options, which can help protect programs which use it.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_partition_for_var_tmp
Identifiers and References

Identifiers:  CCE-82730-3

References:  1.1.7, BP28(R12), SRG-OS-000480-GPOS-00227


Complexity:low
Disruption:high
Strategy:enable

part /var/tmp

Rule   Prefer to use a 64-bit Operating System when supported   [ref]

Prefer installation of 64-bit operating systems when the CPU supports it.
Warning:  There is no remediation besides installing a 64-bit operating system.
Rationale:
Use of a 64-bit operating system offers a few advantages, like a larger address space range for Address Space Layout Randomization (ASLR) and systematic presence of No eXecute and Execute Disable (NX/XD) protection bits.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_prefer_64bit_os
Identifiers and References

Identifiers:  CCE-83694-0

References:  BP28(R10)

Group   Account and Access Control   Group contains 11 groups and 23 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 8.
Group   Protect Accounts by Configuring PAM   Group contains 4 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 http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html.
Group   Set Lockouts for Failed Password Attempts   Group contains 5 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   Limit Password Reuse   [ref]

Do not allow users to reuse recent passwords. This can be accomplished by using the remember option for the pam_unix or pam_pwhistory PAM modules.

In the file /etc/pam.d/system-auth, append remember=2 to the line which refers to the pam_unix.so or pam_pwhistory.somodule, as shown below:
  • for the pam_unix.so case:
    password sufficient pam_unix.so ...existing_options... remember=2
  • for the pam_pwhistory.so case:
    password requisite pam_pwhistory.so ...existing_options... remember=2
The DoD STIG requirement is 5 passwords.
Rationale:
Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_remember
Identifiers and References

Identifiers:  CCE-80666-1

References:  5.3.3, 5.6.2.1.1, 3.5.8, CCI-000200, IA-5(f), IA-5(1)(e), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.5, SRG-OS-000077-GPOS-00045, SRG-OS-000077-VMM-000440, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, BP28(R18), RHEL-08-020220, SV-230368r599732_rule


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


var_password_pam_unix_remember="2"



AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

for pamFile in "${AUTH_FILES[@]}"
do
	if grep -q "remember=" $pamFile; then
		sed -i --follow-symlinks "s/\(^password.*sufficient.*pam_unix.so.*\)\(\(remember *= *\)[^ $]*\)/\1remember=$var_password_pam_unix_remember/" $pamFile
	else
		sed -i --follow-symlinks "/^password[[:space:]]\+sufficient[[:space:]]\+pam_unix.so/ s/$/ remember=$var_password_pam_unix_remember/" $pamFile
	fi
done

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

Complexity:low
Disruption:medium
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80666-1
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-08-020220
    - NIST-800-171-3.5.8
    - NIST-800-53-IA-5(1)(e)
    - NIST-800-53-IA-5(f)
    - PCI-DSS-Req-8.2.5
    - accounts_password_pam_unix_remember
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
- name: XCCDF Value var_password_pam_unix_remember # promote to variable
  set_fact:
    var_password_pam_unix_remember: !!str 2
  tags:
    - always

- name: Do not allow users to reuse recent passwords - system-auth (change)
  replace:
    dest: /etc/pam.d/system-auth
    regexp: ^(password\s+sufficient\s+pam_unix\.so\s.*remember\s*=\s*)(\S+)(.*)$
    replace: \g<1>{{ var_password_pam_unix_remember }}\g<3>
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80666-1
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-08-020220
    - NIST-800-171-3.5.8
    - NIST-800-53-IA-5(1)(e)
    - NIST-800-53-IA-5(f)
    - PCI-DSS-Req-8.2.5
    - accounts_password_pam_unix_remember
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed

- name: Do not allow users to reuse recent passwords - system-auth (add)
  replace:
    dest: /etc/pam.d/system-auth
    regexp: ^password\s+sufficient\s+pam_unix\.so\s(?!.*remember\s*=\s*).*$
    replace: \g<0> remember={{ var_password_pam_unix_remember }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80666-1
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-08-020220
    - NIST-800-171-3.5.8
    - NIST-800-53-IA-5(1)(e)
    - NIST-800-53-IA-5(f)
    - PCI-DSS-Req-8.2.5
    - accounts_password_pam_unix_remember
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed

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. Modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • Add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=900 fail_interval=900
  • Add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=900 fail_interval=900
    
  • Add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so
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
Identifiers and References

Identifiers:  CCE-80669-5

References:  CCI-000044, CCI-002236, CCI-002237, CCI-002238, CM-6(a), AC-7(a), PR.AC-7, FIA_AFL.1, SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005, SRG-OS-000021-VMM-000050, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 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, DSS05.04, DSS05.10, DSS06.10, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, 1, 12, 15, 16, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020012, SV-230334r599829_rule


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

# include our remediation functions library

var_accounts_passwords_pam_faillock_fail_interval="900"



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

for pam_file in "${AUTH_FILES[@]}"
do
    # is auth required pam_faillock.so preauth present?
    if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*'"fail_interval"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\('"fail_interval"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ '"fail_interval"'='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        fi
    # auth required pam_faillock.so preauth is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent '"fail_interval"'='"$var_accounts_passwords_pam_faillock_fail_interval" "$pam_file"
    fi
    # is auth default pam_faillock.so authfail present?
    if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*'"fail_interval"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\('"fail_interval"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ '"fail_interval"'='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        fi
    # auth default pam_faillock.so authfail is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail '"fail_interval"'='"$var_accounts_passwords_pam_faillock_fail_interval" "$pam_file"
    fi
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        sed -E -i --follow-symlinks '/^\s*account\s*required\s*pam_unix.so/i account     required      pam_faillock.so' "$pam_file"
    fi
done

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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: Add auth pam_faillock preauth fail_interval before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: required
    new_module_path: pam_faillock.so
    module_arguments: preauth silent fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
      }}
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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: Add fail_interval argument to auth pam_faillock preauth
  pamd:
    name: '{{ item }}'
    type: auth
    control: required
    module_path: pam_faillock.so
    module_arguments: preauth silent fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
      }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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: Add auth pam_faillock aufthfail fail_interval after pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: authfail fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
      }}
    state: after
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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: Add fail_interval argument to auth pam_faillock authfail
  pamd:
    name: '{{ item }}'
    type: auth
    control: '[default=die]'
    module_path: pam_faillock.so
    module_arguments: authfail fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
      }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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: Add account pam_faillock before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80669-5
    - DISA-STIG-RHEL-08-020012
    - 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

Rule   Set Lockout Time for Failed Password Attempts   [ref]

To configure the system to lock out accounts after a number of incorrect login attempts and require an administrator to unlock the account using pam_faillock.so, modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=900 fail_interval=900
  • add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=900 fail_interval=900
  • add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so
If unlock_time is set to 0, manual intervention by an administrator is required to unlock a user.
Rationale:
Locking out user accounts after a number of incorrect attempts prevents direct password guessing attacks. Ensuring that an administrator is involved in unlocking locked accounts draws appropriate attention to such situations.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time
Identifiers and References

Identifiers:  CCE-80670-3

References:  5.3.2, 5.5.3, 3.1.8, CCI-000044, CCI-002236, CCI-002237, CCI-002238, CM-6(a), AC-7(b), PR.AC-7, FIA_AFL.1, Req-8.1.7, SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005, SRG-OS-000329-VMM-001180, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 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, DSS05.04, DSS05.10, DSS06.10, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, 1, 12, 15, 16, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020014, SV-230336r599831_rule


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


var_accounts_passwords_pam_faillock_unlock_time="900"



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

for pam_file in "${AUTH_FILES[@]}"
do
    # is auth required pam_faillock.so preauth present?
    if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*'"unlock_time"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\('"unlock_time"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ '"unlock_time"'='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        fi
    # auth required pam_faillock.so preauth is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent '"unlock_time"'='"$var_accounts_passwords_pam_faillock_unlock_time" "$pam_file"
    fi
    # is auth default pam_faillock.so authfail present?
    if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*'"unlock_time"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\('"unlock_time"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ '"unlock_time"'='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        fi
    # auth default pam_faillock.so authfail is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail '"unlock_time"'='"$var_accounts_passwords_pam_faillock_unlock_time" "$pam_file"
    fi
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        sed -E -i --follow-symlinks '/^\s*account\s*required\s*pam_unix.so/i account     required      pam_faillock.so' "$pam_file"
    fi
done

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - 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 900
  tags:
    - always

- name: Add auth pam_faillock preauth unlock_time before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: required
    new_module_path: pam_faillock.so
    module_arguments: preauth silent unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
      }}
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - accounts_passwords_pam_faillock_unlock_time
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add unlock_time argument to pam_faillock preauth
  pamd:
    name: '{{ item }}'
    type: auth
    control: required
    module_path: pam_faillock.so
    module_arguments: preauth silent unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
      }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - accounts_passwords_pam_faillock_unlock_time
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add auth pam_faillock authfail unlock_interval after pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: authfail unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
      }}
    state: after
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - accounts_passwords_pam_faillock_unlock_time
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add unlock_time argument to auth pam_faillock authfail
  pamd:
    name: '{{ item }}'
    type: auth
    control: '[default=die]'
    module_path: pam_faillock.so
    module_arguments: authfail unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
      }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - accounts_passwords_pam_faillock_unlock_time
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add account pam_faillock before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80670-3
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020014
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(b)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.7
    - accounts_passwords_pam_faillock_unlock_time
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

Rule   Set Deny For Failed Password Attempts   [ref]

To configure the system to lock out accounts after a number of incorrect login attempts using pam_faillock.so, modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=900 fail_interval=900
  • add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=900 fail_interval=900
  • add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so
Rationale:
Locking out user accounts after a number of incorrect attempts prevents direct password guessing attacks.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny
Identifiers and References

Identifiers:  CCE-80667-9

References:  5.3.2, 5.5.3, 3.1.8, CCI-000044, CCI-002236, CCI-002237, CCI-002238, CM-6(a), AC-7(a), PR.AC-7, FIA_AFL.1, Req-8.1.6, SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005, SRG-OS-000021-VMM-000050, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 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, DSS05.04, DSS05.10, DSS06.10, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, 1, 12, 15, 16, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020010, SV-230332r599827_rule


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


var_accounts_passwords_pam_faillock_deny="3"



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

for pam_file in "${AUTH_FILES[@]}"
do
    # is auth required pam_faillock.so preauth present?
    if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*'"deny"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\('"deny"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ '"deny"'='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        fi
    # auth required pam_faillock.so preauth is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent '"deny"'='"$var_accounts_passwords_pam_faillock_deny" "$pam_file"
    fi
    # is auth default pam_faillock.so authfail present?
    if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then
        # is the option set?
        if grep -qE '^\s*auth\s+(\[default=die\])\s+pam_faillock\.so\s+authfail.*'"deny"'=([0-9]*).*$' "$pam_file" ; then
            # just change the value of option to a correct value
            sed -i --follow-symlinks 's/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\('"deny"' *= *\).*/\1\2'"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        # the option is not set.
        else
            # append the option
            sed -i --follow-symlinks '/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ '"deny"'='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        fi
    # auth default pam_faillock.so authfail is not present, insert the whole line
    else
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail '"deny"'='"$var_accounts_passwords_pam_faillock_deny" "$pam_file"
    fi
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        sed -E -i --follow-symlinks '/^\s*account\s*required\s*pam_unix.so/i account     required      pam_faillock.so' "$pam_file"
    fi
done

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_deny # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_deny: !!str 3
  tags:
    - always

- name: Add auth pam_faillock preauth deny before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: required
    new_module_path: pam_faillock.so
    module_arguments: preauth silent deny={{ var_accounts_passwords_pam_faillock_deny
      }}
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add deny argument to auth pam_faillock preauth
  pamd:
    name: '{{ item }}'
    type: auth
    control: required
    module_path: pam_faillock.so
    module_arguments: preauth silent deny={{ var_accounts_passwords_pam_faillock_deny
      }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add auth pam_faillock authfail deny after pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: authfail deny={{ var_accounts_passwords_pam_faillock_deny }}
    state: after
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add deny argument to auth pam_faillock authfail
  pamd:
    name: '{{ item }}'
    type: auth
    new_type: auth
    control: '[default=die]'
    module_path: pam_faillock.so
    module_arguments: authfail deny={{ var_accounts_passwords_pam_faillock_deny }}
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Add account pam_faillock before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80667-9
    - CJIS-5.5.3
    - DISA-STIG-RHEL-08-020010
    - NIST-800-171-3.1.8
    - NIST-800-53-AC-7(a)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.1.6
    - accounts_passwords_pam_faillock_deny
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

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

To configure the system to lock out the root account after a number of incorrect login attempts using pam_faillock.so, modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • Modify the following line in the AUTH section to add even_deny_root:
    auth required pam_faillock.so preauth silent even_deny_root deny=3 unlock_time=900 fail_interval=900
  • Modify the following line in the AUTH section to add even_deny_root:
    auth [default=die] pam_faillock.so authfail even_deny_root deny=3 unlock_time=900 fail_interval=900
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_deny_root
Identifiers and References

Identifiers:  CCE-80668-7

References:  CCI-002238, CM-6(a), AC-7(b), IA-5(c), PR.AC-7, FMT_MOF_EXT.1, SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 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, DSS05.04, DSS05.10, DSS06.10, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, 1, 12, 15, 16, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020022, SV-230344r599839_rule


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

AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

# This script fixes absence of pam_faillock.so in PAM stack or the
# absense of even_deny_root in pam_faillock.so arguments
# When inserting auth pam_faillock.so entries,
# the entry with preauth argument will be added before pam_unix.so module
# and entry with authfail argument will be added before pam_deny.so module.

# The placement of pam_faillock.so entries will not be changed
# if they are already present

for pamFile in "${AUTH_FILES[@]}"
do
	# if PAM file is missing, system is not using PAM or broken
	if [ ! -f $pamFile ]; then
		continue
	fi

	# is 'auth required' here?
	if grep -q "^auth.*required.*pam_faillock.so.*" $pamFile; then
		# has 'auth required' even_deny_root option?
		if ! grep -q "^auth.*required.*pam_faillock.so.*preauth.*even_deny_root" $pamFile; then
			# even_deny_root is not present
			sed -i --follow-symlinks "s/\(^auth.*required.*pam_faillock.so.*preauth.*\).*/\1 even_deny_root/" $pamFile
		fi
	else
		# no 'auth required', add it
		sed -i --follow-symlinks "/^auth.*pam_unix.so.*/i auth required pam_faillock.so preauth silent even_deny_root" $pamFile
	fi

	# is 'auth [default=die]' here?
	if grep -q "^auth.*\[default=die\].*pam_faillock.so.*" $pamFile; then
		# has 'auth [default=die]' even_deny_root option?
		if ! grep -q "^auth.*\[default=die\].*pam_faillock.so.*authfail.*even_deny_root" $pamFile; then
			# even_deny_root is not present
			sed -i --follow-symlinks "s/\(^auth.*\[default=die\].*pam_faillock.so.*authfail.*\).*/\1 even_deny_root/" $pamFile
		fi
	else
		# no 'auth [default=die]', add it
		sed -i --follow-symlinks "/^auth.*pam_unix.so.*/a auth [default=die] pam_faillock.so authfail silent even_deny_root" $pamFile
	fi
done

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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: Add auth pam_faillock preauth even_deny_root before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: required
    new_module_path: pam_faillock.so
    module_arguments: preauth silent even_deny_root
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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: Add even_deny_root argument to auth pam_faillock preauth
  pamd:
    name: '{{ item }}'
    type: auth
    control: required
    module_path: pam_faillock.so
    module_arguments: preauth silent even_deny_root
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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: Add auth pam_faillock authfail even_deny_root after pam_unix.so
  pamd:
    name: '{{ item }}'
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: authfail even_deny_root
    state: after
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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: Add even_deny_root argument to auth pam_faillock authfail
  pamd:
    name: '{{ item }}'
    type: auth
    control: '[default=die]'
    module_path: pam_faillock.so
    module_arguments: authfail even_deny_root
    state: args_present
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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: Add account pam_faillock before pam_unix.so
  pamd:
    name: '{{ item }}'
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  loop:
    - system-auth
    - password-auth
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80668-7
    - DISA-STIG-RHEL-08-020022
    - 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
Group   Set Password Quality Requirements   Group contains 1 group and 5 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 5 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 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 reuiqred 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
Identifiers and References

Identifiers:  CCE-80665-3

References:  6.3.2, CCI-000192, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, Req-8.2.3, SRG-OS-000069-GPOS-00037, SRG-OS-000069-VMM-000360, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020110, SV-230357r599732_rule


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


var_password_pam_ucredit="-1"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/security/pwquality.conf' '^ucredit' $var_password_pam_ucredit 'CCE-80665-3' '%s = %s'

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80665-3
    - DISA-STIG-RHEL-08-020110
    - 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 variable ucredit is set accordingly
  lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ucredit
    line: ucredit = {{ var_password_pam_ucredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80665-3
    - DISA-STIG-RHEL-08-020110
    - 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

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
Identifiers and References

Identifiers:  CCE-80655-4

References:  CCI-000193, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, Req-8.2.3, SRG-OS-000070-GPOS-00038, SRG-OS-000070-VMM-000370, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020120, SV-230358r599732_rule


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


var_password_pam_lcredit="-1"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/security/pwquality.conf' '^lcredit' $var_password_pam_lcredit 'CCE-80655-4' '%s = %s'

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80655-4
    - DISA-STIG-RHEL-08-020120
    - 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_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 variable lcredit is set accordingly
  lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*lcredit
    line: lcredit = {{ var_password_pam_lcredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80655-4
    - DISA-STIG-RHEL-08-020120
    - 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_lcredit
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

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
Identifiers and References

Identifiers:  CCE-80653-9

References:  6.3.2, CCI-000194, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, Req-8.2.3, SRG-OS-000071-GPOS-00039, SRG-OS-000071-VMM-000380, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020130, SV-230359r599732_rule


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


var_password_pam_dcredit="-1"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/security/pwquality.conf' '^dcredit' $var_password_pam_dcredit 'CCE-80653-9' '%s = %s'

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80653-9
    - DISA-STIG-RHEL-08-020130
    - 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_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 variable dcredit is set accordingly
  lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*dcredit
    line: dcredit = {{ var_password_pam_dcredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80653-9
    - DISA-STIG-RHEL-08-020130
    - 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_dcredit
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

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=18 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 compromose the password.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen
Identifiers and References

Identifiers:  CCE-80656-2

References:  6.3.2, 5.6.2.1.1, CCI-000205, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, Req-8.2.3, SRG-OS-000078-GPOS-00046, SRG-OS-000072-VMM-000390, SRG-OS-000078-VMM-000450, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020230, SV-230369r599732_rule


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


var_password_pam_minlen="18"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/security/pwquality.conf' '^minlen' $var_password_pam_minlen 'CCE-80656-2' '%s = %s'

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80656-2
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-08-020230
    - 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_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 18
  tags:
    - always

- name: Ensure PAM variable minlen is set accordingly
  lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*minlen
    line: minlen = {{ var_password_pam_minlen }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80656-2
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-08-020230
    - 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_minlen
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

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 possble 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
Identifiers and References

Identifiers:  CCE-80663-8

References:  CCI-001619, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, SRG-OS-000266-GPOS-00101, SRG-OS-000266-VMM-000940, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, BP28(R18), RHEL-08-020280, SV-230375r599732_rule


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


var_password_pam_ocredit="-1"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/security/pwquality.conf' '^ocredit' $var_password_pam_ocredit 'CCE-80663-8' '%s = %s'

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-80663-8
    - DISA-STIG-RHEL-08-020280
    - 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 variable ocredit is set accordingly
  lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ocredit
    line: ocredit = {{ var_password_pam_ocredit }}
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-80663-8
    - DISA-STIG-RHEL-08-020280
    - 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
Group   Set Password Hashing Algorithm   Group contains 1 rule
[ref]   The system's default algorithm for storing password hashes in /etc/shadow is SHA-512. This can be configured in several locations.

Rule   Set PAM's Password Hashing Algorithm   [ref]

The PAM system service can be configured to only store encrypted representations of passwords. In /etc/pam.d/system-auth, the password section of the file controls which PAM modules execute during a password change. Set the pam_unix.so module in the password section to include the argument sha512, as shown below:
password    sufficient    pam_unix.so sha512 other arguments...

This will help ensure when local users change their passwords, hashes for the new passwords will be generated using the SHA-512 algorithm. This is the default.
Rationale:
Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are encrypted with a weak algorithm are no more protected than if they are kepy in plain text.

This setting ensures user and group account administration utilities are configured to store only encrypted representations of passwords. Additionally, the crypt_style configuration option ensures the use of a strong hashing algorithm that makes password cracking attacks more difficult.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth
Identifiers and References

Identifiers:  CCE-80893-1

References:  5.4.4, 5.6.2.2, 3.13.11, CCI-000196, IA-5(c), IA-5(1)(c), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.1, SRG-OS-000073-GPOS-00041, SRG-OS-000480-VMM-002000, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, 0418, 1055, 1402, BP28(R32)


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

AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

for pamFile in "${AUTH_FILES[@]}"
do
	if ! grep -q "^password.*sufficient.*pam_unix.so.*sha512" $pamFile; then
		sed -i --follow-symlinks "/^password.*sufficient.*pam_unix.so/ s/$/ sha512/" $pamFile
	fi
done

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

Rule   Set Up a Private Namespace in PAM Configuration   [ref]

To setup a private namespace add the following line to /etc/pam.d/login:
session    required     pam_namespace.so
Rationale:
The pam_namespace PAM module sets up a private namespace for a session with polyinstantiated directories. A polyinstantiated directory provides a different instance of itself based on user name, or when using SELinux, user name, security context or both. The polyinstatied directories can be used to dedicate separate temporary directories to each account.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_enable_pam_namespace
Identifiers and References

Identifiers:  CCE-83744-3

References:  BP28(R39)


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

if ! grep -Eq '^\s*session\s+required\s+pam_namespace.so\s*$' '/etc/pam.d/login' ; then
    echo "session    required     pam_namespace.so" >> "/etc/pam.d/login"
fi

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-83744-3
    - enable_pam_namespace
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy

- name: Make changes to /etc/pam.d/login
  lineinfile:
    path: /etc/pam.d/login
    create: false
    regexp: ^\s*session\s+required\s+pam_namespace.so\s*$
    line: session    required     pam_namespace.so
    state: present
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-83744-3
    - enable_pam_namespace
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy
Group   Protect Accounts by Restricting Password-Based Login   Group contains 3 groups and 5 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   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   Direct root Logins Not Allowed   [ref]

To further limit access to the root account, administrators can disable root logins at the console by editing the /etc/securetty file. This file lists all devices the root user is allowed to login to. If the file does not exist at all, the root user can login through any communication device on the system, whether via the console or via a raw network interface. This is dangerous as user can login to the system as root via Telnet, which sends the password in plain text over the network. By default, Red Hat Enterprise Linux 8's /etc/securetty file only allows the root user to login at the console physically attached to the system. To prevent root from logging in, remove the contents of this file. To prevent direct root logins, remove the contents of this file by typing the following command:
$ sudo echo > /etc/securetty
Rationale:
Disabling direct root logins ensures proper accountability and multifactor authentication to privileged accounts. Users will first login, then escalate to privileged (root) access via su / sudo. This is required for FISMA Low and FISMA Moderate systems.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_no_direct_root_logins
Identifiers and References

Identifiers:  CCE-80840-2

References:  5.6, 3.1.1, 3.1.6, 164.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), IA-2, CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, SR 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, 4.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, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 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, 1, 12, 15, 16, 5, BP28(R19)


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

echo > /etc/securetty

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Direct root Logins Not Allowed
  copy:
    dest: /etc/securetty
    content: ''
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80840-2
    - NIST-800-171-3.1.1
    - NIST-800-171-3.1.6
    - NIST-800-53-CM-6(a)
    - NIST-800-53-IA-2
    - low_complexity
    - low_disruption
    - medium_severity
    - no_direct_root_logins
    - no_reboot_needed
    - restrict_strategy

apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,
        mode: 0600
        path: /etc/securetty
        overwrite: true
Group   Verify Proper Storage and Existence of Password Hashes   Group contains 2 rules
[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   Set number of Password Hashing Rounds - system-auth   [ref]

Configure the number or rounds for the password hashing algorithm. This can be accomplished by using the rounds option for the pam_unix PAM module.

In file /etc/pam.d/system-auth append rounds=65536 to the pam_unix.so file, as shown below:
password sufficient pam_unix.so ...existing_options... rounds=65536
The system's default number of rounds is 5000.
Warning:  Setting a high number of hashing rounds makes it more difficult to brute force the password, but requires more CPU resources to authenticate users.
Rationale:
Using a higher number of rounds makes password cracking attacks more difficult.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_rounds_system_auth
Identifiers and References

Identifiers:  CCE-83386-3

References:  BP28(R32)


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


var_password_pam_unix_rounds="65536"



pamFile="/etc/pam.d/system-auth"

if grep -q "rounds=" $pamFile; then
    sed -iP --follow-symlinks "/password[[:space:]]\+sufficient[[:space:]]\+pam_unix\.so/ \
                                    s/rounds=[[:digit:]]\+/rounds=$var_password_pam_unix_rounds/" $pamFile
else
    sed -iP --follow-symlinks "/password[[:space:]]\+sufficient[[:space:]]\+pam_unix\.so/ s/$/ rounds=$var_password_pam_unix_rounds/" $pamFile
fi

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

Complexity:low
Disruption:medium
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-83386-3
    - accounts_password_pam_unix_rounds_system_auth
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
- name: XCCDF Value var_password_pam_unix_rounds # promote to variable
  set_fact:
    var_password_pam_unix_rounds: !!str 65536
  tags:
    - always

- name: Configure number of system-auth password hashing rounds in pam_unix.so
  pamd:
    name: system-auth
    type: password
    control: sufficient
    module_path: pam_unix.so
    module_arguments: rounds={{ var_password_pam_unix_rounds }}
    state: args_present
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-83386-3
    - accounts_password_pam_unix_rounds_system_auth
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed

Rule   Set number of Password Hashing Rounds - password-auth   [ref]

Configure the number or rounds for the password hashing algorithm. This can be accomplished by using the rounds option for the pam_unix PAM module.

In file /etc/pam.d/password-auth append rounds=65536 to the pam_unix.so file, as shown below:
password sufficient pam_unix.so ...existing_options... rounds=65536
The system's default number of rounds is 5000.
Warning:  Setting a high number of hashing rounds makes it more difficult to brute force the password, but requires more CPU resources to authenticate users.
Rationale:
Using a higher number of rounds makes password cracking attacks more difficult.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_rounds_password_auth
Identifiers and References

Identifiers:  CCE-83403-6

References:  BP28(R32)


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


var_password_pam_unix_rounds="65536"



pamFile="/etc/pam.d/password-auth"

if grep -q "rounds=" $pamFile; then
    sed -iP --follow-symlinks "/password[[:space:]]\+sufficient[[:space:]]\+pam_unix\.so/ \
                                    s/rounds=[[:digit:]]\+/rounds=$var_password_pam_unix_rounds/" $pamFile
else
    sed -iP --follow-symlinks "/password[[:space:]]\+sufficient[[:space:]]\+pam_unix\.so/ s/$/ rounds=$var_password_pam_unix_rounds/" $pamFile
fi

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

Complexity:low
Disruption:medium
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
    - CCE-83403-6
    - accounts_password_pam_unix_rounds_password_auth
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
- name: XCCDF Value var_password_pam_unix_rounds # promote to variable
  set_fact:
    var_password_pam_unix_rounds: !!str 65536
  tags:
    - always

- name: Configure number of password-auth password hashing rounds in pam_unix.so
  pamd:
    name: password-auth
    type: password
    control: sufficient
    module_path: pam_unix.so
    module_arguments: rounds={{ var_password_pam_unix_rounds }}
    state: args_present
  when: '"pam" in ansible_facts.packages'
  tags:
    - CCE-83403-6
    - accounts_password_pam_unix_rounds_password_auth
    - configure_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
Group   Set Password Expiration Parameters   Group contains 2 rules
[ref]   The file /etc/login.defs controls several password-related settings. Programs such as passwd, su, and login consult /etc/login.defs to determine behavior with regard to password aging, expiration warnings, and length. See the man page login.defs(5) for more information.

Users should be forced to change their passwords, in order to decrease the utility of compromised passwords. However, the need to change passwords often should be balanced against the risk that users will reuse or write down passwords if forced to change them too often. Forcing password changes every 90-360 days, depending on the environment, is recommended. Set the appropriate value as PASS_MAX_DAYS and apply it to existing accounts with the -M flag.

The PASS_MIN_DAYS (-m) setting prevents password changes for 7 days after the first change, to discourage password cycling. If you use this setting, train users to contact an administrator for an emergency password change in case a new password becomes compromised. The PASS_WARN_AGE (-W) setting gives users 7 days of warnings at login time that their passwords are about to expire.

For example, for each existing human user USER, expiration parameters could be adjusted to a 180 day maximum password age, 7 day minimum password age, and 7 day warning period with the following command:
$ sudo chage -M 180 -m 7 -W 7 USER
Group   Secure Session Configuration Files for Login Accounts   Group contains 1 group and 6 rules
[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.
Group   Ensure that Users Have Sensible Umask Values   Group contains 3 rules
[ref]   The umask setting controls the default permissions for the creation of new files. With a default umask setting of 077, files and directories created by users will not be readable by any other user on the system. Users who wish to make specific files group- or world-readable can accomplish this by using the chmod command. Additionally, users can make all their files readable to their group by default by setting a umask of 027 in their shell configuration files. If default per-user groups exist (that is, if every user has a default group whose name is the same as that user's username and whose only member is the user), then it may even be safe for users to select a umask of 007, making it very easy to intentionally share files with groups of which the user is a member.

Rule   Ensure the Default Umask is Set Correctly in /etc/profile   [ref]

To ensure the default umask controlled by /etc/profile is set properly, add or correct the umask setting in /etc/profile to read as follows:
umask 077
Rationale:
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.
Severity: 
unknown
Rule ID:xccdf_org.ssgproject.content_rule_accounts_umask_etc_profile
Identifiers and References

Identifiers:  CCE-81035-8

References:  5.4.4, CCI-000366, AC-6(1), CM-6(a), PR.IP-2, 4.3.4.3.3, APO13.01, BAI03.01, BAI03.02, BAI03.03, A.14.1.1, A.14.2.1, A.14.2.5, A.6.1.5, 18, BP28(R35), SRG-OS-000480-GPOS-00228



var_accounts_user_umask="077"



grep -q umask /etc/profile && \
  sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/profile
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/profile
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_accounts_user_umask # promote to variable
  set_fact:
    var_accounts_user_umask: !!str 077
  tags:
    - always

- name: Set user umask in /etc/profile
  replace:
    path: /etc/profile
    regexp: umask.*
    replace: umask {{ var_accounts_user_umask }}
  tags:
    - CCE-81035-8
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - accounts_umask_etc_profile
    - low_complexity
    - low_disruption
    - no_reboot_needed
    - restrict_strategy
    - unknown_severity

Rule   Ensure the Default Bash Umask is Set Correctly   [ref]

To ensure the default umask for users of the Bash shell is set properly, add or correct the umask setting in /etc/bashrc to read as follows:
umask 077
Rationale:
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_accounts_umask_etc_bashrc
Identifiers and References

Identifiers:  CCE-81036-6

References:  5.4.4, CCI-000366, AC-6(1), CM-6(a), PR.IP-2, 4.3.4.3.3, APO13.01, BAI03.01, BAI03.02, BAI03.03, A.14.1.1, A.14.2.1, A.14.2.5, A.6.1.5, 18, SRG-OS-000480-GPOS-00228, BP28(R35), RHEL-08-020353, SV-230385r599732_rule



var_accounts_user_umask="077"



grep -q umask /etc/bashrc && \
  sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/bashrc
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/bashrc
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_accounts_user_umask # promote to variable
  set_fact:
    var_accounts_user_umask: !!str 077
  tags:
    - always

- name: Set user umask in /etc/bashrc
  replace:
    path: /etc/bashrc
    regexp: umask.*
    replace: umask {{ var_accounts_user_umask }}
  tags:
    - CCE-81036-6
    - DISA-STIG-RHEL-08-020353
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - accounts_umask_etc_bashrc
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

Rule   Configure Polyinstantiation of /var/tmp Directories   [ref]

To configure polyinstantiated /tmp directories, first create the parent directories which will hold the polyinstantiation child directories. Use the following command:
$ sudo mkdir --mode 000 /var/tmp/tmp-inst
Then, add the following entry to /etc/security/namespace.conf:
/var/tmp /var/tmp/tmp-inst/    level      root,adm
Rationale:
Polyinstantiation of temporary directories is a proactive security measure which reduces chances of attacks that are made possible by /var/tmp directories being world-writable.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_accounts_polyinstantiated_var_tmp
Identifiers and References

Identifiers:  CCE-83778-1

References:  BP28(R39)


if ! [ -d /tmp-inst ] ; then
    mkdir --mode 000 /var/tmp/tmp-inst
fi
chmod 000 /var/tmp/tmp-inst
chcon --reference=/var/tmp/ /var/tmp/tmp-inst

if ! grep -Eq '^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then
    if grep -Eq '^\s*/var/tmp\s+' /etc/security/namespace.conf ; then
        sed -i '/^\s*\/var\/tmp/d' /etc/security/namespace.conf
    fi
    echo "/var/tmp /var/tmp/tmp-inst/    level      root,adm" >> /etc/security/namespace.conf
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Create /var/tmp/tmp-inst directory
  file:
    path: /var/tmp/tmp-inst
    state: directory
    mode: '000'
    seuser: system_u
    serole: object_r
    setype: tmp_t
  tags:
    - CCE-83778-1
    - accounts_polyinstantiated_var_tmp
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy

- name: Make changes to /etc/security/namespace.conf
  lineinfile:
    path: /etc/security/namespace.conf
    create: false
    regexp: ^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$
    line: /var/tmp /var/tmp/tmp-inst/    level      root,adm
    state: present
  tags:
    - CCE-83778-1
    - accounts_polyinstantiated_var_tmp
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy

Rule   Set Interactive Session Timeout   [ref]

Setting the TMOUT option in /etc/profile ensures that all user sessions will terminate based on inactivity. The TMOUT setting in /etc/profile should read as follows:
TMOUT=600
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
Identifiers and References

Identifiers:  CCE-80673-7

References:  3.1.11, CCI-002361, CCI-001133, AC-12, SC-10, AC-2(5), CM-6(a), PR.AC-7, FMT_MOF_EXT.1, SRG-OS-000163-GPOS-00072, SRG-OS-000029-GPOS-00010, SRG-OS-000163-VMM-000700, SRG-OS-000279-VMM-001010, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 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, DSS05.04, DSS05.10, DSS06.10, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, 1, 12, 15, 16, 5.5.3, BP28(R29)


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


var_accounts_tmout="600"



if grep --silent '^\s*TMOUT' /etc/profile ; then
        sed -i -E "s/^(\s*)TMOUT\s*=\s*(\w|\$)*(.*)$/\1TMOUT=$var_accounts_tmout\3/g" /etc/profile
else
        echo -e "\n# Set TMOUT to $var_accounts_tmout per security requirements" >> /etc/profile
        echo "TMOUT=$var_accounts_tmout" >> /etc/profile
fi

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

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

- name: Set Interactive Session Timeout
  block:

    - name: Deduplicate values from /etc/profile
      lineinfile:
        path: /etc/profile
        create: false
        regexp: ^\s*TMOUT=
        state: absent

    - name: Check if /etc/profile.d exists
      stat:
        path: /etc/profile.d
      register: _etc_profile_d_exists

    - name: Check if the parameter TMOUT is present in /etc/profile.d
      find:
        paths: /etc/profile.d
        recurse: 'yes'
        follow: 'no'
        contains: ^\s*TMOUT=
      register: _etc_profile_d_has_parameter
      when: _etc_profile_d_exists.stat.isdir is defined and _etc_profile_d_exists.stat.isdir

    - name: Remove parameter from files in /etc/profile.d
      lineinfile:
        path: '{{ item.path }}'
        create: false
        regexp: ^\s*TMOUT=
        state: absent
      with_items: '{{ _etc_profile_d_has_parameter.files }}'
      when: _etc_profile_d_has_parameter.matched

    - name: Insert correct line to /etc/profile
      lineinfile:
        path: /etc/profile
        create: true
        line: TMOUT={{ var_accounts_tmout }}
        state: present
        validate: bash -n %s
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80673-7
    - 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
    - accounts_tmout
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

Rule   Configure Polyinstantiation of /tmp Directories   [ref]

To configure polyinstantiated /tmp directories, first create the parent directories which will hold the polyinstantiation child directories. Use the following command:
$ sudo mkdir --mode 000 /tmp/tmp-inst
Then, add the following entry to /etc/security/namespace.conf:
/tmp     /tmp/tmp-inst/            level      root,adm
Rationale:
Polyinstantiation of temporary directories is a proactive security measure which reduces chances of attacks that are made possible by /tmp directories being world-writable.
Severity: 
low
Rule ID:xccdf_org.ssgproject.content_rule_accounts_polyinstantiated_tmp
Identifiers and References

Identifiers:  CCE-83732-8

References:  BP28(R39)


if ! [ -d /tmp/tmp-inst ] ; then
    mkdir --mode 000 /tmp/tmp-inst
fi
chmod 000 /tmp/tmp-inst
chcon --reference=/tmp /tmp/tmp-inst

if ! grep -Eq '^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then
    if grep -Eq '^\s*/tmp\s+' /etc/security/namespace.conf ; then
        sed -i '/^\s*\/tmp/d' /etc/security/namespace.conf
    fi
    echo "/tmp     /tmp/tmp-inst/        level      root,adm" >> /etc/security/namespace.conf
fi

Complexity:low
Disruption:low
Strategy:restrict
- name: Create /tmp/tmp-inst directory
  file:
    path: /tmp/tmp-inst
    state: directory
    mode: '000'
    seuser: system_u
    serole: object_r
    setype: tmp_t
  tags:
    - CCE-83732-8
    - accounts_polyinstantiated_tmp
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy

- name: Make changes to /etc/security/namespace.conf
  lineinfile:
    path: /etc/security/namespace.conf
    create: false
    regexp: ^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$
    line: /tmp     /tmp/tmp-inst/        level      root,adm
    state: present
  tags:
    - CCE-83732-8
    - accounts_polyinstantiated_tmp
    - low_complexity
    - low_disruption
    - low_severity
    - no_reboot_needed
    - restrict_strategy
Group   System Accounting with auditd   Group contains 2 groups and 1 rule
[ref]   The audit service provides substantial capabilities for recording system activities. By default, the service audits about SELinux AVC denials and certain types of security-relevant events such as system logins, account modifications, and authentication events performed by programs such as sudo. Under its default configuration, auditd has modest disk space requirements, and should not noticeably impact system performance.

NOTE: The Linux Audit daemon auditd can be configured to use the augenrules program to read audit rules files (*.rules) located in /etc/audit/rules.d location and compile them to create the resulting form of the /etc/audit/audit.rules configuration file during the daemon startup (default configuration). Alternatively, the auditd daemon can use the auditctl utility to read audit rules from the /etc/audit/audit.rules configuration file during daemon startup, and load them into the kernel. The expected behavior is configured via the appropriate ExecStartPost directive setting in the /usr/lib/systemd/system/auditd.service configuration file. To instruct the auditd daemon to use the augenrules program to read audit rules (default configuration), use the following setting:
ExecStartPost=-/sbin/augenrules --load
in the /usr/lib/systemd/system/auditd.service configuration file. In order to instruct the auditd daemon to use the auditctl utility to read audit rules, use the following setting:
ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules
in the /usr/lib/systemd/system/auditd.service configuration file. Refer to [Service] section of the /usr/lib/systemd/system/auditd.service configuration file for further details.

Government networks often have substantial auditing requirements and auditd can be configured to meet these requirements. Examining some example audit records demonstrates how the Linux audit system satisfies common requirements. The following example from Fedora Documentation available at https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/sect-Security-Enhanced_Linux-Troubleshooting-Fixing_Problems.html#sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages shows the substantial amount of information captured in a two typical "raw" audit messages, followed by a breakdown of the most important fields. In this example the message is SELinux-related and reports an AVC denial (and the associated system call) that occurred when the Apache HTTP Server attempted to access the /var/www/html/file1 file (labeled with the samba_share_t type):
type=AVC msg=audit(1226874073.147:96): avc:  denied  { getattr } for pid=2465 comm="httpd"
path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file

type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13
a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48
gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd"
exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  • msg=audit(1226874073.147:96)
    • The number in parentheses is the unformatted time stamp (Epoch time) for the event, which can be converted to standard time by using the date command.
  • { getattr }
    • The item in braces indicates the permission that was denied. getattr indicates the source process was trying to read the target file's status information. This occurs before reading files. This action is denied due to the file being accessed having the wrong label. Commonly seen permissions include getattr, read, and write.
  • comm="httpd"
    • The executable that launched the process. The full path of the executable is found in the exe= section of the system call (SYSCALL) message, which in this case, is exe="/usr/sbin/httpd".
  • path="/var/www/html/file1"
    • The path to the object (target) the process attempted to access.
  • scontext="unconfined_u:system_r:httpd_t:s0"
    • The SELinux context of the process that attempted the denied action. In this case, it is the SELinux context of the Apache HTTP Server, which is running in the httpd_t domain.
  • tcontext="unconfined_u:object_r:samba_share_t:s0"
    • The SELinux context of the object (target) the process attempted to access. In this case, it is the SELinux context of file1. Note: the samba_share_t type is not accessible to processes running in the httpd_t domain.
  • From the system call (SYSCALL) message, two items are of interest:
    • success=no: indicates whether the denial (AVC) was enforced or not. success=no indicates the system call was not successful (SELinux denied access). success=yes indicates the system call was successful - this can be seen for permissive domains or unconfined domains, such as initrc_t and kernel_t.
    • exe="/usr/sbin/httpd": the full path to the executable that launched the process, which in this case, is exe="/usr/sbin/httpd".
Group   Configure auditd Rules for Comprehensive Auditing   Group contains 1 group and 1 rule
[ref]   The auditd program can perform comprehensive monitoring of system activity. This section describes recommended configuration settings for comprehensive auditing, but a full description of the auditing system's capabilities is beyond the scope of this guide. The mailing list linux-audit@redhat.com exists to facilitate community discussion of the auditing system.

The audit subsystem supports extensive collection of events, including:
  • Tracing of arbitrary system calls (identified by name or number) on entry or exit.
  • Filtering by PID, UID, call success, system call argument (with some limitations), etc.
  • Monitoring of specific files for modifications to the file's contents or metadata.

Auditing rules at startup are controlled by the file /etc/audit/audit.rules. Add rules to it to meet the auditing requirements for your organization. Each line in /etc/audit/audit.rules represents a series of arguments that can be passed to auditctl and can be individually tested during runtime. See documentation in /usr/share/doc/audit-VERSION and in the related man pages for more details.

If copying any example audit rulesets from /usr/share/doc/audit-VERSION, be sure to comment out the lines containing arch= which are not appropriate for your system's architecture. Then review and understand the following rules, ensuring rules are activated as needed for the appropriate architecture.

After reviewing all the rules, reading the following sections, and editing as needed, the new rules can be activated as follows:
$ sudo service auditd restart
Group   Record Information on the Use of Privileged Commands   Group contains 1 rule
[ref]   At a minimum, the audit system should collect the execution of privileged commands for all users and root.

Rule   Ensure auditd Collects Information on the Use of Privileged Commands - sudo   [ref]

At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/sudo -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/sudo -F auid>=1000 -F auid!=unset -F key=privileged
Rationale:
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_sudo
Identifiers and References

Identifiers:  CCE-80737-0

References:  3.1.7, CCI-000130, CCI-000135, CCI-000172, CCI-002884, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1, FAU_GEN.1.1.c, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000471-VMM-001910, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2, 4.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, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2, 1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9, BP28(R19)


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



PATTERN="-a always,exit -F path=/usr/bin/sudo\\s\\+.*"
GROUP="privileged"
# Although the fix doesn't use ARCH, we reset it because it could have been set by some other remediation
ARCH=""
FULL_RULE="-a always,exit -F path=/usr/bin/sudo -F auid>=1000 -F auid!=unset -F key=privileged"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
#   https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool				tool used to load audit rules,
# 					either 'auditctl', or 'augenrules
# * audit rules' pattern		audit rule skeleton for same syscall
# * syscall group			greatest common string this rule shares
# 					with other rules from the same group
# * architecture			architecture this rule is intended for
# * full form of new rule to add	expected full form of audit rule as to be
# 					added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
#	See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
	echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
	echo "Aborting."
	exit 1
fi

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
# 
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

retval=0

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
	echo "Unknown audit rules loading tool: $1. Aborting."
	echo "Use either 'auditctl' or 'augenrules'!"
	return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
	files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
	# Extract audit $key from audit rule so we can use it later
	matches=()
	key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
	readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
	if [ $? -ne 0 ]
	then
		retval=1
	fi
	for match in "${matches[@]}"
	do
		files_to_inspect+=("${match}")
	done
	# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
	if [ ${#files_to_inspect[@]} -eq "0" ]
	then
		file_to_inspect="/etc/audit/rules.d/$key.rules"
		files_to_inspect=("$file_to_inspect")
		if [ ! -e "$file_to_inspect" ]
		then
			touch "$file_to_inspect"
			chmod 0640 "$file_to_inspect"
		fi
	fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do
	# Filter existing $audit_file rules' definitions to select those that:
	# * follow the rule pattern, and
	# * meet the hardware architecture requirement, and
	# * are current syscall group specific
	readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file")
	if [ $? -ne 0 ]
	then
		retval=1
	fi

	# Process rules found case-by-case
	for rule in "${existing_rules[@]}"
	do
		# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
		if [ "${rule}" != "${full_rule}" ]
		then
			# If so, isolate just '(-S \w)+' substring of that rule
			rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
			# Check if list of '-S syscall' arguments of that rule is subset
			# of '-S syscall' list of expected $full_rule
			if grep -q -- "$rule_syscalls" <<< "$full_rule"
			then
				# Rule is covered (i.e. the list of -S syscalls for this rule is
				# subset of -S syscalls of $full_rule => existing rule can be deleted
				# Thus delete the rule from audit.rules & our array
				sed -i -e "\;${rule};d" "$audit_file"
				if [ $? -ne 0 ]
				then
					retval=1
				fi
				existing_rules=("${existing_rules[@]//$rule/}")
			else
				# Rule isn't covered by $full_rule - it besides -S syscall arguments
				# for this group contains also -S syscall arguments for other syscall
				# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
				# since 'lchown' & 'fchownat' share 'chown' substring
				# Therefore:
				# * 1) delete the original rule from audit.rules
				# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
				# * 2) delete the -S syscall arguments for this syscall group, but
				# keep those not belonging to this syscall group
				# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
				# * 3) append the modified (filtered) rule again into audit.rules
				# if the same rule not already present
				#
				# 1) Delete the original rule
				sed -i -e "\;${rule};d" "$audit_file"
				if [ $? -ne 0 ]
				then
					retval=1
				fi

				# 2) Delete syscalls for this group, but keep those from other groups
				# Convert current rule syscall's string into array splitting by '-S' delimiter
				IFS_BKP="$IFS"
				IFS=$'-S'
				read -a rule_syscalls_as_array <<< "$rule_syscalls"
				# Reset IFS back to default
				IFS="$IFS_BKP"
				# Splitting by "-S" can't be replaced by the readarray functionality easily

				# Declare new empty string to hold '-S syscall' arguments from other groups
				new_syscalls_for_rule=''
				# Walk through existing '-S syscall' arguments
				for syscall_arg in "${rule_syscalls_as_array[@]}"
				do
					# Skip empty $syscall_arg values
					if [ "$syscall_arg" == '' ]
					then
						continue
					fi
					# If the '-S syscall' doesn't belong to current group add it to the new list
					# (together with adding '-S' delimiter back for each of such item found)
					if grep -q -v -- "$group" <<< "$syscall_arg"
					then
						new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
					fi
				done
				# Replace original '-S syscall' list with the new one for this rule
				updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
				# Squeeze repeated whitespace characters in rule definition (if any) into one
				updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
				# 3) Append the modified / filtered rule again into audit.rules
				#    (but only in case it's not present yet to prevent duplicate definitions)
				if ! grep -q -- "$updated_rule" "$audit_file"
				then
					echo "$updated_rule" >> "$audit_file"
				fi
			fi
		else
			# $audit_file already contains the expected rule form for this
			# architecture & key => don't insert it second time
			append_expected_rule=1
		fi
	done

	# We deleted all rules that were subset of the expected one for this arch & key.
	# Also isolated rules containing system calls not from this system calls group.
	# Now append the expected rule if it's not present in $audit_file yet
	if [[ ${append_expected_rule} -eq "0" ]]
	then
		echo "$full_rule" >> "$audit_file"
	fi
done

return $retval

}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"

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

Complexity:low
Disruption:low
Strategy:restrict
- name: Search /etc/audit/rules.d for audit rule entries
  find:
    paths: /etc/audit/rules.d
    recurse: false
    contains: ^.*path=/usr/bin/sudo.*$
    patterns: '*.rules'
  register: find_sudo
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80737-0
    - NIST-800-171-3.1.7
    - NIST-800-53-AC-6(9)
    - NIST-800-53-AU-12(c)
    - NIST-800-53-AU-2(d)
    - NIST-800-53-CM-6(a)
    - audit_rules_privileged_commands_sudo
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Use /etc/audit/rules.d/privileged.rules as the recipient for the rule
  set_fact:
    all_files:
      - /etc/audit/rules.d/privileged.rules
  when:
    - find_sudo.matched is defined and find_sudo.matched == 0
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80737-0
    - NIST-800-171-3.1.7
    - NIST-800-53-AC-6(9)
    - NIST-800-53-AU-12(c)
    - NIST-800-53-AU-2(d)
    - NIST-800-53-CM-6(a)
    - audit_rules_privileged_commands_sudo
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Use matched file as the recipient for the rule
  set_fact:
    all_files:
      - '{{ find_sudo.files | map(attribute=''path'') | list | first }}'
  when:
    - find_sudo.matched is defined and find_sudo.matched > 0
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80737-0
    - NIST-800-171-3.1.7
    - NIST-800-53-AC-6(9)
    - NIST-800-53-AU-12(c)
    - NIST-800-53-AU-2(d)
    - NIST-800-53-CM-6(a)
    - audit_rules_privileged_commands_sudo
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Inserts/replaces the sudo rule in rules.d
  lineinfile:
    path: '{{ all_files[0] }}'
    line: -a always,exit -F path=/usr/bin/sudo -F auid>=1000 -F auid!=unset -F key=privileged
    create: true
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80737-0
    - NIST-800-171-3.1.7
    - NIST-800-53-AC-6(9)
    - NIST-800-53-AU-12(c)
    - NIST-800-53-AU-2(d)
    - NIST-800-53-CM-6(a)
    - audit_rules_privileged_commands_sudo
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Inserts/replaces the sudo rule in audit.rules
  lineinfile:
    path: /etc/audit/audit.rules
    line: -a always,exit -F path=/usr/bin/sudo -F auid>=1000 -F auid!=unset -F key=privileged
    create: true
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80737-0
    - NIST-800-171-3.1.7
    - NIST-800-53-AC-6(9)
    - NIST-800-53-AU-12(c)
    - NIST-800-53-AU-2(d)
    - NIST-800-53-CM-6(a)
    - audit_rules_privileged_commands_sudo
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy
Group   File Permissions and Masks   Group contains 7 groups and 43 rules
[ref]   Traditional Unix security relies heavily on file and directory permissions to prevent unauthorized users from reading or modifying files to which they should not have access.

Several of the commands in this section search filesystems for files or directories with certain characteristics, and are intended to be run on every local partition on a given system. When the variable PART appears in one of the commands below, it means that the command is intended to be run repeatedly, with the name of each local partition substituted for PART in turn.

The following command prints a list of all xfs partitions on the local system, which is the default filesystem for Red Hat Enterprise Linux 8 installations:
$ mount -t xfs | awk '{print $3}'
For any systems that use a different local filesystem type, modify this command as appropriate.
Group   Verify Permissions on Important Files and Directories   Group contains 1 group and 13 rules
[ref]   Permissions for many files on a system must be set restrictively to ensure sensitive information is properly protected. This section discusses important permission restrictions which can be verified to ensure that no harmful discrepancies have arisen.
Group   Verify Permissions on Files with Local Account Information and Credentials   Group contains 6 rules

Rule   Verify Permissions on shadow File   [ref]

To properly set the permissions of /etc/shadow, run the command:
$ sudo chmod 0000 /etc/shadow
Rationale:
The /etc/shadow file contains the list of local system accounts and stores password hashes. Protection of this file is critical for system security. Failure to give ownership of this file to root provides the designated owner with access to sensitive information which could weaken the system security posture.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_etc_shadow
Identifiers and References

Identifiers:  CCE-80813-9

References:  BP28(R36), 6.1.3, 5.5.2.2, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5


Complexity:low
Disruption:low
Strategy:configure


chmod 0000 /etc/shadow

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/shadow
  stat:
    path: /etc/shadow
  register: file_exists
  tags:
    - CCE-80813-9
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_shadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure permission 0000 on /etc/shadow
  file:
    path: /etc/shadow
    mode: '0000'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80813-9
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_shadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Verify User Who Owns shadow File   [ref]

To properly set the owner of /etc/shadow, run the command:
$ sudo chown root /etc/shadow 
Rationale:
The /etc/shadow file contains the list of local system accounts and stores password hashes. Protection of this file is critical for system security. Failure to give ownership of this file to root provides the designated owner with access to sensitive information which could weaken the system security posture.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_owner_etc_shadow
Identifiers and References

Identifiers:  CCE-80804-8

References:  6.1.3, 5.5.2.2, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, BP28(R36)


Complexity:low
Disruption:low
Strategy:configure


chown 0 /etc/shadow

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/shadow
  stat:
    path: /etc/shadow
  register: file_exists
  tags:
    - CCE-80804-8
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_owner_etc_shadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure owner 0 on /etc/shadow
  file:
    path: /etc/shadow
    owner: '0'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80804-8
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_owner_etc_shadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Verify User Who Owns gshadow File   [ref]

To properly set the owner of /etc/gshadow, run the command:
$ sudo chown root /etc/gshadow 
Rationale:
The /etc/gshadow file contains group password hashes. Protection of this file is critical for system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_owner_etc_gshadow
Identifiers and References

Identifiers:  CCE-80802-2

References:  6.1.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, BP28(R36)


Complexity:low
Disruption:low
Strategy:configure


chown 0 /etc/gshadow

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/gshadow
  stat:
    path: /etc/gshadow
  register: file_exists
  tags:
    - CCE-80802-2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - configure_strategy
    - file_owner_etc_gshadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure owner 0 on /etc/gshadow
  file:
    path: /etc/gshadow
    owner: '0'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80802-2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - configure_strategy
    - file_owner_etc_gshadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Verify Permissions on passwd File   [ref]

To properly set the permissions of /etc/passwd, run the command:
$ sudo chmod 0644 /etc/passwd
Rationale:
If the /etc/passwd file is writable by a group-owner or the world the risk of its compromise is increased. The file contains the list of accounts on the system and associated information, and protection of this file is critical for system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_etc_passwd
Identifiers and References

Identifiers:  CCE-80812-1

References:  6.1.2, 5.5.2.2, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, BP28(R36)


Complexity:low
Disruption:low
Strategy:configure


chmod 0644 /etc/passwd

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/passwd
  stat:
    path: /etc/passwd
  register: file_exists
  tags:
    - CCE-80812-1
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_passwd
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure permission 0644 on /etc/passwd
  file:
    path: /etc/passwd
    mode: '0644'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80812-1
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_passwd
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Verify Permissions on gshadow File   [ref]

To properly set the permissions of /etc/gshadow, run the command:
$ sudo chmod 0000 /etc/gshadow
Rationale:
The /etc/gshadow file contains group password hashes. Protection of this file is critical for system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_etc_gshadow
Identifiers and References

Identifiers:  CCE-80811-3

References:  BP28(R36), 6.1.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5


Complexity:low
Disruption:low
Strategy:configure


chmod 0000 /etc/gshadow

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/gshadow
  stat:
    path: /etc/gshadow
  register: file_exists
  tags:
    - CCE-80811-3
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - configure_strategy
    - file_permissions_etc_gshadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure permission 0000 on /etc/gshadow
  file:
    path: /etc/gshadow
    mode: '0000'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80811-3
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - configure_strategy
    - file_permissions_etc_gshadow
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Verify Permissions on group File   [ref]

To properly set the permissions of /etc/passwd, run the command:
$ sudo chmod 0644 /etc/passwd
Rationale:
The /etc/group file contains information regarding groups that are configured on the system. Protection of this file is important for system security.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_etc_group
Identifiers and References

Identifiers:  CCE-80810-5

References:  6.1.4, 5.5.2.2, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, BP28(R36)


Complexity:low
Disruption:low
Strategy:configure


chmod 0644 /etc/group

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/group
  stat:
    path: /etc/group
  register: file_exists
  tags:
    - CCE-80810-5
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_group
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

- name: Ensure permission 0644 on /etc/group
  file:
    path: /etc/group
    mode: '0644'
  when: file_exists.stat is defined and file_exists.stat.exists
  tags:
    - CCE-80810-5
    - CJIS-5.5.2.2
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - PCI-DSS-Req-8.7.c
    - configure_strategy
    - file_permissions_etc_group
    - low_complexity
    - low_disruption
    - medium_severity
    - no_reboot_needed

Rule   Ensure No World-Writable Files Exist   [ref]

It is generally a good idea to remove global (other) write access to a file when it is discovered. However, check with documentation for specific applications before making changes. Also, monitor for recurring world-writable files, as these may be symptoms of a misconfigured application or user account. Finally, this applies to real files and not virtual files that are a part of pseudo file systems such as sysfs or procfs.
Rationale:
Data in world-writable files can be modified by any user on the system. In almost all circumstances, files can be configured using a combination of user and group permissions to support whatever legitimate access is needed without the risk caused by world-writable files.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable
Identifiers and References

Identifiers:  CCE-80818-8

References:  6.1.10, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, BP28(R40)



find / -xdev -type f -perm -002 -exec chmod o-w {} \;

Rule   Verify that All World-Writable Directories Have Sticky Bits Set   [ref]

When the so-called 'sticky bit' is set on a directory, only the owner of a given file may remove that file from the directory. Without the sticky bit, any user with write access to a directory may remove any file in the directory. Setting the sticky bit prevents users from removing each other's files. In cases where there is no reason for a directory to be world-writable, a better solution is to remove that permission rather than to set the sticky bit. However, if a directory is used by a particular application, consult that application's documentation instead of blindly changing modes.
To set the sticky bit on a world-writable directory DIR, run the following command:
$ sudo chmod +t DIR
Rationale:
Failing to set the sticky bit on public directories allows unauthorized users to delete files in the directory structure.

The only authorized public directories are those temporary directories supplied with the system, or those designed to be temporary file repositories. The setting is normally reserved for directories used by the system, by users for temporary file storage (such as /tmp), and for directories requiring global read/write access.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits
Identifiers and References

Identifiers:  CCE-80783-4

References:  1.1.21, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5, SRG-OS-000138-GPOS-00069, BP28(R40), RHEL-08-010190, SV-230243r599732_rule


df --local -P | awk '{if (NR!=1) print $6}' \
| xargs -I '{}' find '{}' -xdev -type d \
\( -perm -0002 -a ! -perm -1000 \) 2>/dev/null \
| xargs chmod a+t

Rule   Ensure All SGID Executables Are Authorized   [ref]

The SGID (set group id) bit should be set only on files that were installed via authorized means. A straightforward means of identifying unauthorized SGID files is determine if any were not installed as part of an RPM package, which is cryptographically verified. Investigate the origin of any unpackaged SGID files. This configuration check considers authorized SGID files which were installed via RPM. It is assumed that when an individual has sudo access to install an RPM and all packages are signed with an organizationally-recognized GPG key, the software should be considered an approved package on the system. Any SGID file not deployed through an RPM will be flagged for further review.
Rationale:
Executable files with the SGID permission run with the privileges of the owner of the file. SGID files of uncertain provenance could allow for unprivileged users to elevate privileges. The presence of these files should be strictly controlled on the system.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_sgid
Identifiers and References

Identifiers:  CCE-80816-2

References:  BP28(R37), BP28(R38), 6.1.14, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5

Rule   Ensure All World-Writable Directories Are Owned by root user   [ref]

All directories in local partitions which are world-writable should be owned by root. If any world-writable directories are not owned by root, this should be investigated. Following this, the files should be deleted or assigned to root user.
Rationale:
Allowing a user account to own a world-writable directory is undesirable because it allows the owner of that directory to remove or replace any files that may be placed in the directory by other users.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_dir_perms_world_writable_root_owned
Identifiers and References

Identifiers:  CCE-83375-6

References:  BP28(R40), SRG-OS-000480-GPOS-00227, RHEL-08-010700, SV-230318r599732_rule


#!/bin/bash

find / -not -fstype afs -not -fstype ceph -not -fstype cifs -not -fstype smb3 -not -fstype smbfs -not -fstype sshfs -not -fstype ncpfs -not -fstype ncp -not -fstype nfs -not -fstype nfs4 -not -fstype gfs -not -fstype gfs2 -not -fstype glusterfs -not -fstype gpfs -not -fstype pvfs2 -not -fstype ocfs2 -not -fstype lustre -not -fstype davfs -not -fstype fuse.sshfs -type d -perm -0002 -uid +0 -exec chown root {} \;

Complexity:low
Disruption:medium
Strategy:restrict
- name: Configure excluded (non local) file systems
  set_fact:
    excluded_fstypes:
      - afs
      - ceph
      - cifs
      - smb3
      - smbfs
      - sshfs
      - ncpfs
      - ncp
      - nfs
      - nfs4
      - gfs
      - gfs2
      - glusterfs
      - gpfs
      - pvfs2
      - ocfs2
      - lustre
      - davfs
      - fuse.sshfs
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Create empty list of excluded paths
  set_fact:
    excluded_paths: []
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Detect nonlocal file systems and add them to excluded paths
  set_fact:
    excluded_paths: '{{ excluded_paths | union([item.mount]) }}'
  loop: '{{ ansible_mounts }}'
  when: item.fstype in excluded_fstypes
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Find all directories excluding non-local partitions
  find:
    paths: /
    excludes: excluded_paths
    file_type: directory
    hidden: true
    recurse: true
  register: found_dirs
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Create list of world writable directories
  set_fact:
    world_writable_dirs: '{{ found_dirs.files | selectattr(''woth'') | list }}'
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

- name: Change owner to root on directories which are world writable
  file:
    path: '{{ item.path }}'
    owner: root
  loop: '{{ world_writable_dirs }}'
  ignore_errors: true
  tags:
    - CCE-83375-6
    - DISA-STIG-RHEL-08-010700
    - dir_perms_world_writable_root_owned
    - low_complexity
    - medium_disruption
    - medium_severity
    - no_reboot_needed
    - restrict_strategy

Rule   Ensure All SUID Executables Are Authorized   [ref]

The SUID (set user id) bit should be set only on files that were installed via authorized means. A straightforward means of identifying unauthorized SUID files is determine if any were not installed as part of an RPM package, which is cryptographically verified. Investigate the origin of any unpackaged SUID files. This configuration check considers authorized SUID files which were installed via RPM. It is assumed that when an individual has sudo access to install an RPM and all packages are signed with an organizationally-recognized GPG key, the software should be considered an approved package on the system. Any SUID file not deployed through an RPM will be flagged for further review.
Rationale:
Executable files with the SUID permission run with the privileges of the owner of the file. SUID files of uncertain provenance could allow for unprivileged users to elevate privileges. The presence of these files should be strictly controlled on the system.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_suid
Identifiers and References

Identifiers:  CCE-80817-0

References:  BP28(R37), BP28(R38), 6.1.13, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, SR 2.1, SR 5.2, 4.3.3.7.3, APO01.06, DSS05.04, DSS05.07, DSS06.02, A.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, 12, 13, 14, 15, 16, 18, 3, 5

Group   Restrict Partition Mount Options   Group contains 15 rules
[ref]   System partitions can be mounted with certain options that limit what files on those partitions can do. These options are set in the /etc/fstab configuration file, and can be used to make certain types of malicious behavior more difficult.

Rule   Add nosuid Option to /boot   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /boot. The SUID and SGID permissions should not be required on the boot partition. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /boot.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from boot partitions.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_boot_nosuid
Identifiers and References

Identifiers:  CCE-81033-3

References:  CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SRG-OS-000368-GPOS-00154, BP28(R12), RHEL-08-010571, SV-230300r599732_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /boot || { echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/boot" "nosuid" "" ""

	ensure_partition_is_mounted "/boot"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/boot'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81033-3
    - DISA-STIG-RHEL-08-010571
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81033-3
    - DISA-STIG-RHEL-08-010571
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /boot options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81033-3
    - DISA-STIG-RHEL-08-010571
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_nosuid
    - no_reboot_needed

- name: Ensure /boot is mounted with nosuid option
  mount:
    path: /boot
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81033-3
    - DISA-STIG-RHEL-08-010571
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /boot --mountoptions="nosuid"

Rule   Add noexec Option to /home   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /home. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /home.
Rationale:
The /home directory contains data of individual users. Binaries in this directory should not be considered as trusted and users should not be able to execute them.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_home_noexec
Identifiers and References

Identifiers:  CCE-83328-5

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /home || { echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/home" "noexec" "" ""

	ensure_partition_is_mounted "/home"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/home'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83328-5
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83328-5
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /home options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83328-5
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_noexec
    - no_reboot_needed

- name: Ensure /home is mounted with noexec option
  mount:
    path: /home
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83328-5
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /home --mountoptions="noexec"

Rule   Add nosuid Option to /srv   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /srv. The SUID and SGID permissions should not be required in this directory. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /srv.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. The /srv directory contains files served by various network services such as FTP. Users should not be able to execute SUID or SGID binaries from this directory.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_srv_nosuid
Identifiers and References

Identifiers:  CCE-83322-8

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /srv || { echo "Not remediating, because there is no record of /srv in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/srv" "nosuid" "" ""

	ensure_partition_is_mounted "/srv"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/srv'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83322-8
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_srv_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83322-8
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_srv_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /srv options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83322-8
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_srv_nosuid
    - no_reboot_needed

- name: Ensure /srv is mounted with nosuid option
  mount:
    path: /srv
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83322-8
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_srv_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /srv --mountoptions="nosuid"

Rule   Add nosuid Option to /var/log   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /var/log. The SUID and SGID permissions should not be required in directories containing log files. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var/log.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from partitions designated for log files.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_log_nosuid
Identifiers and References

Identifiers:  CCE-82065-4

References:  CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SRG-OS-000368-GPOS-00154, BP28(R12), RHEL-08-040127, SV-230515r599802_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var/log || { echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var/log" "nosuid" "" ""

	ensure_partition_is_mounted "/var/log"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var/log'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82065-4
    - DISA-STIG-RHEL-08-040127
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82065-4
    - DISA-STIG-RHEL-08-040127
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /var/log options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82065-4
    - DISA-STIG-RHEL-08-040127
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_nosuid
    - no_reboot_needed

- name: Ensure /var/log is mounted with nosuid option
  mount:
    path: /var/log
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82065-4
    - DISA-STIG-RHEL-08-040127
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /var/log --mountoptions="nosuid"

Rule   Add nosuid Option to /tmp   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /tmp. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /tmp.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_tmp_nosuid
Identifiers and References

Identifiers:  CCE-82140-5

References:  1.1.4, CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SR 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, 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, 4.3.4.3.2, 4.3.4.3.3, APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2, 11, 13, 14, 3, 8, 9, BP28(R12), SRG-OS-000368-GPOS-00154, RHEL-08-040124, SV-230512r599732_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /tmp || { echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/tmp" "nosuid" "" ""

	ensure_partition_is_mounted "/tmp"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/tmp'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82140-5
    - DISA-STIG-RHEL-08-040124
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82140-5
    - DISA-STIG-RHEL-08-040124
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /tmp options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82140-5
    - DISA-STIG-RHEL-08-040124
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_nosuid
    - no_reboot_needed

- name: Ensure /tmp is mounted with nosuid option
  mount:
    path: /tmp
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82140-5
    - DISA-STIG-RHEL-08-040124
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /tmp --mountoptions="nosuid"

Rule   Add noexec Option to /tmp   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /tmp. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /tmp.
Rationale:
Allowing users to execute binaries from world-writable directories such as /tmp should never be necessary in normal operation and can expose the system to potential compromise.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_tmp_noexec
Identifiers and References

Identifiers:  CCE-82139-7

References:  1.1.5, CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SR 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, 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, 4.3.4.3.2, 4.3.4.3.3, APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2, 11, 13, 14, 3, 8, 9, BP28(R12), SRG-OS-000368-GPOS-00154, RHEL-08-040125, SV-230513r599800_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /tmp || { echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/tmp" "noexec" "" ""

	ensure_partition_is_mounted "/tmp"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/tmp'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82139-7
    - DISA-STIG-RHEL-08-040125
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82139-7
    - DISA-STIG-RHEL-08-040125
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /tmp options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82139-7
    - DISA-STIG-RHEL-08-040125
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_noexec
    - no_reboot_needed

- name: Ensure /tmp is mounted with noexec option
  mount:
    path: /tmp
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82139-7
    - DISA-STIG-RHEL-08-040125
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_tmp_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /tmp --mountoptions="noexec"

Rule   Add noexec Option to /var/log   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /var/log. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var/log.
Rationale:
Allowing users to execute binaries from directories containing log files such as /var/log should never be necessary in normal operation and can expose the system to potential compromise.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_log_noexec
Identifiers and References

Identifiers:  CCE-82008-4

References:  CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SRG-OS-000368-GPOS-00154, BP28(R12), RHEL-08-040128, SV-230516r599803_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var/log || { echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var/log" "noexec" "" ""

	ensure_partition_is_mounted "/var/log"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var/log'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82008-4
    - DISA-STIG-RHEL-08-040128
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82008-4
    - DISA-STIG-RHEL-08-040128
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /var/log options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82008-4
    - DISA-STIG-RHEL-08-040128
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_noexec
    - no_reboot_needed

- name: Ensure /var/log is mounted with noexec option
  mount:
    path: /var/log
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82008-4
    - DISA-STIG-RHEL-08-040128
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_log_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /var/log --mountoptions="noexec"

Rule   Add nosuid Option to /home   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /home. The SUID and SGID permissions should not be required in these user data directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /home.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from user home directory partitions.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_home_nosuid
Identifiers and References

Identifiers:  CCE-81050-7

References:  CCI-000366, 1.1.3, CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-2, PR.PT-3, SR 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, 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, 4.3.4.3.2, 4.3.4.3.3, APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2, 11, 13, 14, 3, 8, 9, BP28(R12), SRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00227, RHEL-08-010570, SV-230299r599732_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /home || { echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/home" "nosuid" "" ""

	ensure_partition_is_mounted "/home"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/home'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81050-7
    - DISA-STIG-RHEL-08-010570
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81050-7
    - DISA-STIG-RHEL-08-010570
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /home options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81050-7
    - DISA-STIG-RHEL-08-010570
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_nosuid
    - no_reboot_needed

- name: Ensure /home is mounted with nosuid option
  mount:
    path: /home
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-81050-7
    - DISA-STIG-RHEL-08-010570
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_home_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /home --mountoptions="nosuid"

Rule   Add noexec Option to /var   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /var. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var.
Rationale:
The /var directory contains variable system data such as logs, mails and caches. No binaries should be executed from this directory.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_noexec
Identifiers and References

Identifiers:  CCE-83330-1

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var || { echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var" "noexec" "" ""

	ensure_partition_is_mounted "/var"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83330-1
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83330-1
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /var options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83330-1
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_noexec
    - no_reboot_needed

- name: Ensure /var is mounted with noexec option
  mount:
    path: /var
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83330-1
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /var --mountoptions="noexec"

Rule   Add nosuid Option to /opt   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /opt. The SUID and SGID permissions should not be required in this directory. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /opt.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. The /opt directory contains additional software packages. Users should not be able to execute SUID or SGID binaries from this directory.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_opt_nosuid
Identifiers and References

Identifiers:  CCE-83319-4

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /opt || { echo "Not remediating, because there is no record of /opt in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/opt" "nosuid" "" ""

	ensure_partition_is_mounted "/opt"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/opt'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83319-4
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_opt_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83319-4
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_opt_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /opt options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83319-4
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_opt_nosuid
    - no_reboot_needed

- name: Ensure /opt is mounted with nosuid option
  mount:
    path: /opt
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83319-4
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_opt_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /opt --mountoptions="nosuid"

Rule   Add noexec Option to /boot   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /boot. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /boot.
Rationale:
The /boot partition contains the kernel and the bootloader. No binaries should be executed from this partition after the booting process finishes.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_boot_noexec
Identifiers and References

Identifiers:  CCE-83316-0

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /boot || { echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/boot" "noexec" "" ""

	ensure_partition_is_mounted "/boot"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/boot'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83316-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83316-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /boot options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83316-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_noexec
    - no_reboot_needed

- name: Ensure /boot is mounted with noexec option
  mount:
    path: /boot
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83316-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_boot_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /boot --mountoptions="noexec"

Rule   Add noexec Option to /var/tmp   [ref]

The noexec mount option can be used to prevent binaries from being executed out of /var/tmp. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var/tmp.
Rationale:
Allowing users to execute binaries from world-writable directories such as /var/tmp should never be necessary in normal operation and can expose the system to potential compromise.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_tmp_noexec
Identifiers and References

Identifiers:  CCE-82151-2

References:  1.1.10, BP28(R12), SRG-OS-000368-GPOS-00154, RHEL-08-040134, SV-230522r599809_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var/tmp || { echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var/tmp" "noexec" "" ""

	ensure_partition_is_mounted "/var/tmp"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var/tmp'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82151-2
    - DISA-STIG-RHEL-08-040134
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_noexec
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82151-2
    - DISA-STIG-RHEL-08-040134
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_noexec
    - no_reboot_needed

- name: Make sure noexec option is part of the to /var/tmp options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
    - mount_info is defined and "noexec" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82151-2
    - DISA-STIG-RHEL-08-040134
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_noexec
    - no_reboot_needed

- name: Ensure /var/tmp is mounted with noexec option
  mount:
    path: /var/tmp
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82151-2
    - DISA-STIG-RHEL-08-040134
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_noexec
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /var/tmp --mountoptions="noexec"

Rule   Add nodev Option to Non-Root Local Partitions   [ref]

The nodev mount option prevents files from being interpreted as character or block devices. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of any non-root local partitions.
Rationale:
The nodev mount option prevents files from being interpreted as character or block devices. The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails, for which it is not advised to set nodev on these filesystems.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_nodev_nonroot_local_partitions
Identifiers and References

Identifiers:  CCE-82069-6

References:  1.1.11, CM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7, PR.IP-1, PR.PT-3, SR 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, 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, 4.3.4.3.2, 4.3.4.3.3, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06, A.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, 11, 14, 3, 9, SRG-OS-000368-GPOS-00154, BP28(R12), RHEL-08-010580, SV-230301r599732_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

MOUNT_OPTION="nodev"
# Create array of local non-root partitions
readarray -t partitions_records < <(findmnt --mtab --raw --evaluate | grep "^/\w" | grep "\s/dev/\w")

for partition_record in "${partitions_records[@]}"; do
    # Get all important information for fstab
    mount_point="$(echo ${partition_record} | cut -d " " -f1)"
    device="$(echo ${partition_record} | cut -d " " -f2)"
    device_type="$(echo ${partition_record} | cut -d " " -f3)"
    # device and device_type will be used only in case when the device doesn't have fstab record
    ensure_mount_option_in_fstab "$mount_point" "$MOUNT_OPTION" "$device" "$device_type"
    ensure_partition_is_mounted "$mount_point"
done

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

Complexity:low
Disruption:high
Strategy:configure
- name: Ensure non-root local partitions are mounted with nodev option
  mount:
    path: '{{ item.mount }}'
    src: '{{ item.device }}'
    opts: '{{ item.options }},nodev'
    state: mounted
    fstype: '{{ item.fstype }}'
  when:
    - item.mount is match('/\w')
    - item.options is not search('nodev')
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  with_items:
    - '{{ ansible_facts.mounts }}'
  tags:
    - CCE-82069-6
    - DISA-STIG-RHEL-08-010580
    - NIST-800-53-AC-6
    - NIST-800-53-AC-6(1)
    - NIST-800-53-CM-6(a)
    - NIST-800-53-CM-7(a)
    - NIST-800-53-CM-7(b)
    - NIST-800-53-MP-7
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_nodev_nonroot_local_partitions
    - no_reboot_needed

Rule   Add nosuid Option to /var   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /var. The SUID and SGID permissions should not be required for this directory. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var.
Rationale:
The presence of SUID and SGID executables should be tightly controlled.
Severity: 
unknown
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_nosuid
Identifiers and References

Identifiers:  CCE-83383-0

References:  BP28(R12)


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var || { echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var" "nosuid" "" ""

	ensure_partition_is_mounted "/var"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83383-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - mount_option_var_nosuid
    - no_reboot_needed
    - unknown_severity

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83383-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - mount_option_var_nosuid
    - no_reboot_needed
    - unknown_severity

- name: Make sure nosuid option is part of the to /var options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83383-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - mount_option_var_nosuid
    - no_reboot_needed
    - unknown_severity

- name: Ensure /var is mounted with nosuid option
  mount:
    path: /var
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-83383-0
    - configure_strategy
    - high_disruption
    - low_complexity
    - mount_option_var_nosuid
    - no_reboot_needed
    - unknown_severity

Complexity:low
Disruption:high
Strategy:enable

part /var --mountoptions="nosuid"

Rule   Add nosuid Option to /var/tmp   [ref]

The nosuid mount option can be used to prevent execution of setuid programs in /var/tmp. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var/tmp.
Rationale:
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_mount_option_var_tmp_nosuid
Identifiers and References

Identifiers:  CCE-82154-6

References:  1.1.9, BP28(R12), SRG-OS-000368-GPOS-00154, RHEL-08-040133, SV-230521r599808_rule


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

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
# $3: filesystem of new mount point (used when adding new entry in fstab)
# $4: mount type of new mount point (used when adding new entry in fstab)
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _filesystem=$3 _type=$4 _vfstype_points=()
        readarray -t _vfstype_points < <(grep -E "[[:space:]]${_vfstype}[[:space:]]" /etc/fstab | awk '{print $2}')

        for _vfstype_point in "${_vfstype_points[@]}"
        do
                ensure_mount_option_in_fstab "$_vfstype_point" "$_new_opt" "$_filesystem" "$_type"
        done
}

# $1: mount point
# $2: new mount point option
# $3: device or virtual string (used when adding new entry in fstab)
# $4: mount type of mount point (used when adding new entry in fstab)
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _device=$3 _type=$4
	local _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ "$(grep -c "$_mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then
		# runtime opts without some automatic kernel/userspace-added defaults
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
					| sed -E "s/(rw|defaults|seclabel|${_new_opt})(,|$)//g;s/,$//")
		[ "$_previous_mount_opts" ] && _previous_mount_opts+=","
		echo "${_device} ${_mount_point} ${_type} defaults,${_previous_mount_opts}${_new_opt} 0 0" >> /etc/fstab
	elif [ "$(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt")" -eq 0 ]; then
		_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
		sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
	fi
}

# $1: mount point
function get_mount_point_regexp {
		printf "[[:space:]]%s[[:space:]]" "$1"
}

# $1: mount point
function assert_mount_point_in_fstab {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	grep "$_mount_point_match_regexp" -q /etc/fstab \
		|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}

# $1: mount point
function remove_defaults_from_fstab_if_overriden {
	local _mount_point_match_regexp
	_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
	if grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,"
	then
		sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
	fi
}

# $1: mount point
function ensure_partition_is_mounted {
	local _mount_point="$1"
	mkdir -p "$_mount_point" || return 1
	if mountpoint -q "$_mount_point"; then
		mount -o remount --target "$_mount_point"
	else
		mount --target "$_mount_point"
	fi
}
include_mount_options_functions

function perform_remediation {
	# test "$mount_has_to_exist" = 'yes'
	if test "yes" = 'yes'; then
		assert_mount_point_in_fstab /var/tmp || { echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; }
	fi

	ensure_mount_option_in_fstab "/var/tmp" "nosuid" "" ""

	ensure_partition_is_mounted "/var/tmp"
}

perform_remediation

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

Complexity:low
Disruption:high
Strategy:configure
- name: Check information associated to mountpoint
  command: findmnt --fstab '/var/tmp'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82154-6
    - DISA-STIG-RHEL-08-040133
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_nosuid
    - no_reboot_needed

- name: Create mount_info dictionary variable
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
    - '{{ device_name.stdout_lines[0].split() | list | lower }}'
    - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
    - device_name.stdout is defined and device_name.stdout_lines is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82154-6
    - DISA-STIG-RHEL-08-040133
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_nosuid
    - no_reboot_needed

- name: Make sure nosuid option is part of the to /var/tmp options
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
    - mount_info is defined and "nosuid" not in mount_info.options
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82154-6
    - DISA-STIG-RHEL-08-040133
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_nosuid
    - no_reboot_needed

- name: Ensure /var/tmp is mounted with nosuid option
  mount:
    path: /var/tmp
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
    - device_name.stdout is defined
    - (device_name.stdout | length > 0)
    - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-82154-6
    - DISA-STIG-RHEL-08-040133
    - configure_strategy
    - high_disruption
    - low_complexity
    - medium_severity
    - mount_option_var_tmp_nosuid
    - no_reboot_needed

Complexity:low
Disruption:high
Strategy:enable

part /var/tmp --mountoptions="nosuid"
Group   Restrict Programs from Dangerous Execution Patterns   Group contains 3 groups and 15 rules
[ref]   The recommendations in this section are designed to ensure that the system's features to protect against potentially dangerous program execution are activated. These protections are applied at the system initialization or kernel level, and defend against certain types of badly-configured or compromised programs.
Group   Disable Core Dumps   Group contains 1 rule
[ref]   A core dump file is the memory image of an executable program when it was terminated by the operating system due to errant behavior. In most cases, only software developers legitimately need to access these files. The core dump files may also contain sensitive information, or unnecessarily occupy large amounts of disk space.

Once a hard limit is set in /etc/security/limits.conf, or to a file within the /etc/security/limits.d/ directory, a user cannot increase that limit within his or her own session. If access to core dumps is required, consider restricting them to only certain users or groups. See the limits.conf man page for more information.

The core dumps of setuid programs are further protected. The sysctl variable fs.suid_dumpable controls whether the kernel allows core dumps from these programs at all. The default value of 0 is recommended.

Rule   Disable Core Dumps for SUID programs   [ref]

To set the runtime status of the fs.suid_dumpable kernel parameter, run the following command:
$ sudo sysctl -w fs.suid_dumpable=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
fs.suid_dumpable = 0
Rationale:
The core dump of a setuid program is more likely to contain sensitive data, as the program itself runs with greater privileges than the user who initiated execution of the program. Disabling the ability for any setuid program to write a core file decreases the risk of unauthorized access of such data.
Severity: 
medium
Rule ID:xccdf_org.ssgproject.content_rule_sysctl_fs_suid_dumpable
Identifiers and References

Identifiers:  CCE-80912-9

References:  1.6.1, 164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e), SI-11(a), SI-11(b), BP28(R23)


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



#
# Set runtime for fs.suid_dumpable
#
/sbin/sysctl -q -n -w fs.suid_dumpable="0"

#
# If fs.suid_dumpable present in /etc/sysctl.conf, change value to "0"
#	else, add "fs.suid_dumpable = 0" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file:		Configuration file that will be modified
# key:			Configuration option to change
# value:		Value of the configuration option to change
# cce:			The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format:		The printf-like format string that will be given stripped key and value as arguments,
#			so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format:		Optional argument to specify the format of how key/value should be
# 			modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
#     With default format of 'key = value':
#     replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
#     With custom key/value format:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
#     With a variable:
#     replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
  local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  if [ "$case_insensitive_mode" = yes ]; then
    sed_case_insensitive_option="i"
    grep_case_insensitive_option="-i"
  fi
  [ -n "$format" ] || format="$default_format"
  # Check sanity of the input
  [ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  sed_command=('sed' '-i')
  if test -L "$config_file"; then
    sed_command+=('--follow-symlinks')
  fi

  # Test that the cce arg is not empty or does not equal @CCENUM@.
  # If @CCENUM@ exists, it means that there is no CCE assigned.
  if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
    cce="${cce}"
  else
    cce="CCE"
  fi

  # 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' <<< "$key")

  # shellcheck disable=SC2059
  printf -v formatted_output "$format" "$stripped_key" "$value"

  # 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 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
    "${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
  else
    # \n is precaution for case where file ends without trailing newline
    printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
    printf '%s\n' "$formatted_output" >> "$config_file"
  fi
}
replace_or_append '/etc/sysctl.conf' '^fs.suid_dumpable' "0" 'CCE-80912-9'

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

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure sysctl fs.suid_dumpable is set to 0
  sysctl:
    name: fs.suid_dumpable
    value: '0'
    state: present
    reload: true
  when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
  tags:
    - CCE-80912-9
    - NIST-800-53-SI-11(a)
    - NIST-800-53-SI-11(b)
    - disable_strategy
    - low_complexity
    - medium_disruption
    - medium_severity
    - reboot_required
    - sysctl_fs_suid_dumpable
Group   Enable Execute Disable (XD) or No Execute (NX) Support on x86 Systems   Group contains 2 rules
[ref]   Recent processors in the x86 family support the ability to prevent code execution on a per memory page basis. Generically and on AMD processors, this ability is called No Execute (NX), while on Intel processors it is called Execute Disable (XD). This ability can help prevent exploitation of buffer overflow vulnerabilities and should be activated whenever possible. Extra steps must be taken to ensure that this protection is enabled, particularly on 32-bit x86 systems. Other processors, such as Itanium and POWER, have included such support since inception and the standard kernel for those platforms supports the feature. This is enabled by default on the latest Red Hat and Fedora systems if supported by the hardware.

Rule   Install PAE Kernel on Supported 32-bit x86 Systems