## Guide to the Secure Configuration of Debian 12

> with profile ANSSI-BP-028 (minimal)
> This profile contains configurations that align to ANSSI-BP-028 v2.0 at the minimal hardening level. ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. A copy of the ANSSI-BP-028 can be found at the ANSSI website: https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/

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

## Profile Information

Profile Title | ANSSI-BP-028 (minimal)
Profile ID | xccdf_org.ssgproject.content_profile_anssi_bp28_minimal

#### CPE Platforms

- cpe:/o:debian:debian_linux:12

## Revision History

Current version: 0.1.76

- draft (as of 2025-03-01)

## Table of Contents

1. System Settings
   1. Installing and Maintaining Software
   2. Account and Access Control
   3. File Permissions and Masks
2. Services
   1. DHCP
   2. Mail Server Software
   3. Obsolete Services

## Checklist

Group Guide to the Secure Configuration of Debian 12 Group contains 25 groups and 35 rules
Group System Settings Group contains 13 groups and 21 rules
[ref] Contains rules that check correct system settings.
Group Installing and Maintaining Software Group contains 1 group and 1 rule
[ref] The following sections contain information on security-relevant choices during the initial operating system installation process and the setup of software updates.
Group Updating Software Group contains 1 rule

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

#### Rule Ensure Software Patches Installed [ref]

If the system has an apt repository available, run the following command to install updates:

    $ apt update && apt full-upgrade

NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy dictates.
Warning: The OVAL feed of Debian 12 is not a XML file, which may not be understood by all scanners.

Rationale: | Installing software updates is a fundamental mitigation against the exploitation of publicly-known vulnerabilities. If the most recent security patches and updates are not installed, unauthorized users may take advantage of weaknesses in the unpatched software. The lack of prompt attention to patching could result in a system compromise.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_security_patches_up_to_date

References:

cis-csc | 18, 20, 4
cjis | 5.10.4.1
cobit5 | APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02
disa | CCI-000366
isa-62443-2009 | 4.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9
iso27001-2013 | A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3
nist | SI-2(5), SI-2(c), CM-6(a)
nist-csf | ID.RA-1, PR.IP-12
ospp | FMT_MOF_EXT.1
pcidss | Req-6.2
os-srg | SRG-OS-000480-GPOS-00227
anssi | R61
pcidss4 | 6.3.3, 6.3

Group Account and Access Control Group contains 8 groups and 15 rules

[ref] In traditional Unix security, if an attacker gains shell access to a certain login account, they can perform any action or access any file to which that account has access. Therefore, making it more difficult for unauthorized people to gain shell access to accounts, particularly to privileged accounts, is a necessary part of securing a system. This section introduces mechanisms for restricting access to accounts under Debian 12.

Group Protect Accounts by Configuring PAM Group contains 4 groups and 13 rules

[ref] PAM, or Pluggable Authentication Modules, is a system which implements modular authentication for Linux programs. PAM provides a flexible and configurable architecture for authentication, and it should be configured to minimize exposure to unnecessary risk. This section contains guidance on how to accomplish that.
PAM is implemented as a set of shared objects which are loaded and invoked whenever an application wishes to authenticate a user. Typically, the application must be running as root in order to take advantage of PAM, because PAM's modules often need to be able to access sensitive stores of account information, such as /etc/shadow. Traditional privileged network listeners (e.g. sshd) or SUID programs (e.g. sudo) already meet this requirement. An SUID root application, userhelper, is provided so that programs which are not SUID or privileged themselves can still take advantage of PAM.
PAM looks in the directory `/etc/pam.d` for application-specific configuration information. For instance, if the program login attempts to authenticate a user, then PAM's libraries follow the instructions in the file `/etc/pam.d/login` to determine what actions should be taken.
One very important file in `/etc/pam.d` is `/etc/pam.d/system-auth`. This file, which is included by many other PAM configuration files, defines 'default' system authentication measures. Modifying this file is a good way to make far-reaching authentication changes, for instance when implementing a centralized authentication service.
Warning: Be careful when making changes to PAM's configuration files. The syntax for these files is complex, and modifications can have unexpected consequences. The default configurations shipped with applications should be sufficient for most users.
Warning: Running `authconfig` or `system-config-authentication` will re-write the PAM configuration files, destroying any manually made changes and replacing them with a series of system defaults. One reference to the configuration file syntax can be found at https://fossies.org/linux/Linux-PAM-docs/doc/sag/Linux-PAM_SAG.pdf.

Group Set Lockouts for Failed Password Attempts Group contains 4 rules

[ref] The `pam_faillock` PAM module provides the capability to lock out user accounts after a number of failed login attempts. Its documentation is available in `/usr/share/doc/pam-VERSION/txts/README.pam_faillock`.
Warning: Locking out user accounts presents the risk of a denial-of-service attack. The lockout policy must weigh whether the risk of such a denial-of-service attack outweighs the benefits of thwarting password guessing attacks.

#### Rule Limit Password Reuse [ref]

Do not allow users to reuse recent passwords. This can be accomplished by using the `remember` option for the `pam_unix` or `pam_pwhistory` PAM modules.
Warning: If the system relies on `authselect` tool to manage PAM settings, the remediation will also use `authselect` tool. However, if any manual modification was made in PAM files, the `authselect` integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report.
Warning: Newer versions of `authselect` contain an authselect feature to easily and properly enable `pam_pwhistory.so` module. If this feature is not yet available in your system, an authselect custom profile must be used to avoid integrity issues in PAM files.

Rationale: | Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_remember

References:

cis-csc | 1, 12, 15, 16, 5
cjis | 5.6.2.1.1
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui | 3.5.8
disa | CCI-000200
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(f), IA-5(1)(e)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
pcidss | Req-8.2.5
os-srg | SRG-OS-000077-GPOS-00045
anssi | R31
pcidss4 | 8.3.7, 8.3

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_unix_remember='2'

    if [ -e "/etc/pam.d/common-password" ] ; then
        valueRegex="$var_password_pam_unix_remember" defaultValue="$var_password_pam_unix_remember"
        # non-empty values need to be preceded by an equals sign
        [ -n "${valueRegex}" ] && valueRegex="=${valueRegex}"
        # add an equals sign to non-empty values
        [ -n "${defaultValue}" ] && defaultValue="=${defaultValue}"

        # fix 'type' if it's wrong
        if grep -q -P "^\\s*(?"'!'"password\\s)[[:alnum:]]+\\s+[[:alnum:]]+\\s+pam_unix.so" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*)[[:alnum:]]+(\\s+[[:alnum:]]+\\s+pam_unix.so)/\\1password\\2/" "/etc/pam.d/common-password"
        fi

        # fix 'control' if it's wrong
        if grep -q -P "^\\s*password\\s+(?"'!'"\[success=[[:alnum:]].*\])[[:alnum:]]+\\s+pam_unix.so" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+)[[:alnum:]]+(\\s+pam_unix.so)/\\1\[success=[[:alnum:]].*\]\\2/" "/etc/pam.d/common-password"
        fi

        # fix the value for 'option' if one exists but does not match 'valueRegex'
        if grep -q -P "^\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so(\\s.+)?\\s+remember(?"'!'"${valueRegex}(\\s|\$))" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so(\\s.+)?\\s)remember=[^[:space:]]*/\\1remember${defaultValue}/" "/etc/pam.d/common-password"

        # add 'option=default' if option is not set
        elif grep -q -E "^\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so" < "/etc/pam.d/common-password" &&
                grep    -E "^\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so" < "/etc/pam.d/common-password" | grep -q -E -v "\\sremember(=|\\s|\$)" ; then

            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so[^\\n]*)/\\1 remember${defaultValue}/" "/etc/pam.d/common-password"
        # add a new entry if none exists
        elif ! grep -q -P "^\\s*password\\s+\[success=[[:alnum:]].*\]\\s+pam_unix.so(\\s.+)?\\s+remember${valueRegex}(\\s|\$)" < "/etc/pam.d/common-password" ; then
            echo "password \[success=[[:alnum:]].*\] pam_unix.so remember${defaultValue}" >> "/etc/pam.d/common-password"
        fi
    else
        echo "/etc/pam.d/common-password doesn't exist" >&2
    fi

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | medium
Reboot: | false
Strategy: | configure

    - name: Gather the package facts
      package_facts:
        manager: auto
      tags:
      - CJIS-5.6.2.1.1
      - NIST-800-171-3.5.8
      - NIST-800-53-IA-5(1)(e)
      - NIST-800-53-IA-5(f)
      - PCI-DSS-Req-8.2.5
      - PCI-DSSv4-8.3
      - PCI-DSSv4-8.3.7
      - accounts_password_pam_unix_remember
      - configure_strategy
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
    - name: XCCDF Value var_password_pam_unix_remember # promote to variable
      set_fact:
        var_password_pam_unix_remember: !!str 2
      tags:
        - always

    - name: Limit Password Reuse - Check if the required PAM module option is present
        in /etc/pam.d/common-password
      ansible.builtin.lineinfile:
        path: /etc/pam.d/common-password
        regexp: ^\s*password\s+\[success=[A-Za-z0-9].*\]\s+pam_unix.so\s*.*\sremember\b
        state: absent
      check_mode: true
      changed_when: false
      register: result_pam_module_remember_option_present
      when: '"libpam-runtime" in ansible_facts.packages'
      tags:
      - CJIS-5.6.2.1.1
      - NIST-800-171-3.5.8
      - NIST-800-53-IA-5(1)(e)
      - NIST-800-53-IA-5(f)
      - PCI-DSS-Req-8.2.5
      - PCI-DSSv4-8.3
      - PCI-DSSv4-8.3.7
      - accounts_password_pam_unix_remember
      - configure_strategy
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed

    - name: Limit Password Reuse - Ensure the "remember" PAM option for "pam_unix.so"
        is included in /etc/pam.d/common-password
      ansible.builtin.lineinfile:
        path: /etc/pam.d/common-password
        backrefs: true
        regexp: ^(\s*password\s+\[success=[A-Za-z0-9].*\]\s+pam_unix.so.*)
        line: \1 remember={{ var_password_pam_unix_remember }}
        state: present
      register: result_pam_remember_add
      when:
      - '"libpam-runtime" in ansible_facts.packages'
      - result_pam_module_remember_option_present.found == 0
      tags:
      - CJIS-5.6.2.1.1
      - NIST-800-171-3.5.8
      - NIST-800-53-IA-5(1)(e)
      - NIST-800-53-IA-5(f)
      - PCI-DSS-Req-8.2.5
      - PCI-DSSv4-8.3
      - PCI-DSSv4-8.3.7
      - accounts_password_pam_unix_remember
      - configure_strategy
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed

    - name: Limit Password Reuse - Ensure the required value for "remember" PAM option
        from "pam_unix.so" in /etc/pam.d/common-password
      ansible.builtin.lineinfile:
        path: /etc/pam.d/common-password
        backrefs: true
        regexp: ^(\s*password\s+\[success=[A-Za-z0-9].*\]\s+pam_unix.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*)
        line: \1\2={{ var_password_pam_unix_remember }} \3
      register: result_pam_remember_edit
      when:
      - '"libpam-runtime" in ansible_facts.packages'
      - result_pam_module_remember_option_present.found > 0
      tags:
      - CJIS-5.6.2.1.1
      - NIST-800-171-3.5.8
      - NIST-800-53-IA-5(1)(e)
      - NIST-800-53-IA-5(f)
      - PCI-DSS-Req-8.2.5
      - PCI-DSSv4-8.3
      - PCI-DSSv4-8.3.7
      - accounts_password_pam_unix_remember
      - configure_strategy
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed

#### Rule Lock Accounts After Failed Password Attempts [ref]

This rule configures the system to lock out accounts after a number of incorrect login attempts using `pam_faillock.so`. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. Ensure that the file `/etc/security/faillock.conf` contains the following entry: `deny = <count>` Where count should be less than or equal to 3 and greater than 0.
Warning: If the system relies on `authselect` tool to manage PAM settings, the remediation will also use `authselect` tool. However, if any manual modification was made in PAM files, the `authselect` integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the `/etc/security/faillock.conf` file, the pam_faillock parameters should be defined in `faillock.conf` file.

Rationale: | By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny

References:

cis-csc | 1, 12, 15, 16
cjis | 5.5.3
cobit5 | DSS05.04, DSS05.10, DSS06.10
cui | 3.1.8
disa | CCI-000044, CCI-002238
isa-62443-2009 | 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nist | CM-6(a), AC-7(a)
nist-csf | PR.AC-7
ospp | FIA_AFL.1
pcidss | Req-8.1.6
os-srg | SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssi | R31
pcidss4 | 8.3.4, 8.3

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_accounts_passwords_pam_faillock_deny='3'

    if [ -f /usr/bin/authselect ]; then
        if ! authselect check; then
    echo "
    authselect integrity check failed. Remediation aborted!
    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
    It is not recommended to manually edit the PAM files when authselect tool is available.
    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
    exit 1
    fi
    authselect enable-feature with-faillock

    authselect apply-changes -b
    else

    pam_file="/etc/pam.d/common-auth"
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # insert at the top
        sed -i --follow-symlinks '/^# here are the per-package modules/i auth        required      pam_faillock.so preauth' "$pam_file"
    fi
    if ! grep -qE '^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then

        num_lines=$(sed -n 's/^\s*auth.*success=\([1-9]\).*pam_unix\.so.*/\1/p' "$pam_file")
        if [ ! -z "$num_lines" ]; then

            # Add pam_faillock (authfail) module below pam_unix, skipping N-1 lines, where N is
            # the number of jumps in the pam_unix success=N statement. Ignore commented and empty lines.

            append_position=$(cat -n "${pam_file}" \
                              | grep -P "^\s+\d+\s+auth\s+.*$" \
                              | grep -w "pam_unix.so" -A $(( num_lines - 1 )) \
                              | tail -n 1 | cut -f 1 | tr -d ' '
                             )
            sed -i --follow-symlinks ''${append_position}'a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        else
            sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        fi
    fi
    if ! grep -qE '^\s*auth\s+sufficient\s+pam_faillock\.so\s+authsucc.*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*pam_faillock\.so.*authfail.*/a auth        sufficient      pam_faillock.so authsucc' "$pam_file"
    fi

    pam_file="/etc/pam.d/common-account"
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        echo 'account   required     pam_faillock.so' >> "$pam_file"
    fi

    fi

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

    FAILLOCK_CONF="/etc/security/faillock.conf"
    if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
        regex="^\s*deny\s*="
        line="deny = $var_accounts_passwords_pam_faillock_deny"
        if ! grep -q $regex $FAILLOCK_CONF; then
            echo $line >> $FAILLOCK_CONF
        else
            sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF
        fi

        for pam_file in "${AUTH_FILES[@]}"
        do
            if [ -e "$pam_file" ] ; then
                PAM_FILE_PATH="$pam_file"
                if [ -f /usr/bin/authselect ]; then

                    if ! authselect check; then
                    echo "
                    authselect integrity check failed. Remediation aborted!
                    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                    It is not recommended to manually edit the PAM files when authselect tool is available.
                    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                    exit 1
                    fi

                    CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                    # If not already in use, a custom profile is created preserving the enabled features.
                    if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                        ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                        # The "local" profile does not contain essential security features required by multiple Benchmarks.
                        # If currently used, it is replaced by "sssd", which is the best option in this case.
                        if [[ $CURRENT_PROFILE == local ]]; then
                            CURRENT_PROFILE="sssd"
                        fi
                        authselect create-profile hardening -b $CURRENT_PROFILE
                        CURRENT_PROFILE="custom/hardening"

                        authselect apply-changes -b --backup=before-hardening-custom-profile
                        authselect select $CURRENT_PROFILE
                        for feature in $ENABLED_FEATURES; do
                            authselect enable-feature $feature;
                        done

                        authselect apply-changes -b --backup=after-hardening-custom-profile
                    fi
                    PAM_FILE_NAME=$(basename "$pam_file")
                    PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                    authselect apply-changes -b
                fi

            if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b" "$PAM_FILE_PATH"; then
                sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
            fi
                if [ -f /usr/bin/authselect ]; then

                    authselect apply-changes -b
                fi
            else
                echo "$pam_file was not found" >&2
            fi
        done

    else
        for pam_file in "${AUTH_FILES[@]}"
        do
            if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*deny' "$pam_file"; then
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
            else
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
            fi
        done
    fi

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

#### Rule Set Interval For Counting Failed Password Attempts [ref]

Utilizing `pam_faillock.so`, the `fail_interval` directive configures the system to lock out an account after a number of incorrect login attempts within a specified time period. Ensure that the file `/etc/security/faillock.conf` contains the following entry: `fail_interval = <interval-in-seconds>` where `interval-in-seconds` is `900` or greater.
Warning: If the system relies on `authselect` tool to manage PAM settings, the remediation will also use `authselect` tool. However, if any manual modification was made in PAM files, the `authselect` integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the `/etc/security/faillock.conf` file, the pam_faillock parameters should be defined in `faillock.conf` file.

Rationale: | By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval

References:

cis-csc | 1, 12, 15, 16
cobit5 | DSS05.04, DSS05.10, DSS06.10
disa | CCI-000044, CCI-002238
isa-62443-2009 | 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nist | CM-6(a), AC-7(a)
nist-csf | PR.AC-7
ospp | FIA_AFL.1
os-srg | SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssi | R31

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_accounts_passwords_pam_faillock_fail_interval='900'

    if [ -f /usr/bin/authselect ]; then
        if ! authselect check; then
    echo "
    authselect integrity check failed. Remediation aborted!
    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
    It is not recommended to manually edit the PAM files when authselect tool is available.
    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
    exit 1
    fi
    authselect enable-feature with-faillock

    authselect apply-changes -b
    else

    pam_file="/etc/pam.d/common-auth"
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # insert at the top
        sed -i --follow-symlinks '/^# here are the per-package modules/i auth        required      pam_faillock.so preauth' "$pam_file"
    fi
    if ! grep -qE '^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then

        num_lines=$(sed -n 's/^\s*auth.*success=\([1-9]\).*pam_unix\.so.*/\1/p' "$pam_file")
        if [ ! -z "$num_lines" ]; then

            # Add pam_faillock (authfail) module below pam_unix, skipping N-1 lines, where N is
            # the number of jumps in the pam_unix success=N statement. Ignore commented and empty lines.

            append_position=$(cat -n "${pam_file}" \
                              | grep -P "^\s+\d+\s+auth\s+.*$" \
                              | grep -w "pam_unix.so" -A $(( num_lines - 1 )) \
                              | tail -n 1 | cut -f 1 | tr -d ' '
                             )
            sed -i --follow-symlinks ''${append_position}'a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        else
            sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        fi
    fi
    if ! grep -qE '^\s*auth\s+sufficient\s+pam_faillock\.so\s+authsucc.*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*pam_faillock\.so.*authfail.*/a auth        sufficient      pam_faillock.so authsucc' "$pam_file"
    fi

    pam_file="/etc/pam.d/common-account"
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        echo 'account   required     pam_faillock.so' >> "$pam_file"
    fi

    fi

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

    FAILLOCK_CONF="/etc/security/faillock.conf"
    if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
        regex="^\s*fail_interval\s*="
        line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval"
        if ! grep -q $regex $FAILLOCK_CONF; then
            echo $line >> $FAILLOCK_CONF
        else
            sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF
        fi

        for pam_file in "${AUTH_FILES[@]}"
        do
            if [ -e "$pam_file" ] ; then
                PAM_FILE_PATH="$pam_file"
                if [ -f /usr/bin/authselect ]; then

                    if ! authselect check; then
                    echo "
                    authselect integrity check failed. Remediation aborted!
                    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                    It is not recommended to manually edit the PAM files when authselect tool is available.
                    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                    exit 1
                    fi

                    CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                    # If not already in use, a custom profile is created preserving the enabled features.
                    if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                        ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                        # The "local" profile does not contain essential security features required by multiple Benchmarks.
                        # If currently used, it is replaced by "sssd", which is the best option in this case.
                        if [[ $CURRENT_PROFILE == local ]]; then
                            CURRENT_PROFILE="sssd"
                        fi
                        authselect create-profile hardening -b $CURRENT_PROFILE
                        CURRENT_PROFILE="custom/hardening"

                        authselect apply-changes -b --backup=before-hardening-custom-profile
                        authselect select $CURRENT_PROFILE
                        for feature in $ENABLED_FEATURES; do
                            authselect enable-feature $feature;
                        done

                        authselect apply-changes -b --backup=after-hardening-custom-profile
                    fi
                    PAM_FILE_NAME=$(basename "$pam_file")
                    PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                    authselect apply-changes -b
                fi

            if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b" "$PAM_FILE_PATH"; then
                sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
            fi
                if [ -f /usr/bin/authselect ]; then

                    authselect apply-changes -b
                fi
            else
                echo "$pam_file was not found" >&2
            fi
        done

    else
        for pam_file in "${AUTH_FILES[@]}"
        do
            if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*fail_interval' "$pam_file"; then
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
            else
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
            fi
        done
    fi

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

#### Rule Set Lockout Time for Failed Password Attempts [ref]

This rule configures the system to lock out accounts during a specified time period after a number of incorrect login attempts using `pam_faillock.so`. Ensure that the file `/etc/security/faillock.conf` contains the following entry: `unlock_time=<interval-in-seconds>` where `interval-in-seconds` is `900` or greater. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid any errors when manually editing these files, it is recommended to use the appropriate tools, such as `authselect` or `authconfig`, depending on the OS version. If `unlock_time` is set to `0`, manual intervention by an administrator is required to unlock a user. This should be done using the `faillock` tool.
Warning: If the system supports the new `/etc/security/faillock.conf` file but the pam_faillock.so parameters are defined directly in `/etc/pam.d/system-auth` and `/etc/pam.d/password-auth`, the remediation will migrate the `unlock_time` parameter to `/etc/security/faillock.conf` to ensure compatibility with `authselect` tool. The parameters `deny` and `fail_interval`, if used, also have to be migrated by their respective remediation.
Warning: If the system relies on `authselect` tool to manage PAM settings, the remediation will also use `authselect` tool. However, if any manual modification was made in PAM files, the `authselect` integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the `/etc/security/faillock.conf` file, the pam_faillock parameters should be defined in `faillock.conf` file.

Rationale: | By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time

References:

cis-csc | 1, 12, 15, 16
cjis | 5.5.3
cobit5 | DSS05.04, DSS05.10, DSS06.10
cui | 3.1.8
disa | CCI-000044, CCI-002238
isa-62443-2009 | 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nist | CM-6(a), AC-7(b)
nist-csf | PR.AC-7
ospp | FIA_AFL.1
pcidss | Req-8.1.7
os-srg | SRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssi | R31
pcidss4 | 8.3.4, 8.3

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_accounts_passwords_pam_faillock_unlock_time='900'

    if [ -f /usr/bin/authselect ]; then
        if ! authselect check; then
    echo "
    authselect integrity check failed. Remediation aborted!
    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
    It is not recommended to manually edit the PAM files when authselect tool is available.
    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
    exit 1
    fi
    authselect enable-feature with-faillock

    authselect apply-changes -b
    else

    pam_file="/etc/pam.d/common-auth"
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
        # insert at the top
        sed -i --follow-symlinks '/^# here are the per-package modules/i auth        required      pam_faillock.so preauth' "$pam_file"
    fi
    if ! grep -qE '^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then

        num_lines=$(sed -n 's/^\s*auth.*success=\([1-9]\).*pam_unix\.so.*/\1/p' "$pam_file")
        if [ ! -z "$num_lines" ]; then

            # Add pam_faillock (authfail) module below pam_unix, skipping N-1 lines, where N is
            # the number of jumps in the pam_unix success=N statement. Ignore commented and empty lines.

            append_position=$(cat -n "${pam_file}" \
                              | grep -P "^\s+\d+\s+auth\s+.*$" \
                              | grep -w "pam_unix.so" -A $(( num_lines - 1 )) \
                              | tail -n 1 | cut -f 1 | tr -d ' '
                             )
            sed -i --follow-symlinks ''${append_position}'a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        else
            sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/a auth        [default=die]      pam_faillock.so authfail' "$pam_file"
        fi
    fi
    if ! grep -qE '^\s*auth\s+sufficient\s+pam_faillock\.so\s+authsucc.*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*pam_faillock\.so.*authfail.*/a auth        sufficient      pam_faillock.so authsucc' "$pam_file"
    fi

    pam_file="/etc/pam.d/common-account"
    if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
        echo 'account   required     pam_faillock.so' >> "$pam_file"
    fi

    fi

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

    FAILLOCK_CONF="/etc/security/faillock.conf"
    if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
        regex="^\s*unlock_time\s*="
        line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time"
        if ! grep -q $regex $FAILLOCK_CONF; then
            echo $line >> $FAILLOCK_CONF
        else
            sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF
        fi

        for pam_file in "${AUTH_FILES[@]}"
        do
            if [ -e "$pam_file" ] ; then
                PAM_FILE_PATH="$pam_file"
                if [ -f /usr/bin/authselect ]; then

                    if ! authselect check; then
                    echo "
                    authselect integrity check failed. Remediation aborted!
                    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                    It is not recommended to manually edit the PAM files when authselect tool is available.
                    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                    exit 1
                    fi

                    CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                    # If not already in use, a custom profile is created preserving the enabled features.
                    if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                        ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                        # The "local" profile does not contain essential security features required by multiple Benchmarks.
                        # If currently used, it is replaced by "sssd", which is the best option in this case.
                        if [[ $CURRENT_PROFILE == local ]]; then
                            CURRENT_PROFILE="sssd"
                        fi
                        authselect create-profile hardening -b $CURRENT_PROFILE
                        CURRENT_PROFILE="custom/hardening"

                        authselect apply-changes -b --backup=before-hardening-custom-profile
                        authselect select $CURRENT_PROFILE
                        for feature in $ENABLED_FEATURES; do
                            authselect enable-feature $feature;
                        done

                        authselect apply-changes -b --backup=after-hardening-custom-profile
                    fi
                    PAM_FILE_NAME=$(basename "$pam_file")
                    PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                    authselect apply-changes -b
                fi

            if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b" "$PAM_FILE_PATH"; then
                sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
            fi
                if [ -f /usr/bin/authselect ]; then

                    authselect apply-changes -b
                fi
            else
                echo "$pam_file was not found" >&2
            fi
        done

    else
        for pam_file in "${AUTH_FILES[@]}"
        do
            if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*unlock_time' "$pam_file"; then
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
                sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
            else
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
                sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
            fi
        done
    fi

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

Group Set Password Quality Requirements Group contains 1 group and 7 rules

[ref] The default `pam_pwquality` PAM module provides strength checking for passwords. It performs a number of checks, such as making sure passwords are not similar to dictionary words, are of at least a certain length, are not the previous password reversed, and are not simply a change of case from the previous password. It can also require passwords to be in certain character classes. The `pam_pwquality` module is the preferred way of configuring password requirements.
The man pages `pam_pwquality(8)` provide information on the capabilities and configuration of each.

Group Set Password Quality Requirements with pam_pwquality Group contains 7 rules

[ref] The `pam_pwquality` PAM module can be configured to meet requirements for a variety of policies.
For example, to configure `pam_pwquality` to require at least one uppercase character, lowercase character, digit, and other (special) character, make sure that `pam_pwquality` exists in `/etc/pam.d/system-auth`:

    password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=

If no such line exists, add one as the first line of the password section in `/etc/pam.d/system-auth`. Next, modify the settings in `/etc/security/pwquality.conf` to match the following:

    difok = 4
    minlen = 14
    dcredit = -1
    ucredit = -1
    lcredit = -1
    ocredit = -1
    maxrepeat = 3

The arguments can be modified to ensure compliance with your organization's security policy. Discussion of each parameter follows.

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

The pam_pwquality module's `dcredit` parameter controls requirements for usage of digits in a password. When set to a negative number, any password will be required to contain that many digits. When set to a positive number, pam_pwquality will grant +1 additional length credit for each digit. Modify the `dcredit` setting in `/etc/security/pwquality.conf` to require the use of a digit in passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring digits makes password guessing attacks more difficult by ensuring a larger search space.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit

References:

cis-csc | 1, 12, 15, 16, 5
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
ospp | FMT_SMF_EXT.1
pcidss | Req-8.2.3
os-srg | SRG-OS-000071-GPOS-00039
anssi | R31
pcidss4 | 8.3.6, 8.3

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_dcredit='-1'

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

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

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

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

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

The pam_pwquality module's `lcredit` parameter controls requirements for usage of lowercase letters in a password. When set to a negative number, any password will be required to contain that many lowercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each lowercase character. Modify the `lcredit` setting in `/etc/security/pwquality.conf` to require the use of a lowercase character in passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possble combinations that need to be tested before the password is compromised. Requiring a minimum number of lowercase characters makes password guessing attacks more difficult by ensuring a larger search space.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit

References:

cis-csc | 1, 12, 15, 16, 5
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
ospp | FMT_SMF_EXT.1
pcidss | Req-8.2.3
os-srg | SRG-OS-000070-GPOS-00038
anssi | R31
pcidss4 | 8.3.6, 8.3

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_lcredit='-1'

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

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

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

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

#### Rule Ensure PAM Enforces Password Requirements - Minimum Different Categories [ref]

The pam_pwquality module's `minclass` parameter controls requirements for usage of different character classes, or types, of character that must exist in a password before it is considered valid. For example, setting this value to three (3) requires that any password must have characters from at least three different categories in order to be approved. The default value is zero (0), meaning there are no required classes. There are four categories available:

    * Upper-case characters
    * Lower-case characters
    * Digits
    * Special characters (for example, punctuation)

Modify the `minclass` setting in `/etc/security/pwquality.conf` entry to require 4 differing categories of characters when changing passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.
Requiring a minimum number of character categories makes password guessing attacks more difficult by ensuring a larger search space.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_minclass

References:

cis-csc | 1, 12, 15, 16, 5
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
os-srg | SRG-OS-000072-GPOS-00040
anssi | R68

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_minclass='4'

    # 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' <<< "^minclass")

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    - name: Gather the package facts
      package_facts:
        manager: auto
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-IA-5(1)(a)
      - NIST-800-53-IA-5(4)
      - NIST-800-53-IA-5(c)
      - accounts_password_pam_minclass
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy
    - name: XCCDF Value var_password_pam_minclass # promote to variable
      set_fact:
        var_password_pam_minclass: !!str 4
      tags:
        - always

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

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

The pam_pwquality module's `minlen` parameter controls requirements for minimum characters required in a password. Add `minlen=15` after pam_pwquality to set minimum password length requirements.

Rationale:

The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.
Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. Password length is one factor of several that helps to determine strength and how long it takes to crack a password. Use of more characters in a password helps to exponentially increase the time and/or resources required to compromise the password.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen

References:

cis-csc | 1, 12, 15, 16, 5
cjis | 5.6.2.1.1
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
ospp | FMT_SMF_EXT.1
pcidss | Req-8.2.3
os-srg | SRG-OS-000078-GPOS-00046
anssi | R31, R68
pcidss4 | 8.3.6, 8.3

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_minlen='15'

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

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

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

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

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

The pam_pwquality module's `ocredit=` parameter controls requirements for usage of special (or "other") characters in a password. When set to a negative number, any password will be required to contain that many special characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each special character. Modify the `ocredit` setting in `/etc/security/pwquality.conf` to equal -1 to require use of a special character in passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring a minimum number of special characters makes password guessing attacks more difficult by ensuring a larger search space.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_ocredit

References:

cis-csc | 1, 12, 15, 16, 5
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
ospp | FMT_SMF_EXT.1
os-srg | SRG-OS-000266-GPOS-00101
anssi | R31

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_ocredit='-1'

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

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

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

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

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

To configure the number of retry prompts that are permitted per-session: Edit the `pam_pwquality.so` statement in `/etc/pam.d/common-password` to show `retry=3` , or a lower value if site policy is more restrictive. The DoD requirement is a maximum of 3 prompts per session.
Rationale: | Setting the password retry prompts that are permitted on a per-session basis to a low value requires some software, such as SSH, to re-connect. This can slow down and draw additional attention to some types of password-guessing attacks. Note that this is different from account lockout, which is provided by the pam_faillock module.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_retry

References:

cis-csc | 1, 11, 12, 15, 16, 3, 5, 9
cjis | 5.5.3
cobit5 | BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 7.6
iso27001-2013 | A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | CM-6(a), AC-7(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7, PR.IP-1
os-srg | SRG-OS-000069-GPOS-00037, SRG-OS-000480-GPOS-00227
anssi | R68

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_retry='3'

    if [ -e "/etc/pam.d/common-password" ] ; then
        valueRegex="$var_password_pam_retry" defaultValue="$var_password_pam_retry"
        # non-empty values need to be preceded by an equals sign
        [ -n "${valueRegex}" ] && valueRegex="=${valueRegex}"
        # add an equals sign to non-empty values
        [ -n "${defaultValue}" ] && defaultValue="=${defaultValue}"

        # fix 'type' if it's wrong
        if grep -q -P "^\\s*(?"'!'"password\\s)[[:alnum:]]+\\s+[[:alnum:]]+\\s+pam_pwquality.so" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*)[[:alnum:]]+(\\s+[[:alnum:]]+\\s+pam_pwquality.so)/\\1password\\2/" "/etc/pam.d/common-password"
        fi

        # fix 'control' if it's wrong
        if grep -q -P "^\\s*password\\s+(?"'!'"requisite)[[:alnum:]]+\\s+pam_pwquality.so" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+)[[:alnum:]]+(\\s+pam_pwquality.so)/\\1requisite\\2/" "/etc/pam.d/common-password"
        fi

        # fix the value for 'option' if one exists but does not match 'valueRegex'
        if grep -q -P "^\\s*password\\s+requisite\\s+pam_pwquality.so(\\s.+)?\\s+retry(?"'!'"${valueRegex}(\\s|\$))" < "/etc/pam.d/common-password" ; then
            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+requisite\\s+pam_pwquality.so(\\s.+)?\\s)retry=[^[:space:]]*/\\1retry${defaultValue}/" "/etc/pam.d/common-password"

        # add 'option=default' if option is not set
        elif grep -q -E "^\\s*password\\s+requisite\\s+pam_pwquality.so" < "/etc/pam.d/common-password" &&
                grep    -E "^\\s*password\\s+requisite\\s+pam_pwquality.so" < "/etc/pam.d/common-password" | grep -q -E -v "\\sretry(=|\\s|\$)" ; then

            sed --follow-symlinks -i -E -e "s/^(\\s*password\\s+requisite\\s+pam_pwquality.so[^\\n]*)/\\1 retry${defaultValue}/" "/etc/pam.d/common-password"
        # add a new entry if none exists
        elif ! grep -q -P "^\\s*password\\s+requisite\\s+pam_pwquality.so(\\s.+)?\\s+retry${valueRegex}(\\s|\$)" < "/etc/pam.d/common-password" ; then
            echo "password requisite pam_pwquality.so retry${defaultValue}" >> "/etc/pam.d/common-password"
        fi
    else
        echo "/etc/pam.d/common-password doesn't exist" >&2
    fi

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

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

The pam_pwquality module's `ucredit=` parameter controls requirements for usage of uppercase letters in a password. When set to a negative number, any password will be required to contain that many uppercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each uppercase character. Modify the `ucredit` setting in `/etc/security/pwquality.conf` to require the use of an uppercase character in passwords.

Rationale:

Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_ucredit

References:

cis-csc | 1, 12, 15, 16, 5
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
disa | CCI-004066
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
ospp | FMT_SMF_EXT.1
pcidss | Req-8.2.3
os-srg | SRG-OS-000069-GPOS-00037, SRG-OS-000070-GPOS-00038
anssi | R31

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_ucredit='-1'

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

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

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

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

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

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

Group Set Password Hashing Algorithm Group contains 1 rule
[ref] The system's default algorithm for storing password hashes in `/etc/shadow` is SHA-512. This can be configured in several locations.

#### Rule Set Password Hashing Algorithm in /etc/login.defs [ref]

In `/etc/login.defs`, add or update the following line to ensure the system will use YESCRYPT as the hashing algorithm:

    ENCRYPT_METHOD YESCRYPT

Rationale:

Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are encrypted with a weak algorithm are no more protected than if they are kept in plain text.
Using a stronger hashing algorithm makes password cracking attacks more difficult.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs

References:

cis-csc | 1, 12, 15, 16, 5
cjis | 5.6.2.2
cobit5 | DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui | 3.13.11
disa | CCI-004062
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism | 0418, 1055, 1402
iso27001-2013 | A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nist | IA-5(c), IA-5(1)(c), CM-6(a)
nist-csf | PR.AC-1, PR.AC-6, PR.AC-7
pcidss | Req-8.2.1
os-srg | SRG-OS-000073-GPOS-00041
pcidss4 | 8.3.2, 8.3

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'login' 2>/dev/null | grep -q '^installed'; then

    var_password_hashing_algorithm='YESCRYPT'

    # Allow multiple algorithms, but choose the first one for remediation
    #
    var_password_hashing_algorithm="$(echo $var_password_hashing_algorithm | cut -d \| -f 1)"

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

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

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

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

#### Rule Install pam_pwquality Package [ref]

The `libpam-pwquality` package can be installed with the following command:

    $ apt-get install libpam-pwquality

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. "pwquality" enforces complex password construction configuration and has the ability to limit brute-force attacks on the system.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_pam_pwquality_installed

References:

disa | CCI-000366
os-srg | SRG-OS-000480-GPOS-00225

Remediation Shell script ⇲

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

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    DEBIAN_FRONTEND=noninteractive apt-get install -y "libpam-pwquality"

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

Remediation OSBuild Blueprint snippet ⇲

    [[packages]]
    name = "libpam-pwquality"
    version = "*"

Remediation Ansible snippet ⇲

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

    - name: Gather the package facts
      package_facts:
        manager: auto
      tags:
      - enable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_pam_pwquality_installed

    - name: Ensure libpam-pwquality is installed
      package:
        name: libpam-pwquality
        state: present
      when: '"libpam-runtime" in ansible_facts.packages'
      tags:
      - enable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_pam_pwquality_installed

Remediation Puppet snippet ⇲

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

    include install_libpam-pwquality

    class install_libpam-pwquality {
      package { 'libpam-pwquality':
        ensure => 'installed',
      }
    }

Group Protect Accounts by Restricting Password-Based Login Group contains 2 groups and 2 rules

[ref] Conventionally, Unix shell accounts are accessed by providing a username and password to a login program, which tests these values for correctness using the `/etc/passwd` and `/etc/shadow` files. Password-based login is vulnerable to guessing of weak passwords, and to sniffing and man-in-the-middle attacks against passwords entered over a network or at an insecure console. Therefore, mechanisms for accessing accounts by entering usernames and passwords should be restricted to those which are operationally necessary.

Group Set Password Expiration Parameters Group contains 1 rule

[ref] The file `/etc/login.defs` controls several password-related settings. Programs such as `passwd`, `su`, and `login` consult `/etc/login.defs` to determine behavior with regard to password aging, expiration warnings, and length. See the man page `login.defs(5)` for more information.
Users should be forced to change their passwords, in order to decrease the utility of compromised passwords. However, the need to change passwords often should be balanced against the risk that users will reuse or write down passwords if forced to change them too often. Forcing password changes every 90-360 days, depending on the environment, is recommended. Set the appropriate value as `PASS_MAX_DAYS` and apply it to existing accounts with the `-M` flag.
The `PASS_MIN_DAYS` (`-m`) setting prevents password changes for 7 days after the first change, to discourage password cycling. If you use this setting, train users to contact an administrator for an emergency password change in case a new password becomes compromised. The `PASS_WARN_AGE` (`-W`) setting gives users 7 days of warnings at login time that their passwords are about to expire.
For example, for each existing human user USER, expiration parameters could be adjusted to a 180 day maximum password age, 7 day minimum password age, and 7 day warning period with the following command:

    $ sudo chage -M 180 -m 7 -W 7 USER

#### Rule Set Root Account Password Maximum Age [ref]

Configure the root account to enforce a 365-day maximum password lifetime restriction by running the following command:

    $ sudo chage -M 365 root

Rationale: | Any password, no matter how complex, can eventually be cracked. Therefore, passwords need to be changed periodically. If the operating system does not limit the lifetime of passwords and force users to change their passwords, there is the risk that the operating system passwords could be compromised.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_set_max_life_root

References:

anssi | R31

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | restrict

    var_accounts_maximum_age_root='365'

    chage -M $var_accounts_maximum_age_root root

Group Verify Proper Storage and Existence of Password Hashes Group contains 1 rule

[ref] By default, password hashes for local accounts are stored in the second field (colon-separated) in `/etc/shadow`. This file should be readable only by processes running with root credentials, preventing users from casually accessing others' password hashes and attempting to crack them. However, it remains possible to misconfigure the system and store password hashes in world-readable files such as `/etc/passwd`, or to even store passwords themselves in plaintext on the system. Using system-provided tools for password change/creation should allow administrators to avoid such misconfiguration.

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

Configure the number or rounds for the password hashing algorithm. This can be accomplished by using the `rounds` option for the `pam_unix` PAM module.
In file `/etc/pam.d/common-password` append `rounds=11` to the `pam_unix.so` entry, as shown below:

    password [success=1 default=ignore] pam_unix.so ...existing_options... rounds=11

Warning: Setting a high number of hashing rounds makes it more difficult to brute force the password, but requires more CPU resources to authenticate users.

Rationale: | Using a higher number of rounds makes password cracking attacks more difficult.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_rounds_password_auth

References:

disa | CCI-000803, CCI-004062
os-srg | SRG-OS-000073-GPOS-00041
anssi | R68

Remediation Shell script ⇲

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q '^installed'; then

    var_password_pam_unix_rounds='11'

    if [ -e "/etc/pam.d/common-password" ] ; then
        PAM_FILE_PATH="/etc/pam.d/common-password"
        if [ -f /usr/bin/authselect ]; then

            if ! authselect check; then
            echo "
            authselect integrity check failed. Remediation aborted!
            This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
            It is not recommended to manually edit the PAM files when authselect tool is available.
            In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
            exit 1
            fi

            CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
            # If not already in use, a custom profile is created preserving the enabled features.
            if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                # The "local" profile does not contain essential security features required by multiple Benchmarks.
                # If currently used, it is replaced by "sssd", which is the best option in this case.
                if [[ $CURRENT_PROFILE == local ]]; then
                    CURRENT_PROFILE="sssd"
                fi
                authselect create-profile hardening -b $CURRENT_PROFILE
                CURRENT_PROFILE="custom/hardening"

                authselect apply-changes -b --backup=before-hardening-custom-profile
                authselect select $CURRENT_PROFILE
                for feature in $ENABLED_FEATURES; do
                    authselect enable-feature $feature;
                done

                authselect apply-changes -b --backup=after-hardening-custom-profile
            fi
            PAM_FILE_NAME=$(basename "/etc/pam.d/common-password")
            PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

            authselect apply-changes -b
        fi

            if ! grep -qP "^\s*password\s+\\[success=1 default=ignore\\]\s+pam_unix.so\s*.*" "$PAM_FILE_PATH"; then
                # Line matching group + control + module was not found. Check group + module.
                if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then
                    # The control is updated only if one single line matches.
                    sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_unix.so.*)/\1\[success=1 default=ignore\] \2/" "$PAM_FILE_PATH"
                else
                    echo "password    \[success=1 default=ignore\]    pam_unix.so" >> "$PAM_FILE_PATH"
                fi
            fi
            # Check the option
            if ! grep -qP "^\s*password\s+\\[success=1 default=ignore\\]\s+pam_unix.so\s*.*\srounds\b" "$PAM_FILE_PATH"; then
                sed -i -E --follow-symlinks "/\s*password\s+\\[success=1 default=ignore\\]\s+pam_unix.so.*/ s/$/ rounds=$var_password_pam_unix_rounds/" "$PAM_FILE_PATH"
            else
                sed -i -E --follow-symlinks "s/(\s*password\s+\\[success=1 default=ignore\\]\s+pam_unix.so\s+.*)(rounds=)[[:alnum:]]+\s*(.*)/\1\2$var_password_pam_unix_rounds \3/" "$PAM_FILE_PATH"
            fi
        if [ -f /usr/bin/authselect ]; then

            authselect apply-changes -b
        fi
    else
        echo "/etc/pam.d/common-password was not found" >&2
    fi

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

Group File Permissions and Masks Group contains 1 group and 5 rules

[ref] Traditional Unix security relies heavily on file and directory permissions to prevent unauthorized users from reading or modifying files to which they should not have access.
Several of the commands in this section search filesystems for files or directories with certain characteristics, and are intended to be run on every local partition on a given system. When the variable PART appears in one of the commands below, it means that the command is intended to be run repeatedly, with the name of each local partition substituted for PART in turn.
The following command prints a list of all xfs partitions on the local system, which is the default filesystem for Debian 12 installations:

    $ mount -t xfs | awk '{print $3}'

For any systems that use a different local filesystem type, modify this command as appropriate.

Group Verify Permissions on Important Files and Directories Group contains 5 rules
[ref] Permissions for many files on a system must be set restrictively to ensure sensitive information is properly protected. This section discusses important permission restrictions which can be verified to ensure that no harmful discrepancies have arisen.

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

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

References:

disa | CCI-000366, CCI-001090
os-srg | SRG-OS-000480-GPOS-00227, SRG-OS-000138-GPOS-00069
anssi | R54

Remediation Shell script ⇲

Complexity: | low
Disruption: | medium
Reboot: | false
Strategy: | restrict

    # At least under containerized env /proc can have files w/o possilibity to
    # modify even as root. And touching /proc is not good idea anyways.
    find / -path /proc -prune -o \
        -not -fstype afs -not -fstype ceph -not -fstype cifs -not -fstype smb3 -not -fstype smbfs \
        -not -fstype sshfs -not -fstype ncpfs -not -fstype ncp -not -fstype nfs -not -fstype nfs4 \
        -not -fstype gfs -not -fstype gfs2 -not -fstype glusterfs -not -fstype gpfs \
        -not -fstype pvfs2 -not -fstype ocfs2 -not -fstype lustre -not -fstype davfs \
        -not -fstype fuse.sshfs -type d -perm -0002 -uid +0 -exec chown root {} \;

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | medium
Reboot: | false
Strategy: | restrict

    - name: Ensure All World-Writable Directories Are Owned by root User - Define Excluded
        (Non-Local) File Systems and Paths
      ansible.builtin.set_fact:
        excluded_fstypes:
        - afs
        - ceph
        - cifs
        - smb3
        - smbfs
        - sshfs
        - ncpfs
        - ncp
        - nfs
        - nfs4
        - gfs
        - gfs2
        - glusterfs
        - gpfs
        - pvfs2
        - ocfs2
        - lustre
        - davfs
        - fuse.sshfs
        excluded_paths:
        - dev
        - proc
        - run
        - sys
        search_paths: []
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Find Relevant
        Root Directories Ignoring Pre-Defined Excluded Paths
      ansible.builtin.find:
        paths: /
        file_type: directory
        excludes: '{{ excluded_paths }}'
        hidden: true
        recurse: false
      register: result_relevant_root_dirs
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Include Relevant
        Root Directories in a List of Paths to be Searched
      ansible.builtin.set_fact:
        search_paths: '{{ search_paths | union([item.path]) }}'
      loop: '{{ result_relevant_root_dirs.files }}'
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Increment Search
        Paths List with Local Partitions Mount Points
      ansible.builtin.set_fact:
        search_paths: '{{ search_paths | union([item.mount]) }}'
      loop: '{{ ansible_mounts }}'
      when:
      - item.fstype not in excluded_fstypes
      - item.mount != '/'
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Increment Search
        Paths List with Local NFS File System Targets
      ansible.builtin.set_fact:
        search_paths: '{{ search_paths | union([item.device.split('':'')[1]]) }}'
      loop: '{{ ansible_mounts }}'
      when: item.device is search("localhost:")
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Define Rule
        Specific Facts
      ansible.builtin.set_fact:
        world_writable_dirs: []
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Find All Uncompliant
        Directories in Local File Systems
      ansible.builtin.command:
        cmd: find {{ item }} -xdev -type d -perm -0002 -uid +0
      loop: '{{ search_paths }}'
      changed_when: false
      register: result_found_dirs
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Create List
        of World Writable Directories Not Owned by root
      ansible.builtin.set_fact:
        world_writable_dirs: '{{ world_writable_dirs | union(item.stdout_lines) | list
          }}'
      loop: '{{ result_found_dirs.results }}'
      when: item is not skipped
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

    - name: Ensure All World-Writable Directories Are Owned by root User - Ensure root
        Ownership on Local World Writable Directories
      ansible.builtin.file:
        path: '{{ item }}'
        owner: root
      loop: '{{ world_writable_dirs }}'
      tags:
      - dir_perms_world_writable_root_owned
      - low_complexity
      - medium_disruption
      - medium_severity
      - no_reboot_needed
      - restrict_strategy

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

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

    $ sudo chmod +t DIR

Warning: This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of directories present on the system. It is not a problem in most cases, but especially systems with a large number of directories can be affected. See `https://access.redhat.com/articles/6999111`.

Rationale:

Failing to set the sticky bit on public directories allows unauthorized users to delete files in the directory structure.
The only authorized public directories are those temporary directories supplied with the system, or those designed to be temporary file repositories. The setting is normally reserved for directories used by the system, by users for temporary file storage (such as `/tmp`), and for directories requiring global read/write access.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits

References:

cis-csc | 12, 13, 14, 15, 16, 18, 3, 5
cobit5 | APO01.06, DSS05.04, DSS05.07, DSS06.02
disa | CCI-001090
isa-62443-2009 | 4.3.3.7.3
isa-62443-2013 | SR 2.1, SR 5.2
iso27001-2013 | A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cip | CIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nist | CM-6(a), AC-6(1)
nist-csf | PR.AC-4, PR.DS-5
os-srg | SRG-OS-000138-GPOS-00069
anssi | R54
pcidss4 | 2.2.6, 2.2

#### Rule Ensure No World-Writable Files Exist [ref]

It is generally a good idea to remove global (other) write access to a file when it is discovered. However, check with documentation for specific applications before making changes. Also, monitor for recurring world-writable files, as these may be symptoms of a misconfigured application or user account. Finally, this applies to real files and not virtual files that are a part of pseudo file systems such as `sysfs` or `procfs`.
Warning: This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of files present on the system. It is not a problem in most cases, but especially systems with a large number of files can be affected. See `https://access.redhat.com/articles/6999111`.

Rationale: | Data in world-writable files can be modified by any user on the system. In almost all circumstances, files can be configured using a combination of user and group permissions to support whatever legitimate access is needed without the risk caused by world-writable files.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable

References:

cis-csc | 12, 13, 14, 15, 16, 18, 3, 5
cobit5 | APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-2009 | 4.3.3.7.3
isa-62443-2013 | SR 2.1, SR 5.2
iso27001-2013 | A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cip | CIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nist | CM-6(a), AC-6(1)
nist-csf | PR.AC-4, PR.DS-5
anssi | R54
pcidss4 | 2.2.6, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | configure

    FILTER_NODEV=$(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,)

    # Do not consider /sysroot partition because it contains only the physical
    # read-only root on bootable containers.
    PARTITIONS=$(findmnt -n -l -k -it $FILTER_NODEV | awk '{ print $1 }' | grep -v "/sysroot")

    for PARTITION in $PARTITIONS; do
      find "${PARTITION}" -xdev -type f -perm -002 -exec chmod o-w {} \; 2>/dev/null
    done

    # Ensure /tmp is also fixed when tmpfs is used.
    if grep "^tmpfs /tmp" /proc/mounts; then
      find /tmp -xdev -type f -perm -002 -exec chmod o-w {} \; 2>/dev/null
    fi

#### Rule Ensure All Files Are Owned by a Group [ref]

If any file is not group-owned by a valid defined group, the cause of the lack of group-ownership must be investigated. Following this, those files should be deleted or assigned to an appropriate group. The groups need to be defined in `/etc/group` or in `/usr/lib/group` if `nss-altfiles` are configured to be used in `/etc/nsswitch.conf`. Locate the mount points related to local devices by the following command:

    $ findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,)

For all mount points listed by the previous command, it is necessary to search for files which do not belong to a valid group using the following command:

    $ sudo find MOUNTPOINT -xdev -nogroup 2>/dev/null

Warning: This rule only considers local groups as valid groups. If you have your groups defined outside `/etc/group` or `/usr/lib/group`, the rule won't consider those.
Warning: This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of files present on the system. It is not a problem in most cases, but especially systems with a large number of files can be affected. See `https://access.redhat.com/articles/6999111`.

Rationale:

Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account, or other similar cases. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_file_permissions_ungroupowned

References:

cis-csc | 1, 11, 12, 13, 14, 15, 16, 18, 3, 5
cobit5 | APO01.06, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.06, DSS06.10
disa | CCI-000366
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2
iso27001-2013 | A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nist | CM-6(a), AC-6(1)
nist-csf | PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5, PR.PT-3
os-srg | SRG-OS-000480-GPOS-00227
anssi | R53
pcidss4 | 2.2.6, 2.2

#### Rule Ensure All Files Are Owned by a User [ref]

If any files are not owned by a user, then the cause of their lack of ownership should be investigated. Following this, the files should be deleted or assigned to an appropriate user. Locate the mount points related to local devices by the following command:

    $ findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,)

For all mount points listed by the previous command, it is necessary to search for files which do not belong to a valid user using the following command:

    $ sudo find MOUNTPOINT -xdev -nouser 2>/dev/null

Warning: For this rule to evaluate centralized user accounts, `getent` must be working properly so that running the command

    getent passwd

returns a list of all users in your organization. If using the System Security Services Daemon (SSSD),

    enumerate = true

must be configured in your organization's domain to return a complete list of users
Warning: This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of files present on the system. It is not a problem in most cases, but especially systems with a large number of files can be affected. See `https://access.redhat.com/articles/6999111`.

Rationale:

Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account, or other similar cases. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.

Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_no_files_unowned_by_user

References:

cis-csc | 11, 12, 13, 14, 15, 16, 18, 3, 5, 9
cobit5 | APO01.06, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06
disa | CCI-000366
isa-62443-2009 | 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, SR 7.6
iso27001-2013 | A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nist | CM-6(a), AC-6(1)
nist-csf | PR.AC-4, PR.AC-6, PR.DS-5, PR.IP-1, PR.PT-3
os-srg | SRG-OS-000480-GPOS-00227
anssi | R53
pcidss4 | 2.2.6, 2.2

Group Services Group contains 10 groups and 14 rules

[ref] The best protection against vulnerable software is running less software. This section describes how to review the software which Debian 12 installs on a system and disable software which is not needed. It then enumerates the software packages installed on a default Debian 12 system and provides guidance about which ones can be safely disabled.
Debian 12 provides a convenient minimal install option that essentially installs the bare necessities for a functional system. When building Debian 12 systems, it is highly recommended to select the minimal packages and then build up the system from there.

Group DHCP Group contains 1 group and 2 rules

[ref] The Dynamic Host Configuration Protocol (DHCP) allows systems to request and obtain an IP address and other configuration parameters from a server.
This guide recommends configuring networking on clients by manually editing the appropriate files under `/etc/sysconfig`. Use of DHCP can make client systems vulnerable to compromise by rogue DHCP servers, and should be avoided unless necessary. If using DHCP is necessary, however, there are best practices that should be followed to minimize security risk.

Group Disable DHCP Server Group contains 2 rules
[ref] The DHCP server `dhcpd` is not installed or activated by default. If the software was installed and activated, but the system does not need to act as a DHCP server, it should be disabled and removed.

#### Rule Uninstall DHCP Server Package [ref]

If the system does not need to act as a DHCP server, the dhcp package can be uninstalled. The `dhcp` package can be removed with the following command:

    $ apt-get remove dhcp

Rationale: | Removing the DHCP server ensures that it cannot be easily or accidentally reactivated and disrupt network operation.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_dhcp_removed

References:

cis-csc | 11, 14, 3, 9
cobit5 | BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
disa | CCI-000366
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013 | A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a)
nist-csf | PR.IP-1, PR.PT-3
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove dhcp
    #	   from the system, and may remove any packages
    #	   that depend on dhcp. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "dhcp"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure dhcp is removed
      package:
        name: dhcp
        state: absent
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_dhcp_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_dhcp

    class remove_dhcp {
      package { 'dhcp':
        ensure => 'purged',
      }
    }

#### Rule Uninstall kea Package [ref]

If the system does not need to act as a DHCP server, the kea package can be uninstalled.
Rationale: | Removing the DHCP server ensures that it cannot be easily or accidentally reactivated and disrupt network operation.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_kea_removed

References:

anssi | R62

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove kea
    #	   from the system, and may remove any packages
    #	   that depend on kea. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "kea"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure kea is removed
      package:
        name: kea
        state: absent
      tags:
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_kea_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_kea

    class remove_kea {
      package { 'kea':
        ensure => 'purged',
      }
    }

Group Mail Server Software Group contains 1 rule

[ref] Mail servers are used to send and receive email over the network. Mail is a very common service, and Mail Transfer Agents (MTAs) are obvious targets of network attack. Ensure that systems are not running MTAs unnecessarily, and configure needed MTAs as defensively as possible.
Very few systems at any site should be configured to directly receive email over the network. Users should instead use mail client programs to retrieve email from a central server that supports protocols such as IMAP or POP3. However, it is normal for most systems to be independently capable of sending email, for instance so that cron jobs can report output to an administrator. Most MTAs, including Postfix, support a submission-only mode in which mail can be sent from the local system to a central site MTA (or directly delivered to a local account), but the system still cannot receive mail directly over a network.
The `alternatives` program in Debian 12 permits selection of other mail server software (such as Sendmail), but Postfix is the default and is preferred. Postfix was coded with security in mind and can also be more effectively contained by SELinux as its modular design has resulted in separate processes performing specific actions. More information is available on its website, http://www.postfix.org.

#### Rule Uninstall Sendmail Package [ref]

Sendmail is not the default mail transfer agent and is not installed by default. The `sendmail` package can be removed with the following command:

    $ apt-get remove sendmail

Rationale: | The sendmail software was not developed with security in mind and its design prevents it from being effectively contained by SELinux. Postfix should be used instead.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_sendmail_removed

References:

cis-csc | 11, 14, 3, 9
cobit5 | BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
disa | CCI-000366, CCI-000381
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013 | A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a)
nist-csf | PR.IP-1, PR.PT-3
os-srg | SRG-OS-000480-GPOS-00227, SRG-OS-000095-GPOS-00049
anssi | R62

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}
    ' 'linux-base' 2>/dev/null | grep -q ^installed; then

    # CAUTION: This remediation script will remove sendmail
    #	   from the system, and may remove any packages
    #	   that depend on sendmail. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "sendmail"

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Gather the package facts
      package_facts:
        manager: auto
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_sendmail_removed

    - name: Ensure sendmail is removed
      package:
        name: sendmail
        state: absent
      when: '"linux-base" in ansible_facts.packages'
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_sendmail_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_sendmail

    class remove_sendmail {
      package { 'sendmail':
        ensure => 'purged',
      }
    }

Group Obsolete Services Group contains 6 groups and 11 rules

[ref] This section discusses a number of network-visible services which have historically caused problems for system security, and for which disabling or severely limiting the service has been the best available guidance for some time. As a result of this, many of these services are not installed as part of Debian 12 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 `iptables` to restrict access to the vulnerable service to only those remote hosts which have a known need to use it.

Group Xinetd Group contains 1 rule
[ref] The `xinetd` service acts as a dedicated listener for some network services (mostly, obsolete ones) and can be used to provide access controls and perform some logging. It has been largely obsoleted by other features, and it is not installed by default. The older Inetd service is not even available as part of Debian 12.

#### Rule Uninstall xinetd Package [ref]

The `xinetd` package can be removed with the following command:

    $ apt-get remove xinetd

Rationale: | Removing the `xinetd` package decreases the risk of the xinetd service's accidental (or intentional) activation.
Severity: | low
Rule ID: | xccdf_org.ssgproject.content_rule_package_xinetd_removed

References:

cis-csc | 11, 12, 14, 15, 3, 8, 9
cobit5 | APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
disa | CCI-000305
hipaa | 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)
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013 | A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a)
nist-csf | PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # Remediation is applicable only in certain platforms
    if dpkg-query --show --showformat='${db:Status-Status}
    ' 'linux-base' 2>/dev/null | grep -q ^installed; then

    # CAUTION: This remediation script will remove xinetd
    #	   from the system, and may remove any packages
    #	   that depend on xinetd. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "xinetd"

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

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Gather the package facts
      package_facts:
        manager: auto
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - low_severity
      - no_reboot_needed
      - package_xinetd_removed

    - name: Ensure xinetd is removed
      package:
        name: xinetd
        state: absent
      when: '"linux-base" in ansible_facts.packages'
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - low_severity
      - no_reboot_needed
      - package_xinetd_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_xinetd

    class remove_xinetd {
      package { 'xinetd':
        ensure => 'purged',
      }
    }

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

#### Rule Remove NIS Client [ref]

The Network Information Service (NIS), formerly known as Yellow Pages, is a client-server directory service protocol used to distribute system configuration files. The NIS client (`ypbind`) was used to bind a system to an NIS server and receive the distributed configuration files.
Rationale: | The NIS service is inherently an insecure system that has been vulnerable to DOS attacks, buffer overflows and has poor authentication for querying NIS maps. NIS generally has been replaced by such protocols as Lightweight Directory Access Protocol (LDAP). It is recommended that the service be removed.
Severity: | unknown
Rule ID: | xccdf_org.ssgproject.content_rule_package_ypbind_removed

References:

hipaa | 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)
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove ypbind-mt
    #	   from the system, and may remove any packages
    #	   that depend on ypbind-mt. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "ypbind-mt"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure ypbind-mt is removed
      package:
        name: ypbind-mt
        state: absent
      tags:
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_ypbind_removed
      - unknown_severity

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_ypbind-mt

    class remove_ypbind-mt {
      package { 'ypbind-mt':
        ensure => 'purged',
      }
    }

#### Rule Uninstall ypserv Package [ref]

The `ypserv` package can be removed with the following command:

    $ apt-get remove ypserv

Rationale: | The NIS service provides an unencrypted authentication service which does not provide for the confidentiality and integrity of user passwords or the remote session. Removing the `ypserv` package decreases the risk of the accidental (or intentional) activation of NIS or NIS+ services.
Severity: | high
Rule ID: | xccdf_org.ssgproject.content_rule_package_ypserv_removed

References:

cis-csc | 11, 12, 14, 15, 3, 8, 9
cobit5 | APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
disa | CCI-000381
hipaa | 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)
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013 | A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a), IA-5(1)(c)
nist-csf | PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
pcidss | Req-2.2.2
os-srg | SRG-OS-000095-GPOS-00049
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove ypserv
    #	   from the system, and may remove any packages
    #	   that depend on ypserv. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "ypserv"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure ypserv is removed
      package:
        name: ypserv
        state: absent
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - NIST-800-53-IA-5(1)(c)
      - PCI-DSS-Req-2.2.2
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - high_severity
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_ypserv_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_ypserv

    class remove_ypserv {
      package { 'ypserv':
        ensure => 'purged',
      }
    }

Group Rlogin, Rsh, and Rexec Group contains 2 rules
[ref] The Berkeley r-commands are legacy services which allow cleartext remote access and have an insecure trust model.

#### Rule Uninstall rsh-server Package [ref]

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

    $ apt-get remove rsh-server

Rationale:

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

Severity: | high
Rule ID: | xccdf_org.ssgproject.content_rule_package_rsh-server_removed

References:

cis-csc | 11, 12, 14, 15, 3, 8, 9
cobit5 | APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
disa | CCI-000381
hipaa | 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)
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013 | A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a), IA-5(1)(c)
nist-csf | PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
os-srg | SRG-OS-000095-GPOS-00049
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove rsh-server
    #	   from the system, and may remove any packages
    #	   that depend on rsh-server. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "rsh-server"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure rsh-server is removed
      package:
        name: rsh-server
        state: absent
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - NIST-800-53-IA-5(1)(c)
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - high_severity
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_rsh-server_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_rsh-server

    class remove_rsh-server {
      package { 'rsh-server':
        ensure => 'purged',
      }
    }

#### Rule Uninstall rsh Package [ref]

The `rsh` package contains the client commands for the rsh services

Rationale:

These legacy clients contain numerous security exposures and have been replaced with the more secure SSH package. Even if the server is removed, it is best to ensure the clients are also removed to prevent users from inadvertently attempting to use these commands and therefore exposing their credentials. Note that removing the `rsh` package removes the clients for `rsh`,`rcp`, and `rlogin`.

Severity: | unknown
Rule ID: | xccdf_org.ssgproject.content_rule_package_rsh_removed

References:

cui | 3.1.13
hipaa | 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)
iso27001-2013 | A.8.2.3, A.13.1.1, A.13.2.1, A.13.2.3, A.14.1.2, A.14.1.3
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove rsh
    #	   from the system, and may remove any packages
    #	   that depend on rsh. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "rsh"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure rsh is removed
      package:
        name: rsh
        state: absent
      tags:
      - NIST-800-171-3.1.13
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_rsh_removed
      - unknown_severity

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_rsh

    class remove_rsh {
      package { 'rsh':
        ensure => 'purged',
      }
    }

Group Chat/Messaging Services Group contains 2 rules
[ref] The talk software makes it possible for users to send and receive messages across systems through a terminal session.

#### Rule Uninstall talk-server Package [ref]

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

     $ apt-get remove talk-server

Rationale: | The talk software presents a security risk as it uses unencrypted protocols for communications. Removing the `talk-server` package decreases the risk of the accidental (or intentional) activation of talk services.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_talk-server_removed

References:

hipaa | 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)
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove talk-server
    #	   from the system, and may remove any packages
    #	   that depend on talk-server. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "talk-server"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure talk-server is removed
      package:
        name: talk-server
        state: absent
      tags:
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_talk-server_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_talk-server

    class remove_talk-server {
      package { 'talk-server':
        ensure => 'purged',
      }
    }

#### Rule Uninstall talk Package [ref]

The `talk` package contains the client program for the Internet talk protocol, which allows the user to chat with other users on different systems. Talk is a communication program which copies lines from one terminal to the terminal of another user. The `talk` package can be removed with the following command:

    $ apt-get remove talk

Rationale: | The talk software presents a security risk as it uses unencrypted protocols for communications. Removing the `talk` package decreases the risk of the accidental (or intentional) activation of talk client program.
Severity: | medium
Rule ID: | xccdf_org.ssgproject.content_rule_package_talk_removed

References:

hipaa | 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)
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove talk
    #	   from the system, and may remove any packages
    #	   that depend on talk. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "talk"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure talk is removed
      package:
        name: talk
        state: absent
      tags:
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - medium_severity
      - no_reboot_needed
      - package_talk_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_talk

    class remove_talk {
      package { 'talk':
        ensure => 'purged',
      }
    }

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

#### Rule Uninstall telnet-server Package [ref]

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

    $ apt-get remove telnet-server

Rationale:

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

Severity: | high
Rule ID: | xccdf_org.ssgproject.content_rule_package_telnet-server_removed

References:

cis-csc | 11, 12, 14, 15, 3, 8, 9
cobit5 | APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
disa | CCI-000381
hipaa | 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)
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013 | A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a)
nist-csf | PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
pcidss | Req-2.2.2
os-srg | SRG-OS-000095-GPOS-00049
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove telnet-server
    #	   from the system, and may remove any packages
    #	   that depend on telnet-server. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "telnet-server"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure telnet-server is removed
      package:
        name: telnet-server
        state: absent
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - PCI-DSS-Req-2.2.2
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - high_severity
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_telnet-server_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_telnet-server

    class remove_telnet-server {
      package { 'telnet-server':
        ensure => 'purged',
      }
    }

#### Rule Remove telnet Clients [ref]

The 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 Debian 12.
Severity: | low
Rule ID: | xccdf_org.ssgproject.content_rule_package_telnet_removed

References:

cui | 3.1.13
hipaa | 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)
iso27001-2013 | A.8.2.3, A.13.1.1, A.13.2.1, A.13.2.3, A.14.1.2, A.14.1.3
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove telnet
    #	   from the system, and may remove any packages
    #	   that depend on telnet. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "telnet"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure telnet is removed
      package:
        name: telnet
        state: absent
      tags:
      - NIST-800-171-3.1.13
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - low_severity
      - no_reboot_needed
      - package_telnet_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_telnet

    class remove_telnet {
      package { 'telnet':
        ensure => 'purged',
      }
    }

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

#### Rule Uninstall tftp-server Package [ref]

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

     $ apt-get remove tftp-server

Rationale: | Removing the `tftp-server` package decreases the risk of the accidental (or intentional) activation of tftp services. If TFTP is required for operational support (such as transmission of router configurations), its use must be documented with the Information Systems Securty Manager (ISSM), restricted to only authorized personnel, and have access control rules established.
Severity: | high
Rule ID: | xccdf_org.ssgproject.content_rule_package_tftp-server_removed

References:

cis-csc | 11, 12, 14, 15, 3, 8, 9
cobit5 | APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
disa | CCI-000366
isa-62443-2009 | 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013 | SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013 | A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nist | CM-7(a), CM-7(b), CM-6(a)
nist-csf | PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
os-srg | SRG-OS-000480-GPOS-00227
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove tftp-server
    #	   from the system, and may remove any packages
    #	   that depend on tftp-server. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "tftp-server"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure tftp-server is removed
      package:
        name: tftp-server
        state: absent
      tags:
      - NIST-800-53-CM-6(a)
      - NIST-800-53-CM-7(a)
      - NIST-800-53-CM-7(b)
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - high_severity
      - low_complexity
      - low_disruption
      - no_reboot_needed
      - package_tftp-server_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_tftp-server

    class remove_tftp-server {
      package { 'tftp-server':
        ensure => 'purged',
      }
    }

#### Rule Remove tftp Daemon [ref]

Trivial File Transfer Protocol (TFTP) is a simple file transfer protocol, typically used to automatically transfer configuration or boot files between systems. TFTP does not support authentication and can be easily hacked. The package `tftp` is a client program that allows for connections to a `tftp` server.
Rationale: | It is recommended that TFTP be removed, unless there is a specific need for TFTP (such as a boot server). In that case, use extreme caution when configuring the services.
Severity: | low
Rule ID: | xccdf_org.ssgproject.content_rule_package_tftp_removed

References:

disa | CCI-000197
os-srg | SRG-OS-000074-GPOS-00042
anssi | R62
pcidss4 | 2.2.4, 2.2

Remediation Shell script ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    # CAUTION: This remediation script will remove tftp
    #	   from the system, and may remove any packages
    #	   that depend on tftp. Execute this
    #	   remediation AFTER testing on a non-production
    #	   system!

    DEBIAN_FRONTEND=noninteractive apt-get remove -y "tftp"

Remediation Ansible snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    - name: Ensure tftp is removed
      package:
        name: tftp
        state: absent
      tags:
      - PCI-DSSv4-2.2
      - PCI-DSSv4-2.2.4
      - disable_strategy
      - low_complexity
      - low_disruption
      - low_severity
      - no_reboot_needed
      - package_tftp_removed

Remediation Puppet snippet ⇲

Complexity: | low
Disruption: | low
Reboot: | false
Strategy: | disable

    include remove_tftp

    class remove_tftp {
      package { 'tftp':
        ensure => 'purged',
      }
    }

Red Hat and Red Hat Enterprise Linux are either registered trademarks or trademarks of Red Hat, Inc. in the United States and other countries. All other names are registered trademarks or trademarks of their respective companies.