Guide to the Secure Configuration of Red Hat Enterprise Linux 7

with profile DISA STIG for Red Hat Enterprise Linux 7
This profile contains configuration checks that align to the DISA STIG for Red Hat Enterprise Linux V1R4. In addition to being applicable to RHEL7, DISA recognizes this configuration baseline as applicable to the operating system tier of Red Hat technologies that are based off RHEL7, such as: - Red Hat Enterprise Linux Server - Red Hat Enterprise Linux Workstation and Desktop - Red Hat Virtualization Hypervisor (RHV-H) - Red Hat Enterprise Linux for HPC - Red Hat Storage
This guide presents a catalog of security-relevant configuration settings for Red Hat Enterprise Linux 7. 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.

This benchmark is a direct port of a SCAP Security Guide benchmark developed for Red Hat Enterprise Linux. It has been modified through an automated process to remove specific dependencies on Red Hat Enterprise Linux and to function with Scientifc Linux. The result is a generally useful SCAP Security Guide benchmark with the following caveats:

  • Scientifc Linux is not an exact copy of Red Hat Enterprise Linux. Scientific Linux is a Linux distribution produced by Fermi National Accelerator Laboratory. It is a free and open source operating system based on Red Hat Enterprise Linux and aims to be "as close to the commercial enterprise distribution as we can get it." There may be configuration differences that produce false positives and/or false negatives. If this occurs please file a bug report.
  • Scientifc Linux is derived from the free and open source software made available by Red Hat, but it is not produced, maintained or supported by Red Hat. Scientifc Linux has its own build system, compiler options, patchsets, and is a community supported, non-commercial operating system. Scientifc Linux does not inherit certifications or evaluations from Red Hat Enterprise Linux. As such, some configuration rules (such as those requiring FIPS 140-2 encryption) will continue to fail on Scientifc Linux.

Members of the Scientifc Linux community are invited to participate in OpenSCAP and SCAP Security Guide development. Bug reports and patches can be sent to GitHub: https://github.com/OpenSCAP/scap-security-guide. The mailing list is at https://fedorahosted.org/mailman/listinfo/scap-security-guide.

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 TitleDISA STIG for Red Hat Enterprise Linux 7
Profile IDxccdf_org.ssgproject.content_profile_stig-rhel7-disa

Revision History

Current version: 0.1.41

  • draft (as of 2018-10-09)

Platforms

  • cpe:/o:redhat:enterprise_linux:7
  • cpe:/o:scientificlinux:scientificlinux:7
  • cpe:/o:redhat:enterprise_linux:7::client
  • cpe:/o:redhat:enterprise_linux:7::computenode

Table of Contents

  1. Services
    1. Obsolete Services
    2. FTP Server
    3. SNMP Server
    4. Cron and At Daemons
    5. X Window System
    6. Mail Server Software
    7. System Security Services Daemon
    8. Network Time Protocol
    9. Base Services
    10. NFS and RPC
    11. SSH Server
  2. System Settings
    1. Installing and Maintaining Software
    2. Configure Syslog
    3. Network Configuration and Firewalls
    4. Set Boot Loader Password
    5. SELinux
    6. Account and Access Control
    7. System Accounting with auditd
    8. File Permissions and Masks

Checklist

contains 243 rules

Services   [ref]group

The best protection against vulnerable software is running less software. This section describes how to review the software which Red Hat Enterprise Linux 7 installs on a system and disable software which is not needed. It then enumerates the software packages installed on a default Red Hat Enterprise Linux 7 system and provides guidance about which ones can be safely disabled.

Red Hat Enterprise Linux 7 provides a convenient minimal install option that essentially installs the bare necessities for a functional system. When building Red Hat Enterprise Linux 7 systems, it is highly recommended to select the minimal packages and then build up the system from there.

contains 46 rules

Obsolete Services   [ref]group

This section discusses a number of network-visible services which have historically caused problems for system security, and for which disabling or severely limiting the service has been the best available guidance for some time. As a result of this, many of these services are not installed as part of Red Hat Enterprise Linux 7 by default.

Organizations which are running these services should switch to more secure equivalents as soon as possible. If it remains absolutely necessary to run one of these services for legacy reasons, care should be taken to restrict the service as much as possible, for instance by configuring host firewall software such as firewalld to restrict access to the vulnerable service to only those remote hosts which have a known need to use it.

contains 7 rules

Rlogin, Rsh, and Rexec   [ref]group

The Berkeley r-commands are legacy services which allow cleartext remote access and have an insecure trust model.

contains 3 rules

Remove Host-Based Authentication Files   [ref]rule

The shosts.equiv file list remote hosts and users that are trusted by the local system. To remove these files, run the following command to delete them from any location:

$ sudo rm /[path]/[to]/[file]/shosts.equiv

Rationale:

The shosts.equiv files are used to configure host-based authentication for the system via SSH. Host-based authentication is not sufficient for preventing unauthorized access to the system, as it does not require interactive identification and authentication of a connection request, or for the use of two-factor authentication.

Severity:  high

Remediation Shell script:   (show)


# Identify local mounts
MOUNT_LIST=$(df | grep "^/dev" | awk '{ print $6 }') 

# Find file on each listed mount point
for cur_mount in ${MOUNT_LIST}
do
	find ${cur_mount} -xdev -type f -name "shosts.equiv" -exec rm -f {} \;
done

Remove User Host-Based Authentication Files   [ref]rule

The ~/.shosts (in each user's home directory) files list remote hosts and users that are trusted by the local system. To remove these files, run the following command to delete them from any location:

$ sudo rm ~/.shosts

Rationale:

The .shosts files are used to configure host-based authentication for individual users or the system via SSH. Host-based authentication is not sufficient for preventing unauthorized access to the system, as it does not require interactive identification and authentication of a connection request, or for the use of two-factor authentication.false

Severity:  high

Remediation Shell script:   (show)


# Identify local mounts
MOUNT_LIST=$(df | grep "^/dev" | awk '{ print $6 }') 

# Find file on each listed mount point
for cur_mount in ${MOUNT_LIST}
do
	find ${cur_mount} -xdev -type f -name ".shosts" -exec rm -f {} \;
done

Uninstall rsh-server Package   [ref]rule

The rsh-server package can be removed with the following command:

$ sudo yum erase rsh-server

Rationale:

The rsh-server service provides unencrypted remote access service which does not provide for the confidentiality and integrity of user passwords or the remote session and has very weak authentication. If a privileged user were to login using this service, the privileged user password could be compromised. The rsh-server package provides several obsolete and insecure network services. Removing it decreases the risk of those services' accidental (or intentional) activation.

Severity:  high

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove rsh-server
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure rsh-server is removed
  package:
    name: rsh-server
    state: absent
  tags:
    - package_rsh-server_removed
    - high_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-7(a)
    - DISA-STIG-RHEL-07-020000
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_rsh-server

class remove_rsh-server {
  package { 'rsh-server':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=rsh-server

Telnet   [ref]group

The telnet protocol does not provide confidentiality or integrity for information transmitted on the network. This includes authentication information such as passwords. Organizations which use telnet should be actively working to migrate to a more secure protocol.

contains 1 rule

Uninstall telnet-server Package   [ref]rule

The telnet-server package can be removed with the following command:

$ sudo yum erase telnet-server

Rationale:

It is detrimental for operating systems to provide, or install by default, functionality exceeding requirements or mission objectives. These unnecessary capabilities are often overlooked and therefore may remain unsecure. They increase the risk to the platform by providing additional attack vectors.
The telnet service provides an unencrypted remote access service which does not provide for the confidentiality and integrity of user passwords or the remote session. If a privileged user were to login using this service, the privileged user password could be compromised.
Removing the telnet-server package decreases the risk of the telnet service's accidental (or intentional) activation.

Severity:  high

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove telnet-server
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure telnet-server is removed
  package:
    name: telnet-server
    state: absent
  tags:
    - package_telnet-server_removed
    - high_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-7(a)
    - DISA-STIG-RHEL-07-021710
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_telnet-server

class remove_telnet-server {
  package { 'telnet-server':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=telnet-server

NIS   [ref]group

The Network Information Service (NIS), also known as 'Yellow Pages' (YP), and its successor NIS+ have been made obsolete by Kerberos, LDAP, and other modern centralized authentication services. NIS should not be used because it suffers from security problems inherent in its design, such as inadequate protection of important authentication information.

contains 1 rule

Uninstall ypserv Package   [ref]rule

The ypserv package can be removed with the following command:

$ sudo yum erase ypserv

Rationale:

The NIS service provides an unencrypted authentication service which does not provide for the confidentiality and integrity of user passwords or the remote session. Removing the ypserv package decreases the risk of the accidental (or intentional) activation of NIS or NIS+ services.

Severity:  high

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove ypserv
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure ypserv is removed
  package:
    name: ypserv
    state: absent
  tags:
    - package_ypserv_removed
    - high_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-7(a)
    - DISA-STIG-RHEL-07-020010
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_ypserv

class remove_ypserv {
  package { 'ypserv':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=ypserv

TFTP Server   [ref]group

TFTP is a lightweight version of the FTP protocol which has traditionally been used to configure networking equipment. However, TFTP provides little security, and modern versions of networking operating systems frequently support configuration via SSH or other more secure protocols. A TFTP server should be run only if no more secure method of supporting existing equipment can be found.

contains 2 rules

Uninstall tftp-server Package   [ref]rule

The tftp-server package can be removed with the following command:

 $ sudo yum erase tftp-server

Rationale:

Removing the tftp-server package decreases the risk of the accidental (or intentional) activation of tftp services.

If TFTP is required for operational support (such as transmission of router configurations), its use must be documented with the Information Systems Securty Manager (ISSM), restricted to only authorized personnel, and have access control rules established.

Severity:  high

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove tftp-server
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure tftp-server is removed
  package:
    name: tftp-server
    state: absent
  tags:
    - package_tftp-server_removed
    - high_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-6(c)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040700
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_tftp-server

class remove_tftp-server {
  package { 'tftp-server':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=tftp-server

Ensure tftp Daemon Uses Secure Mode   [ref]rule

If running the tftp service is necessary, it should be configured to change its root directory at startup. To do so, ensure /etc/xinetd.d/tftp includes -s as a command line argument, as shown in the following example (which is also the default):

server_args = -s /var/lib/tftpboot

Rationale:

Using the -s option causes the TFTP service to only serve files from the given directory. Serving files from an intentionally-specified directory reduces the risk of sharing files which should remain private.

Severity:  medium

FTP Server   [ref]group

FTP is a common method for allowing remote access to files. Like telnet, the FTP protocol is unencrypted, which means that passwords and other data transmitted during the session can be captured and that the session is vulnerable to hijacking. Therefore, running the FTP server software is not recommended.

However, there are some FTP server configurations which may be appropriate for some environments, particularly those which allow only read-only anonymous access as a means of downloading data available to the public.

contains 1 rule

Disable vsftpd if Possible   [ref]group

To minimize attack surface, disable vsftpd if at all possible.

contains 1 rule

Uninstall vsftpd Package   [ref]rule

The vsftpd package can be removed with the following command:

 $ sudo yum erase vsftpd

Rationale:

Removing the vsftpd package decreases the risk of its accidental activation.

Severity:  high

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove vsftpd
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure vsftpd is removed
  package:
    name: vsftpd
    state: absent
  tags:
    - package_vsftpd_removed
    - high_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040690
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_vsftpd

class remove_vsftpd {
  package { 'vsftpd':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=vsftpd

SNMP Server   [ref]group

The Simple Network Management Protocol allows administrators to monitor the state of network devices, including computers. Older versions of SNMP were well-known for weak security, such as plaintext transmission of the community string (used for authentication) and usage of easily-guessable choices for the community string.

contains 1 rule

Configure SNMP Server if Necessary   [ref]group

If it is necessary to run the snmpd agent on the system, some best practices should be followed to minimize the security risk from the installation. The multiple security models implemented by SNMP cannot be fully covered here so only the following general configuration advice can be offered:

  • use only SNMP version 3 security models and enable the use of authentication and encryption
  • write access to the MIB (Management Information Base) should be allowed only if necessary
  • all access to the MIB should be restricted following a principle of least privilege
  • network access should be limited to the maximum extent possible including restricting to expected network addresses both in the configuration files and in the system firewall rules
  • ensure SNMP agents send traps only to, and accept SNMP queries only from, authorized management stations
  • ensure that permissions on the snmpd.conf configuration file (by default, in /etc/snmp) are 640 or more restrictive
  • ensure that any MIB files' permissions are also 640 or more restrictive

contains 1 rule

Ensure Default SNMP Password Is Not Used   [ref]rule

Edit /etc/snmp/snmpd.conf, remove or change the default community strings of public and private. Once the default community strings have been changed, restart the SNMP service:

$ sudo service snmpd restart

Rationale:

Whether active or not, default simple network management protocol (SNMP) community strings must be changed to maintain security. If the service is running with the default authenticators, then anyone can gather data about the system and the network and use the information to potentially compromise the integrity of the system and network(s).

Severity:  high

Remediation Shell script:   (show)


if grep -s "public\|private" /etc/snmp/snmpd.conf | grep -qv "^#"; then
	sed -i "/^\s*#/b;/public\|private/ s/^/#/" /etc/snmp/snmpd.conf
fi

Cron and At Daemons   [ref]group

The cron and at services are used to allow commands to be executed at a later time. The cron service is required by almost all systems to perform necessary maintenance tasks, while at may or may not be required on a given system. Both daemons should be configured defensively.

contains 2 rules

Restrict at and cron to Authorized Users if Necessary   [ref]group

The /etc/cron.allow and /etc/at.allow files contain lists of users who are allowed to use cron and at to delay execution of processes. If these files exist and if the corresponding files /etc/cron.deny and /etc/at.deny do not exist, then only users listed in the relevant allow files can run the crontab and at commands to submit jobs to be run at scheduled intervals. On many systems, only the system administrator needs the ability to schedule jobs. Note that even if a given user is not listed in cron.allow, cron jobs can still be run as that user. The cron.allow file controls only administrative access to the crontab command for scheduling and modifying cron jobs.

To restrict at and cron to only authorized users:

  • Remove the cron.deny file:
    $ sudo rm /etc/cron.deny
  • Edit /etc/cron.allow, adding one line for each user allowed to use the crontab command to create cron jobs.
  • Remove the at.deny file:
    $ sudo rm /etc/at.deny
  • Edit /etc/at.allow, adding one line for each user allowed to use the at command to create at jobs.

contains 2 rules

Verify Group Who Owns /etc/cron.allow file   [ref]rule

If /etc/cron.allow exists, it must be group-owned by root. To properly set the group owner of /etc/cron.allow, run the command:

$ sudo chgrp root /etc/cron.allow

Rationale:

If the owner of the cron.allow file is not set to root, the possibility exists for an unauthorized user to view or edit sensitive information.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:configure

chgrp 0 /etc/cron.allow
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/cron.allow
  stat:
    path: /etc/cron.allow
  register: file_exists

- name: Ensure group owner 0 on /etc/cron.allow
  file:
    path: /etc/cron.allow
    group: 0
  when: file_exists.stat.exists
  tags:
    - file_groupowner_cron_allow
    - medium_severity
    - configure_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021120

Verify User Who Owns /etc/cron.allow file   [ref]rule

If /etc/cron.allow exists, it must be owned by root. To properly set the owner of /etc/cron.allow, run the command:

$ sudo chown root /etc/cron.allow 

Rationale:

If the owner of the cron.allow file is not set to root, the possibility exists for an unauthorized user to view or edit sensitive information.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:configure

chown 0 /etc/cron.allow
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:configure
- name: Test for existence /etc/cron.allow
  stat:
    path: /etc/cron.allow
  register: file_exists

- name: Ensure owner 0 on /etc/cron.allow
  file:
    path: /etc/cron.allow
    owner: 0
  when: file_exists.stat.exists
  tags:
    - file_owner_cron_allow
    - medium_severity
    - configure_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021110

X Window System   [ref]group

The X Window System implementation included with the system is called X.org.

contains 1 rule

Disable X Windows   [ref]group

Unless there is a mission-critical reason for the system to run a graphical user interface, ensure X is not set to start automatically at boot and remove the X Windows software packages. There is usually no reason to run X Windows on a dedicated server system, as it increases the system's attack surface and consumes system resources. Administrators of server systems should instead login via SSH or on the text console.

contains 1 rule

Remove the X Windows Package Group   [ref]rule

By removing the xorg-x11-server-common package, the system no longer has X Windows installed. If X Windows is not installed then the system cannot boot into graphical user mode. This prevents the system from being accidentally or maliciously booted into a graphical.target mode. To do so, run the following command:

$ sudo yum groupremove "X Window System"
$ sudo yum remove xorg-x11-server-common

Rationale:

Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security vulnerabilities and should not be installed unless approved and documented.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:disable
# Function to remove packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_remove telnet-server
#
function package_remove {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_remove 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if rpm -q --quiet "$package"; then
    dnf remove -y "$package"
  fi
elif which yum ; then
  if rpm -q --quiet "$package"; then
    yum remove -y "$package"
  fi
elif which apt-get ; then
  apt-get remove -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_remove xorg-x11-server-common
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Ensure xorg-x11-server-common is removed
  package:
    name: xorg-x11-server-common
    state: absent
  tags:
    - package_xorg-x11-server-common_removed
    - medium_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8).1(ii)
    - DISA-STIG-RHEL-07-040730
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
include remove_xorg-x11-server-common

class remove_xorg-x11-server-common {
  package { 'xorg-x11-server-common':
    ensure => 'purged',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable

package --remove=xorg-x11-server-common

Mail Server Software   [ref]group

Mail servers are used to send and receive email over the network. Mail is a very common service, and Mail Transfer Agents (MTAs) are obvious targets of network attack. Ensure that systems are not running MTAs unnecessarily, and configure needed MTAs as defensively as possible.

Very few systems at any site should be configured to directly receive email over the network. Users should instead use mail client programs to retrieve email from a central server that supports protocols such as IMAP or POP3. However, it is normal for most systems to be independently capable of sending email, for instance so that cron jobs can report output to an administrator. Most MTAs, including Postfix, support a submission-only mode in which mail can be sent from the local system to a central site MTA (or directly delivered to a local account), but the system still cannot receive mail directly over a network.

The alternatives program in Red Hat Enterprise Linux permits selection of other mail server software (such as Sendmail), but Postfix is the default and is preferred. Postfix was coded with security in mind and can also be more effectively contained by SELinux as its modular design has resulted in separate processes performing specific actions. More information is available on its website, http://www.postfix.org.

contains 1 rule

Configure Operating System to Protect Mail Server   [ref]group

The guidance in this section is appropriate for any host which is operating as a site MTA, whether the mail server runs using Sendmail, Postfix, or some other software.

contains 1 rule

Configure Postfix if Necessary   [ref]group

Postfix stores its configuration files in the directory /etc/postfix by default. The primary configuration file is /etc/postfix/main.cf.

contains 1 rule

Control Mail Relaying   [ref]group

Postfix's mail relay controls are implemented with the help of the smtpd recipient restrictions option, which controls the restrictions placed on the SMTP dialogue once the sender and recipient envelope addresses are known. The guidance in the following sections should be applied to all systems. If there are systems which must be allowed to relay mail, but which cannot be trusted to relay unconditionally, configure SMTP AUTH with SSL support.

contains 1 rule

Prevent Unrestricted Mail Relaying   [ref]rule

Modify the

/etc/postfix/main.cf
file to restrict client connections to the local network with the following command:
$ sudo postconf -e 'smtpd_client_restrictions = permit_mynetworks,reject'

Rationale:

If unrestricted mail relaying is permitted, unauthorized senders could use this host as a mail relay for the purpose of sending spam or other unauthorized activity.

Severity:  medium

Remediation Shell script:   (show)


if ! grep -q ^smtpd_client_restrictions /etc/postfix/main.cf; then
	echo "smtpd_client_restrictions = permit_mynetworks,reject" >> /etc/postfix/main.cf
else
	sed -i "s/^smtpd_client_restrictions.*/smtpd_client_restrictions = permit_mynetworks,reject/g" /etc/postfix/main.cf
fi

System Security Services Daemon   [ref]group

The System Security Services Daemon (SSSD) is a system daemon that provides access to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline support to systems utilizing SSSD. SSSD using caching to reduce load on authentication servers permit offline authentication as well as store extended user data.

For more information, see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System-Level_Authentication_Guide/SSSD.html

contains 4 rules

System Security Services Daemon (SSSD) - LDAP   [ref]group

The System Security Services Daemon (SSSD) is a system daemon that provides access to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline support to systems utilizing SSSD. SSSD using caching to reduce load on authentication servers permit offline authentication as well as store extended user data.

SSSD can support many backends including LDAP. The sssd-ldap backend allows SSSD to fetch identity information from an LDAP server.

contains 3 rules

Configure SSSD LDAP Backend Client CA Certificate Location   [ref]rule

Configure SSSD to implement cryptography to protect the integrity of LDAP remote access sessions. By setting the

ldap_tls_cacertdir
option in
/etc/sssd/sssd.conf
to point to the path for the X.509 certificates used for peer authentication.
ldap_tls_cacertdir /path/to/tls/cacert

Rationale:

Without cryptographic integrity protections, information can be altered by unauthorized users without detection.

Cryptographic mechanisms used for protecting the integrity of information include, for example, signed hash functions using asymmetric cryptography enabling distribution of the public key to verify the hash information while maintaining the confidentiality of the key used to generate the hash.

Severity:  medium

Remediation Shell script:   (show)


var_sssd_ldap_tls_ca_dir="/etc/openldap/cacerts"

SSSD_CONF="/etc/sssd/sssd.conf"
LDAP_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*ldap_tls_cacertdir'
DOMAIN_REGEX="[[:space:]]*\[domain\/[^]]*]"

# Try find [domain/..] and ldap_tls_cacertdir in sssd.conf, if it exists, set to CA directory
# if it isn't here, add it, if [domain/..] doesn't exist, add it here for default domain
if grep -qzosP $LDAP_REGEX $SSSD_CONF; then
        sed -i "s~ldap_tls_cacertdir[^(\n)]*~ldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir~" $SSSD_CONF
elif grep -qs $DOMAIN_REGEX $SSSD_CONF; then
        sed -i "/$DOMAIN_REGEX/a ldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir" $SSSD_CONF
else
        mkdir -p /etc/sssd
        touch $SSSD_CONF
        echo -e "[domain/default]\nldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir" >> $SSSD_CONF
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: XCCDF Value var_sssd_ldap_tls_ca_dir # promote to variable
  set_fact:
    var_sssd_ldap_tls_ca_dir: !!str |-
        /etc/openldap/cacerts
  tags:
    - always

- name: "Test for domain group"
  shell: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf
  register: test_grep_domain
  ignore_errors: yes
  tags:
    - sssd_ldap_configure_tls_ca_dir
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - DISA-STIG-RHEL-07-040190

- name: "Add default domain group and set CA directory (if no domain there)"
  lineinfile:
    path: /etc/sssd/sssd.conf
    create: yes
    line: "[domain/default]\nldap_tls_cacertdir = {{ var_sssd_ldap_tls_ca_dir }}\n"
  when: test_grep_domain.stdout == ""
  tags:
    - sssd_ldap_configure_tls_ca_dir
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - DISA-STIG-RHEL-07-040190

- name: "Configure LDAPs path to CA directory"
  lineinfile:
    path: /etc/sssd/sssd.conf
    regexp: '^\s*ldap_tls_cacertdir'
    insertafter: '\s*\[domain\/[^]]*]'
    line: 'ldap_tls_cacertdir = {{ var_sssd_ldap_tls_ca_dir }}'
  tags:
    - sssd_ldap_configure_tls_ca_dir
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - DISA-STIG-RHEL-07-040190

Configure SSSD LDAP Backend to Use TLS For All Transactions   [ref]rule

This check verifies that RHEL7 implements cryptography to protect the integrity of remote LDAP authentication sessions.

To determine if LDAP is being used for authentication, use the following command:

$ sudo grep -i useldapauth /etc/sysconfig/authconfig


If USELDAPAUTH=yes, then LDAP is being used. To check if LDAP is configured to use TLS, use the following command:
$ sudo grep -i ldap_id_use_start_tls /etc/sssd/sssd.conf

Rationale:

Without cryptographic integrity protections, information can be altered by unauthorized users without detection. The ssl directive specifies whether to use TLS or not. If not specified it will default to no. It should be set to start_tls rather than doing LDAP over SSL.

Severity:  medium

Remediation Shell script:   (show)



AUTHCONFIG="/etc/sysconfig/authconfig"
USELDAPAUTH_REGEX="^USELDAPAUTH="
SSSD_CONF="/etc/sssd/sssd.conf"
LDAP_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*ldap_id_use_start_tls'
DOMAIN_REGEX="[[:space:]]*\[domain\/[^]]*]"

# Try find USELDAPAUTH in authconfig. If its here set to 'yes', otherwise append USELDAPAUTH=yes
grep -qs "^USELDAPAUTH=" "$AUTHCONFIG" && sed -i 's/^USELDAPAUTH=.*/USELDAPAUTH=yes/g' $AUTHCONFIG
if ! [ $? -eq 0 ]; then
        echo "USELDAPAUTH=yes" >> $AUTHCONFIG
fi

# Try find [domain/..] and ldap_id_use_start_tls in sssd.conf, if it exists, set to 'True'
# if ldap_id_use_start_tls isn't here, add it
# if [domain/..] doesn't exist, add it here for default domain
if grep -qzosP $LDAP_REGEX $SSSD_CONF; then
        sed -i 's/ldap_id_use_start_tls[^(\n)]*/ldap_id_use_start_tls = True/' $SSSD_CONF
elif grep -qs $DOMAIN_REGEX $SSSD_CONF; then
        sed -i "/$DOMAIN_REGEX/a ldap_id_use_start_tls = True" $SSSD_CONF
else
        mkdir -p /etc/sssd
        touch $SSSD_CONF
        echo -e "[domain/default]\nldap_id_use_start_tls = True" >> $SSSD_CONF
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium

- name: "Set LDAP to be used for authentication"
  lineinfile:
    path: /etc/sysconfig/authconfig
    regexp: '^USELDAPAUTH='
    line: 'USELDAPAUTH=yes'
    create: yes
  tags:
    - sssd_ldap_start_tls
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-17(2)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040180

- name: "Test for domain group"
  shell: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf
  register: test_grep_domain
  ignore_errors: yes
  tags:
    - sssd_ldap_start_tls
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-17(2)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040180

- name: "Add default domain group and use STARTTLS (if no domain there)"
  lineinfile:
    path: /etc/sssd/sssd.conf
    line: "[domain/default]\nldap_id_use_start_tls = True\n"
  when: test_grep_domain.stdout == ""
  tags:
    - sssd_ldap_start_tls
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-17(2)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040180

- name: "Configure LDAP to use STARTTLS"
  lineinfile:
    path: /etc/sssd/sssd.conf
    regexp: '^\s*ldap_id_use_start_tls'
    insertafter: '\s*\[domain\/[^]]*]'
    line: 'ldap_id_use_start_tls = True'
  tags:
    - sssd_ldap_start_tls
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-17(2)
    - NIST-800-53-CM-7
    - DISA-STIG-RHEL-07-040180

Configure SSSD LDAP Backend Client CA Certificate   [ref]rule

Configure SSSD to implement cryptography to protect the integrity of LDAP remote access sessions. By setting the

ldap_tls_cacert
option in
/etc/sssd/sssd.conf
to point to the path for the X.509 certificates used for peer authentication.
ldap_tls_cacert /path/to/tls/ca.cert

Rationale:

Without cryptographic integrity protections, information can be altered by unauthorized users without detection.

Cryptographic mechanisms used for protecting the integrity of information include, for example, signed hash functions using asymmetric cryptography enabling distribution of the public key to verify the hash information while maintaining the confidentiality of the key used to generate the hash.

Severity:  medium

Configure PAM in SSSD Services   [ref]rule

SSSD should be configured to run SSSD pam services. To configure SSSD to known SSH hosts, add pam to services under the [sssd] section in /etc/sssd/sssd.conf. For example:

[sssd]
services = sudo, autofs, pam

Rationale:

Using an authentication device, such as a CAC or token that is separate from the information system, ensures that even if the information system is compromised, that compromise will not affect credentials stored on the authentication device.

Severity:  medium

Remediation Shell script:   (show)



SSSD_SERVICES_PAM_REGEX="^[[:space:]]*\[sssd]([^\n]*\n+)+?[[:space:]]*services.*pam.*$"
SSSD_SERVICES_REGEX="^[[:space:]]*\[sssd]([^\n]*\n+)+?[[:space:]]*services.*$"
SSSD_PAM_SERVICES="[sssd]
services = pam"
SSSD_CONF="/etc/sssd/sssd.conf"

# If there is services line with pam, good
# If there is services line without pam, append pam
# If not echo services line with pam
grep -q "$SSSD_SERVICES_PAM_REGEX" $SSSD_CONF || \
	grep -q "$SSSD_SERVICES_REGEX" $SSSD_CONF && \
	sed -i "s/$SSSD_SERVICES_REGEX/&, pam/" $SSSD_CONF || \
	echo "$SSSD_PAM_SERVICES" >> $SSSD_CONF

Network Time Protocol   [ref]group

The Network Time Protocol is used to manage the system clock over a network. Computer clocks are not very accurate, so time will drift unpredictably on unmanaged systems. Central time protocols can be used both to ensure that time is consistent among a network of systems, and that their time is consistent with the outside world.

If every system on a network reliably reports the same time, then it is much easier to correlate log messages in case of an attack. In addition, a number of cryptographic protocols (such as Kerberos) use timestamps to prevent certain types of attacks. If your network does not have synchronized time, these protocols may be unreliable or even unusable.

Depending on the specifics of the network, global time accuracy may be just as important as local synchronization, or not very important at all. If your network is connected to the Internet, using a public timeserver (or one provided by your enterprise) provides globally accurate timestamps which may be essential in investigating or responding to an attack which originated outside of your network.

A typical network setup involves a small number of internal systems operating as NTP servers, and the remainder obtaining time information from those internal servers.

There is a choice between the daemons ntpd and chronyd, which are available from the repositories in the ntp and chrony packages respectively.

The default chronyd daemon can work well when external time references are only intermittently accesible, can perform well even when the network is congested for longer periods of time, can usually synchronize the clock faster and with better time accuracy, and quickly adapts to sudden changes in the rate of the clock, for example, due to changes in the temperature of the crystal oscillator. Chronyd should be considered for all systems which are frequently suspended or otherwise intermittently disconnected and reconnected to a network. Mobile and virtual systems for example.

The ntpd NTP daemon fully supports NTP protocol version 4 (RFC 5905), including broadcast, multicast, manycast clients and servers, and the orphan mode. It also supports extra authentication schemes based on public-key cryptography (RFC 5906). The NTP daemon (ntpd) should be considered for systems which are normally kept permanently on. Systems which are required to use broadcast or multicast IP, or to perform authentication of packets with the Autokey protocol, should consider using ntpd.

Refer to https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/ch-Configuring_NTP_Using_the_chrony_Suite.html for more detailed comparison of features of chronyd and ntpd daemon features respectively, and for further guidance how to choose between the two NTP daemons.

The upstream manual pages at http://chrony.tuxfamily.org/manual.html for chronyd and http://www.ntp.org for ntpd provide additional information on the capabilities and configuration of each of the NTP daemons.

contains 1 rule

Configure Time Service Maxpoll Interval   [ref]rule

The maxpoll should be configured to 10 in /etc/ntp.conf or /etc/chrony.conf to continuously poll time servers. To configure maxpoll in /etc/ntp.conf or /etc/chrony.conf add the following:

maxpoll 10

Rationale:

Inaccurate time stamps make it more difficult to correlate events and can lead to an inaccurate analysis. Determining the correct time a particular event occurred on a system is critical when conducting forensic analysis and investigating system events. Sources outside the configured acceptable allowance (drift) may be inaccurate.

Severity:  unknown

Remediation Shell script:   (show)


var_time_service_set_maxpoll="10"


config_file="/etc/ntp.conf"
/usr/sbin/pidof ntpd || config_file="/etc/chrony.conf"


# Set maxpoll values to var_time_service_set_maxpoll
sed -i "s/^\(server.*maxpoll\) [0-9][0-9]*\(.*\)$/\1 $var_time_service_set_maxpoll \2/" "$config_file"

# Add maxpoll to server entries without maxpoll
grep "^server" "$config_file" | grep -v maxpoll | while read -r line ; do
        sed -i "s/$line/& maxpoll $var_time_service_set_maxpoll/" "$config_file"
done

Base Services   [ref]group

This section addresses the base services that are installed on a Red Hat Enterprise Linux 7 default installation which are not covered in other sections. Some of these services listen on the network and should be treated with particular discretion. Other services are local system utilities that may or may not be extraneous. In general, system services should be disabled if not required.

contains 1 rule

Disable KDump Kernel Crash Analyzer (kdump)   [ref]rule

The kdump service provides a kernel crash dump analyzer. It uses the kexec system call to boot a secondary kernel ("capture" kernel) following a system crash, which can load information from the crashed kernel for analysis. The kdump service can be disabled with the following command:

$ sudo systemctl disable kdump.service

Rationale:

Kernel core dumps may contain the full contents of system memory at the time of the crash. Kernel core dumps consume a considerable amount of disk space and may result in denial of service by exhausting the available space on the target file system partition. Unless the system is used for kernel development or testing, there is little need to run the kdump service.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" stop 'kdump.service'
"$SYSTEMCTL_EXEC" disable 'kdump.service'
# Disable socket activation if we have a unit file for it
"$SYSTEMCTL_EXEC" list-unit-files | grep -q '^kdump.socket\>' && "$SYSTEMCTL_EXEC" disable 'kdump.socket'
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
"$SYSTEMCTL_EXEC" reset-failed 'kdump.service'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:disable
- name: Disable service kdump
  service:
    name: kdump
    enabled: "no"
    state: "stopped"
  register: service_result
  failed_when: "service_result is failed and ('Could not find the requested service' not in service_result.msg)"
  tags:
    - service_kdump_disabled
    - medium_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-7
    - NIST-800-53-CM-6(b)
    - DISA-STIG-RHEL-07-021300


- name: Disable socket of service kdump if applicable
  service:
    name: kdump.socket
    enabled: "no"
    state: "stopped"
  register: socket_result
  failed_when: "socket_result is failed and ('Could not find the requested service' not in socket_result.msg)"
  tags:
    - service_kdump_disabled
    - medium_severity
    - disable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8)
    - NIST-800-53-CM-7
    - NIST-800-53-CM-6(b)
    - DISA-STIG-RHEL-07-021300
Remediation Anaconda snippet:   (show)


kdump --disable

NFS and RPC   [ref]group

The Network File System is a popular distributed filesystem for the Unix environment, and is very widely deployed. This section discusses the circumstances under which it is possible to disable NFS and its dependencies, and then details steps which should be taken to secure NFS's configuration. This section is relevant to systems operating as NFS clients, as well as to those operating as NFS servers.

contains 3 rules

Configure NFS Clients   [ref]group

The steps in this section are appropriate for systems which operate as NFS clients.

contains 3 rules

Mount Remote Filesystems with Restrictive Options   [ref]group

Edit the file /etc/fstab. For each filesystem whose type (column 3) is nfs or nfs4, add the text ,nodev,nosuid to the list of mount options in column 4. If appropriate, also add ,noexec.

See the section titled "Restrict Partition Mount Options" for a description of the effects of these options. In general, execution of files mounted via NFS should be considered risky because of the possibility that an adversary could intercept the request and substitute a malicious file. Allowing setuid files to be executed from remote servers is particularly risky, both for this reason and because it requires the clients to extend root-level trust to the NFS server.

contains 3 rules

Mount Remote Filesystems with Kerberos Security   [ref]rule

Add the sec=krb5:krb5i:krb5p option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.

Rationale:

When an NFS server is configured to use AUTH_SYS a selected userid and groupid are used to handle requests from the remote user. The userid and groupid could mistakenly or maliciously be set incorrectly. The AUTH_GSS method of authentication uses certificates on the server and client systems to more securely authenticate the remote mount request.

Severity:  medium

Remediation Shell script:   (show)

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _vfstype_points=()
        _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"
        done
}

# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ $(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

ensure_mount_option_for_vfstype "nfs[4]?" "sec=krb5:krb5i:krb5p"
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Strategy:configure

- name: "Get nfs and nfs4 mount points, that don't have Kerberos security option"
  shell: grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | grep -v "sec=krb5:krb5i:krb5p" | awk '{print $2}'
  register: points_register
  check_mode: no
  tags:
    - mount_option_krb_sec_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-14(1)
    - DISA-STIG-RHEL-07-040750

- name: "Add Kerberos security to mount points"
  shell: awk '$2=="{{ item }}"{$4=$4",sec=krb5:krb5i:krb5p"}1' /etc/fstab > fstab.tmp && mv fstab.tmp /etc/fstab
  with_items:
    - "{{ points_register.stdout_lines }}"
  tags:
    - mount_option_krb_sec_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-14(1)
    - DISA-STIG-RHEL-07-040750

Mount Remote Filesystems with noexec   [ref]rule

Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.

Rationale:

The noexec mount option causes the system not to execute binary files. This option must be used for mounting any file system not containing approved binary files as they may be incompatible. Executing files from untrusted file systems increases the opportunity for unprivileged users to attain unauthorized administrative access.

Severity:  medium

Remediation Shell script:   (show)

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _vfstype_points=()
        _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"
        done
}

# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ $(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

ensure_mount_option_for_vfstype "nfs[4]?" "noexec"
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Strategy:configure

- name: "Get nfs and nfs4 mount points, that don't have noexec"
  shell: grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | grep -v "noexec" | awk '{print $2}'
  register: points_register
  check_mode: no
  tags:
    - mount_option_noexec_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021021

- name: "Add noexec to mount points"
  shell: awk '$2=="{{ item }}"{$4=$4",noexec"}1' /etc/fstab > fstab.tmp && mv fstab.tmp /etc/fstab
  with_items:
    - "{{ points_register.stdout_lines }}"
  when: points_register.stdout | length > 0
  tags:
    - mount_option_noexec_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021021

Mount Remote Filesystems with nosuid   [ref]rule

Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.

Rationale:

NFS mounts should not present suid binaries to users. Only vendor-supplied suid executables should be installed to their default location on the local filesystem.

Severity:  medium

Remediation Shell script:   (show)

function include_mount_options_functions {
	:
}

# $1: type of filesystem
# $2: new mount point option
function ensure_mount_option_for_vfstype {
        local _vfstype="$1" _new_opt="$2" _vfstype_points=()
        _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"
        done
}

# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
	local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
	_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"

	if [ $(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

ensure_mount_option_for_vfstype "nfs[4]?" "nosuid"
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Strategy:configure

- name: "Get nfs and nfs4 mount points, that don't have nosuid"
  shell: grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | grep -v "nosuid" | awk '{print $2}'
  register: points_register
  check_mode: no
  tags:
    - mount_option_nosuid_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021020

- name: "Add nosuid to mount points"
  shell: awk '$2=="{{ item }}"{$4=$4",nosuid"}1' /etc/fstab > fstab.tmp && mv fstab.tmp /etc/fstab
  with_items:
    - "{{ points_register.stdout_lines }}"
  when: points_register.stdout | length > 0
  tags:
    - mount_option_nosuid_remote_filesystems
    - medium_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - DISA-STIG-RHEL-07-021020

SSH Server   [ref]group

The SSH protocol is recommended for remote login and remote file transfer. SSH provides confidentiality and integrity for data exchanged between two systems, as well as server authentication, through the use of public key cryptography. The implementation included with the system is called OpenSSH, and more detailed documentation is available from its website, http://www.openssh.org. Its server program is called sshd and provided by the RPM package openssh-server.

contains 24 rules

Configure OpenSSH Server if Necessary   [ref]group

If the system needs to act as an SSH server, then certain changes should be made to the OpenSSH daemon configuration file /etc/ssh/sshd_config. The following recommendations can be applied to this file. See the sshd_config(5) man page for more detailed information.

contains 20 rules

Disable SSH Support for User Known Hosts   [ref]rule

SSH can allow system users user host-based authentication to connect to systems if a cache of the remote systems public keys are available. This should be disabled.

To ensure this behavior is disabled, add or correct the following line in /etc/ssh/sshd_config:

IgnoreUserKnownHosts yes

Rationale:

Configuring this setting for the SSH daemon provides additional assurance that remove login via SSH will require a password, even in the event of misconfiguration elsewhere.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^IgnoreUserKnownHosts' 'yes' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Disable SSH Support for User Known Hosts"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^IgnoreUserKnownHosts
    line: IgnoreUserKnownHosts yes
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_disable_user_known_hosts
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(a)
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040380

Disable SSH Access via Empty Passwords   [ref]rule

To explicitly disallow SSH login from accounts with empty passwords, add or correct the following line in /etc/ssh/sshd_config:

PermitEmptyPasswords no

Any accounts with empty passwords should be disabled immediately, and PAM configuration should prevent users from being able to assign themselves empty passwords.

Rationale:

Configuring this setting for the SSH daemon provides additional assurance that remote login via SSH will require a password, even in the event of misconfiguration elsewhere.

Severity:  high

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^PermitEmptyPasswords' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Disable SSH Access via Empty Passwords
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^PermitEmptyPasswords
    line: PermitEmptyPasswords no
    validate: sshd -t -f %s
  tags:
    - sshd_disable_empty_passwords
    - high_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-AC-6
    - NIST-800-53-CM-6(b)
    - NIST-800-171-3.1.1
    - NIST-800-171-3.1.5
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-010300

Set SSH Client Alive Count   [ref]rule

To ensure the SSH idle timeout occurs precisely when the ClientAliveInterval is set, edit /etc/ssh/sshd_config as follows:

ClientAliveCountMax 0

Rationale:

This ensures a user login will be terminated as soon as the ClientAliveInterval is reached.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^ClientAliveCountMax' '0' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Set SSH Client Alive Count
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^ClientAliveCountMax
    line: ClientAliveCountMax 0
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_set_keepalive
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-2(5)
    - NIST-800-53-SA-8
    - NIST-800-53-AC-12
    - NIST-800-171-3.1.11
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040340

Set SSH Idle Timeout Interval   [ref]rule

SSH allows administrators to set an idle timeout interval. After this interval has passed, the idle user will be automatically logged out.

To set an idle timeout interval, edit the following line in /etc/ssh/sshd_config as follows:

ClientAliveInterval 600
The timeout interval is given in seconds. To have a timeout of 15 minutes, set interval to 900.

If a shorter timeout has already been set for the login shell, that value will preempt any SSH setting made here. Keep in mind that some processes may stop SSH from correctly detecting that the user is idle.

Rationale:

Terminating an idle ssh session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been let unattended.

Severity:  unknown

Remediation Shell script:   (show)


sshd_idle_timeout_value="600"
# 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-${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/ssh/sshd_config' '^ClientAliveInterval' $sshd_idle_timeout_value '' '%s %s'
Remediation Ansible snippet:   (show)

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

- name: Set SSH Idle Timeout Interval
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^ClientAliveInterval
    line: "ClientAliveInterval {{ sshd_idle_timeout_value }}"
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_set_idle_timeout
    - unknown_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-2(5)
    - NIST-800-53-SA-8(i)
    - NIST-800-53-AC-12
    - NIST-800-171-3.1.11
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040320

Enable SSH Warning Banner   [ref]rule

To enable the warning banner and ensure it is consistent across the system, add or correct the following line in /etc/ssh/sshd_config:

Banner /etc/issue
Another section contains information on how to create an appropriate system-wide warning banner.

Rationale:

The warning message reinforces policy awareness during the logon process and facilitates possible legal action against attackers. Alternatively, systems whose ownership should not be obvious should ensure usage of a banner that does not provide easy attribution.

Severity:  medium

Remediation Shell script:   (show)


grep -q ^Banner /etc/ssh/sshd_config && \
  sed -i "s/Banner.*/Banner \/etc\/issue/g" /etc/ssh/sshd_config
if ! [ $? -eq 0 ]; then
    echo "Banner /etc/issue" >> /etc/ssh/sshd_config
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Enable SSH Warning Banner
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^Banner
    line: Banner /etc/issue
    validate: sshd -t -f %s
  tags:
    - sshd_enable_warning_banner
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-8(a)
    - NIST-800-53-AC-8(b)
    - NIST-800-53-AC-8(c)(1)
    - NIST-800-53-AC-8(c)(2)
    - NIST-800-53-AC-8(c)(3)
    - NIST-800-171-3.1.9
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040170

Use Only FIPS 140-2 Validated MACs   [ref]rule

Limit the MACs to those hash algorithms which are FIPS-approved. The following line in /etc/ssh/sshd_config demonstrates use of FIPS-approved MACs:

MACs hmac-sha2-512,hmac-sha2-256


Only the following message authentication codes are FIPS 140-2 certified on RHEL 7:
- hmac-sha1
- hmac-sha2-256
- hmac-sha2-512
- hmac-sha1-etm@openssh.com
- hmac-sha2-256-etm@openssh.com
- hmac-sha2-512-etm@openssh.com

Any combination of the above MACs will pass this check. Official FIPS 140-2 paperwork for RHEL7 can be found at http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2630.pdf.

Rationale:

DoD Information Systems are required to use FIPS-approved cryptographic hash functions. The only SSHv2 hash algorithms meeting this requirement is SHA2.

Severity:  medium

Remediation Shell script:   (show)


sshd_approved_macs="hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
# 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-${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/ssh/sshd_config' '^MACs' "$sshd_approved_macs" '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value sshd_approved_macs # promote to variable
  set_fact:
    sshd_approved_macs: !!str |-
        hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
  tags:
    - always

- name: "Use Only Approved MACs"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^MACs
    line: "MACs {{ sshd_approved_macs }}"
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_use_approved_macs
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(2)
    - NIST-800-53-IA-7
    - NIST-800-53-SC-13
    - NIST-800-171-3.1.13
    - NIST-800-171-3.13.11
    - NIST-800-171-3.13.8
    - DISA-STIG-RHEL-07-040400

Do Not Allow SSH Environment Options   [ref]rule

To ensure users are not able to override environment options to the SSH daemon, add or correct the following line in /etc/ssh/sshd_config:

PermitUserEnvironment no

Rationale:

SSH environment options potentially allow users to bypass access restriction in some configurations.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^PermitUserEnvironment' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Do Not Allow SSH Environment Options
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^PermitUserEnvironment
    line: PermitUserEnvironment no
    validate: sshd -t -f %s
  tags:
    - sshd_do_not_permit_user_env
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-171-3.1.12
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-010460

Disable Kerberos Authentication   [ref]rule

Unless needed, SSH should not permit extraneous or unnecessary authentication mechanisms like Kerberos. To disable Kerberos authentication, add or correct the following line in the /etc/ssh/sshd_config file:

KerberosAuthentication no

Rationale:

Kerberos authentication for SSH is often implemented using GSSAPI. If Kerberos is enabled through SSH, the SSH daemon provides a means of access to the system's Kerberos implementation. Vulnerabilities in the system's Kerberos implementations may be subject to exploitation.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^KerberosAuthentication' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Disable Kerberos Authentication"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: (?i)^#?kerberosauthentication
    line: KerberosAuthentication no
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_disable_kerb_auth
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(c)
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040440

Allow Only SSH Protocol 2   [ref]rule

Only SSH protocol version 2 connections should be permitted. The default setting in /etc/ssh/sshd_config is correct, and can be verified by ensuring that the following line appears:

Protocol 2

Warning:  As of openssh-server version 7.4 and above, the only protocol supported is version 2, and line
Protocol 2
in /etc/ssh/sshd_config is not necessary.
Rationale:

SSH protocol version 1 is an insecure implementation of the SSH protocol and has many well-known vulnerability exploits. Exploits of the SSH daemon could provide immediate root access to the system.

Severity:  high

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^Protocol' '2' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict

- name: "Allow Only SSH Protocol 2"
  lineinfile:
    dest: /etc/ssh/sshd_config
    regexp: "^Protocol [0-9]"
    line: "Protocol 2"
    validate: sshd -t -f %s
  #notify: :reload ssh
  tags:
    - sshd_allow_only_protocol2
    - high_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-17(8).1(ii)
    - NIST-800-53-IA-5(1)(c)
    - NIST-800-171-3.1.13
    - NIST-800-171-3.5.4
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040390

Disable SSH Support for .rhosts Files   [ref]rule

SSH can emulate the behavior of the obsolete rsh command in allowing users to enable insecure access to their accounts via .rhosts files.

To ensure this behavior is disabled, add or correct the following line in /etc/ssh/sshd_config:

IgnoreRhosts yes

Rationale:

SSH trust relationships mean a compromise on one host can allow an attacker to move trivially to other hosts.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^IgnoreRhosts' 'yes' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Disable SSH Support for .rhosts Files
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^IgnoreRhosts
    line: IgnoreRhosts yes
    validate: sshd -t -f %s
  tags:
    - sshd_disable_rhosts
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-CM-6(a)
    - NIST-800-171-3.1.12
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040350

Disable SSH Support for Rhosts RSA Authentication   [ref]rule

SSH can allow authentication through the obsolete rsh command through the use of the authenticating user's SSH keys. This should be disabled.

To ensure this behavior is disabled, add or correct the following line in /etc/ssh/sshd_config:

RhostsRSAAuthentication no

Warning:  As of openssh-server version 7.4 and above, the RhostsRSAAuthentication option has been deprecated, and the line
RhostsRSAAuthentication no
in /etc/ssh/sshd_config is not necessary.
Rationale:

Configuring this setting for the SSH daemon provides additional assurance that remove login via SSH will require a password, even in the event of misconfiguration elsewhere.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^RhostsRSAAuthentication' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Disable SSH Support for Rhosts RSA Authentication
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^RhostsRSAAuthentication
    line: RhostsRSAAuthentication no
    validate: sshd -t -f %s
  tags:
    - sshd_disable_rhosts_rsa
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(a)
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040330

Enable Encrypted X11 Forwarding   [ref]rule

By default, remote X11 connections are not encrypted when initiated by users. SSH has the capability to encrypt remote X11 connections when SSH's X11Forwarding option is enabled.

To enable X11 Forwarding, add or correct the following line in /etc/ssh/sshd_config:

X11Forwarding yes

Rationale:

Open X displays allow an attacker to capture keystrokes and to execute commands remotely.

Severity:  high

Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Enable Encrypted X11 Forwarding
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^X11Forwarding
    line: X11Forwarding yes
    validate: sshd -t -f %s
  tags:
    - sshd_enable_x11_forwarding
    - high_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-2(1)(b)
    - NIST-800-171-3.1.13
    - DISA-STIG-RHEL-07-040710

Use Only FIPS 140-2 Validated Ciphers   [ref]rule

Limit the ciphers to those algorithms which are FIPS-approved. Counter (CTR) mode is also preferred over cipher-block chaining (CBC) mode. The following line in /etc/ssh/sshd_config demonstrates use of FIPS 140-2 validated ciphers:

Ciphers aes128-ctr,aes192-ctr,aes256-ctr


The following ciphers are FIPS 140-2 certified on RHEL 7:
- aes128-ctr
- aes192-ctr
- aes256-ctr
- aes128-cbc
- aes192-cbc
- aes256-cbc
- 3des-cbc
- rijndael-cbc@lysator.liu.se

Any combination of the above ciphers will pass this check. Official FIPS 140-2 paperwork for RHEL7 can be found at http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2630.pdf.

Rationale:

Unapproved mechanisms that are used for authentication to the cryptographic module are not verified and therefore cannot be relied upon to provide confidentiality or integrity, and system data may be compromised.
Operating systems utilizing encryption are required to use FIPS-compliant mechanisms for authenticating to cryptographic modules.
FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules utilize authentication that meets industry and government requirements. For government systems, this allows Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^Ciphers' 'aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Use Only Approved Ciphers
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^Ciphers
    line: Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_use_approved_ciphers
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-AC-17(2)
    - NIST-800-53-AU-10(5)
    - NIST-800-53-CM-6(b)
    - NIST-800-53-IA-5(1)(c)
    - NIST-800-53-IA-7
    - NIST-800-171-3.1.13
    - NIST-800-171-3.13.11
    - NIST-800-171-3.13.8
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-040110

Disable Host-Based Authentication   [ref]rule

SSH's cryptographic host-based authentication is more secure than .rhosts authentication. However, it is not recommended that hosts unilaterally trust one another, even within an organization.

To disable host-based authentication, add or correct the following line in /etc/ssh/sshd_config:

HostbasedAuthentication no

Rationale:

SSH trust relationships mean a compromise on one host can allow an attacker to move trivially to other hosts.

Severity:  medium

Remediation Shell script:   (show)

grep -q ^HostbasedAuthentication /etc/ssh/sshd_config && \
  sed -i "s/HostbasedAuthentication.*/HostbasedAuthentication no/g" /etc/ssh/sshd_config
if ! [ $? -eq 0 ]; then
    echo "HostbasedAuthentication no" >> /etc/ssh/sshd_config
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Disable Host-Based Authentication
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^HostbasedAuthentication
    line: HostbasedAuthentication no
  tags:
    - disable_host_auth
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-CM-6(b)
    - NIST-800-171-3.1.12
    - CJIS-5.5.6
    - DISA-STIG-RHEL-07-010470

Enable Use of Strict Mode Checking   [ref]rule

SSHs StrictModes option checks file and ownership permissions in the user's home directory .ssh folder before accepting login. If world- writable permissions are found, logon is rejected. To enable StrictModes in SSH, add or correct the following line in the /etc/ssh/sshd_config file:

StrictModes yes

Rationale:

If other users have access to modify user-specific SSH configuration files, they may be able to log into the system as another user.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^StrictModes' 'yes' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Enable Use of Strict Mode Checking"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: (?i)^#?strictmodes
    line: StrictModes yes
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_enable_strictmodes
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040450

Enable Use of Privilege Separation   [ref]rule

When enabled, SSH will create an unprivileged child process that has the privilege of the authenticated user. To enable privilege separation in SSH, add or correct the following line in the /etc/ssh/sshd_config file:

UsePrivilegeSeparation sandbox

Rationale:

SSH daemon privilege separation causes the SSH process to drop root privileges when not needed which would decrease the impact of software vulnerabilities in the unprivileged section.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^UsePrivilegeSeparation' 'sandbox' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Enable use of Privilege Separation"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: (?i)^#?useprivilegeseparation
    line: UsePrivilegeSeparation sandbox
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_use_priv_separation
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040460

Print Last Log   [ref]rule

When enabled, SSH will display the date and time of the last successful account logon. To enable LastLog in SSH, add or correct the following line in the /etc/ssh/sshd_config file:

PrintLastLog yes

Rationale:

Providing users feedback on when account accesses last occurred facilitates user recognition and reporting of unauthorized account use.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^PrintLastLog' 'yes' '' '%s %s'
Remediation Ansible snippet:   (show)

- name: Print last log
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: ^PrintLastLog
    line: PrintLastLog yes
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_print_last_log
    - medium_severity
    - NIST-800-53-AC-9
    - DISA-STIG-RHEL-07-040360

Disable GSSAPI Authentication   [ref]rule

Unless needed, SSH should not permit extraneous or unnecessary authentication mechanisms like GSSAPI. To disable GSSAPI authentication, add or correct the following line in the /etc/ssh/sshd_config file:

GSSAPIAuthentication no

Rationale:

GSSAPI authentication is used to provide additional authentication mechanisms to applications. Allowing GSSAPI authentication through SSH exposes the system's GSSAPI to remote hosts, increasing the attack surface of the system.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^GSSAPIAuthentication' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Disable GSSAPI Authentication"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: (?i)^#?gssapiauthentication
    line: GSSAPIAuthentication no
    validate: sshd -t -f %s
  #notify: sshd -t -f %s
  tags:
    - sshd_disable_gssapi_auth
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(c)
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040430

Disable Compression Or Set Compression to delayed   [ref]rule

Compression is useful for slow network connections over long distances but can cause performance issues on local LANs. If use of compression is required, it should be enabled only after a user has authenticated; otherwise , it should be disabled. To disable compression or delay compression until after a user has successfully authenticated, add or correct the following line in the /etc/ssh/sshd_config file:

Compression no
or
Compression delayed

Rationale:

If compression is allowed in an SSH connection prior to authentication, vulnerabilities in the compression software could result in compromise of the system from an unauthenticated connection, potentially wih root privileges.

Severity:  medium

Remediation Shell script:   (show)

# 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-${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/ssh/sshd_config' '^Compression' 'no' '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Disable Compression or Set Compression to delayed"
  lineinfile:
    create: yes
    dest: /etc/ssh/sshd_config
    regexp: (?i)^#?compression 
    line: Compression delayed
    validate: sshd -t -f %s
  #notify: restart sshd
  tags:
    - sshd_disable_compression
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-171-3.1.12
    - DISA-STIG-RHEL-07-040470

Install the OpenSSH Server Package   [ref]rule

The openssh-server package should be installed. The openssh-server package can be installed with the following command:

$ sudo yum install openssh-server

Rationale:

Without protection of the transmitted information, confidentiality, and integrity may be compromised because unprotected communications can be intercepted and either read or altered.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable
# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install openssh-server
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
- name: Ensure openssh-server is installed
  package:
    name: openssh-server
    state: present
  tags:
    - package_openssh-server_installed
    - medium_severity
    - enable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-SC-8
    - DISA-STIG-RHEL-07-040300
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
include install_openssh-server

class install_openssh-server {
  package { 'openssh-server':
    ensure => 'installed',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable

package --add=openssh-server

Enable the OpenSSH Service   [ref]rule

The SSH server service, sshd, is commonly needed. The sshd service can be enabled with the following command:

$ sudo systemctl enable sshd.service

Rationale:

Without protection of the transmitted information, confidentiality, and integrity may be compromised because unprotected communications can be intercepted and either read or altered.

This checklist item applies to both internal and external networks and all types of information system components from which information can be transmitted (e.g., servers, mobile devices, notebook computers, printers, copiers, scanners, etc). Communication paths outside the physical protection of a controlled boundary are exposed to the possibility of interception and modification.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'sshd.service'
"$SYSTEMCTL_EXEC" enable 'sshd.service'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
- name: Enable service sshd
  service:
    name: sshd
    enabled: "yes"
    state: "started"
  tags:
    - service_sshd_enabled
    - medium_severity
    - enable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-SC-8
    - NIST-800-171-3.1.13
    - NIST-800-171-3.5.4
    - NIST-800-171-3.13.8
    - DISA-STIG-RHEL-07-040310

Verify Permissions on SSH Server Public *.pub Key Files   [ref]rule

To properly set the permissions of /etc/ssh/*.pub, run the command:

$ sudo chmod 0644 /etc/ssh/*.pub

Rationale:

If a public host key file is modified by an unauthorized user, the SSH service may be compromised.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:configure

chmod 0644 /etc/ssh/*.pub
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:configure
- name: Ensure permission 0644 on /etc/ssh/*.pub
  file:
    path: /etc/ssh/*.pub
    mode: 0644
  tags:
    - file_permissions_sshd_pub_key
    - medium_severity
    - configure_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - NIST-800-171-3.1.13
    - NIST-800-171-3.13.10
    - DISA-STIG-RHEL-07-040410
Remediation Puppet snippet:   (show)

include ssh_public_key_perms

class ssh_public_key_perms {
  exec { 'sshd_pub_key':
    command => "chmod 0644 /etc/ssh/*.pub",
    path    => '/bin:/usr/bin'
  }
}

Verify Permissions on SSH Server Private *_key Key Files   [ref]rule

To properly set the permissions of /etc/ssh/*_key, run the command:

$ sudo chmod 0640 /etc/ssh/*_key

Rationale:

If an unauthorized user obtains the private SSH host key file, the host could be impersonated.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:configure

chmod 0640 /etc/ssh/*_key
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:configure
- name: Ensure permission 0640 on /etc/ssh/*_key
  file:
    path: /etc/ssh/*_key
    mode: 0640
  tags:
    - file_permissions_sshd_private_key
    - medium_severity
    - configure_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-6
    - NIST-800-171-3.1.13
    - NIST-800-171-3.13.10
    - DISA-STIG-RHEL-07-040420
Remediation Puppet snippet:   (show)

include ssh_private_key_perms

class ssh_private_key_perms {
  exec { 'sshd_priv_key':
    command => "chmod 0640 /etc/ssh/*_key",
    path    => '/bin:/usr/bin'
  }
}

System Settings   [ref]group

Contains rules that check correct system settings.

contains 197 rules

Installing and Maintaining Software   [ref]group

The following sections contain information on security-relevant choices during the initial operating system installation process and the setup of software updates.

contains 35 rules

Disk Partitioning   [ref]group

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.

contains 4 rules

Ensure /home Located On Separate Partition   [ref]rule

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

Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:high
Strategy:enable

part /home

Ensure /var Located On Separate Partition   [ref]rule

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

Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:high
Strategy:enable

part /var

Ensure /tmp Located On Separate Partition   [ref]rule

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

Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:high
Strategy:enable

part /tmp

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

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

Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:high
Strategy:enable

part /var/log/audit

Sudo   [ref]group

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.

contains 2 rules

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

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

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

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

System and Software Integrity   [ref]group

System and software integrity can be gained by installing antivirus, increasing system encryption strength with FIPS, verifying installed software, enabling SELinux, installing an Intrusion Prevention System, etc. However, installing or enabling integrity checking tools cannot prevent intrusions, but they can detect that an intrusion may have occurred. Requirements for integrity checking may be highly dependent on the environment in which the system will be used. Snapshot-based approaches such as AIDE may induce considerable overhead in the presence of frequent software updates.

contains 13 rules

Operating System Vendor Support and Certification   [ref]group

The assurance of a vendor to provide operating system support and maintenance for their product is an important criterion to ensure product stability and security over the life of the product. A certified product that follows the necessary standards and government certification requirements guarantees that known software vulnerabilities will be remediated, and proper guidance for protecting and securing the operating system will be given.

contains 1 rule

The Installed Operating System Is Vendor Supported and Certified   [ref]rule

The installed operating system must be maintained and certified by a vendor. Red Hat Enterprise Linux is supported by Red Hat, Inc. As the Red Hat Enterprise Linux vendor, Red Hat, Inc. is responsible for providing security patches as well as meeting and maintaining goverment certifications and standards.

Warning:  There is no remediation besides switching to a different operating system.
Rationale:

An operating system is considered "supported" if the vendor continues to provide security patches for the product as well as maintain government certification requirements. With an unsupported release, it will not be possible to resolve security issue discovered in the system software as well as meet government certifications.

Severity:  high

Federal Information Processing Standard (FIPS)   [ref]group

The Federal Information Processing Standard (FIPS) is a computer security standard which is developed by the U.S. Government and industry working groups to validate the quality of cryptographic modules. The FIPS standard provides four security levels to ensure adequate coverage of different industries, implementation of cryptographic modules, and organizational sizes and requirements.

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

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

contains 1 rule

Enable FIPS Mode in GRUB2   [ref]rule

To ensure FIPS mode is enabled, install package dracut-fips, and rebuild initramfs by running the following commands:

$ sudo yum install dracut-fips
dracut -f
After the dracut command has been run, add the argument fips=1 to the default GRUB 2 command line for the Linux operating system in /etc/default/grub, in the manner below:
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=VolGroup/LogVol06 rd.lvm.lv=VolGroup/lv_swap rhgb quiet rd.shell=0 fips=1"
Finally, rebuild the grub.cfg file by using the
grub2-mkconfig -o
command as follows:
  • On BIOS-based machines, issue the following command as root:
    ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
  • On UEFI-based machines, issue the following command as root:
    ~]# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg

Warning:  Running
dracut -f
will overwrite the existing initramfs file.
Warning:  The system needs to be rebooted for these changes to take effect.
Warning:  The ability to enable FIPS does not denote FIPS compliancy or certification. Red Hat, Inc. and Red Hat Enterprise Linux are respectively FIPS certified and compliant. Community projects such as CentOS, Scientific Linux, Fedora, etc. do not necessarily meet FIPS certification and compliancy. Therefore, non-certified vendors and/or projects do not meet this requirement even if technically feasible.

See http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401vend.htm for a list of FIPS certified vendors.
Rationale:

Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.

Severity:  high

Endpoint Protection Software   [ref]group

Endpoint protection security software that is not provided or supported by Red Hat can be installed to provide complementary or duplicative security capabilities to those provided by the base platform. Add-on software may not be appropriate for some specialized systems.

contains 2 rules

McAfee Endpoint Security Software   [ref]group

In DoD environments, McAfee Host-based Security System (HBSS) and VirusScan Enterprise for Linux (VSEL) is required to be installed on all systems.

contains 2 rules

Install McAfee Virus Scanning Software   [ref]rule

Install McAfee VirusScan Enterprise for Linux antivirus software which is provided for DoD systems and uses signatures to search for the presence of viruses on the filesystem.

Warning:  Due to McAfee HIPS being 3rd party software, automated remediation is not available for this configuration check.
Rationale:

Virus scanning software can be used to detect if a system has been compromised by computer viruses, as well as to limit their spread to other systems.

Severity:  high

Virus Scanning Software Definitions Are Updated   [ref]rule

Ensure virus definition files are no older than 7 days or their last release.

Rationale:

Virus scanning software can be used to detect if a system has been compromised by computer viruses, as well as to limit their spread to other systems.

Severity:  medium

Software Integrity Checking   [ref]group

Both the AIDE (Advanced Intrusion Detection Environment) software and the RPM package management system provide mechanisms for verifying the integrity of installed software. AIDE uses snapshots of file metadata (such as hashes) and compares these to current system files in order to detect changes.

The RPM package management system can conduct integrity checks by comparing information in its metadata database with files installed on the system.

contains 9 rules

Verify Integrity with RPM   [ref]group

The RPM package management system includes the ability to verify the integrity of installed packages by comparing the installed files with information about the files taken from the package metadata stored in the RPM database. Although an attacker could corrupt the RPM database (analogous to attacking the AIDE database as described above), this check can still reveal modification of important files. To list which files on the system differ from what is expected by the RPM database:

$ rpm -qVa
See the man page for rpm to see a complete explanation of each column.

contains 3 rules

Verify and Correct File Permissions with RPM   [ref]rule

The RPM package management system can check file access permissions of installed software packages, including many that are important to system security. Verify that the file permissions of system files and commands match vendor values. Check the file permissions with the following command:

$ sudo rpm -Va | grep '^.M'
Output indicates files that do not match vendor defaults. After locating a file with incorrect permissions, run the following command to determine which package owns it:
$ rpm -qf FILENAME

Next, run the following command to reset its permissions to the correct values:
$ sudo rpm --quiet --setperms PACKAGENAME

Warning:  Note: Due to a bug in the gdm package, the RPM verify command may continue to fail even after file permissions have been correctly set on /var/log/gdm. This is being tracked in Red Hat Bugzilla #1277603.
Rationale:

Permissions on system binaries and configuration files that are too generous could allow an unauthorized user to gain privileges that they should not have. The permissions set by the vendor should be maintained. Any deviations from this baseline should be investigated.

Severity:  high

Remediation Shell script:   (show)

Complexity:high
Disruption:medium
Strategy:restrict

# Declare array to hold list of RPM packages we need to correct permissions for
declare -a SETPERMS_RPM_LIST

# Create a list of files on the system having permissions different from what
# is expected by the RPM database
FILES_WITH_INCORRECT_PERMS=($(rpm -Va --nofiledigest | grep '^.M' | awk '{print $NF}'))

# For each file path from that list:
# * Determine the RPM package the file path is shipped by,
# * Include it into SETPERMS_RPM_LIST array

for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}"
do
	RPM_PACKAGE=$(rpm -qf "$FILE_PATH")
	SETPERMS_RPM_LIST=("${SETPERMS_RPM_LIST[@]}" "$RPM_PACKAGE")
done

# Remove duplicate mention of same RPM in $SETPERMS_RPM_LIST (if any)
SETPERMS_RPM_LIST=( $(echo "${SETPERMS_RPM_LIST[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ') )

# For each of the RPM packages left in the list -- reset its permissions to the
# correct values
for RPM_PACKAGE in "${SETPERMS_RPM_LIST[@]}"
do
	rpm --quiet --setperms "${RPM_PACKAGE}"
done
Remediation Ansible snippet:   (show)

Complexity:high
Disruption:medium
Strategy:restrict
- name: "Read list of files with incorrect permissions"
  shell: "rpm -Va --nofiledigest | awk '/^.M/ {print $NF}'"
  register: files_with_incorrect_permissions
  failed_when: False
  changed_when: False
  check_mode: no
  tags:
    - rpm_verify_permissions
    - high_severity
    - restrict_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9(1)
    - NIST-800-53-AU-9(3)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010010

- name: "Correct file permissions with RPM"
  shell: "rpm --quiet --setperms $(rpm -qf '{{item}}')"
  with_items: "{{ files_with_incorrect_permissions.stdout_lines }}"
  when: files_with_incorrect_permissions.stdout_lines | length > 0
  tags:
    - rpm_verify_permissions
    - high_severity
    - restrict_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9(1)
    - NIST-800-53-AU-9(3)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010010

Verify and Correct Ownership with RPM   [ref]rule

The RPM package management system can check file ownership permissions of installed software packages, including many that are important to system security. After locating a file with incorrect permissions, which can be found with

rpm -Va | grep "^.....\(U\|.G\)"
run the following command to determine which package owns it:
$ rpm -qf FILENAME
Next, run the following command to reset its permissions to the correct values:
$ sudo rpm --setugids PACKAGENAME

Warning:  Note: Due to a bug in the gdm package, the RPM verify command may continue to fail even after file permissions have been correctly set on /var/log/gdm. This is being tracked in Red Hat Bugzilla #1277603.
Rationale:

Ownership of binaries and configuration files that is incorrect could allow an unauthorized user to gain privileges that they should not have. The ownership set by the vendor should be maintained. Any deviations from this baseline should be investigated.

Severity:  high

Remediation Shell script:   (show)

Complexity:high
Disruption:medium
Strategy:restrict

# Declare array to hold list of RPM packages we need to correct permissions for
SETPERMS_RPM_LIST=()

# Create a list of files on the system having permissions different from what
# is expected by the RPM database
FILES_WITH_INCORRECT_PERMS=($(rpm -Va --nofiledigest | grep "^.*\(G\|U\)" | cut -d ' ' -f4-))

# For each file path from that list:
# * Determine the RPM package the file path is shipped by,
# * Include it into SETPERMS_RPM_LIST array

for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}"
do
        RPM_PACKAGE=$(rpm -qf "$FILE_PATH")
	SETPERMS_RPM_LIST+=("$RPM_PACKAGE")
done

# Remove duplicate mention of same RPM in $SETPERMS_RPM_LIST (if any)
SETPERMS_RPM_LIST=( $(printf "%s\n" "${SETPERMS_RPM_LIST[@]}" | sort -u) )

# For each of the RPM packages left in the list -- reset its permissions to the
# correct values
for RPM_PACKAGE in "${SETPERMS_RPM_LIST[@]}"
do
        rpm --setugids "${RPM_PACKAGE}"
done
Remediation Ansible snippet:   (show)

Complexity:high
Disruption:medium
Strategy:restrict
- name: "Read list of files with incorrect ownership"
  shell: rpm -Va --nofiledigest | grep "^.....\(U\|.G\)" | awk '{print $NF}'
  register: files_with_incorrect_ownership
  failed_when: False
  changed_when: False
  check_mode: no
  tags:
    - rpm_verify_ownership
    - high_severity
    - restrict_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9(1)
    - NIST-800-53-AU-9(3)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    
- name: Create list of uniq packages
  shell: "rpm -qf {{ files_with_incorrect_ownership.stdout_lines }}|sort |uniq"
  register: uniq_list_of_packages
  when: files_with_incorrect_ownership.stdout_lines | length > 0
  tags:
    - rpm_verify_ownership
    - high_severity
    - restrict_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9(1)
    - NIST-800-53-AU-9(3)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1

- name: "Correct file ownership with RPM"
  shell: "rpm --quiet --setugids '{{item}}'"
  with_items: "{{ uniq_list_of_packages.stdout_lines }}"
  when: files_with_incorrect_ownership.stdout_lines | length > 0
  tags:
    - rpm_verify_ownership
    - high_severity
    - restrict_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9(1)
    - NIST-800-53-AU-9(3)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1

Verify File Hashes with RPM   [ref]rule

Without cryptographic integrity protections, system executables and files can be altered by unauthorized users without detection. The RPM package management system can check the hashes of installed software packages, including many that are important to system security. To verify that the cryptographic hash of system files and commands match vendor values, run the following command to list which files on the system have hashes that differ from what is expected by the RPM database:

$ rpm -Va | grep '^..5'
A "c" in the second column indicates that a file is a configuration file, which may appropriately be expected to change. If the file was not expected to change, investigate the cause of the change using audit logs or other means. The package can then be reinstalled to restore the file. Run the following command to determine which package owns the file:
$ rpm -qf FILENAME
The package can be reinstalled from a yum repository using the command:
$ sudo yum reinstall PACKAGENAME
Alternatively, the package can be reinstalled from trusted media using the command:
$ sudo rpm -Uvh PACKAGENAME

Rationale:

The hashes of important files like system executables should match the information given by the RPM database. Executables with erroneous hashes could be a sign of nefarious activity on the system.

Severity:  high

Remediation Shell script:   (show)


# Find which files have incorrect hash (not in /etc, because there are all system related config. files) and then get files names
files_with_incorrect_hash="$(rpm -Va | grep -E '^..5.* /(bin|sbin|lib|lib64|usr)/' | awk '{print $NF}' )"
# From files names get package names and change newline to space, because rpm writes each package to new line
packages_to_reinstall="$(rpm -qf $files_with_incorrect_hash | tr '\n' ' ')"

yum reinstall -y $packages_to_reinstall
Remediation Ansible snippet:   (show)

Complexity:high
Disruption:medium
- name: "Set fact: Package manager reinstall command (dnf)"
  set_fact:
    package_manager_reinstall_cmd: dnf reinstall -y
  when: ansible_distribution == "Fedora"
  tags:
    - rpm_verify_hashes
    - high_severity
    - unknown_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SI-7(1)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010020

- name: "Set fact: Package manager reinstall command (yum)"
  set_fact:
    package_manager_reinstall_cmd: yum reinstall -y
  when: ansible_distribution == "RedHat" or ansible_distribution == "OracleLinux"
  tags:
    - rpm_verify_hashes
    - high_severity
    - unknown_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SI-7(1)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010020

- name: "Read files with incorrect hash"
  shell: "rpm -Va | grep -E '^..5.* /(bin|sbin|lib|lib64|usr)/' | awk '{print $NF}'"
  register: files_with_incorrect_hash
  changed_when: False
  when: package_manager_reinstall_cmd is defined
  check_mode: no
  tags:
    - rpm_verify_hashes
    - high_severity
    - unknown_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SI-7(1)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010020

- name: "Reinstall packages of files with incorrect hash"
  shell: "{{package_manager_reinstall_cmd}} $(rpm -qf '{{item}}')"
  with_items: "{{ files_with_incorrect_hash.stdout_lines }}"
  when: package_manager_reinstall_cmd is defined and (files_with_incorrect_hash.stdout_lines | length > 0)
  tags:
    - rpm_verify_hashes
    - high_severity
    - unknown_strategy
    - high_complexity
    - medium_disruption
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SI-7(1)
    - NIST-800-171-3.3.8
    - NIST-800-171-3.4.1
    - PCI-DSS-Req-11.5
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-010020

Verify Integrity with AIDE   [ref]group

AIDE conducts integrity checks by comparing information about files with previously-gathered information. Ideally, the AIDE database is created immediately after initial system configuration, and then again after any software update. AIDE is highly configurable, with further configuration information located in /usr/share/doc/aide-VERSION.

contains 6 rules

Install AIDE   [ref]rule

The aide package can be installed with the following command:

$ sudo yum install aide

Rationale:

The AIDE package must be installed if it is to be available for integrity checking.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable
# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install aide
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
- name: Ensure aide is installed
  package:
    name: aide
    state: present
  tags:
    - package_aide_installed
    - medium_severity
    - enable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-3(d)
    - NIST-800-53-CM-3(e)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SC-28
    - NIST-800-53-SI-7
    - PCI-DSS-Req-11.5
    - CJIS-5.10.1.3
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
include install_aide

class install_aide {
  package { 'aide':
    ensure => 'installed',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable

package --add=aide

Configure AIDE to Verify Extended Attributes   [ref]rule

By default, the xattrs option is added to the FIPSR ruleset in AIDE. If using a custom ruleset or the xattrs option is missing, add xattrs to the appropriate ruleset. For example, add xattrs to the following line in /etc/aide.conf:

FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
AIDE rules can be configured in multiple ways; this is merely one example that is already configured by default.

Rationale:

Extended attributes in file systems are used to contain arbitrary data and file metadata with security implications.

Severity:  medium

Remediation Shell script:   (show)

# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install aide

aide_conf="/etc/aide.conf"

groups=$(LC_ALL=C grep "^[A-Z]\+" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u)

for group in $groups
do
	config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ')

	if ! [[ $config = *xattrs* ]]
	then
		if [[ -z $config ]]
		then
			config="xattrs"
		else
			config=$config"+xattrs"
		fi
	fi
	sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf
done

Configure AIDE to Verify Access Control Lists (ACLs)   [ref]rule

By default, the acl option is added to the FIPSR ruleset in AIDE. If using a custom ruleset or the acl option is missing, add acl to the appropriate ruleset. For example, add acl to the following line in /etc/aide.conf:

FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
AIDE rules can be configured in multiple ways; this is merely one example that is already configured by default.

Rationale:

ACLs can provide permissions beyond those permitted through the file mode and must be verified by the file integrity tools.

Severity:  medium

Remediation Shell script:   (show)

# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install aide

aide_conf="/etc/aide.conf"

groups=$(LC_ALL=C grep "^[A-Z]\+" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u)

for group in $groups
do
	config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ')

	if ! [[ $config = *acl* ]]
	then
		if [[ -z $config ]]
		then
			config="acl"
		else
			config=$config"+acl"
		fi
	fi
	sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf
done

Configure AIDE to Use FIPS 140-2 for Validating Hashes   [ref]rule

By default, the sha512 option is added to the NORMAL ruleset in AIDE. If using a custom ruleset or the sha512 option is missing, add sha512 to the appropriate ruleset. For example, add sha512 to the following line in /etc/aide.conf:

NORMAL = FIPSR+sha512
AIDE rules can be configured in multiple ways; this is merely one example that is already configured by default.

Rationale:

File integrity tools use cryptographic hashes for verifying file contents and directories have not been altered. These hashes must be FIPS 140-2 approved cryptographic hashes.

Severity:  medium

Configure Notification of Post-AIDE Scan Details   [ref]rule

AIDE should notify appropriate personnel of the details of a scan after the scan has been run. If AIDE has already been configured for periodic execution in /etc/crontab, append the following line to the existing AIDE line:

 | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost
Otherwise, add the following line to /etc/crontab:
05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost
AIDE can be executed periodically through other means; this is merely one example.

Rationale:

Unauthorized changes to the baseline configuration could make the system vulnerable to various attacks or allow unauthorized access to the operating system. Changes to operating system configurations can have unintended side effects, some of which may be relevant to security.

Detecting such changes and providing an automated response can help avoid unintended, negative consequences that could ultimately affect the security state of the operating system. The operating system's Information Management Officer (IMO)/Information System Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or monitoring system trap when there is an unauthorized modification of a configuration item.

Severity:  medium

Remediation Shell script:   (show)

# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install aide

CRONTAB=/etc/crontab
CRONDIRS='/etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.monthly'

if [ -f /var/spool/cron/root ]; then
	VARSPOOL=/var/spool/cron/root
fi

if ! grep -qR '^.*\/usr\/sbin\/aide\s*\-\-check.*|.*\/bin\/mail\s*-s\s*".*"\s*root@.*$' $CRONTAB $VARSPOOL $CRONDIRS; then
	echo '0 5 * * * root /usr/sbin/aide  --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost' >> $CRONTAB
fi

Configure Periodic Execution of AIDE   [ref]rule

At a minimum, AIDE should be configured to run a weekly scan. At most, AIDE should be run daily. To implement a daily execution of AIDE at 4:05am using cron, add the following line to /etc/crontab:

05 4 * * * root /usr/sbin/aide --check
To implement a weekly execution of AIDE at 4:05am using cron, add the following line to /etc/crontab:
05 4 * * 0 root /usr/sbin/aide --check
AIDE can be executed periodically through other means; this is merely one example. The usage of cron's special time codes, such as @daily and @weekly is acceptable.

Rationale:

By default, AIDE does not install itself for periodic execution. Periodically running AIDE is necessary to reveal unexpected changes in installed files.

Unauthorized changes to the baseline configuration could make the system vulnerable to various attacks or allow unauthorized access to the operating system. Changes to operating system configurations can have unintended side effects, some of which may be relevant to security.

Detecting such changes and providing an automated response can help avoid unintended, negative consequences that could ultimately affect the security state of the operating system. The operating system's Information Management Officer (IMO)/Information System Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or monitoring system trap when there is an unauthorized modification of a configuration item.

Severity:  medium

Remediation Shell script:   (show)

# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install aide

if ! grep -q "/usr/sbin/aide --check" /etc/crontab ; then
    echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Ensure AIDE is installed"
  package:
    name: "{{item}}"
    state: present
  with_items:
    - aide
  tags:
    - aide_periodic_cron_checking
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-3(d)
    - NIST-800-53-CM-3(e)
    - NIST-800-53-CM-3(5)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SC-28
    - NIST-800-53-SI-7
    - PCI-DSS-Req-11.5
    - CJIS-5.10.1.3
    - DISA-STIG-RHEL-07-020030

- name: "Configure Periodic Execution of AIDE"
  cron:
    name: "run AIDE check"
    minute: 05
    hour: 04
    weekday: 0
    user: root
    job: "/usr/sbin/aide --check"
  tags:
    - aide_periodic_cron_checking
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-3(d)
    - NIST-800-53-CM-3(e)
    - NIST-800-53-CM-3(5)
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-6(3)
    - NIST-800-53-SC-28
    - NIST-800-53-SI-7
    - PCI-DSS-Req-11.5
    - CJIS-5.10.1.3
    - DISA-STIG-RHEL-07-020030

Updating Software   [ref]group

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 7 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.

contains 5 rules

Ensure Software Patches Installed   [ref]rule

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

Remediation Shell script:   (show)

Complexity:low
Disruption:high
Reboot:true
Strategy:patch
yum -y update
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:high
Reboot:true
Strategy:patch
- name: "Security patches are up to date"
  package:
    name: "*"
    state: "latest"
  tags:
    - security_patches_up_to_date
    - high_severity
    - patch_strategy
    - low_complexity
    - high_disruption
    - NIST-800-53-SI-2
    - NIST-800-53-SI-2(c)
    - NIST-800-53-MA-1(b)
    - PCI-DSS-Req-6.2
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-020260

Ensure gpgcheck Enabled for Local Packages   [ref]rule

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

Remediation Shell script:   (show)

# 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-${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' ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: Check existence of yum on Fedora
  stat:
    path: /etc/yum.conf
  register: yum_config_file
  check_mode: no
  when: ansible_distribution == "Fedora"

# Old versions of Fedora use yum

- name: Ensure GPG check Enabled for Local Packages (Yum)
  ini_file:
    dest: "{{item}}"
    section: main
    option: localpkg_gpgcheck
    value: 1
    create: True
  with_items: "/etc/yum.conf"
  when: ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or yum_config_file.stat.exists
  tags:
    - ensure_gpgcheck_local_packages
    - high_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-5(3)
    - NIST-800-171-3.4.8
    - DISA-STIG-RHEL-07-020060

- name: Ensure GPG check Enabled for Local Packages (DNF)
  ini_file:
    dest: "{{item}}"
    section: main
    option: localpkg_gpgcheck
    value: 1
    create: True
  with_items: "/etc/dnf/dnf.conf"
  when: ansible_distribution == "Fedora"
  tags:
    - ensure_gpgcheck_local_packages
    - high_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-5(3)
    - NIST-800-171-3.4.8
    - DISA-STIG-RHEL-07-020060

Ensure yum Removes Previous Package Versions   [ref]rule

yum should be configured to remove previous software components after previous versions have been installed. To configure yum to remove the previous software components after updating, set the clean_requirements_on_remove to 1 in /etc/yum.conf.

Rationale:

Previous versions of software components that are not removed from the information system after updates have been installed may be exploited by some adversaries.

Severity:  low

Remediation Shell script:   (show)


if grep --silent ^clean_requirements_on_remove /etc/yum.conf ; then
        sed -i "s/^clean_requirements_on_remove.*/clean_requirements_on_remove=1/g" /etc/yum.conf
else
        echo -e "\n# Set clean_requirements_on_remove to 1 per security requirements" >> /etc/yum.conf
        echo "clean_requirements_on_remove=1" >> /etc/yum.conf
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: "Ensure YUM Removes Previous Package Versions"
  lineinfile:
      dest: /etc/yum.conf
      regexp: ^#?clean_requirements_on_remove
      line: clean_requirements_on_remove=1
      insertafter: '\[main\]'
  tags:
    - clean_components_post_updating
    - low_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-SI-2(6)
    - NIST-800-171-3.4.8
    - DISA-STIG-RHEL-07-020200

Ensure gpgcheck Enabled In Main yum Configuration   [ref]rule

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

Remediation Shell script:   (show)

# 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-${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' ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: Check existence of yum on Fedora
  stat:
    path: /etc/yum.conf
  register: yum_config_file
  check_mode: no
  when: ansible_distribution == "Fedora"

# Old versions of Fedora use yum

- name: Ensure GPG check is globally activated (yum)
  ini_file:
    dest: "{{item}}"
    section: main
    option: gpgcheck
    value: 1
    create: False
  with_items: "/etc/yum.conf"
  when: ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or yum_config_file.stat.exists
  tags:
    - ensure_gpgcheck_globally_activated
    - high_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-5(3)
    - NIST-800-53-SI-7
    - NIST-800-53-MA-1(b)
    - NIST-800-171-3.4.8
    - PCI-DSS-Req-6.2
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-020050

- name: Ensure GPG check is globally activated (dnf)
  ini_file:
    dest: "{{item}}"
    section: main
    option: gpgcheck
    value: 1
    create: False
  with_items: "/etc/dnf/dnf.conf"
  when: ansible_distribution == "Fedora"
  tags:
    - ensure_gpgcheck_globally_activated
    - high_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-5(3)
    - NIST-800-53-SI-7
    - NIST-800-53-MA-1(b)
    - NIST-800-171-3.4.8
    - PCI-DSS-Req-6.2
    - CJIS-5.10.4.1
    - DISA-STIG-RHEL-07-020050

GNOME Desktop Environment   [ref]group

GNOME is a graphical desktop environment bundled with many Linux distributions that allow users to easily interact with the operating system graphically rather than textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user switching contexts as well as display server management.

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

For more information on GNOME and the GNOME Project, see https://www.gnome.org.

contains 11 rules

Configure GNOME Screen Locking   [ref]group

In the default GNOME3 desktop, the screen can be locked by selecting the user name in the far right corner of the main panel and selecting Lock.

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

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

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

For more information about enforcing preferences in the GNOME3 environment using the DConf configuration system, see http://wiki.gnome.org/dconf and the man page dconf(1). For Red Hat specific information on configuring DConf settings, see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Desktop_Migration_and_Administration_Guide/part-Configuration_and_Administration.html

contains 8 rules

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

If not already configured, ensure that users cannot change GNOME3 session idle settings by adding /org/gnome/desktop/session/idle-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:

/org/gnome/desktop/session/idle-delay
After the settings have been set, run dconf update.

Rationale:

A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_lock 'org/gnome/desktop/session' 'idle-delay' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium

- name: "Prevent user modification of GNOME Session idle-delay"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/session/idle-delay'
    line: '/org/gnome/desktop/session/idle-delay'
    create: yes
  tags:
    - dconf_gnome_session_idle_user_locks
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - DISA-STIG-RHEL-07-010082

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

To activate the locking delay of the screensaver in the GNOME3 desktop when the screensaver is activated, add or set lock-delay to uint32 5 in /etc/dconf/db/local.d/00-security-settings. For example:

[org/gnome/desktop/screensaver]
lock-delay=uint32 5
Once the setting has been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-delay
After the settings have been set, run dconf update.

Rationale:

A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

Severity:  medium

Remediation Shell script:   (show)


var_screensaver_lock_delay="5"
function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_settings 'org/gnome/desktop/screensaver' 'lock-delay' "uint32 ${var_screensaver_lock_delay}" 'local.d' '00-security-settings'
dconf_lock 'org/gnome/desktop/screensaver' 'lock-delay' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: "Set GNOME3 Screensaver Lock Delay After Activation Period"
  ini_file:
    dest: "/etc/dconf/db/local.d/00-security-settings"
    section: "org/gnome/desktop/screensaver"
    option: lock-delay
    value: uint32 5
    create: yes
  tags:
    - dconf_gnome_screensaver_lock_delay
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - DISA-STIG-RHEL-07-010110

- name: "Prevent user modification of GNOME lock-delay"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/screensaver/lock-delay'
    line: '/org/gnome/desktop/screensaver/lock-delay'
    create: yes
  tags:
    - dconf_gnome_screensaver_lock_delay
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - DISA-STIG-RHEL-07-010110

Ensure Users Cannot Change GNOME3 Screensaver Settings   [ref]rule

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding /org/gnome/desktop/screensaver/lock-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:

/org/gnome/desktop/screensaver/lock-delay
After the settings have been set, run dconf update.

Rationale:

A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_lock 'org/gnome/desktop/screensaver' 'lock-delay' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: "Prevent user modification of GNOME lock-delay"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/screensaver/lock-delay'
    line: '/org/gnome/desktop/screensaver/lock-delay'
    create: yes
  tags:
    - dconf_gnome_screensaver_user_locks
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - DISA-STIG-RHEL-07-010081

Enable GNOME3 Screensaver Idle Activation   [ref]rule

To activate the screensaver in the GNOME3 desktop after a period of inactivity, add or set idle-activation-enabled to true in /etc/dconf/db/local.d/00-security-settings. For example:

[org/gnome/desktop/screensaver]
idle_activation_enabled=true
Once the setting has been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/idle-activation-enabled
After the settings have been set, run dconf update.

Rationale:

A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock.

Enabling idle activation of the screensaver ensures the screensaver will be activated after the idle delay. Applications requiring continuous, real-time screen display (such as network management products) require the login session does not have administrator rights and the display station is located in a controlled-access area.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_settings 'org/gnome/desktop/screensaver' 'idle-activation-enabled' 'true' 'local.d' '00-security-settings'
dconf_lock 'org/gnome/desktop/screensaver' 'idle-activation-enabled' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: "Enable GNOME3 Screensaver Idle Activation"
  ini_file:
    dest: "/etc/dconf/db/local.d/00-security-settings"
    section: "org/gnome/desktop/screensaver"
    option: idle_activation_enabled
    value: "true"
    create: yes
  tags:
    - dconf_gnome_screensaver_idle_activation_enabled
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010100

- name: "Prevent user modification of GNOME idle_activation_enabled"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/screensaver/idle-activation-enabled'
    line: '/org/gnome/desktop/screensaver/idle-activation-enabled'
    create: yes
  tags:
    - dconf_gnome_screensaver_idle_activation_enabled
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010100

Set GNOME3 Screensaver Inactivity Timeout   [ref]rule

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

For example, to configure the system for a 15 minute delay, add the following to /etc/dconf/db/local.d/00-security-settings:

[org/gnome/desktop/session]
idle-delay='uint32 900'
Once the setting has been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/session/idle-delay
After the settings have been set, run dconf update.

Rationale:

A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME3 can be configured to identify when a user's session has idled and take action to initiate a session lock.

Severity:  medium

Remediation Shell script:   (show)


inactivity_timeout_value="900"
function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_settings 'org/gnome/desktop/session' 'idle-delay' "uint32 ${inactivity_timeout_value}" 'local.d' '00-security-settings'
dconf_lock 'org/gnome/desktop/session' 'idle-delay' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: XCCDF Value inactivity_timeout_value # promote to variable
  set_fact:
    inactivity_timeout_value: !!str |-
        900
  tags:
    - always

- name: "Set GNOME3 Screensaver Inactivity Timeout"
  ini_file:
    dest: "/etc/dconf/db/local.d/00-security-settings"
    section: "org/gnome/desktop/screensaver"
    option: idle-delay
    value: "{{ inactivity_timeout_value }}"
    create: yes
  tags:
    - dconf_gnome_screensaver_idle_delay
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010070

- name: "Prevent user modification of GNOME idle-delay"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/screensaver/idle-delay'
    line: '/org/gnome/desktop/screensaver/idle-delay'
    create: yes
  tags:
    - dconf_gnome_screensaver_idle_delay
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010070

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

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding

/org/gnome/desktop/screensaver/lock-enabled
to /etc/dconf/db/local.d/00-security-settings. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.

Rationale:

A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_lock 'org/gnome/desktop/screensaver' 'lock-enabled' 'local.d' '00-security-settings-lock'

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

If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding

/org/gnome/desktop/screensaver/idle-activation-enabled
to /etc/dconf/db/local.d/00-security-settings. For example:
/org/gnome/desktop/screensaver/idle-activation-enabled
After the settings have been set, run dconf update.

Rationale:

A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_lock 'org/gnome/desktop/screensaver' 'idle-activation-enabled' 'local.d' '00-security-settings-lock'

Enable GNOME3 Screensaver Lock After Idle Period   [ref]rule

To activate locking of the screensaver in the GNOME3 desktop when it is activated, add or set lock-enabled to true in /etc/dconf/db/local.d/00-security-settings. For example:

[org/gnome/desktop/screensaver]
lock-enabled=true
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.

Rationale:

A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_settings 'org/gnome/desktop/screensaver' 'lock-enabled' 'true' 'local.d' '00-security-settings'
dconf_lock 'org/gnome/desktop/screensaver' 'lock-enabled' 'local.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: "Enable GNOME3 Screensaver Lock After Idle Period"
  ini_file:
    dest: "/etc/dconf/db/local.d/00-security-settings"
    section: "org/gnome/desktop/screensaver"
    option: lock-enabled
    value: "true"
    create: yes
  tags:
    - dconf_gnome_screensaver_lock_enabled
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(b)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010060

- name: "Prevent user modification of GNOME lock-enabled"
  lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/desktop/screensaver/lock-enabled'
    line: '/org/gnome/desktop/screensaver/lock-enabled'
    create: yes
  tags:
    - dconf_gnome_screensaver_lock_enabled
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-11(b)
    - NIST-800-171-3.1.10
    - PCI-DSS-Req-8.1.8
    - CJIS-5.5.5
    - DISA-STIG-RHEL-07-010060
contains 3 rules

Enable the GNOME3 Login Smartcard Authentication   [ref]rule

In the default graphical environment, smart card authentication can be enabled on the login screen by setting enable-smartcard-authentication to true.

To enable, add or edit enable-smartcard-authentication to /etc/dconf/db/gdm.d/00-security-settings. For example:

[org/gnome/login-screen]
enable-smartcard-authentication=true
Once the setting has been added, add a lock to /etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/login-screen/enable-smartcard-authentication
After the settings have been set, run dconf update.

Rationale:

Smart card login provides two-factor authentication stronger than that provided by a username and password combination. Smart cards leverage PKI (public key infrastructure) in order to provide and verify credentials.

Severity:  medium

Remediation Shell script:   (show)

function include_dconf_settings {
	:
}

# Function to configure DConf settings for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_settings 'org/gnome/login-screen' 'banner-message-enable' 'true' 'local.d' '10-banner'
#
function dconf_settings {
	local _path=$1 _key=$2 _value=$3 _db=$4 _settingFile=$5

	# Check sanity of the input
	if [ $# -ne "5" ]
	then
		echo "Usage: dconf_settings 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_settingsfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	SETTINGSFILES=($(grep -r "\[${_path}]" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1))
	DCONFFILE="/etc/dconf/db/${_db}/${_settingFile}"
	DBDIR="/etc/dconf/db/${_db}"

	mkdir -p "${DBDIR}"

	if [[ -z "${SETTINGSFILES[@]}" ]]
	then
		[ ! -z ${DCONFFILE} ] || $(echo "" >> ${DCONFFILE})
		echo "[${_path}]" >> ${DCONFFILE}
		echo "${_key}=${_value}" >> ${DCONFFILE}
	else
		if grep -q "^(?!#)${_key}" ${SETTINGSFILES[@]}
		then
			sed -i "s/${_key}\s*=\s*.*/${_key}=${_value}/g" ${SETTINGSFILES[@]}
		else
			sed -i "\|\[${_path}]|a\\${_key}=${_value}" ${SETTINGSFILES[@]}
		fi
	fi

	dconf update
}

# Function to configure DConf locks for RHEL and Fedora systems.
#
# Example Call(s):
#
#     dconf_lock 'org/gnome/login-screen' 'banner-message-enable' 'local.d' 'banner'
#
function dconf_lock {
	local _key=$1 _setting=$2 _db=$3 _lockFile=$4

	# Check sanity of the input
	if [ $# -ne "4" ]
	then
		echo "Usage: dconf_lock 'dconf_path' 'dconf_setting' 'dconf_db' 'dconf_lockfile'"
		echo "Aborting."
		exit 1
	fi

	# Check for setting in any of the DConf db directories
	LOCKFILES=$(grep -r "^/${_key}/${_setting}$" "/etc/dconf/db/" | grep -v "distro\|ibus" | cut -d":" -f1)
	LOCKSFOLDER="/etc/dconf/db/${_db}/locks"

	mkdir -p "${LOCKSFOLDER}"

	if [[ -z "${LOCKFILES}" ]]
	then
		echo "/${_key}/${_setting}" >> "/etc/dconf/db/${_db}/locks/${_lockFile}"
	fi
}


include_dconf_settings

dconf_settings 'org/gnome/login-screen' 'enable-smartcard-authentication' 'true' 'gdm.d' '00-security-settings'
dconf_lock 'org/gnome/login-screen' 'enable-smartcard-authentication' 'gdm.d' '00-security-settings-lock'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
- name: "Enable the GNOME3 Login Smartcard Authentication"
  ini_file:
    dest: /etc/dconf/db/gdm.d/00-security-settings
    section: org/gnome/login-screen
    option: enable-smartcard-authentication
    value: "true"
    create: yes
  tags:
    - dconf_gnome_enable_smartcard_auth
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - PCI-DSS-Req-8.3
    - DISA-STIG-RHEL-07-010061

- name: "Prevent user modification of GNOME3 disablement of Smartcard Authentication"
  lineinfile:
    path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock
    regexp: '^/org/gnome/login-screen/enable-smartcard-authentication'
    line: '/org/gnome/login-screen/enable-smartcard-authentication'
    create: yes
  tags:
    - dconf_gnome_enable_smartcard_auth
    - medium_severity
    - unknown_strategy
    - low_complexity
    - medium_disruption
    - PCI-DSS-Req-8.3
    - DISA-STIG-RHEL-07-010061

Configure Syslog   [ref]group

The syslog service has been the default Unix logging mechanism for many years. It has a number of downsides, including inconsistent log format, lack of authentication for received messages, and lack of authentication, encryption, or reliable transport for messages sent over a network. However, due to its long history, syslog is a de facto standard which is supported by almost all Unix applications.

In Red Hat Enterprise Linux 7, rsyslog has replaced ksyslogd as the syslog daemon of choice, and it includes some additional security features such as reliable, connection-oriented (i.e. TCP) transmission of logs, the option to log to database formats, and the encryption of log data en route to a central logging server. This section discusses how to configure rsyslog for best effect, and how to use tools provided with the system to maintain and monitor logs.

contains 3 rules

Rsyslog Logs Sent To Remote Host   [ref]group

If system logs are to be useful in detecting malicious activities, it is necessary to send logs to a remote server. An intruder who has compromised the root account on a system may delete the log entries which indicate that the system was attacked before they are seen by an administrator.

However, it is recommended that logs be stored on the local host in addition to being sent to the loghost, especially if rsyslog has been configured to use the UDP protocol to send messages over a network. UDP does not guarantee reliable delivery, and moderately busy sites will lose log messages occasionally, especially in periods of high traffic which may be the result of an attack. In addition, remote rsyslog messages are not authenticated in any way by default, so it is easy for an attacker to introduce spurious messages to the central log server. Also, some problems cause loss of network connectivity, which will prevent the sending of messages to the central server. For all of these reasons, it is better to store log messages both centrally and on each host, so that they can be correlated if necessary.

contains 1 rule

Ensure Logs Sent To Remote Host   [ref]rule

To configure rsyslog to send logs to a remote log server, open /etc/rsyslog.conf and read and understand the last section of the file, which describes the multiple directives necessary to activate remote logging. Along with these other directives, the system can be configured to forward its logs to a particular log server by adding or correcting one of the following lines, substituting loghost.example.com appropriately. The choice of protocol depends on the environment of the system; although TCP and RELP provide more reliable message delivery, they may not be supported in all environments.
To use UDP for log message delivery:

*.* @loghost.example.com

To use TCP for log message delivery:
*.* @@loghost.example.com

To use RELP for log message delivery:
*.* :omrelp:loghost.example.com

There must be a resolvable DNS CNAME or Alias record set to "logcollector" for logs to be sent correctly to the centralized logging utility.

Rationale:

A log server (loghost) receives syslog messages from one or more systems. This data can be used as an additional log source in the event a system is compromised and its local logs are suspect. Forwarding log messages to a remote loghost also provides system administrators with a centralized place to view the status of multiple hosts within the enterprise.

Severity:  unknown

Remediation Shell script:   (show)


rsyslog_remote_loghost_address="logcollector"
# 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-${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/rsyslog.conf' '^\*\.\*' "@@$rsyslog_remote_loghost_address" '' '%s %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value rsyslog_remote_loghost_address # promote to variable
  set_fact:
    rsyslog_remote_loghost_address: !!str |-
        logcollector
  tags:
    - always

- name: "Set rsyslog remote loghost"
  lineinfile:
    dest: /etc/rsyslog.conf
    regexp: "^\\*\\.\\*"
    line: "*.* @@{{ rsyslog_remote_loghost_address }}"
    create: yes
  tags:
    - rsyslog_remote_loghost
    - unknown_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AU-3(2)
    - NIST-800-53-AU-4(1)
    - NIST-800-53-AU-9
    - DISA-STIG-RHEL-07-031000

Ensure Proper Configuration of Log Files   [ref]group

The file /etc/rsyslog.conf controls where log message are written. These are controlled by lines called rules, which consist of a selector and an action. These rules are often customized depending on the role of the system, the requirements of the environment, and whatever may enable the administrator to most effectively make use of log data. The default rules in Red Hat Enterprise Linux 7 are:

*.info;mail.none;authpriv.none;cron.none                /var/log/messages
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log
See the man page rsyslog.conf(5) for more information. Note that the rsyslog daemon can be configured to use a timestamp format that some log processing programs may not understand. If this occurs, edit the file /etc/rsyslog.conf and add or edit the following line:
$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat

contains 1 rule

Ensure cron Is Logging To Rsyslog   [ref]rule

Cron logging must be implemented to spot intrusions or trace cron job status. If cron is not logging to rsyslog, it can be implemented by adding the following to the RULES section of /etc/rsyslog.conf:

cron.*                                                  /var/log/cron

Rationale:

Cron logging can be used to trace the successful or unsuccessful execution of cron jobs. It can also be used to spot intrusions into the use of the cron facility by unauthorized and malicious users.

Severity:  medium

Remediation Shell script:   (show)


if ! grep -s "^\s*cron\.\*\s*/var/log/cron$" /etc/rsyslog.conf /etc/rsyslog.d/*.conf; then
	mkdir -p /etc/rsyslog.d
	echo "cron.*	/var/log/cron" >> /etc/rsyslog.d/cron.conf
fi

Configure rsyslogd to Accept Remote Messages If Acting as a Log Server   [ref]group

By default, rsyslog does not listen over the network for log messages. If needed, modules can be enabled to allow the rsyslog daemon to receive messages from other systems and for the system thus to act as a log server. If the system is not a log server, then lines concerning these modules should remain commented out.

contains 1 rule

Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server   [ref]rule

The rsyslog daemon should not accept remote messages unless the system acts as a log server. To ensure that it is not listening on the network, ensure the following lines are not found in /etc/rsyslog.conf:

$ModLoad imtcp
$InputTCPServerRun port
$ModLoad imudp
$UDPServerRun port
$ModLoad imrelp
$InputRELPServerRun port

Rationale:

Any process which receives messages from the network incurs some risk of receiving malicious messages. This risk can be eliminated for rsyslog by configuring it not to listen on the network.

Severity:  unknown

Network Configuration and Firewalls   [ref]group

Most systems must be connected to a network of some sort, and this brings with it the substantial risk of network attack. This section discusses the security impact of decisions about networking which must be made when configuring a system.

This section also discusses firewalls, network access controls, and other network security frameworks, which allow system-level rules to be written that can limit an attackers' ability to connect to your system. These rules can specify that network traffic should be allowed or denied from certain IP addresses, hosts, and networks. The rules can also specify which of the system's network services are available to particular hosts or networks.

contains 18 rules

IPv6   [ref]group

The system includes support for Internet Protocol version 6. A major and often-mentioned improvement over IPv4 is its enormous increase in the number of available addresses. Another important feature is its support for automatic configuration of many network settings.

contains 1 rule

Configure IPv6 Settings if Necessary   [ref]group

A major feature of IPv6 is the extent to which systems implementing it can automatically configure their networking devices using information from the network. From a security perspective, manually configuring important configuration information is preferable to accepting it from the network in an unauthenticated fashion.

contains 1 rule

Disable Automatic Configuration   [ref]group

Disable the system's acceptance of router advertisements and redirects by adding or correcting the following line in /etc/sysconfig/network (note that this does not disable sending router solicitations):

IPV6_AUTOCONF=no

contains 1 rule

Configure Kernel Parameter for Accepting IPv6 Source-Routed Packets for All Interfaces   [ref]rule

To set the runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter, run the following command:

$ sudo sysctl -w net.ipv6.conf.all.accept_source_route=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.all.accept_source_route = 0

Rationale:

Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures. This requirement applies only to the forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and the system is functioning as a router.

Accepting source-routed packets in the IPv6 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv6_conf_all_accept_source_route_value="0"

#
# Set runtime for net.ipv6.conf.all.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_source_route=$sysctl_net_ipv6_conf_all_accept_source_route_value

#
# If net.ipv6.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.all.accept_source_route = value" 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-${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' '^net.ipv6.conf.all.accept_source_route' "$sysctl_net_ipv6_conf_all_accept_source_route_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_all_accept_source_route_value: !!str |-
        0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.all.accept_source_route is set
  sysctl:
    name: net.ipv6.conf.all.accept_source_route
    value: "{{ sysctl_net_ipv6_conf_all_accept_source_route_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv6_conf_all_accept_source_route
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-171-3.1.20
    - DISA-STIG-RHEL-07-040830

IPSec Support   [ref]group

Support for Internet Protocol Security (IPsec) is provided in Red Hat Enterprise Linux 7 with Libreswan.

contains 1 rule

Verify Any Configured IPSec Tunnel Connections   [ref]rule

Libreswan provides an implementation of IPsec and IKE, which permits the creation of secure tunnels over untrusted networks. As such, IPsec can be used to circumvent certain network requirements such as filtering. Verify that if any IPsec connection (conn) configured in /etc/ipsec.conf and /etc/ipsec.d exists is an approved organizational connection.

Rationale:

IP tunneling mechanisms can be used to bypass network filtering.

Severity:  medium

firewalld   [ref]group

The dynamic firewall daemon firewalld provides a dynamically managed firewall with support for network “zones” to assign a level of trust to a network and its associated connections and interfaces. It has support for IPv4 and IPv6 firewall settings. It supports Ethernet bridges and has a separation of runtime and permanent configuration options. It also has an interface for services or applications to add firewall rules directly.
A graphical configuration tool, firewall-config, is used to configure firewalld, which in turn uses iptables tool to communicate with Netfilter in the kernel which implements packet filtering.
The firewall service provided by firewalld is dynamic rather than static because changes to the configuration can be made at anytime and are immediately implemented. There is no need to save or apply the changes. No unintended disruption of existing network connections occurs as no part of the firewall has to be reloaded.

contains 4 rules

Strengthen the Default Ruleset   [ref]group

The default rules can be strengthened. The system scripts that activate the firewall rules expect them to be defined in configuration files under the /etc/firewalld/services and /etc/firewalld/zones directories.

The following recommendations describe how to strengthen the default ruleset configuration file. An alternative to editing this configuration file is to create a shell script that makes calls to the firewall-cmd program to load in rules under the /etc/firewalld/services and /etc/firewalld/zones directories.

Instructions apply to both unless otherwise noted. Language and address conventions for regular firewalld rules are used throughout this section.

Warning:  The program firewall-config allows additional services to penetrate the default firewall rules and automatically adjusts the firewalld ruleset(s).
contains 3 rules

Set Default firewalld Zone for Incoming Packets   [ref]rule

To set the default zone to drop for the built-in default zone which processes incoming IPv4 and IPv6 packets, modify the following line in /etc/firewalld/firewalld.conf to be:

DefaultZone=drop

Warning:  To prevent denying any access to the system, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above.
Rationale:

In firewalld the default zone is applied only after all the applicable rules in the table are examined for a match. Setting the default zone to drop implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.

Severity:  medium

Configure the Firewalld Ports   [ref]rule

Configure the firewalld ports to allow approved services to have access to the system. To configure firewalld to open ports, run the following command:

$ sudo firewall-cmd --permanent --add-port=port_number/tcp
or
$ sudo firewall-cmd --permanent --add-port=service_name
Run the command list above for each of the ports listed below: To configure firewalld to allow access, run the following command(s): firewall-cmd --permanent --add-service=ssh

Rationale:

In order to prevent unauthorized connection of devices, unauthorized transfer of information, or unauthorized tunneling (i.e., embedding of data types within data types), organizations must disable or restrict unused or unnecessary physical and logical ports/protocols on information systems.

Operating systems are capable of providing a wide variety of functions and services. Some of the functions and services provided by default may not be necessary to support essential organizational operations. Additionally, it is sometimes convenient to provide multiple services from a single component (e.g., VPN and IPS); however, doing so increases risk over limiting the services provided by any one component.

To support the requirements and principles of least functionality, the operating system must support the organizational requirements, providing only essential capabilities and limiting the use of ports, protocols, and/or services to only those required, authorized, and approved to conduct official business or to address authorized quality of life issues.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:configure
# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install firewalld

firewalld_sshd_zone="public"

# This assumes that firewalld_sshd_zone is one of the pre-defined zones
if [ ! -f /etc/firewalld/zones/${firewalld_sshd_zone}.xml ]; then
    cp /usr/lib/firewalld/zones/${firewalld_sshd_zone}.xml /etc/firewalld/zones/${firewalld_sshd_zone}.xml
fi
if ! grep -q 'service name="ssh"' /etc/firewalld/zones/${firewalld_sshd_zone}.xml; then
    sed -i '/<\/description>/a \
  <service name="ssh"/>' /etc/firewalld/zones/${firewalld_sshd_zone}.xml
fi

# Check if any eth interface is bounded to the zone with SSH service enabled
nic_bound=false
eth_interface_list=$(ip link show up | cut -d ' ' -f2 | cut -d ':' -s -f1 | grep -E '^(en|eth)')
for interface in $eth_interface_list; do
    if grep -q "ZONE=$firewalld_sshd_zone" /etc/sysconfig/network-scripts/ifcfg-$interface; then
        nic_bound=true
        break;
    fi
done

if [ $nic_bound = false ];then
    # Add first NIC to SSH enabled zone

    if ! firewall-cmd --state -q; 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-${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/sysconfig/network-scripts/ifcfg-${eth_interface_list[0]}" '^ZONE=' "$firewalld_sshd_zone" '' '%s=%s'
    else
        # If firewalld service is running, we need to do this step with firewall-cmd
        # Otherwise firewalld will comunicate with NetworkManage and will revert assigned zone
        # of NetworkManager managed interfaces upon reload
        firewall-cmd --zone=$firewalld_sshd_zone --add-interface=${eth_interface_list[0]}
        firewall-cmd --reload
    fi
fi

Configure firewalld To Rate Limit Connections   [ref]rule

Create a direct firewall rule to protect against DoS attacks with the following command:

$ sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT_direct 0 -p tcp -m limit --limit 25/minute --limit-burst 100  -j ACCEPT

Rationale:

DoS is a condition when a resource is not available for legitimate users. When this occurs, the organization either cannot accomplish its mission or must operate at degraded capacity.

This requirement addresses the configuration of the operating system to mitigate the impact of DoS attacks that have occurred or are ongoing on system availability. For each system, known and potential DoS attacks must be identified and solutions for each type implemented. A variety of technologies exist to limit or, in some cases, eliminate the effects of DoS attacks (e.g., limiting processes or establishing memory partitions). Employing increased capacity and bandwidth, combined with service redundancy, may reduce the susceptibility to some DoS attacks.

Severity:  medium

Inspect and Activate Default firewalld Rules   [ref]group

Firewalls can be used to separate networks into different zones based on the level of trust the user has decided to place on the devices and traffic within that network. NetworkManager informs firewalld to which zone an interface belongs. An interface's assigned zone can be changed by NetworkManager or via the firewall-config tool.
The zone settings in /etc/firewalld/ are a range of preset settings which can be quickly applied to a network interface. These are the zones provided by firewalld sorted according to the default trust level of the zones from untrusted to trusted:

  • drop

    Any incoming network packets are dropped, there is no reply. Only outgoing network connections are possible.

  • block

    Any incoming network connections are rejected with an icmp-host-prohibited message for IPv4 and icmp6-adm-prohibited for IPv6. Only network connections initiated from within the system are possible.

  • public

    For use in public areas. You do not trust the other computers on the network to not harm your computer. Only selected incoming connections are accepted.

  • external

    For use on external networks with masquerading enabled especially for routers. You do not trust the other computers on the network to not harm your computer. Only selected incoming connections are accepted.

  • dmz

    For computers in your demilitarized zone that are publicly-accessible with limited access to your internal network. Only selected incoming connections are accepted.

  • work

    For use in work areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.

  • home

    For use in home areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.

  • internal

    For use on internal networks. You mostly trust the other computers on the networks to not harm your computer. Only selected incoming connections are accepted.

  • trusted

    All network connections are accepted.


It is possible to designate one of these zones to be the default zone. When interface connections are added to NetworkManager, they are assigned to the default zone. On installation, the default zone in firewalld is set to be the public zone.
To find out all the settings of a zone, for example the public zone, enter the following command as root:
# firewall-cmd --zone=public --list-all
Example output of this command might look like the following:
# firewall-cmd --zone=public --list-all
public
  interfaces:
  services: mdns dhcpv6-client ssh
  ports:
  forward-ports:
  icmp-blocks: source-quench
To view the network zones currently active, enter the following command as root:
# firewall-cmd --get-service
The following listing displays the result of this command on common Red Hat Enterprise Linux 7 system:
# firewall-cmd --get-service
amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp
high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd
ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn
pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind
samba samba-client smtp ssh telnet tftp tftp-client transmission-client
vnc-server wbem-https
Finally to view the network zones that will be active after the next firewalld service reload, enter the following command as root:
# firewall-cmd --get-service --permanent

contains 1 rule

Verify firewalld Enabled   [ref]rule

The firewalld service can be enabled with the following command:

$ sudo systemctl enable firewalld.service

Rationale:

Access control methods provide the ability to enhance system security posture by restricting services and known good IP addresses and address ranges. This prevents connections from unknown hosts and protocols.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'firewalld.service'
"$SYSTEMCTL_EXEC" enable 'firewalld.service'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
- name: Enable service firewalld
  service:
    name: firewalld
    enabled: "yes"
    state: "started"
  tags:
    - service_firewalld_enabled
    - medium_severity
    - enable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-171-3.1.3
    - NIST-800-171-3.4.7
    - DISA-STIG-RHEL-07-040520

Kernel Parameters Which Affect Networking   [ref]group

The sysctl utility is used to set parameters which affect the operation of the Linux kernel. Kernel parameters which affect networking and have security implications are described here.

contains 8 rules

Network Related Kernel Runtime Parameters for Hosts and Routers   [ref]group

Certain kernel parameters should be set for systems which are acting as either hosts or routers to improve the system's ability defend against certain types of IPv4 protocol attacks.

contains 5 rules

Configure Kernel Parameter for Accepting Source-Routed Packets By Default   [ref]rule

To set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.accept_source_route = 0

Rationale:

Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures.
Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required, such as when IPv4 forwarding is enabled and the system is legitimately functioning as a router.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_accept_source_route_value="0"

#
# Set runtime for net.ipv4.conf.default.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route=$sysctl_net_ipv4_conf_default_accept_source_route_value

#
# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_source_route = value" 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-${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' '^net.ipv4.conf.default.accept_source_route' "$sysctl_net_ipv4_conf_default_accept_source_route_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_default_accept_source_route_value: !!str |-
        0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.default.accept_source_route is set
  sysctl:
    name: net.ipv4.conf.default.accept_source_route
    value: "{{ sysctl_net_ipv4_conf_default_accept_source_route_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_default_accept_source_route
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-53-SC-7
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040620

Configure Kernel Parameter for Accepting ICMP Redirects By Default   [ref]rule

To set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.accept_redirects = 0

Rationale:

ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages modify the host's route table and are unauthenticated. An illicit ICMP redirect message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should be disabled unless absolutely required.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_accept_redirects_value="0"

#
# Set runtime for net.ipv4.conf.default.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects=$sysctl_net_ipv4_conf_default_accept_redirects_value

#
# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_redirects = value" 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-${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' '^net.ipv4.conf.default.accept_redirects' "$sysctl_net_ipv4_conf_default_accept_redirects_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_default_accept_redirects_value: !!str |-
        0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.default.accept_redirects is set
  sysctl:
    name: net.ipv4.conf.default.accept_redirects
    value: "{{ sysctl_net_ipv4_conf_default_accept_redirects_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_default_accept_redirects
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-53-SC-7
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040640

Configure Kernel Parameter for Accepting IPv4 Source-Routed Packets for All Interfaces   [ref]rule

To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.accept_source_route = 0

Rationale:

Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures. This requirement applies only to the forwarding of source-routerd traffic, such as when IPv4 forwarding is enabled and the system is functioning as a router.

Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_accept_source_route_value="0"

#
# Set runtime for net.ipv4.conf.all.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_source_route=$sysctl_net_ipv4_conf_all_accept_source_route_value

#
# If net.ipv4.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.accept_source_route = value" 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-${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' '^net.ipv4.conf.all.accept_source_route' "$sysctl_net_ipv4_conf_all_accept_source_route_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_all_accept_source_route_value: !!str |-
        0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.all.accept_source_route is set
  sysctl:
    name: net.ipv4.conf.all.accept_source_route
    value: "{{ sysctl_net_ipv4_conf_all_accept_source_route_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_all_accept_source_route
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-171-3.1.20
    - DISA-STIG-RHEL-07-040610

Configure Kernel Parameter for Accepting ICMP Redirects for All Interfaces   [ref]rule

To set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.accept_redirects = 0

Rationale:

ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages modify the host's route table and are unauthenticated. An illicit ICMP redirect message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should be disabled unless absolutely required.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_accept_redirects_value="0"

#
# Set runtime for net.ipv4.conf.all.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects=$sysctl_net_ipv4_conf_all_accept_redirects_value

#
# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.accept_redirects = value" 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-${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' '^net.ipv4.conf.all.accept_redirects' "$sysctl_net_ipv4_conf_all_accept_redirects_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_all_accept_redirects_value: !!str |-
        0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.all.accept_redirects is set
  sysctl:
    name: net.ipv4.conf.all.accept_redirects
    value: "{{ sysctl_net_ipv4_conf_all_accept_redirects_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_all_accept_redirects
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-6(d)
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040641

Configure Kernel Parameter to Ignore ICMP Broadcast Echo Requests   [ref]rule

To set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.icmp_echo_ignore_broadcasts = 1

Rationale:

Responding to broadcast (ICMP) echoes facilitates network mapping and provides a vector for amplification attacks.
Ignoring ICMP echo requests (pings) sent to broadcast or multicast addresses makes the system slightly more difficult to enumerate on the network.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value="1"

#
# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts
#
/sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts=$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value

#
# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" 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-${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' '^net.ipv4.icmp_echo_ignore_broadcasts' "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable
  set_fact:
    sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: !!str |-
        1
  tags:
    - always

- name: Ensure sysctl net.ipv4.icmp_echo_ignore_broadcasts is set
  sysctl:
    name: net.ipv4.icmp_echo_ignore_broadcasts
    value: "{{ sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value }}"
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_icmp_echo_ignore_broadcasts
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040630

Network Parameters for Hosts Only   [ref]group

If the system is not going to be used as a router, then setting certain kernel parameters ensure that the host will not perform routing of network traffic.

contains 3 rules

Disable Kernel Parameter for IP Forwarding   [ref]rule

To set the runtime status of the net.ipv4.ip_forward kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.ip_forward=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.ip_forward = 0

Rationale:

Routing protocol daemons are typically used on routers to exchange network topology information with other routers. If this capability is used when not required, system network information may be unnecessarily transmitted across the network.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


#
# Set runtime for net.ipv4.ip_forward
#
/sbin/sysctl -q -n -w net.ipv4.ip_forward=0

#
# If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.ip_forward = 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-${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' '^net.ipv4.ip_forward' "0" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure sysctl net.ipv4.ip_forward is set to 0
  sysctl:
    name: net.ipv4.ip_forward
    value: 0
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_ip_forward
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-53-SC-32
    - NIST-800-171-3.1.20
    - DISA-STIG-RHEL-07-040740

Disable Kernel Parameter for Sending ICMP Redirects for All Interfaces   [ref]rule

To set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.send_redirects = 0

Rationale:

ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages contain information from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


#
# Set runtime for net.ipv4.conf.all.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects=0

#
# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.all.send_redirects = 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-${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' '^net.ipv4.conf.all.send_redirects' "0" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure sysctl net.ipv4.conf.all.send_redirects is set to 0
  sysctl:
    name: net.ipv4.conf.all.send_redirects
    value: 0
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_all_send_redirects
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5(1)
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040660

Disable Kernel Parameter for Sending ICMP Redirects by Default   [ref]rule

To set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
If this is not the system default value, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.send_redirects = 0

Rationale:

ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages contain information from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


#
# Set runtime for net.ipv4.conf.default.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects=0

#
# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.default.send_redirects = 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-${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' '^net.ipv4.conf.default.send_redirects' "0" ''
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure sysctl net.ipv4.conf.default.send_redirects is set to 0
  sysctl:
    name: net.ipv4.conf.default.send_redirects
    value: 0
    state: present
    reload: yes
  tags:
    - sysctl_net_ipv4_conf_default_send_redirects
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-AC-4
    - NIST-800-53-CM-7
    - NIST-800-53-SC-5
    - NIST-800-53-SC-7
    - NIST-800-171-3.1.20
    - CJIS-5.10.1.1
    - DISA-STIG-RHEL-07-040650

Uncommon Network Protocols   [ref]group

The system includes support for several network protocols which are not commonly used. Although security vulnerabilities in kernel networking code are not frequently discovered, the consequences can be dramatic. Ensuring uncommon network protocols are disabled reduces the system's risk to attacks targeted at its implementation of those protocols.

Warning:  Although these protocols are not commonly used, avoid disruption in your network environment by ensuring they are not needed prior to disabling them.
contains 1 rule

Disable DCCP Support   [ref]rule

The Datagram Congestion Control Protocol (DCCP) is a relatively new transport layer protocol, designed to support streaming media and telephony. To configure the system to prevent the dccp kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install dccp /bin/true

Rationale:

Disabling DCCP protects the system against exploitation of any flaws in its implementation.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
if LC_ALL=C grep -q -m 1 "^install dccp" /etc/modprobe.d/dccp.conf ; then
	sed -i 's/^install dccp.*/install dccp /bin/true/g' /etc/modprobe.d/dccp.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/dccp.conf
	echo "install dccp /bin/true" >> /etc/modprobe.d/dccp.conf
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'dccp' is disabled
  lineinfile:
    create: yes
    dest: "/etc/modprobe.d/dccp.conf"
    regexp: 'dccp'
    line: "install dccp /bin/true"
  tags:
    - kernel_module_dccp_disabled
    - medium_severity
    - disable_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-7
    - NIST-800-171-3.4.6
    - CJIS-5.10.1
    - DISA-STIG-RHEL-07-020101

Wireless Networking   [ref]group

Wireless networking, such as 802.11 (WiFi) and Bluetooth, can present a security risk to sensitive or classified systems and networks. Wireless networking hardware is much more likely to be included in laptop or portable systems than in desktops or servers.

Removal of hardware provides the greatest assurance that the wireless capability remains disabled. Acquisition policies often include provisions to prevent the purchase of equipment that will be used in sensitive spaces and includes wireless capabilities. If it is impractical to remove the wireless hardware, and policy permits the device to enter sensitive spaces as long as wireless is disabled, efforts should instead focus on disabling wireless capability via software.

contains 1 rule

Disable Wireless Through Software Configuration   [ref]group

If it is impossible to remove the wireless hardware from the device in question, disable as much of it as possible through software. The following methods can disable software support for wireless networking, but note that these methods do not prevent malicious software or careless users from re-activating the devices.

contains 1 rule

Deactivate Wireless Network Interfaces   [ref]rule

Deactivating wireless network interfaces should prevent normal usage of the wireless capability.

Configure the system to disable all wireless network interfaces with the following command:

$ sudo nmcli radio wifi off

Rationale:

The use of wireless networking can introduce many different attack vectors into the organization's network. Common attack vectors such as malicious association and ad hoc networks will allow an attacker to spoof a wireless access point (AP), allowing validated systems to connect to the malicious AP and enabling the attacker to monitor and record network traffic. These malicious APs can also serve to create a man-in-the-middle attack or be used to create a denial of service to valid network resources.

Severity:  medium

Ensure System is Not Acting as a Network Sniffer   [ref]rule

The system should not be acting as a network sniffer, which can capture all traffic on the network to which it is connected. Run the following to determine if any interface is running in promiscuous mode:

$ ip link | grep PROMISC

Rationale:

Network interfaces in promiscuous mode allow for the capture of all network traffic visible to the system. If unauthorized individuals can access these applications, it may allow them to collect information such as logon IDs, passwords, and key exchanges between systems.

If the system is being used to perform a network troubleshooting function, the use of these tools must be documented with the Information Systems Security Manager (ISSM) and restricted to only authorized personnel.

Severity:  medium

Configure Multiple DNS Servers in /etc/resolv.conf   [ref]rule

Multiple Domain Name System (DNS) Servers should be configured in /etc/resolv.conf. This provides redundant name resolution services in the event that a domain server crashes. To configure the system to contain as least 2 DNS servers, add a corresponding nameserver ip_address entry in /etc/resolv.conf for each DNS server where ip_address is the IP address of a valid DNS server. For example:

search example.com
nameserver 192.168.0.1
nameserver 192.168.0.2

Rationale:

To provide availability for name resolution services, multiple redundant name servers are mandated. A failure in name resolution could lead to the failure of security functions requiring name resolution, which may include time synchronization, centralized authentication, and remote system logging.

Severity:  unknown

Set Boot Loader Password   [ref]group

During the boot process, the boot loader is responsible for starting the execution of the kernel and passing options to it. The boot loader allows for the selection of different kernels - possibly on different partitions or media. The default Red Hat Enterprise Linux 7 boot loader for x86 systems is called GRUB2. Options it can pass to the kernel include single-user mode, which provides root access without any authentication, and the ability to disable SELinux. To prevent local users from modifying the boot parameters and endangering security, protect the boot loader configuration with a password and ensure its configuration file's permissions are set properly.

contains 3 rules

Set Boot Loader Password in grub2   [ref]rule

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

To do so, select a superuser account name and password and and modify the /etc/grub.d/01_users configuration file with the new account name.

Since plaintext passwords are a security risk, generate a hash for the pasword by running the following command:

$ grub2-setpassword
When prompted, enter the password that was selected.

NOTE: It is recommended not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i s/root/bootuser/g /etc/grub.d/01_users


To meet FISMA Moderate, the bootloader superuser account and password MUST differ from the root account and password. Once the superuser account and password have been added, update the grub.cfg file by running:
grub2-mkconfig -o /boot/grub2/grub.cfg
NOTE: Do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.

Warning:  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above.
Rationale:

Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode. For more information on how to configure the grub2 superuser account and password, please refer to

Severity:  high

Boat Loader Is Not Installed On Removeable Media   [ref]rule

The system must not allow removable media to be used as the boot loader. Remove alternate methods of booting the system from removable media. usb0, cd, fd0, etc. are some examples of removeable media which should not exist in the line:

set root='hd0,msdos1'

Rationale:

Malicious users with removable boot media can gain access to a system configured to use removable media as the boot loader.

Severity:  medium

Set the UEFI Boot Loader Password   [ref]rule

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

To do so, select a superuser account name and password and and modify the /etc/grub.d/01_users configuration file with the new account name.

Since plaintext passwords are a security risk, generate a hash for the pasword by running the following command:

$ grub2-setpassword
When prompted, enter the password that was selected.

NOTE: It is recommended not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i s/root/bootuser/g /etc/grub.d/01_users


To meet FISMA Moderate, the bootloader superuser account and password MUST differ from the root account and password. Once the superuser account and password have been added, update the grub.cfg file by running:
grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
NOTE: Do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.

Warning:  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above.
Rationale:

Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode. For more information on how to configure the grub2 superuser account and password, please refer to

Severity:  medium

SELinux   [ref]group

SELinux is a feature of the Linux kernel which can be used to guard against misconfigured or compromised programs. SELinux enforces the idea that programs should be limited in what files they can access and what actions they can take.

The default SELinux policy, as configured on Red Hat Enterprise Linux 7, has been sufficiently developed and debugged that it should be usable on almost any Red Hat system with minimal configuration and a small amount of system administrator training. This policy prevents system services - including most of the common network-visible services such as mail servers, FTP servers, and DNS servers - from accessing files which those services have no valid reason to access. This action alone prevents a huge amount of possible damage from network attacks against services, from trojaned software, and so forth.

This guide recommends that SELinux be enabled using the default (targeted) policy on every Red Hat system, unless that system has unusual requirements which make a stronger policy appropriate.

For more information on SELinux, see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide.

contains 4 rules

Configure SELinux Policy   [ref]rule

The SELinux targeted policy is appropriate for general-purpose desktops and servers, as well as systems in many other roles. To configure the system to use this policy, add or correct the following line in /etc/selinux/config:

SELINUXTYPE=targeted
Other policies, such as mls, provide additional security labeling and greater confinement but are not compatible with many general-purpose use cases.

Rationale:

Setting the SELinux policy to targeted or a more specialized policy ensures the system will confine processes that are likely to be targeted for exploitation, such as network or system services.

Note: During the development or debugging of SELinux modules, it is common to temporarily place non-production systems in permissive mode. In such temporary cases, SELinux policies should be developed, and once work is completed, the system should be reconfigured to targeted.

Severity:  high

Remediation Shell script:   (show)


var_selinux_policy_name="targeted"
# 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-${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/sysconfig/selinux' '^SELINUXTYPE=' $var_selinux_policy_name '' '%s=%s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_selinux_policy_name # promote to variable
  set_fact:
    var_selinux_policy_name: !!str |-
        targeted
  tags:
    - always

- name: "Configure SELinux Policy"
  lineinfile:
    path: /etc/sysconfig/selinux
    regexp: '^SELINUXTYPE='
    line: "SELINUXTYPE={{ var_selinux_policy_name }}"
    create: yes
  tags:
    - selinux_policytype
    - high_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-AC-3(3)
    - NIST-800-53-AC-3(4)
    - NIST-800-53-AC-4
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9
    - NIST-800-53-SI-6(a)
    - NIST-800-171-3.1.2
    - NIST-800-171-3.7.2
    - DISA-STIG-RHEL-07-020220

Ensure No Device Files are Unlabeled by SELinux   [ref]rule

Device files, which are used for communication with important system resources, should be labeled with proper SELinux types. If any device files do not carry the SELinux type device_t, report the bug so that policy can be corrected. Supply information about what the device is and what programs use it.

To check for unlabeled device files, run the following command:

$ sudo find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n"
It should produce no output in a well-configured system.

Warning:  Automatic remediation of this control is not available. The remediation can be achieved by amending SELinux policy.
Rationale:

If a device file carries the SELinux type device_t, then SELinux cannot properly restrict access to the device file.

Severity:  medium

Ensure SELinux State is Enforcing   [ref]rule

The SELinux state should be set to enforcing at system boot time. In the file /etc/selinux/config, add or correct the following line to configure the system to boot into enforcing mode:

SELINUX=enforcing

Rationale:

Setting the SELinux state to enforcing ensures SELinux is able to confine potentially compromised processes to the security policy, which is designed to prevent them from causing damage to the system or further elevating their privileges.

Severity:  high

Remediation Shell script:   (show)


var_selinux_state="enforcing"
# 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-${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/sysconfig/selinux' '^SELINUX=' $var_selinux_state '' '%s=%s'

fixfiles onboot
fixfiles -f relabel
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_selinux_state # promote to variable
  set_fact:
    var_selinux_state: !!str |-
        enforcing
  tags:
    - always

- name: "Ensure SELinux State is Enforcing"
  lineinfile:
    path: /etc/sysconfig/selinux
    regexp: '^SELINUX='
    line: "SELINUX={{ var_selinux_state }}"
    create: yes
  tags:
    - selinux_state
    - high_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-3
    - NIST-800-53-AC-3(3)
    - NIST-800-53-AC-3(4)
    - NIST-800-53-AC-4
    - NIST-800-53-AC-6
    - NIST-800-53-AU-9
    - NIST-800-53-SI-6(a)
    - NIST-800-171-3.1.2
    - NIST-800-171-3.7.2
    - DISA-STIG-RHEL-07-020210

Account and Access Control   [ref]group

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 7.

contains 55 rules

Protect Accounts by Configuring PAM   [ref]group

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.
contains 19 rules

Set Password Hashing Algorithm   [ref]group

The system's default algorithm for storing password hashes in /etc/shadow is SHA-512. This can be configured in several locations.

contains 3 rules

Set Password Hashing Algorithm in /etc/login.defs   [ref]rule

In /etc/login.defs, add or correct the following line to ensure the system will use SHA-512 as the hashing algorithm:

ENCRYPT_METHOD SHA512

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 kept in plain text.

Using a stronger hashing algorithm makes password cracking attacks more difficult.

Severity:  medium

Remediation Shell script:   (show)

if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then
	sed -i 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/g' /etc/login.defs
else
	echo "" >> /etc/login.defs
	echo "ENCRYPT_METHOD SHA512" >> /etc/login.defs
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Set Password Hashing Algorithm in /etc/login.defs
  lineinfile:
      dest: /etc/login.defs
      regexp: ^#?ENCRYPT_METHOD
      line: ENCRYPT_METHOD SHA512
      state: present
  tags:
    - set_password_hashing_algorithm_logindefs
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(c)
    - NIST-800-53-IA-7
    - NIST-800-171-3.13.11
    - PCI-DSS-Req-8.2.1
    - CJIS-5.6.2.2
    - DISA-STIG-RHEL-07-010210

Set Password Hashing Algorithm in /etc/libuser.conf   [ref]rule

In /etc/libuser.conf, add or correct the following line in its [defaults] section to ensure the system will use the SHA-512 algorithm for password hashing:

crypt_style = sha512

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

Remediation Shell script:   (show)


LIBUSER_CONF="/etc/libuser.conf"
CRYPT_STYLE_REGEX='[[:space:]]*\[defaults](.*(\n)+)+?[[:space:]]*crypt_style[[:space:]]*'

# Try find crypt_style in [defaults] section. If it is here, then change algorithm to sha512.
# If it isn't here, then add it to [defaults] section.
if grep -qzosP $CRYPT_STYLE_REGEX $LIBUSER_CONF ; then
        sed -i "s/\(crypt_style[[:space:]]*=[[:space:]]*\).*/\1sha512/g" $LIBUSER_CONF
elif grep -qs "\[defaults]" $LIBUSER_CONF ; then
        sed -i "/[[:space:]]*\[defaults]/a crypt_style = sha512" $LIBUSER_CONF
else
        echo -e "[defaults]\ncrypt_style = sha512" >> $LIBUSER_CONF
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: Set Password Hashing Algorithm in /etc/libuser.conf
  lineinfile:
    dest: /etc/libuser.conf
    insertafter: '^\s*\[defaults]'
    regexp: ^#?crypt_style
    line: crypt_style = sha512
    state: present
  tags:
    - set_password_hashing_algorithm_libuserconf
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(c)
    - NIST-800-53-IA-7
    - NIST-800-171-3.13.11
    - PCI-DSS-Req-8.2.1
    - CJIS-5.6.2.2
    - DISA-STIG-RHEL-07-010220

Set PAM's Password Hashing Algorithm   [ref]rule

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

Remediation Shell script:   (show)


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

Set Lockouts for Failed Password Attempts   [ref]group

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.
contains 5 rules

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

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=never 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=never 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

Remediation Shell script:   (show)


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

# ensure, that pam.d folder exists
mkdir -p "/etc/pam.d"

for pamFile in "${AUTH_FILES[@]}"
do
	# if auth file is missing, create it and add what this rule needs
	if [ ! -f $pamFile ]; then
		touch $pamFile
		echo "
auth required pam_faillock.so preauth silent even_deny_root deny=3 unlock_time=never fail_interval=900
auth sufficient pam_unix.so
auth [default=die] pam_faillock.so authfail silent even_deny_root deny=3 unlock_time=never fail_interval=900
" >> $pamFile
		# everything is set, don't check it again
		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
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_unlock_time: !!str |-
        never
  tags:
    - always
- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_fail_interval: !!str |-
        900
  tags:
    - always

- name: set auth pam_faillock before pam_unix.so
  pamd:
    name: system-auth
    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
        deny: {{ var_accounts_passwords_pam_faillock_deny }}
        unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time }}
        fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval }}'
    state: before
  tags:
    - accounts_passwords_pam_faillock_deny_root
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - DISA-STIG-RHEL-07-010330

- name: set auth pam_faillock after pam_unix.so
  pamd:
    name: system-auth
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: 'preauth
        silent
        even_deny_root
        deny: {{ var_accounts_passwords_pam_faillock_deny }}
        unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time }}
        fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval }}'
    state: after
  tags:
    - accounts_passwords_pam_faillock_deny_root
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - DISA-STIG-RHEL-07-010330

- name: set account pam_faillock before pam_unix.so
  pamd:
    name: system-auth
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  tags:
    - accounts_passwords_pam_faillock_deny_root
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - DISA-STIG-RHEL-07-010330

Set Lockout Time For Failed Password Attempts   [ref]rule

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=never 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=never 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. Ensuring that an administrator is involved in unlocking locked accounts draws appropriate attention to such situations.

Severity:  medium

Remediation Shell script:   (show)


var_accounts_passwords_pam_faillock_unlock_time="never"
function include_set_faillock_option {
	:
}

function insert_preauth {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_authfail {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_account {
	local pam_file="$1"
	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
}

function set_faillock_option {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	insert_preauth "$pam_file" "$option" "$value"
	insert_authfail "$pam_file" "$option" "$value"
	insert_account "$pam_file"
}

include_set_faillock_option

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

for pam_file in "${AUTH_FILES[@]}"
do
	set_faillock_option "$pam_file" "unlock_time" "$var_accounts_passwords_pam_faillock_unlock_time"
done

Limit Password Reuse   [ref]rule

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=5 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=5
  • for the pam_pwhistory.so case:
    password requisite pam_pwhistory.so ...existing_options... remember=5
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

Remediation Shell script:   (show)


var_password_pam_unix_remember="5"

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
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Strategy:configure
- name: XCCDF Value var_password_pam_unix_remember # promote to variable
  set_fact:
    var_password_pam_unix_remember: !!str |-
        5
  tags:
    - always

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

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

Set Interval For Counting Failed Password Attempts   [ref]rule

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=never 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=never 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

Remediation Shell script:   (show)

function include_set_faillock_option {
	:
}

function insert_preauth {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_authfail {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_account {
	local pam_file="$1"
	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
}

function set_faillock_option {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	insert_preauth "$pam_file" "$option" "$value"
	insert_authfail "$pam_file" "$option" "$value"
	insert_account "$pam_file"
}

include_set_faillock_option

var_accounts_passwords_pam_faillock_fail_interval="900"

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

for pam_file in "${AUTH_FILES[@]}"
do
	set_faillock_option "$pam_file" "fail_interval" "$var_accounts_passwords_pam_faillock_fail_interval"
done

Set Deny For Failed Password Attempts   [ref]rule

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=never 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=never 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

Remediation Shell script:   (show)


var_accounts_passwords_pam_faillock_deny="3"
function include_set_faillock_option {
	:
}

function insert_preauth {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_authfail {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	# 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.*$option=([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.*\)\($option *= *\).*/\1\2$value/" "$pam_file"
		# the option is not set.
		else
			# append the option
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ $option=$value/" "$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 $option=$value" "$pam_file"
	fi
}

function insert_account {
	local pam_file="$1"
	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
}

function set_faillock_option {
	local pam_file="$1"
	local option="$2"
	local value="$3"
	insert_preauth "$pam_file" "$option" "$value"
	insert_authfail "$pam_file" "$option" "$value"
	insert_account "$pam_file"
}

include_set_faillock_option

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

for pam_file in "${AUTH_FILES[@]}"
do
	set_faillock_option "$pam_file" "deny" "$var_accounts_passwords_pam_faillock_deny"
done
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_unlock_time: !!str |-
        never
  tags:
    - always
- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_fail_interval: !!str |-
        900
  tags:
    - always

- name: set auth pam_faillock before pam_unix.so
  pamd:
    name: system-auth
    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 }}
        unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time }}
        fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval }}'
    state: before
  tags:
    - accounts_passwords_pam_faillock_deny
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - NIST-800-171-3.1.8
    - PCI-DSS-Req-8.1.6
    - CJIS-5.5.3
    - DISA-STIG-RHEL-07-010320

- name: set auth pam_faillock after pam_unix.so
  pamd:
    name: system-auth
    type: auth
    control: sufficient
    module_path: pam_unix.so
    new_type: auth
    new_control: '[default=die]'
    new_module_path: pam_faillock.so
    module_arguments: 'preauth
        silent
        deny: {{ var_accounts_passwords_pam_faillock_deny }}
        unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time }}
        fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval }}'
    state: after
  tags:
    - accounts_passwords_pam_faillock_deny
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - NIST-800-171-3.1.8
    - PCI-DSS-Req-8.1.6
    - CJIS-5.5.3
    - DISA-STIG-RHEL-07-010320

- name: set account pam_faillock before pam_unix.so
  pamd:
    name: system-auth
    type: account
    control: required
    module_path: pam_unix.so
    new_type: account
    new_control: required
    new_module_path: pam_faillock.so
    state: before
  tags:
    - accounts_passwords_pam_faillock_deny
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-7(b)
    - NIST-800-171-3.1.8
    - PCI-DSS-Req-8.1.6
    - CJIS-5.5.3
    - DISA-STIG-RHEL-07-010320

Set Password Quality Requirements   [ref]group

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 pam_cracklib PAM module can also provide strength checking for passwords as the pam_pwquality module. 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 man pages pam_pwquality(8) and pam_cracklib(8) provide information on the capabilities and configuration of each.

contains 10 rules

Set Password Quality Requirements with pam_pwquality   [ref]group

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.

contains 10 rules

Set Password Minimum Length   [ref]rule

The pam_pwquality module's minlen parameter controls requirements for minimum characters required in a password. Add minlen=15 after pam_pwquality to set minimum password length requirements.

Rationale:

The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.
Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. Password length is one factor of several that helps to determine strength and how long it takes to crack a password. Use of more characters in a password helps to exponentially increase the time and/or resources required to compromose the password.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

var_password_pam_minlen="15"
# 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-${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 '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_password_pam_minlen # promote to variable
  set_fact:
    var_password_pam_minlen: !!str |-
        15
  tags:
    - always

- name: Ensure PAM variable minlen is set accordingly

  lineinfile:
    create: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*minlen'
    line: "minlen = {{ var_password_pam_minlen }}"

  tags:
    - accounts_password_pam_minlen
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(1)(a)
    - PCI-DSS-Req-8.2.3
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-07-010280

Set Password to Maximum of Consecutive Repeating Characters from Same Character Class   [ref]rule

The pam_pwquality module's maxclassrepeat parameter controls requirements for consecutive repeating characters from the same character class. When set to a positive number, it will reject passwords which contain more than that number of consecutive characters from the same character class. Modify the maxclassrepeat setting in /etc/security/pwquality.conf to equal 4 to prevent a run of (4 + 1) or more identical characters.

Rationale:

Use of a complex password helps to increase the time and resources required to comrpomise 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 a password, the greater the number of possible combinations that need to be tested before the password is compromised.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

var_password_pam_maxclassrepeat="4"
# 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-${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' '^maxclassrepeat' $var_password_pam_maxclassrepeat '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_password_pam_maxclassrepeat # promote to variable
  set_fact:
    var_password_pam_maxclassrepeat: !!str |-
        4
  tags:
    - always

- name: Ensure PAM variable maxclassrepeat is set accordingly

  lineinfile:
    create: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*maxclassrepeat'
    line: "maxclassrepeat = {{ var_password_pam_maxclassrepeat }}"

  tags:
    - accounts_password_pam_maxclassrepeat
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5
    - NIST-800-53-IA-5(c)
    - DISA-STIG-RHEL-07-010190

Set Password Maximum Consecutive Repeating Characters   [ref]rule

The pam_pwquality module's maxrepeat parameter controls requirements for consecutive repeating characters. When set to a positive number, it will reject passwords which contain more than that number of consecutive characters. Modify the maxrepeat setting in /etc/security/pwquality.conf to equal 3 to prevent a run of (3 + 1) or more identical characters.

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.

Passwords with excessive repeating characters may be more vulnerable to password-guessing attacks.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

var_password_pam_maxrepeat="3"
# 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-${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' '^maxrepeat' $var_password_pam_maxrepeat '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_password_pam_maxrepeat # promote to variable
  set_fact:
    var_password_pam_maxrepeat: !!str |-
        3
  tags:
    - always

- name: Ensure PAM variable maxrepeat is set accordingly

  lineinfile:
    create: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*maxrepeat'
    line: "maxrepeat = {{ var_password_pam_maxrepeat }}"

  tags:
    - accounts_password_pam_maxrepeat
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5
    - NIST-800-53-IA-5(c)
    - DISA-STIG-RHEL-07-010180

Set Password Strength Minimum Digit Characters   [ref]rule

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 possble 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

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

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-${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 '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*dcredit'
    line: "dcredit = {{ var_password_pam_dcredit }}"

  tags:
    - accounts_password_pam_dcredit
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(1)(a)
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-194
    - PCI-DSS-Req-8.2.3
    - DISA-STIG-RHEL-07-010140

Set Password Strength Minimum Different Categories   [ref]rule

The pam_pwquality module's minclass parameter controls requirements for usage of different character classes, or types, of character that must exist in a password before it is considered valid. For example, setting this value to three (3) requires that any password must have characters from at least three different categories in order to be approved. The default value is zero (0), meaning there are no required classes. There are four categories available:

* Upper-case characters
* Lower-case characters
* Digits
* Special characters (for example, punctuation)
Modify the minclass setting in /etc/security/pwquality.conf entry to require 4 differing categories of characters when changing passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Requiring a minimum number of character categories makes password guessing attacks more difficult by ensuring a larger search space.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

var_password_pam_minclass="4"
# 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-${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' '^minclass' $var_password_pam_minclass '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_password_pam_minclass # promote to variable
  set_fact:
    var_password_pam_minclass: !!str |-
        4
  tags:
    - always

- name: Ensure PAM variable minclass is set accordingly

  lineinfile:
    create: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*minclass'
    line: "minclass = {{ var_password_pam_minclass }}"

  tags:
    - accounts_password_pam_minclass
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5
    - DISA-STIG-RHEL-07-010170

Set Password Strength Minimum Different Characters   [ref]rule

The pam_pwquality module's difok parameter sets the number of characters in a password that must not be present in and old password during a password change.

Modify the difok setting in /etc/security/pwquality.conf to equal 8 to require differing characters when changing passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute–force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Requiring a minimum number of different characters during password changes ensures that newly changed passwords should not resemble previously compromised ones. Note that passwords which are changed on compromised systems will still be compromised, however.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

var_password_pam_difok="8"
# 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-${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' '^difok' $var_password_pam_difok '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- name: XCCDF Value var_password_pam_difok # promote to variable
  set_fact:
    var_password_pam_difok: !!str |-
        8
  tags:
    - always

- name: Ensure PAM variable difok is set accordingly

  lineinfile:
    create: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*difok'
    line: "difok = {{ var_password_pam_difok }}"

  tags:
    - accounts_password_pam_difok
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(b)
    - CJIS-5.6.2.1.1
    - DISA-STIG-RHEL-07-010160

Set Password Strength Minimum Special Characters   [ref]rule

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

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

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-${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 '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*ocredit'
    line: "ocredit = {{ var_password_pam_ocredit }}"

  tags:
    - accounts_password_pam_ocredit
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(a)
    - DISA-STIG-RHEL-07-010150

Set Password Strength Minimum Lowercase Characters   [ref]rule

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

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

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-${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 '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*lcredit'
    line: "lcredit = {{ var_password_pam_lcredit }}"

  tags:
    - accounts_password_pam_lcredit
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(a)
    - PCI-DSS-Req-8.2.3
    - DISA-STIG-RHEL-07-010130

Set Password Strength Minimum Uppercase Characters   [ref]rule

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

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:restrict

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-${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 '' '%s = %s'
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:restrict
- 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: yes
    dest: "/etc/security/pwquality.conf"
    regexp: '^#?\s*ucredit'
    line: "ucredit = {{ var_password_pam_ucredit }}"

  tags:
    - accounts_password_pam_ucredit
    - medium_severity
    - restrict_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-IA-5(b)
    - NIST-800-53-IA-5(c)
    - NIST-800-53-IA-5(1)(a)
    - PCI-DSS-Req-8.2.3
    - DISA-STIG-RHEL-07-010120

Set Password Retry Prompts Permitted Per-Session   [ref]rule

To configure the number of retry prompts that are permitted per-session: Edit the pam_pwquality.so statement in /etc/pam.d/system-auth to show retry=3, or a lower value if site policy is more restrictive. The DoD requirement is a maximum of 3 prompts per session.

Rationale:

Setting the password retry prompts that are permitted on a per-session basis to a low value requires some software, such as SSH, to re-connect. This can slow down and draw additional attention to some types of password-guessing attacks. Note that this is different from account lockout, which is provided by the pam_faillock module.

Severity:  unknown

Remediation Shell script:   (show)


var_password_pam_retry="3"

if grep -q "retry=" /etc/pam.d/system-auth ; then
	sed -i --follow-symlinks "s/\(retry *= *\).*/\1$var_password_pam_retry/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_pwquality.so/ s/$/ retry=$var_password_pam_retry/" /etc/pam.d/system-auth
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Strategy:configure
- name: XCCDF Value var_password_pam_retry # promote to variable
  set_fact:
    var_password_pam_retry: !!str |-
        3
  tags:
    - always

- name: "Set Password Retry Prompts Permitted Per-Session - system-auth (change)"
  replace:
    dest: /etc/pam.d/system-auth
    follow: yes
    regexp: '(^.*\spam_pwquality.so\s.*retry\s*=\s*)(\S+)(.*$)'
    replace: '\g<1>{{ var_password_pam_retry }}\g<3>'
  tags:
    - accounts_password_pam_retry
    - unknown_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-53-IA-5(c)
    - CJIS-5.5.3
    - DISA-STIG-RHEL-07-010119

- name: "Set Password Retry Prompts Permitted Per-Session - system-auth (add)"
  replace:
    dest: /etc/pam.d/system-auth
    follow: yes
    regexp: '^.*\spam_pwquality.so\s(?!.*retry\s*=\s*).*$'
    replace: '\g<0> retry={{ var_password_pam_retry }}'
  tags:
    - accounts_password_pam_retry
    - unknown_severity
    - configure_strategy
    - low_complexity
    - medium_disruption
    - NIST-800-53-CM-6(b)
    - NIST-800-53-IA-5(c)
    - CJIS-5.5.3
    - DISA-STIG-RHEL-07-010119

Protect Physical Console Access   [ref]group

It is impossible to fully protect a system from an attacker with physical access, so securing the space in which the system is located should be considered a necessary step. However, there are some steps which, if taken, make it more difficult for an attacker to quickly or undetectably modify a system from its console.

contains 6 rules

Configure Screen Locking   [ref]group

When a user must temporarily leave an account logged-in, screen locking should be employed to prevent passersby from abusing the account. User education and training is particularly important for screen locking to be effective, and policies can be implemented to reinforce this.

Automatic screen locking is only meant as a safeguard for those cases where a user forgot to lock the screen.

contains 4 rules

Configure Console Screen Locking   [ref]group

A console screen locking mechanism is provided in the screen package, which is not installed by default.

contains 1 rule

Install the screen Package   [ref]rule

To enable console screen locking, install the screen package. The screen package can be installed with the following command:

$ sudo yum install screen
Instruct users to begin new terminal sessions with the following command:
$ screen
The console can now be locked with the following key combination:
ctrl+a x

Rationale:

A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but des not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, operating systems need to be able to identify when a user's session has idled and take action to initiate the session lock.

The screen package allows for a session lock to be implemented and configured.

Severity:  medium

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Strategy:enable
# Function to install packages on RHEL, Fedora, Debian, and possibly other systems.
#
# Example Call(s):
#
#     package_install aide
#
function package_install {

# Load function arguments into local variables
local package="$1"

# Check sanity of the input
if [ $# -ne "1" ]
then
  echo "Usage: package_install 'package_name'"
  echo "Aborting."
  exit 1
fi

if which dnf ; then
  if ! rpm -q --quiet "$package"; then
    dnf install -y "$package"
  fi
elif which yum ; then
  if ! rpm -q --quiet "$package"; then
    yum install -y "$package"
  fi
elif which apt-get ; then
  apt-get install -y "$package"
else
  echo "Failed to detect available packaging system, tried dnf, yum and apt-get!"
  echo "Aborting."
  exit 1
fi

}

package_install screen
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
- name: Ensure screen is installed
  package:
    name: screen
    state: present
  tags:
    - package_screen_installed
    - medium_severity
    - enable_strategy
    - low_complexity
    - low_disruption
    - NIST-800-53-AC-11(a)
    - NIST-800-171-3.1.10
    - DISA-STIG-RHEL-07-010090
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable
include install_screen

class install_screen {
  package { 'screen':
    ensure => 'installed',
  }
}
Remediation Anaconda snippet:   (show)

Complexity:low
Disruption:low
Strategy:enable

package --add=screen