contains 70 rules |
Services
[ref]groupThe 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 15 rules |
Obsolete Services
[ref]groupThis 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 3 rules |
Telnet
[ref]groupThe 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 3 rules |
Remove telnet Clients
[ref]ruleThe telnet client allows users to start connections to other
systems via the telnet protocol. Rationale:The telnet protocol is insecure and unencrypted. The use
of an unencrypted transmission medium could allow an unauthorized user
to steal credentials. The ssh package provides an
encrypted session and stronger security and is included in Red Hat
Enterprise Linux. References:
2.3.4, 3.1.13, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), A.8.2.3, A.13.1.1, A.13.2.1, A.13.2.3, A.14.1.2, A.14.1.3 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
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure telnet is removed
package:
name="{{item}}"
state=absent
with_items:
- telnet
tags:
- package_telnet_removed
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27305-2
- NIST-800-171-3.1.13
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_telnet
class remove_telnet {
package { 'telnet':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=telnet
|
Disable telnet Service
[ref]ruleThe telnet service configuration file /etc/xinetd.d/telnet
is not created automatically. If it was created manually, check the
/etc/xinetd.d/telnet file and ensure that disable = no
is changed to read disable = yes as follows below:
# description: The telnet server serves telnet sessions; it uses \\
# unencrypted username/password pairs for authentication.
service telnet
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
log_on_failure += USERID
disable = yes
}
If the /etc/xinetd.d/telnet file does not exist, make sure that
the activation of the telnet service on system boot is disabled
via the following command:
The rexec socket can be disabled with the following command:
$ sudo systemctl disable rexec.socket Rationale:The telnet protocol uses unencrypted network communication, which
means that data from the login session, including passwords and
all other information transmitted during the session, can be
stolen by eavesdroppers on the network. The telnet protocol is also
subject to man-in-the-middle attacks. References:
2.2.18, 3.1.13, 3.4.7, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-17(8), CM-7, IA-5(1)(c) Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
grep -qi disable /etc/xinetd.d/telnet && \
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/telnet
#
# Disable telnet.socket for all systemd targets
#
systemctl disable telnet.socket
#
# Stop telnet.socket if currently running
#
systemctl stop telnet.socket
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service telnet
service:
name="{{item}}"
enabled="no"
state="stopped"
register: service_result
failed_when: "service_result|failed and ('Could not find the requested service' not in service_result.msg)"
with_items:
- telnet
tags:
- service_telnet_disabled
- high_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27401-9
- NIST-800-53-AC-17(8)
- NIST-800-53-CM-7
- NIST-800-53-IA-5(1)(c)
- NIST-800-171-3.1.13
- NIST-800-171-3.4.7
|
Uninstall telnet-server Package
[ref]ruleThe telnet-server package can be uninstalled 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. References:
SV-86701r1_rule, 2.1.1, CCI-000381, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), A.8.2.3, A.13.1.1, A.13.2.1, A.13.2.3, A.14.1.2, A.14.1.3, AC-17(8), CM-7(a), SRG-OS-000095-GPOS-00049 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="{{item}}"
state=absent
with_items:
- telnet-server
tags:
- package_telnet-server_removed
- high_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27165-0
- 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
|
Base Services
[ref]groupThis 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 Automatic Bug Reporting Tool (abrtd)
[ref]ruleThe Automatic Bug Reporting Tool (abrtd ) daemon collects
and reports crash data when an application crash is detected. Using a variety
of plugins, abrtd can email crash reports to system administrators, log crash
reports to files, or forward crash reports to a centralized issue tracking
system such as RHTSupport.
The abrtd service can be disabled with the following command:
$ sudo systemctl disable abrtd.service Rationale:Mishandling crash data could expose sensitive information about
vulnerabilities in software executing on the system, as well as sensitive
information from within a process's address space or registers. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# 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.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable abrtd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service abrtd
service:
name="{{item}}"
enabled="no"
state="stopped"
register: service_result
failed_when: "service_result|failed and ('Could not find the requested service' not in service_result.msg)"
with_items:
- abrtd
tags:
- service_abrtd_disabled
- unknown_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26872-2
- NIST-800-53-AC-17(8)
- NIST-800-53-CM-7
|
SSH Server
[ref]groupThe 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 11 rules |
Configure OpenSSH Server if Necessary
[ref]groupIf 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 11 rules |
Disable SSH Access via Empty Passwords
[ref]ruleTo 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. References:
FIA_AFL.1, SV-86563r2_rule, 5.2.9, 5.5.6, 3.1.1, 3.1.5, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-3, AC-6, CM-6(b), SRG-OS-000480-GPOS-00229 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27471-2' '%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
- CCE-27471-2
- 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]ruleTo ensure the SSH idle timeout occurs precisely when the ClientAliveCountMax is set,
edit /etc/ssh/sshd_config as follows:
ClientAliveCountMax 0 Rationale:This ensures a user login will be terminated as soon as the ClientAliveCountMax
is reached. References:
SV-86865r3_rule, 5.2.12, 5.5.6, 3.1.11, CCI-001133, CCI-002361, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-2(5), SA-8, AC-12, SRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27082-7' '%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
- CCE-27082-7
- 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
|
Enable SSH Warning Banner
[ref]ruleTo 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. References:
FMT_MOF_EXT.1, SV-86849r3_rule, 5.2.16, 5.5.6, 3.1.9, CCI-000048, CCI-000050, CCI-001384, CCI-001385, CCI-001386, CCI-001387, CCI-001388, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-8(a), AC-8(b), AC-8(c)(1), AC-8(c)(2), AC-8(c)(3), SRG-OS-000023-GPOS-00006, SRG-OS-000024-GPOS-00007, SRG-OS-000228-GPOS-00088 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 grep -q $grep_case_insensitive_option "${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' '^Banner' '/etc/issue' 'CCE-27314-4' '%s %s'
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
- CCE-27314-4
- 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
|
Do Not Allow SSH Environment Options
[ref]ruleTo 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. References:
SV-86581r2_rule, 5.2.10, 5.5.6, 3.1.12, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), CM-6(b), SRG-OS-000480-GPOS-00229 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27363-1' '%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
- CCE-27363-1
- NIST-800-53-CM-6(b)
- NIST-800-171-3.1.12
- CJIS-5.5.6
- DISA-STIG-RHEL-07-010460
|
Allow Only SSH Protocol 2
[ref]ruleOnly 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 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. References:
SV-86875r3_rule, 5.2.2, 5.5.6, 3.1.13, 3.5.4, CCI-000197, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-17(8).1(ii), IA-5(1)(c), SRG-OS-000074-GPOS-00042, SRG-OS-000480-GPOS-00227 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27320-1' '%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
- CCE-27320-1
- 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]ruleSSH 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. 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27377-1' '%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
- CCE-27377-1
- 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
|
Set SSH Idle Timeout Interval
[ref]ruleSSH 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 interval
The timeout interval is given in seconds. To have a timeout
of 10 minutes, set interval to 600.
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. References:
SV-86861r3_rule, 5.2.12, 5.5.6, 3.1.11, CCI-001133, CCI-002361, AC-2(5), SA-8(i), AC-12, Req-8.1.8, SRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109 Remediation Shell script: (show)
sshd_idle_timeout_value="300"
# 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 grep -q $grep_case_insensitive_option "${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 'CCE-27433-2' '%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: 300
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
- CCE-27433-2
- 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
|
Use Only FIPS 140-2 Validated Ciphers
[ref]ruleLimit 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. References:
SV-86845r2_rule, 5.2.10, 5.5.6, 3.1.13, 3.13.11, 3.13.8, CCI-000068, CCI-000366, CCI-000803, 164.308(b)(1), 164.308(b)(2), 164.312(e)(1), 164.312(e)(2)(i), 164.312(e)(2)(ii), 164.314(b)(2)(i), AC-3, AC-17(2), AU-10(5), CM-6(b), IA-5(1)(c), IA-7, SRG-OS-000033-GPOS-00014, SRG-OS-000120-GPOS-00061, SRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173 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 grep -q $grep_case_insensitive_option "${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' 'CCE-27295-5' '%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
- CCE-27295-5
- 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]ruleSSH'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. References:
FIA_AFL.1, SV-86583r2_rule, 5.2.7, 5.5.6, 3.1.12, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-3, CM-6(b), SRG-OS-000480-GPOS-00229 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
- CCE-27413-4
- 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 SSH Server firewalld Firewall exception
[ref]ruleBy default, inbound connections to SSH's port are allowed. If
the SSH server is being used but denied by the firewall, this exception should
be added to the firewall configuration.
To configure firewalld to allow access, run the following command(s):
firewall-cmd --permanent --add-service=ssh Rationale:If inbound SSH connections are expected, adding a firewall rule exception
will allow remote access through the SSH port. Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Ensure firewalld is installed
package:
name="{{item}}"
state=present
with_items:
- firewalld
tags:
- firewalld_sshd_port_enabled
- unknown_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-80361-9
- NIST-800-171-3.1.12
- name: XCCDF Value sshd_listening_port # promote to variable
set_fact:
sshd_listening_port: (N/A)
tags:
- always
- name: Enable SSHD in firewalld (custom port)
firewalld:
port: "{{ sshd_listening_port }}/tcp"
permanent: yes
state: enabled
when: sshd_listening_port != 22
tags:
- firewalld_sshd_port_enabled
- unknown_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-80361-9
- NIST-800-171-3.1.12
- name: Enable SSHD in firewalld (default port)
firewalld:
service: ssh
permanent: yes
state: enabled
when: sshd_listening_port == 22
tags:
- firewalld_sshd_port_enabled
- unknown_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-80361-9
- NIST-800-171-3.1.12
|
Disable SSH Root Login
[ref]ruleThe root user should never be allowed to login to a
system directly over a network.
To disable root login via SSH, add or correct the following line
in /etc/ssh/sshd_config :
PermitRootLogin no Rationale:Even though the communications channel may be encrypted, an additional layer of
security is gained by extending the policy of not logging directly on as root.
In addition, logging in with a user-specific account provides individual
accountability of actions performed on the system and also helps to minimize
direct attack attempts on root's password. References:
FIA_AFL.1, SV-86871r2_rule, 5.2.8, 5.5.6, 3.1.1, 3.1.5, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), AC-3, AC-6(2), IA-2(1), IA-2(5), SRG-OS-000480-GPOS-00227 Remediation Shell script: (show)
SSHD_CONFIG='/etc/ssh/sshd_config'
# Obtain line number of first uncommented case-insensitive occurrence of Match
# block directive (possibly prefixed with whitespace) present in $SSHD_CONFIG
FIRST_MATCH_BLOCK=$(sed -n '/^[[:space:]]*Match[^\n]*/I{=;q}' $SSHD_CONFIG)
# Obtain line number of first uncommented case-insensitive occurence of
# PermitRootLogin directive (possibly prefixed with whitespace) present in
# $SSHD_CONFIG
FIRST_PERMIT_ROOT_LOGIN=$(sed -n '/^[[:space:]]*PermitRootLogin[^\n]*/I{=;q}' $SSHD_CONFIG)
# Case: Match block directive not present in $SSHD_CONFIG
if [ -z "$FIRST_MATCH_BLOCK" ]
then
# Case: PermitRootLogin directive not present in $SSHD_CONFIG yet
if [ -z "$FIRST_PERMIT_ROOT_LOGIN" ]
then
# Append 'PermitRootLogin no' at the end of $SSHD_CONFIG
echo -e "\nPermitRootLogin no" >> $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG already
else
# Replace first uncommented case-insensitive occurrence
# of PermitRootLogin directive
sed -i "$FIRST_PERMIT_ROOT_LOGIN s/^[[:space:]]*PermitRootLogin.*$/PermitRootLogin no/I" $SSHD_CONFIG
fi
# Case: Match block directive present in $SSHD_CONFIG
else
# Case: PermitRootLogin directive not present in $SSHD_CONFIG yet
if [ -z "$FIRST_PERMIT_ROOT_LOGIN" ]
then
# Prepend 'PermitRootLogin no' before first uncommented
# case-insensitive occurrence of Match block directive
sed -i "$FIRST_MATCH_BLOCK s/^\([[:space:]]*Match[^\n]*\)/PermitRootLogin no\n\1/I" $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG and placed
# before first Match block directive
elif [ "$FIRST_PERMIT_ROOT_LOGIN" -lt "$FIRST_MATCH_BLOCK" ]
then
# Replace first uncommented case-insensitive occurrence
# of PermitRootLogin directive
sed -i "$FIRST_PERMIT_ROOT_LOGIN s/^[[:space:]]*PermitRootLogin.*$/PermitRootLogin no/I" $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG and placed
# after first Match block directive
else
# Prepend 'PermitRootLogin no' before first uncommented
# case-insensitive occurrence of Match block directive
sed -i "$FIRST_MATCH_BLOCK s/^\([[:space:]]*Match[^\n]*\)/PermitRootLogin no\n\1/I" $SSHD_CONFIG
fi
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Disable SSH Root Login"
lineinfile:
create: yes
dest: "/etc/ssh/sshd_config"
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
insertafter: '(?i)^#?authentication'
validate: sshd -t -f %s
#notify: restart sshd
tags:
- sshd_disable_root_login
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27445-6
- NIST-800-53-AC-3
- NIST-800-53-AC-6(2)
- NIST-800-53-IA-2(1)
- NIST-800-53-IA-2(5)
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040370
|
System Settings
[ref]groupContains rules that check correct system settings. |
contains 55 rules |
Network Configuration and Firewalls
[ref]groupMost 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 4 rules |
firewalld
[ref]groupThe 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 2 rules |
Strengthen the Default Ruleset
[ref]groupThe 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. |
contains 1 rule |
Set Default firewalld Zone for Incoming Packets
[ref]ruleTo 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 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. |
Inspect and Activate Default firewalld Rules
[ref]groupFirewalls 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 Server 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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# 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.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable firewalld
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service firewalld
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- firewalld
tags:
- service_firewalld_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27361-5
- NIST-800-53-CM-6(b)
- NIST-800-171-3.1.3
- NIST-800-171-3.4.7
- DISA-STIG-RHEL-07-040520
|
Uncommon Network Protocols
[ref]groupThe 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. |
contains 2 rules |
Disable DCCP Support
[ref]ruleThe 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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^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/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- dccp
tags:
- kernel_module_dccp_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26828-4
- NIST-800-53-CM-7
- NIST-800-171-3.4.6
- CJIS-5.10.1
- DISA-STIG-RHEL-07-020101
|
Disable SCTP Support
[ref]ruleThe Stream Control Transmission Protocol (SCTP) is a
transport layer protocol, designed to support the idea of
message-oriented communication, with several streams of messages
within one connection.
To configure the system to prevent the sctp
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install sctp /bin/true Rationale:Disabling SCTP protects
the system against exploitation of any flaws in its implementation. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install sctp" /etc/modprobe.d/sctp.conf ; then
sed -i 's/^install sctp.*/install sctp /bin/true/g' /etc/modprobe.d/sctp.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf
echo "install sctp /bin/true" >> /etc/modprobe.d/sctp.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'sctp' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- sctp
tags:
- kernel_module_sctp_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27106-4
- NIST-800-53-CM-7
- NIST-800-171-3.4.6
- CJIS-5.10.1
|
SELinux
[ref]groupSELinux 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 2 rules |
Configure SELinux Policy
[ref]ruleThe 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 . References:
SV-86615r3_rule, 1.6.1.3, 3.1.2, 3.7.2, CCI-002696, 164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e), AC-3, AC-3(3), AC-3(4), AC-4, AC-6, AU-9, SI-6(a), SRG-OS-000445-GPOS-00199 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 grep -q $grep_case_insensitive_option "${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 'CCE-27279-9' '%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: 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
- CCE-27279-9
- 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 SELinux State is Enforcing
[ref]ruleThe 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. References:
SV-86613r2_rule, 1.6.1.2, 3.1.2, 3.7.2, CCI-002165, CCI-002696, 164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e), AC-3, AC-3(3), AC-3(4), AC-4, AC-6, AU-9, SI-6(a), SRG-OS-000445-GPOS-00199 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 grep -q $grep_case_insensitive_option "${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 'CCE-27334-2' '%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: 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
- CCE-27334-2
- 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]groupIn 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 23 rules |
Protect Accounts by Restricting Password-Based Login
[ref]groupConventionally, Unix shell accounts are accessed by
providing a username and password to a login program, which tests
these values for correctness using the /etc/passwd and
/etc/shadow files. Password-based login is vulnerable to
guessing of weak passwords, and to sniffing and man-in-the-middle
attacks against passwords entered over a network or at an insecure
console. Therefore, mechanisms for accessing accounts by entering
usernames and passwords should be restricted to those which are
operationally necessary. |
contains 7 rules |
Restrict Root Logins
[ref]groupDirect root logins should be allowed only for emergency use.
In normal situations, the administrator should access the system
via a unique unprivileged account, and then use su or sudo to execute
privileged commands. Discouraging administrators from accessing the
root account directly ensures an audit trail in organizations with
multiple administrators. Locking down the channels through which
root can connect directly also reduces opportunities for
password-guessing against the root account. The login program
uses the file /etc/securetty to determine which interfaces
should allow root logins.
The virtual devices /dev/console
and /dev/tty* represent the system consoles (accessible via
the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default
installation). The default securetty file also contains /dev/vc/* .
These are likely to be deprecated in most environments, but may be retained
for compatibility. Root should also be prohibited from connecting
via network protocols. Other sections of this document
include guidance describing how to prevent root from logging in via SSH. |
contains 2 rules |
Ensure that System Accounts Do Not Run a Shell Upon Login
[ref]ruleSome accounts are not associated with a human user of the system, and exist to
perform some administrative function. Should an attacker be able to log into
these accounts, they should not be granted access to a shell.
The login shell for each local account is stored in the last field of each line
in /etc/passwd . System accounts are those user accounts with a user ID
less than UID_MIN, where value of UID_MIN directive is set in
/etc/login.defs configuration file. In the default configuration UID_MIN is set
to 1000, thus system accounts are those user accounts with a user ID less than
1000. The user ID is stored in the third field. If any system account
SYSACCT (other than root) has a login shell, disable it with the
command: $ sudo usermod -s /sbin/nologin SYSACCT Rationale:Ensuring shells are not given to system accounts upon login makes it more
difficult for attackers to make use of system accounts. |
Verify Only Root Has UID 0
[ref]ruleIf any account other than root has a UID of 0, this misconfiguration should
be investigated and the accounts other than root should be removed or
have their UID changed.
If the account is associated with system commands or applications the UID should be changed
to one greater than "0" but less than "1000." Otherwise assign a UID greater than "1000" that
has not already been assigned. Rationale:An account has root authority if it has a UID of 0. Multiple accounts
with a UID of 0 afford more opportunity for potential intruders to
guess a password for a privileged account. Proper configuration of
sudo is recommended to afford multiple system administrators
access to root privileges in an accountable manner. Remediation Shell script: (show)
awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs passwd -l
|
Set Password Expiration Parameters
[ref]groupThe file /etc/login.defs controls several
password-related settings. Programs such as passwd ,
su , and
login consult /etc/login.defs to determine
behavior with regard to password aging, expiration warnings,
and length. See the man page login.defs(5) for more information.
Users should be forced to change their passwords, in order to
decrease the utility of compromised passwords. However, the need to
change passwords often should be balanced against the risk that
users will reuse or write down passwords if forced to change them
too often. Forcing password changes every 90-360 days, depending on
the environment, is recommended. Set the appropriate value as
PASS_MAX_DAYS and apply it to existing accounts with the
-M flag.
The PASS_MIN_DAYS (-m ) setting prevents password
changes for 7 days after the first change, to discourage password
cycling. If you use this setting, train users to contact an administrator
for an emergency password change in case a new password becomes
compromised. The PASS_WARN_AGE (-W ) setting gives
users 7 days of warnings at login time that their passwords are about to expire.
For example, for each existing human user USER, expiration parameters
could be adjusted to a 180 day maximum password age, 7 day minimum password
age, and 7 day warning period with the following command:
$ sudo chage -M 180 -m 7 -W 7 USER |
contains 3 rules |
Set Password Minimum Length in login.defs
[ref]ruleTo specify password length requirements for new accounts,
edit the file /etc/login.defs and add or correct the following
line:
PASS_MIN_LEN 6
The DoD requirement is 15 .
The FISMA requirement is 12 .
The profile requirement is 6 .
If a program consults /etc/login.defs and also another PAM module
(such as pam_pwquality ) during a password change operation,
then the most restrictive must be satisfied. See PAM section
for more information about enforcing password quality requirements.Rationale:Requiring a minimum password length makes password
cracking attacks more difficult by ensuring a larger
search space. However, any security benefit from an onerous requirement
must be carefully weighed against usability problems, support costs, or counterproductive
behavior that may result. Remediation Shell script: (show)
var_accounts_password_minlen_login_defs="6"
grep -q ^PASS_MIN_LEN /etc/login.defs && \
sed -i "s/PASS_MIN_LEN.*/PASS_MIN_LEN $var_accounts_password_minlen_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_MIN_LEN $var_accounts_password_minlen_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_password_minlen_login_defs # promote to variable
set_fact:
var_accounts_password_minlen_login_defs: 6
tags:
- always
- name: "Set Password Minimum Length in login.defs"
lineinfile:
dest: /etc/login.defs
regexp: "^PASS_MIN_LEN *[0-9]*"
state: present
line: "PASS_MIN_LEN {{ var_accounts_password_minlen_login_defs }}"
tags:
- accounts_password_minlen_login_defs
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27123-9
- NIST-800-53-IA-5(f)
- NIST-800-53-IA-5(1)(a)
- NIST-800-171-3.5.7
- CJIS-5.6.2.1
|
Set Password Warning Age
[ref]ruleTo specify how many days prior to password
expiration that a warning will be issued to users,
edit the file /etc/login.defs and add or correct
the following line:
PASS_WARN_AGE 7
The DoD requirement is 7.
The profile requirement is 7 .Rationale:Setting the password warning age enables users to
make the change at a practical time. Remediation Shell script: (show)
var_accounts_password_warn_age_login_defs="7"
grep -q ^PASS_WARN_AGE /etc/login.defs && \
sed -i "s/PASS_WARN_AGE.*/PASS_WARN_AGE $var_accounts_password_warn_age_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_WARN_AGE $var_accounts_password_warn_age_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_password_warn_age_login_defs # promote to variable
set_fact:
var_accounts_password_warn_age_login_defs: 7
tags:
- always
- name: "Set Password Warning Age"
lineinfile:
dest: /etc/login.defs
regexp: "^PASS_WARN_AGE *[0-9]*"
state: present
line: "PASS_WARN_AGE {{ var_accounts_password_warn_age_login_defs }}"
tags:
- accounts_password_warn_age_login_defs
- unknown_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26486-1
- NIST-800-53-AC-2(2)
- NIST-800-53-IA-5(f)
- NIST-800-171-3.5.8
|
Set Password Minimum Age
[ref]ruleTo specify password minimum age for new accounts,
edit the file /etc/login.defs
and add or correct the following line:
PASS_MIN_DAYS 7
A value of 1 day is considered sufficient for many
environments. The DoD requirement is 1.
The profile requirement is 7 .Rationale:Enforcing a minimum password lifetime helps to prevent repeated password changes to defeat
the password reuse or history enforcement requirement. If users are allowed to immediately
and continually change their password, then the password could be repeatedly changed in a
short period of time to defeat the organization's policy regarding password reuse.
Setting the minimum password age protects against users cycling back to a favorite password
after satisfying the password reuse requirement. Remediation Shell script: (show)
var_accounts_minimum_age_login_defs="7"
grep -q ^PASS_MIN_DAYS /etc/login.defs && \
sed -i "s/PASS_MIN_DAYS.*/PASS_MIN_DAYS $var_accounts_minimum_age_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_MIN_DAYS $var_accounts_minimum_age_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_minimum_age_login_defs # promote to variable
set_fact:
var_accounts_minimum_age_login_defs: 7
tags:
- always
- name: Set Password Minimum Age
lineinfile:
create: yes
dest: /etc/login.defs
regexp: ^#?PASS_MIN_DAYS
line: "PASS_MIN_DAYS {{ var_accounts_minimum_age_login_defs }}"
tags:
- accounts_minimum_age_login_defs
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27002-5
- NIST-800-53-IA-5(f)
- NIST-800-53-IA-5(1)(d)
- NIST-800-171-3.5.8
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010230
|
Verify Proper Storage and Existence of Password
Hashes
[ref]groupBy default, password hashes for local accounts are stored
in the second field (colon-separated) in
/etc/shadow . This file should be readable only by
processes running with root credentials, preventing users from
casually accessing others' password hashes and attempting
to crack them.
However, it remains possible to misconfigure the system
and store password hashes
in world-readable files such as /etc/passwd , or
to even store passwords themselves in plaintext on the system.
Using system-provided tools for password change/creation
should allow administrators to avoid such misconfiguration. |
contains 2 rules |
Verify All Account Password Hashes are Shadowed
[ref]ruleIf any password hashes are stored in /etc/passwd (in the second field,
instead of an x or * ), the cause of this misconfiguration should be
investigated. The account should have its password reset and the hash should be
properly stored, or the account should be deleted entirely. Rationale:The hashes for all user account passwords should be stored in
the file /etc/shadow and never in /etc/passwd ,
which is readable by all users. |
Prevent Log In to Accounts With Empty Password
[ref]ruleIf an account is configured for password authentication
but does not have an assigned password, it may be possible to log
into the account without authentication. Remove any instances of the nullok
option in /etc/pam.d/system-auth to
prevent logins with empty passwords. Rationale:If an account has an empty password, anyone could log in and
run commands with the privileges of that account. Accounts with
empty passwords should never be used in operational environments. References:
FIA_AFL.1, SV-86561r2_rule, 5.5.2, 3.1.1, 3.1.5, CCI-000366, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), AC-6, IA-5(b), IA-5(c), IA-5(1)(a), Req-8.2.3, SRG-OS-000480-GPOS-00227 Remediation Shell script: (show)
sed --follow-symlinks -i 's/\<nullok\>//g' /etc/pam.d/system-auth
sed --follow-symlinks -i 's/\<nullok\>//g' /etc/pam.d/password-auth
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Strategy: | configure |
---|
- name: "Prevent Log In to Accounts With Empty Password - system-auth"
replace:
dest: /etc/pam.d/system-auth
follow: yes
regexp: 'nullok'
tags:
- no_empty_passwords
- high_severity
- configure_strategy
- low_complexity
- medium_disruption
- CCE-27286-4
- NIST-800-53-AC-6
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(a)
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- PCI-DSS-Req-8.2.3
- CJIS-5.5.2
- DISA-STIG-RHEL-07-010290
- name: "Prevent Log In to Accounts With Empty Password - password-auth"
replace:
dest: /etc/pam.d/password-auth
follow: yes
regexp: 'nullok'
tags:
- no_empty_passwords
- high_severity
- configure_strategy
- low_complexity
- medium_disruption
- CCE-27286-4
- NIST-800-53-AC-6
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(a)
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- PCI-DSS-Req-8.2.3
- CJIS-5.5.2
- DISA-STIG-RHEL-07-010290
|
Protect Accounts by Configuring PAM
[ref]groupPAM, 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. |
contains 11 rules |
Set Password Hashing Algorithm
[ref]groupThe 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]ruleIn /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. References:
SV-86545r1_rule, 6.3.1, 5.6.2.2, 3.13.11, CCI-000196, IA-5(b), IA-5(c), IA-5(1)(c), IA-7, Req-8.2.1, SRG-OS-000073-GPOS-00041 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
- CCE-27124-7
- 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 PAM's Password Hashing Algorithm
[ref]ruleThe 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. References:
SV-86543r2_rule, 6.3.1, 5.6.2.2, 3.13.11, CCI-000196, IA-5(b), IA-5(c), IA-5(1)(c), IA-7, Req-8.2.1, SRG-OS-000073-GPOS-00041 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 Password Hashing Algorithm in /etc/libuser.conf
[ref]ruleIn /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. 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: "^.default]"
regexp: ^#?crypt_style
line: crypt_style = sha512
state: present
tags:
- set_password_hashing_algorithm_libuserconf
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27053-8
- 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 Lockouts for Failed Password Attempts
[ref]groupThe 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 .
|
contains 2 rules |
Limit Password Reuse
[ref]ruleDo 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.so module, as shown below:
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. 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: 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
- CCE-26923-3
- 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
- CCE-26923-3
- 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 Deny For Failed Password Attempts
[ref]ruleTo 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=5 unlock_time=(N/A) fail_interval=(N/A) - add the following line immediately
after the pam_unix.so statement in the AUTH section:
auth [default=die] pam_faillock.so authfail deny=5 unlock_time=(N/A) fail_interval=(N/A) - 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. Remediation Shell script: (show)
var_accounts_passwords_pam_faillock_deny="5"
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 deny=[0-9]+ 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
# Invoke the function without args, so its body is substituded right here.
function set_faillock_option_to_value_in_pam_file {
# If invoked with no arguments, exit. This is an intentional behavior.
[ $# -gt 1 ] || return 0
[ $# -ge 3 ] || die "$0 requires exactly zero, three, or four arguments"
[ $# -le 4 ] || die "$0 requires exactly zero, three, or four arguments"
local _pamFile="$1" _option="$2" _value="$3" _insert_lines_callback="$4"
# pam_faillock.so already present?
if grep -q "^auth.*pam_faillock.so.*" "$_pamFile"; then
# pam_faillock.so present, is the option present?
if grep -q "^auth.*[default=die].*pam_faillock.so.*authfail.*$_option=" "$_pamFile"; then
# both pam_faillock.so & option present, just correct option to the right value
sed -i --follow-symlinks "s/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\($_option *= *\).*/\1\2$_value/" "$_pamFile"
sed -i --follow-symlinks "s/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\($_option *= *\).*/\1\2$_value/" "$_pamFile"
# pam_faillock.so present, but the option not yet
else
# append correct option value to appropriate places
sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ $_option=$_value/" "$_pamFile"
sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ $_option=$_value/" "$_pamFile"
fi
# pam_faillock.so not present yet
else
test -z "$_insert_lines_callback" || "$_insert_lines_callback" "$_option" "$_value" "$_pamFile"
# insert pam_faillock.so preauth & authfail rows with proper value of the option in question
fi
}
set_faillock_option_to_value_in_pam_file
function insert_lines_if_pam_faillock_so_not_present {
# insert pam_faillock.so preauth row with proper value of the 'deny' option before pam_unix.so
sed -i --follow-symlinks "/^auth.*pam_unix.so.*/i auth required pam_faillock.so preauth silent $_option=$_value" $_pamFile
# insert pam_faillock.so authfail row with proper value of the 'deny' option before pam_deny.so, after all modules which determine authentication outcome.
sed -i --follow-symlinks "/^auth.*pam_deny.so.*/i auth [default=die] pam_faillock.so authfail $_option=$_value" $_pamFile
}
for pamFile in "${AUTH_FILES[@]}"
do
# 'true &&' has to be there due to build system limitation
true && set_faillock_option_to_value_in_pam_file "$pamFile" deny "$var_accounts_passwords_pam_faillock_deny" insert_lines_if_pam_faillock_so_not_present
# add pam_faillock.so into account phase
if ! grep -q "^account.*required.*pam_faillock.so" $pamFile; then
sed -i --follow-symlinks "/^account.*required.*pam_unix.so/i account required pam_faillock.so" $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: 5
tags:
- always
- name: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable
set_fact:
var_accounts_passwords_pam_faillock_unlock_time: (N/A)
tags:
- always
- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable
set_fact:
var_accounts_passwords_pam_faillock_fail_interval: (N/A)
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
- CCE-27350-8
- 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
- CCE-27350-8
- 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
- CCE-27350-8
- 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]groupThe 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 6 rules |
Set Password Quality Requirements with pam_pwquality
[ref]groupThe 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 6 rules |
Set Password Strength Minimum Digit Characters
[ref]ruleThe 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. Remediation Shell script: (show)
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 grep -q $grep_case_insensitive_option "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^dcredit' $var_password_pam_dcredit 'CCE-27214-6' '%s = %s'
Remediation Ansible snippet: (show)
- name: XCCDF Value var_password_pam_dcredit # promote to variable
set_fact:
var_password_pam_dcredit: -1
tags:
- always
- name: Ensure PAM variable dcredit is set accordingly
lineinfile:
create=yes
dest="/etc/security/pwquality.conf"
regexp="^dcredit"
line="dcredit = {{ var_password_pam_dcredit }}"
tags:
- accounts_password_pam_dcredit
- medium_severity
- CCE-27214-6
- 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 Characters
[ref]ruleThe 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 3 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. Remediation Shell script: (show)
var_password_pam_difok="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 grep -q $grep_case_insensitive_option "${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 'CCE-26631-2' '%s = %s'
Remediation Ansible snippet: (show)
- name: XCCDF Value var_password_pam_difok # promote to variable
set_fact:
var_password_pam_difok: 3
tags:
- always
- name: Ensure PAM variable difok is set accordingly
lineinfile:
create=yes
dest="/etc/security/pwquality.conf"
regexp="^difok"
line="difok = {{ var_password_pam_difok }}"
tags:
- accounts_password_pam_difok
- medium_severity
- CCE-26631-2
- 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]ruleThe 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 -2 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. Remediation Shell script: (show)
var_password_pam_ocredit="-2"
# 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 grep -q $grep_case_insensitive_option "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^ocredit' $var_password_pam_ocredit 'CCE-27360-7' '%s = %s'
Remediation Ansible snippet: (show)
- name: XCCDF Value var_password_pam_ocredit # promote to variable
set_fact:
var_password_pam_ocredit: -2
tags:
- always
- name: Ensure PAM variable ocredit is set accordingly
lineinfile:
create=yes
dest="/etc/security/pwquality.conf"
regexp="^ocredit"
line="ocredit = {{ var_password_pam_ocredit }}"
tags:
- accounts_password_pam_ocredit
- medium_severity
- CCE-27360-7
- 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]ruleThe 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. Remediation Shell script: (show)
var_password_pam_lcredit="-2"
# 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 grep -q $grep_case_insensitive_option "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^lcredit' $var_password_pam_lcredit 'CCE-27345-8' '%s = %s'
Remediation Ansible snippet: (show)
- name: XCCDF Value var_password_pam_lcredit # promote to variable
set_fact:
var_password_pam_lcredit: -2
tags:
- always
- name: Ensure PAM variable lcredit is set accordingly
lineinfile:
create=yes
dest="/etc/security/pwquality.conf"
regexp="^lcredit"
line="lcredit = {{ var_password_pam_lcredit }}"
tags:
- accounts_password_pam_lcredit
- medium_severity
- CCE-27345-8
- 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]ruleThe 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. Remediation Shell script: (show)
var_password_pam_ucredit="-2"
# 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 grep -q $grep_case_insensitive_option "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^ucredit' $var_password_pam_ucredit 'CCE-27200-5' '%s = %s'
Remediation Ansible snippet: (show)
- name: XCCDF Value var_password_pam_ucredit # promote to variable
set_fact:
var_password_pam_ucredit: -2
tags:
- always
- name: Ensure PAM variable ucredit is set accordingly
lineinfile:
create=yes
dest="/etc/security/pwquality.conf"
regexp="^ucredit"
line="ucredit = {{ var_password_pam_ucredit }}"
tags:
- accounts_password_pam_ucredit
- medium_severity
- CCE-27200-5
- 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]ruleTo 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. 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: 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
- CCE-27160-1
- 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
- CCE-27160-1
- 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]groupIt 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 5 rules |
Set Boot Loader Password
[ref]groupDuring 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 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 4 rules |
Verify /boot/grub2/grub.cfg Group Ownership
[ref]ruleThe file /boot/grub2/grub.cfg should
be group-owned by the root group to prevent
destruction or modification of the file.
To properly set the group owner of /boot/grub2/grub.cfg , run the command:
$ sudo chgrp root /boot/grub2/grub.cfg Rationale:The root group is a highly-privileged group. Furthermore, the group-owner of this
file should not have any access privileges anyway. References:
1.4.1, 5.5.2.2, 3.4.5, CCI-000225, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), AC-6(7), Req-7.1 Remediation Shell script: (show)
chgrp root /boot/grub2/grub.cfg
|
Verify /boot/grub2/grub.cfg User Ownership
[ref]ruleThe file /boot/grub2/grub.cfg should
be owned by the root user to prevent destruction
or modification of the file.
To properly set the owner of /boot/grub2/grub.cfg , run the command:
$ sudo chown root /boot/grub2/grub.cfg Rationale:Only root should be able to modify important boot parameters. References:
1.4.1, 5.5.2.2, 3.4.5, CCI-000225, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), AC-6(7), Req-7.1 Remediation Shell script: (show)
chown root /boot/grub2/grub.cfg
|
Verify /boot/grub2/grub.cfg Permissions
[ref]ruleFile permissions for /boot/grub2/grub.cfg should be set to 600.
To properly set the permissions of /boot/grub2/grub.cfg , run the command:
$ sudo chmod 600 /boot/grub2/grub.cfg Rationale:Proper permissions ensure that only the root user can modify important boot
parameters. References:
1.4.1, 3.4.5, CCI-000225, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), AC-6(7) Remediation Shell script: (show)
chmod 600 /boot/grub2/grub.cfg
|
Set Boot Loader Password
[ref]ruleThe 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.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
References:
FIA_AFL.1, SV-86585r4_rule, 1.4.2, 3.4.5, CCI-000213, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), IA-2(1), IA-5(e), AC-3, SRG-OS-000080-GPOS-00048 |
Require Authentication for Single User Mode
[ref]ruleSingle-user mode is intended as a system recovery
method, providing a single user root access to the system by
providing a boot option at startup. By default, no authentication
is performed if single-user mode is selected.
By default, single-user mode is protected by requiring a password and is set
in /usr/lib/systemd/system/rescue.service . Rationale:This prevents attackers with physical access from trivially bypassing security
on the machine and gaining root access. Such accesses are further prevented
by configuring the bootloader password. References:
FIA_AFL.1, SV-92519r1_rule, 1.4.3, 3.1.1, 3.4.5, CCI-000213, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), IA-2(1), AC-3, SRG-OS-000080-GPOS-00048, SV-92519r1_rule Remediation Shell script: (show)
grep -q "^ExecStart=\-.*/sbin/sulogin" /usr/lib/systemd/system/rescue.service
if ! [ $? -eq 0 ]; then
sed -i "s/ExecStart=-.*-c \"/&\/sbin\/sulogin; /g" /usr/lib/systemd/system/rescue.service
fi
|
File Permissions and Masks
[ref]groupTraditional Unix security relies heavily on file and
directory permissions to prevent unauthorized users from reading or
modifying files to which they should not have access.
Several of the commands in this section search filesystems
for files or directories with certain characteristics, and are
intended to be run on every local partition on a given system.
When the variable PART appears in one of the commands below,
it means that the command is intended to be run repeatedly, with the
name of each local partition substituted for PART in turn.
The following command prints a list of all xfs partitions on the local
system, which is the default filesystem for Red Hat Enterprise Linux
7 installations:
$ mount -t xfs | awk '{print $3}'
For any systems that use a different
local filesystem type, modify this command as appropriate. |
contains 16 rules |
Verify Permissions on Important Files and
Directories
[ref]groupPermissions for many files on a system must be set
restrictively to ensure sensitive information is properly protected.
This section discusses important
permission restrictions which can be verified
to ensure that no harmful discrepancies have
arisen. |
contains 16 rules |
Verify Permissions on Files with Local Account Information and Credentials
[ref]groupThe default restrictive permissions for files which act as
important security databases such as passwd , shadow ,
group , and gshadow files must be maintained. Many utilities
need read access to the passwd file in order to function properly, but
read access to the shadow file allows malicious attacks against system
passwords, and should never be enabled. |
contains 12 rules |
Verify Permissions on shadow File
[ref]rule
To properly set the permissions of /etc/shadow , run the command:
$ sudo chmod 0000 /etc/shadow
Rationale:The /etc/shadow file contains the list of local
system accounts and stores password hashes. Protection of this file is
critical for system security. Failure to give ownership of this file
to root provides the designated owner with access to sensitive information
which could weaken the system security posture. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0000 /etc/shadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Ensure permission 0000 on /etc/shadow
file:
path="{{item}}"
mode=0000
with_items:
- /etc/shadow
tags:
- file_permissions_etc_shadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27100-7
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify Group Who Owns group File
[ref]rule
To properly set the group owner of /etc/group , run the command:
$ sudo chgrp root /etc/group
Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_groupowner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27037-1
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27037-1
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify Group Who Owns shadow File
[ref]rule
To properly set the group owner of /etc/shadow , run the command:
$ sudo chgrp root /etc/shadow
Rationale:The /etc/shadow file stores password hashes. Protection of this file is
critical for system security. Remediation Shell script: (show)
|
Verify User Who Owns group File
[ref]rule
To properly set the owner of /etc/group , run the command:
$ sudo chown root /etc/group
Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_owner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26933-2
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26933-2
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify Permissions on group File
[ref]rule
To properly set the permissions of /etc/group , run the command:
$ sudo chmod 644 /etc/group
Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0644 /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_permissions_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26949-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set permissions
file:
path: "{{ item.path }}"
mode: 0644
with_items:
- "{{ files_found.files }}"
tags:
- file_permissions_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26949-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify Group Who Owns gshadow File
[ref]rule
To properly set the group owner of /etc/gshadow , run the command:
$ sudo chgrp root /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/gshadow file(s)
find:
paths: "{{ '/etc/gshadow' | dirname }}"
patterns: "{{ '/etc/gshadow' | basename }}"
register: files_found
tags:
- file_groupowner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26840-9
- NIST-800-53-AC-6
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26840-9
- NIST-800-53-AC-6
|
Verify Group Who Owns passwd File
[ref]rule
To properly set the group owner of /etc/passwd , run the command:
$ sudo chgrp root /etc/passwd
Rationale:The /etc/passwd file contains information about the users that are configured on
the system. Protection of this file is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/passwd file(s)
find:
paths: "{{ '/etc/passwd' | dirname }}"
patterns: "{{ '/etc/passwd' | basename }}"
register: files_found
tags:
- file_groupowner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26639-5
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26639-5
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify User Who Owns gshadow File
[ref]rule
To properly set the owner of /etc/gshadow , run the command:
$ sudo chown root /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/gshadow file(s)
find:
paths: "{{ '/etc/gshadow' | dirname }}"
patterns: "{{ '/etc/gshadow' | basename }}"
register: files_found
tags:
- file_owner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27161-9
- NIST-800-53-AC-6
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27161-9
- NIST-800-53-AC-6
|
Verify User Who Owns passwd File
[ref]rule
To properly set the owner of /etc/passwd , run the command:
$ sudo chown root /etc/passwd
Rationale:The /etc/passwd file contains information about the users that are configured on
the system. Protection of this file is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/passwd file(s)
find:
paths: "{{ '/etc/passwd' | dirname }}"
patterns: "{{ '/etc/passwd' | basename }}"
register: files_found
tags:
- file_owner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27138-7
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27138-7
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify Permissions on gshadow File
[ref]rule
To properly set the permissions of /etc/gshadow , run the command:
$ sudo chmod 0000 /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0000 /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/gshadow file(s)
find:
paths: "{{ '/etc/gshadow' | dirname }}"
patterns: "{{ '/etc/gshadow' | basename }}"
register: files_found
tags:
- file_permissions_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27162-7
- NIST-800-53-AC-6
- name: Set permissions
file:
path: "{{ item.path }}"
mode: 0000
with_items:
- "{{ files_found.files }}"
tags:
- file_permissions_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27162-7
- NIST-800-53-AC-6
|
Verify User Who Owns shadow File
[ref]rule
To properly set the owner of /etc/shadow , run the command:
$ sudo chown root /etc/shadow
Rationale:The /etc/shadow file contains the list of local
system accounts and stores password hashes. Protection of this file is
critical for system security. Failure to give ownership of this file
to root provides the designated owner with access to sensitive information
which could weaken the system security posture. Remediation Shell script: (show)
|
Verify Permissions on passwd File
[ref]rule
To properly set the permissions of /etc/passwd , run the command:
$ sudo chmod 0644 /etc/passwd
Rationale:If the /etc/passwd file is writable by a group-owner or the
world the risk of its compromise is increased. The file contains the list of
accounts on the system and associated information, and protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0644 /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/passwd file(s)
find:
paths: "{{ '/etc/passwd' | dirname }}"
patterns: "{{ '/etc/passwd' | basename }}"
register: files_found
tags:
- file_permissions_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26887-0
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
- name: Set permissions
file:
path: "{{ item.path }}"
mode: 0644
with_items:
- "{{ files_found.files }}"
tags:
- file_permissions_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26887-0
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- CJIS-5.5.2.2
|
Verify File Permissions Within Some Important Directories
[ref]groupSome directories contain files whose confidentiality or integrity
is notably important and may also be susceptible to misconfiguration over time, particularly if
unpackaged software is installed. As such,
an argument exists to verify that files' permissions within these directories remain
configured correctly and restrictively. |
contains 4 rules |
Verify that Shared Library Files Have Root Ownership
[ref]ruleSystem-wide shared library files, which are linked to executables
during process load time or run time, are stored in the following directories
by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also
stored in /lib/modules . All files in these directories should be
owned by the root user. If the directory, or any file in these
directories, is found to be owned by a user other than root correct its
ownership with the following command:
$ sudo chown root FILE Rationale:Files from shared library directories are loaded into the address
space of processes (including privileged ones) or of the kernel itself at
runtime. Proper ownership is necessary to protect the integrity of the system. Remediation Shell script: (show)
for LIBDIR in /usr/lib /usr/lib64 /lib /lib64
do
if [ -d $LIBDIR ]
then
find -L $LIBDIR \! -user root -exec chown root {} \;
fi
done
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list libraries without root ownership"
shell: "find -L /usr/lib /usr/lib64 /lib /lib64 \\! -user root"
register: libraries_not_owned_by_root
changed_when: False
failed_when: False
check_mode: no
tags:
- file_ownership_library_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26648-6
- NIST-800-53-AC-6
- name: "Set ownership of system libraries to root"
file:
path: "{{item}}"
owner: "root"
with_items: "{{ libraries_not_owned_by_root.stdout_lines }}"
when: libraries_not_owned_by_root | length > 0
tags:
- file_ownership_library_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26648-6
- NIST-800-53-AC-6
|
Verify that System Executables Have Restrictive Permissions
[ref]ruleSystem executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should not be group-writable or world-writable.
If any file FILE in these directories is found
to be group-writable or world-writable, correct its permission with the
following command:
$ sudo chmod go-w FILE Rationale:System binaries are executed by privileged users, as well as system services,
and restrictive permissions are necessary to ensure execution of these programs
cannot be co-opted. Remediation Shell script: (show)
DIRS="/bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec"
for dirPath in $DIRS; do
find "$dirPath" -perm /022 -exec chmod go-w '{}' \;
done
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list of world and group writable system executables"
shell: "find /bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec -perm /022 -type f"
register: world_writable_library_files
changed_when: False
failed_when: False
check_mode: no
tags:
- file_permissions_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27075-1
- NIST-800-53-AC-6
- name: "Remove world/group writability of system executables"
file:
path: "{{item}}"
mode: "go-w"
with_items: "{{ world_writable_library_files.stdout_lines }}"
when: world_writable_library_files.stdout_lines | length > 0
tags:
- file_permissions_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27075-1
- NIST-800-53-AC-6
|
Verify that System Executables Have Root Ownership
[ref]ruleSystem executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should be owned by the root user.
If any file FILE in these directories is found
to be owned by a user other than root, correct its ownership with the
following command:
$ sudo chown root FILE Rationale:System binaries are executed by privileged users as well as system services,
and restrictive permissions are necessary to ensure that their
execution of these programs cannot be co-opted. Remediation Shell script: (show)
find /bin/ \
/usr/bin/ \
/usr/local/bin/ \
/sbin/ \
/usr/sbin/ \
/usr/local/sbin/ \
/usr/libexec \
\! -user root -execdir chown root {} \;
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list of system executables without root ownership"
shell: "find /bin/ /usr/bin/ /usr/local/bin/ /sbin/ /usr/sbin/ /usr/local/sbin/ /usr/libexec \\! -user root"
register: no_root_system_executables
changed_when: False
failed_when: False
check_mode: no
tags:
- file_ownership_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27119-7
- NIST-800-53-AC-6
- name: "Set ownership to root of system executables"
file:
path: "{{item}}"
owner: "root"
with_items: "{{ no_root_system_executables.stdout_lines }}"
when: no_root_system_executables.stdout_lines | length > 0
tags:
- file_ownership_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27119-7
- NIST-800-53-AC-6
|
Verify that Shared Library Files Have Restrictive Permissions
[ref]ruleSystem-wide shared library files, which are linked to executables
during process load time or run time, are stored in the following directories
by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are
stored in /lib/modules . All files in these directories
should not be group-writable or world-writable. If any file in these
directories is found to be group-writable or world-writable, correct
its permission with the following command:
$ sudo chmod go-w FILE Rationale:Files from shared library directories are loaded into the address
space of processes (including privileged ones) or of the kernel itself at
runtime. Restrictive permissions are necessary to protect the integrity of the system. Remediation Shell script: (show)
DIRS="/lib /lib64 /usr/lib /usr/lib64"
for dirPath in $DIRS; do
find "$dirPath" -perm /022 -type f -exec chmod go-w '{}' \;
done
Remediation Ansible snippet: (show)
Complexity: | high |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list of world and group writable files in libraries directories"
shell: "find /lib /lib64 /usr/lib /usr/lib64 -perm /022 -type f"
register: world_writable_library_files
changed_when: False
failed_when: False
check_mode: no
tags:
- file_permissions_library_dirs
- medium_severity
- restrict_strategy
- high_complexity
- medium_disruption
- CCE-26966-2
- NIST-800-53-AC-6
- name: "Disable world/group writability to library files"
file:
path: "{{item}}"
mode: "go-w"
with_items: "{{ world_writable_library_files.stdout_lines }}"
when: world_writable_library_files.stdout_lines | length > 0
tags:
- file_permissions_library_dirs
- medium_severity
- restrict_strategy
- high_complexity
- medium_disruption
- CCE-26966-2
- NIST-800-53-AC-6
|
System Accounting with <tt>auditd</tt>
[ref]groupThe audit service provides substantial capabilities
for recording system activities. By default, the service audits about
SELinux AVC denials and certain types of security-relevant events
such as system logins, account modifications, and authentication
events performed by programs such as sudo.
Under its default configuration, auditd has modest disk space
requirements, and should not noticeably impact system performance.
NOTE: The Linux Audit daemon auditd can be configured to use
the augenrules program to read audit rules files (*.rules )
located in /etc/audit/rules.d location and compile them to create
the resulting form of the /etc/audit/audit.rules configuration file
during the daemon startup (default configuration). Alternatively, the auditd
daemon can use the auditctl utility to read audit rules from the
/etc/audit/audit.rules configuration file during daemon startup,
and load them into the kernel. The expected behavior is configured via the
appropriate ExecStartPost directive setting in the
/usr/lib/systemd/system/auditd.service configuration file.
To instruct the auditd daemon to use the augenrules program
to read audit rules (default configuration), use the following setting:
ExecStartPost=-/sbin/augenrules --load
in the /usr/lib/systemd/system/auditd.service configuration file.
In order to instruct the auditd daemon to use the auditctl
utility to read audit rules, use the following setting:
ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules
in the /usr/lib/systemd/system/auditd.service configuration file.
Refer to [Service] section of the /usr/lib/systemd/system/auditd.service
configuration file for further details.
Government networks often have substantial auditing
requirements and auditd can be configured to meet these
requirements.
Examining some example audit records demonstrates how the Linux audit system
satisfies common requirements.
The following example from Fedora Documentation available at
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/sect-Security-Enhanced_Linux-Troubleshooting-Fixing_Problems.html#sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages
shows the substantial amount of information captured in a
two typical "raw" audit messages, followed by a breakdown of the most important
fields. In this example the message is SELinux-related and reports an AVC
denial (and the associated system call) that occurred when the Apache HTTP
Server attempted to access the /var/www/html/file1 file (labeled with
the samba_share_t type):
type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd"
path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13
a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48
gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd"
exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
msg=audit(1226874073.147:96) - The number in parentheses is the unformatted time stamp (Epoch time)
for the event, which can be converted to standard time by using the
date command.
{ getattr } - The item in braces indicates the permission that was denied.
getattr
indicates the source process was trying to read the target file's status information.
This occurs before reading files. This action is denied due to the file being
accessed having the wrong label. Commonly seen permissions include getattr ,
read , and write .
comm="httpd" - The executable that launched the process. The full path of the executable is
found in the
exe= section of the system call (SYSCALL ) message,
which in this case, is exe="/usr/sbin/httpd" .
path="/var/www/html/file1" - The path to the object (target) the process attempted to access.
scontext="unconfined_u:system_r:httpd_t:s0" - The SELinux context of the process that attempted the denied action. In
this case, it is the SELinux context of the Apache HTTP Server, which is running
in the
httpd_t domain.
tcontext="unconfined_u:object_r:samba_share_t:s0" - The SELinux context of the object (target) the process attempted to access.
In this case, it is the SELinux context of
file1 . Note: the samba_share_t
type is not accessible to processes running in the httpd_t domain.
- From the system call (
SYSCALL ) message, two items are of interest:
success=no : indicates whether the denial (AVC) was enforced or not.
success=no indicates the system call was not successful (SELinux denied
access). success=yes indicates the system call was successful - this can
be seen for permissive domains or unconfined domains, such as initrc_t
and kernel_t .
exe="/usr/sbin/httpd" : the full path to the executable that launched
the process, which in this case, is exe="/usr/sbin/httpd" .
|
contains 1 rule |
Configure <tt>auditd</tt> Rules for Comprehensive Auditing
[ref]groupThe auditd program can perform comprehensive
monitoring of system activity. This section describes recommended
configuration settings for comprehensive auditing, but a full
description of the auditing system's capabilities is beyond the
scope of this guide. The mailing list linux-audit@redhat.com exists
to facilitate community discussion of the auditing system.
The audit subsystem supports extensive collection of events, including:
- Tracing of arbitrary system calls (identified by name or number)
on entry or exit.
- Filtering by PID, UID, call success, system call argument (with
some limitations), etc.
- Monitoring of specific files for modifications to the file's
contents or metadata.
Auditing rules at startup are controlled by the file /etc/audit/audit.rules .
Add rules to it to meet the auditing requirements for your organization.
Each line in /etc/audit/audit.rules represents a series of arguments
that can be passed to auditctl and can be individually tested
during runtime. See documentation in /usr/share/doc/audit-VERSION and
in the related man pages for more details.
If copying any example audit rulesets from /usr/share/doc/audit-VERSION ,
be sure to comment out the
lines containing arch= which are not appropriate for your system's
architecture. Then review and understand the following rules,
ensuring rules are activated as needed for the appropriate
architecture.
After reviewing all the rules, reading the following sections, and
editing as needed, the new rules can be activated as follows:
$ sudo service auditd restart |
contains 1 rule |
System Audit Logs Must Have Mode 0640 or Less Permissive
[ref]ruleIf log_group in /etc/audit/auditd.conf is set to a group other than the root
group account, change the mode of the audit log files with the following command:
$ sudo chmod 0640 audit_file
Otherwise, change the mode of the audit log files with the following command:
$ sudo chmod 0600 audit_file Rationale:If users can write to audit logs, audit trails can be modified or destroyed. Remediation Shell script: (show)
if `grep -q ^log_group /etc/audit/auditd.conf` ; then
GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ')
if ! [ "${GROUP}" == 'root' ] ; then
chmod 0640 /var/log/audit/audit.log
chmod 0440 /var/log/audit/audit.log.*
else
chmod 0600 /var/log/audit/audit.log
chmod 0400 /var/log/audit/audit.log.*
fi
chmod 0640 /etc/audit/audit*
chmod 0640 /etc/audit/rules.d/*
else
chmod 0600 /var/log/audit/audit.log
chmod 0400 /var/log/audit/audit.log.*
chmod 0640 /etc/audit/audit*
chmod 0640 /etc/audit/rules.d/*
fi
|
Installing and Maintaining Software
[ref]groupThe following sections contain information on
security-relevant choices during the initial operating system
installation process and the setup of software
updates. |
contains 9 rules |
Disk Partitioning
[ref]groupTo 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 /var Located On Separate Partition
[ref]ruleThe /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. Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | enable |
---|
part /var
|
Ensure /tmp Located On Separate Partition
[ref]ruleThe /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. Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | enable |
---|
part /tmp
|
Ensure /var/log/audit Located On Separate Partition
[ref]ruleAudit 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. Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | enable |
---|
part /var/log/audit
|
Ensure /var/log Located On Separate Partition
[ref]ruleSystem logs are stored in the /var/log directory.
Ensure that it has its own partition or logical
volume at installation time, or migrate it using LVM. Rationale:Placing /var/log in its own partition
enables better separation between log files
and other files in /var/ . Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | enable |
---|
part /var/log
|
System and Software Integrity
[ref]groupSystem 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 1 rule |
Software Integrity Checking
[ref]groupBoth 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 1 rule |
Verify Integrity with AIDE
[ref]groupAIDE 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 1 rule |
Install AIDE
[ref]ruleInstall the AIDE package with the command:
$ sudo yum install aide Rationale:The AIDE package must be installed if it is to be available for integrity checking. 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="{{item}}"
state=present
with_items:
- aide
tags:
- package_aide_installed
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27096-7
- 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
|
Updating Software
[ref]groupThe 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 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.
Oracle Linux system 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 4 rules |
Ensure gpgcheck Enabled For All Yum Package Repositories
[ref]ruleTo ensure signature checking is not disabled for
any repos, remove any lines from files in /etc/yum.repos.d of the form:
gpgcheck=0 Rationale:Verifying the authenticity of the software prior to installation
validates the integrity of the patch or upgrade received from
a vendor. This ensures the software has not been tampered with and
that it has been provided by a trusted vendor. Self-signed
certificates are disallowed by this requirement. Certificates
used to verify the software must be from an approved Certificate
Authority (CA). References:
FAU_GEN.1.1.c, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, MA-1(b), Req-6.2, 366 Remediation Shell script: (show)
sed -i 's/gpgcheck=.*/gpgcheck=1/g' /etc/yum.repos.d/*
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
#
- name: Find All Yum Repositories
find:
paths: "/etc/yum.repos.d/"
patterns: "*.repo"
register: yum_find
- name: Ensure gpgcheck Enabled For All Yum Package Repositories
with_items: "{{ yum_find.files }}"
lineinfile:
create: yes
dest: "{{ item.path }}"
regexp: '^gpgcheck'
line: 'gpgcheck=1'
tags:
- ensure_gpgcheck_never_disabled
- high_severity
- unknown_strategy
- low_complexity
- medium_disruption
- CCE-26876-3
- 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
|
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. 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
- CCE-26895-3
- 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 Red Hat GPG Key Installed
[ref]ruleTo ensure the system can cryptographically verify base software
packages come from Red Hat (and to connect to the Red Hat Network to
receive them), the Red Hat GPG key must properly be installed.
To install the Red Hat GPG key, run:
$ sudo subscription-manager register
If the system is not connected to the Internet or an RHN Satellite,
then install the Red Hat GPG key from trusted media such as
the Red Hat installation CD-ROM or DVD. Assuming the disc is mounted
in /media/cdrom , use the following command as the root user to import
it into the keyring:
$ sudo rpm --import /media/cdrom/RPM-GPG-KEY Rationale:Changes to software components can have significant effects on the
overall security of the operating system. This requirement ensures
the software has not been tampered with and that it has been provided
by a trusted vendor. The Red Hat GPG key is necessary to
cryptographically verify packages are from Red Hat. References:
FAU_GEN.1.1.c, 1.2.3, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, MA-1(b), Req-6.2, 366 Remediation Shell script: (show)
# The two fingerprints below are retrieved from https://access.redhat.com/security/team/key
readonly REDHAT_RELEASE_2_FINGERPRINT="567E 347A D004 4ADE 55BA 8A5F 199E 2F91 FD43 1D51"
readonly REDHAT_AUXILIARY_FINGERPRINT="43A6 E49C 4A38 F4BE 9ABF 2A53 4568 9C88 2FA6 58E0"
# Location of the key we would like to import (once it's integrity verified)
readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")")
# Verify /etc/pki/rpm-gpg directory permissions are safe
if [ "${RPM_GPG_DIR_PERMS}" -le "755" ]
then
# If they are safe, try to obtain fingerprints from the key file
# (to ensure there won't be e.g. CRC error).
IFS=$'\n' GPG_OUT=($(gpg --with-fingerprint "${REDHAT_RELEASE_KEY}" | grep 'Key fingerprint ='))
GPG_RESULT=$?
# Reset IFS back to default
unset IFS
# No CRC error, safe to proceed
if [ "${GPG_RESULT}" -eq "0" ]
then
tr -s ' ' <<< "${GPG_OUT}" | grep -vE "${REDHAT_RELEASE_2_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || {
# If file doesn't contains any keys with unknown fingerprint, import it
rpm --import "${REDHAT_RELEASE_KEY}"
}
fi
fi
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read permission of GPG key directory"
stat:
path: /etc/pki/rpm-gpg/
register: gpg_key_directory_permission
check_mode: no
tags:
- ensure_redhat_gpgkey_installed
- high_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26957-1
- 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
# It should fail if it doesn't find any fingerprints in file - maybe file was not parsed well.
- name: Read signatures in GPG key
shell: gpg --with-fingerprint '/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release' | grep 'Key fingerprint =' | tr -s ' ' | sed 's;.*= ;;g'
changed_when: False
register: gpg_fingerprints
check_mode: no
tags:
- ensure_redhat_gpgkey_installed
- high_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26957-1
- 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
- name: Set Fact - Valid fingerprints
set_fact:
gpg_valid_fingerprints: ("567E 347A D004 4ADE 55BA 8A5F 199E 2F91 FD43 1D51" "43A6 E49C 4A38 F4BE 9ABF 2A53 4568 9C88 2FA6 58E0")
tags:
- ensure_redhat_gpgkey_installed
- high_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26957-1
- 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
- name: Import RedHat GPG key
rpm_key:
state: present
key: /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
when:
(gpg_key_directory_permission.stat.mode <= '0755')
and (( gpg_fingerprints.stdout_lines | difference(gpg_valid_fingerprints)) | length == 0)
and (gpg_fingerprints.stdout_lines | length > 0)
and (ansible_distribution == "RedHat")
tags:
- ensure_redhat_gpgkey_installed
- high_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-26957-1
- 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
|
Ensure gpgcheck Enabled In Main Yum Configuration
[ref]ruleThe 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). References:
FAU_GEN.1.1.c, SV-86601r1_rule, 1.2.2, 5.10.4.1, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), CM-5(3), SI-7, MA-1(b), Req-6.2, SRG-OS-000366-GPOS-00153 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 grep -q $grep_case_insensitive_option "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/yum.conf' '^gpgcheck' '1' 'CCE-26989-4'
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
- CCE-26989-4
- 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
- CCE-26989-4
- 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
|