diff --git a/bin/hardening/apt_gpg_is_configured.sh b/bin/hardening/apt_gpg_is_configured.sh new file mode 100755 index 0000000..71568aa --- /dev/null +++ b/bin/hardening/apt_gpg_is_configured.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure GPG keys are configured (Manual) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=3 +# shellcheck disable=2034 +DESCRIPTION="Ensure GPG keys are configured" +APT_KEY_PATH="/etc/apt/trusted.gpg.d" +APT_KEY_FILE="/etc/apt/trusted.gpg" +# from "man apt-secure" +SOURCES_UNSECURE_OPTION='allow-insecure=yes' +APT_UNSECURE_OPTION='Acquire::AllowInsecureRepositories=true' + +# This function will be called if the script status is on enabled / audit mode +audit() { + + key_files=0 + info "Verifying that apt keys are present" + # apt-key list requires that gnupg2 is installed + # we are not going to install it for the sake of a test, so we only check the presence of key files + is_file_empty "$APT_KEY_FILE" + if [ "$FNRET" -eq 1 ]; then + info "$APT_KEY_FILE present and not empty" + key_files=$((key_files + 1)) + fi + + does_file_exist "$APT_KEY_PATH" + if [ "$FNRET" -ne 0 ]; then + info "$APT_KEY_PATH is missing" + else + asc_files=$(find "$APT_KEY_PATH" -name '*.asc' | wc -l) + key_files=$((key_files + asc_files)) + + gpg_files=$(find "$APT_KEY_PATH" -name '*.gpg' | wc -l) + key_files=$((key_files + gpg_files)) + + if [ "$asc_files" -eq 0 ] && [ "$gpg_files" -eq 0 ]; then + info "No key found in $APT_KEY_PATH" + fi + fi + + if [ "$key_files" -eq 0 ]; then + crit "No GPG file found" + else + # we do not test the GPG keys validity, but we ensure we don't bypass them + info "Ensure an unsecure option is not set in some sources list" + unsecure_sources=$(find /etc/apt/ -name '*.list' -exec grep -l "$SOURCES_UNSECURE_OPTION" {} \;) + if [ -n "$unsecure_sources" ]; then + crit "Some source files use $SOURCES_UNSECURE_OPTION : $unsecure_sources" + fi + + info "Ensure an unsecure option is not set in some apt configuration" + unsecure_option=$(grep -R "$APT_UNSECURE_OPTION" /etc/apt | wc -l) + if [ "$unsecure_option" -gt 0 ]; then + crit "$APT_UNSECURE_OPTION is set in apt configuration" + fi + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + audit + if [ "$FNRET" -gt 0 ]; then + crit "Your configuraiton does not match the recommendation. Please fix it manually" + else + info "Nothing to apply" + fi +} + +# This function will check config parameters required +check_config() { + # No parameter for this script + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/dev_shm_separate_partition.sh b/bin/hardening/dev_shm_separate_partition.sh new file mode 100755 index 0000000..917a88b --- /dev/null +++ b/bin/hardening/dev_shm_separate_partition.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure /dev/shm is a separate partition (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=3 +# shellcheck disable=2034 +DESCRIPTION="Ensure /dev/shm is a separate partition" + +# Quick factoring as many script use the same logic +PARTITION="/dev/shm" + +# This function will be called if the script status is on enabled / audit mode +audit() { + info "Verifying that $PARTITION is a partition" + FNRET=0 + is_a_partition "$PARTITION" + if [ "$FNRET" -gt 0 ]; then + crit "$PARTITION is not a partition" + FNRET=2 + else + ok "$PARTITION is a partition" + is_mounted "$PARTITION" + if [ "$FNRET" -gt 0 ]; then + warn "$PARTITION is not mounted" + FNRET=1 + else + ok "$PARTITION is mounted" + fi + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + audit + if [ "$FNRET" = 0 ]; then + ok "$PARTITION is correctly set" + elif [ "$FNRET" = 2 ]; then + crit "$PARTITION is not a partition, correct this by yourself, I cannot help you here" + else + info "mounting $PARTITION" + mount "$PARTITION" + fi +} + +# This function will check config parameters required +check_config() { + # No parameter for this script + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/enable_libpam_pwquality.sh b/bin/hardening/enable_libpam_pwquality.sh new file mode 100755 index 0000000..535c778 --- /dev/null +++ b/bin/hardening/enable_libpam_pwquality.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure pam_pwquality module is enabled (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure pam_pwquality module is enabled." + +PATTERN_COMMON='pam_pwquality.so' +FILE_COMMON='/etc/pam.d/common-password' + +# This function will be called if the script status is on enabled / audit mode +audit() { + does_pattern_exist_in_file "$FILE_COMMON" "$PATTERN_COMMON" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN_COMMON is present in $FILE_COMMON" + else + crit "$PATTERN_COMMON is not present in $FILE_COMMON" + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + does_pattern_exist_in_file $FILE_COMMON $PATTERN_COMMON + if [ "$FNRET" = 0 ]; then + ok "$PATTERN_COMMON is present in $FILE_COMMON" + else + warn "$PATTERN_COMMON is not present in $FILE_COMMON" + add_line_file_before_pattern "$FILE_COMMON" "password requisite pam_pwquality.so retry=3" "# pam-auth-update(8) for details." + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/install_iptables.sh b/bin/hardening/install_iptables.sh new file mode 100755 index 0000000..d675b6b --- /dev/null +++ b/bin/hardening/install_iptables.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure iptables packages are installed (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure iptables firewall is installed, does not check for its configuration." + +# Note: CIS recommends your iptables rules to be persistent. +# Do as you want, but this script does not handle this + +PACKAGES='iptables iptables-persistent' + +# This function will be called if the script status is on enabled / audit mode +audit() { + FOUND=false + for PACKAGE in $PACKAGES; do + is_pkg_installed "$PACKAGE" + if [ "$FNRET" = 0 ]; then + ok "$PACKAGE provides firewalling feature" + FOUND=true + fi + done + if [ "$FOUND" = false ]; then + crit "None of the following firewall packages are installed: $PACKAGES" + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + for PACKAGE in $PACKAGES; do + is_pkg_installed "$PACKAGE" + if [ "$FNRET" = 0 ]; then + ok "$PACKAGE provides firewalling feature" + FOUND=true + fi + done + if [ "$FOUND" = false ]; then + crit "None of the following firewall packages are installed: $PACKAGES, installing them" + apt_install "$PACKAGES" + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/install_libpam_pwquality.sh b/bin/hardening/install_libpam_pwquality.sh new file mode 100755 index 0000000..4548d8e --- /dev/null +++ b/bin/hardening/install_libpam_pwquality.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure libpam-pwquality is installed (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure libpam-pwquality is installed " + +PACKAGE='libpam-pwquality' + +# This function will be called if the script status is on enabled / audit mode +audit() { + is_pkg_installed "$PACKAGE" + if [ "$FNRET" != 0 ]; then + crit "$PACKAGE is not installed!" + else + ok "$PACKAGE is installed" + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + is_pkg_installed "$PACKAGE" + if [ "$FNRET" = 0 ]; then + ok "$PACKAGE is installed" + else + crit "$PACKAGE is absent, installing it" + apt_install "$PACKAGE" + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/install_nftables.sh b/bin/hardening/install_nftables.sh new file mode 100755 index 0000000..b5777c6 --- /dev/null +++ b/bin/hardening/install_nftables.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure nftables is installed (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure nftables firewall is installed, does not check for its configuration." + +# Note: CIS recommends your iptables rules to be persistent. +# Do as you want, but this script does not handle this + +PACKAGES='nftables' + +# This function will be called if the script status is on enabled / audit mode +audit() { + FOUND=false + for PACKAGE in $PACKAGES; do + is_pkg_installed "$PACKAGE" + if [ "$FNRET" = 0 ]; then + ok "$PACKAGE provides firewalling feature" + FOUND=true + fi + done + if [ "$FOUND" = false ]; then + crit "None of the following firewall packages are installed: $PACKAGES" + fi +} + +# This function will be called if the script status is on enabled mode +apply() { + for PACKAGE in $PACKAGES; do + is_pkg_installed "$PACKAGE" + if [ "$FNRET" = 0 ]; then + ok "$PACKAGE provides firewalling feature" + FOUND=true + fi + done + if [ "$FOUND" = false ]; then + crit "None of the following firewall packages are installed: $PACKAGES, installing them" + apt_install "$PACKAGES" + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/password_complexity.sh b/bin/hardening/password_complexity.sh new file mode 100755 index 0000000..d2a2de4 --- /dev/null +++ b/bin/hardening/password_complexity.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure password complexity is configured (Manual) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure password minimum length is configured " + +OPTIONS='' +FILE_QUALITY='/etc/security/pwquality.conf' + +# This function will be called if the script status is on enabled / audit mode +audit() { + for PW_OPT in $OPTIONS; do + PW_PARAM=$(echo "$PW_OPT" | cut -d= -f1) + PW_VALUE=$(echo "$PW_OPT" | cut -d= -f2) + # note : dont backslash regex characters, as 'does_pattern_exist_in_file' use "grep -E" which don't need it + PATTERN="${PW_PARAM}[[:space:]]?+=[[:space:]]?+$PW_VALUE" + does_pattern_exist_in_file "$FILE_QUALITY" "$PATTERN" + + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE_QUALITY" + else + crit "$PATTERN is not present in $FILE_QUALITY" + fi + done +} + +# This function will be called if the script status is on enabled mode +apply() { + info "The values defined here should be adapted to one needs before applying." +} + +# This function will create the config file for this check with default values +create_config() { + cat <:: +# ex: HOME_OWNER_EXCEPTIONS="/usr/sbin:daemon:root" +HOME_OWNER_EXCEPTIONS="" +# space separated list of path, where permissions are different than 0750 +HOME_PERM_EXCEPTIONS="" + +ERRORS=0 + +check_home_owner() { + # user owns home + local user=$1 + local home=$2 + FNRET=0 + + owner=$(stat -L -c "%U" "$home") + if [ "$owner" != "$user" ]; then + EXCEP_FOUND=0 + for excep in $HOME_OWNER_EXCEPTIONS; do + if [ "$home:$user:$owner" = "$excep" ]; then + ok "The home directory ($home) of user $user is owned by $owner but is part of exceptions ($home:$user:$owner)." + EXCEP_FOUND=1 + break + fi + done + if [ "$EXCEP_FOUND" -eq 0 ]; then + crit "The home directory ($home) of user $user is owned by $owner." + FNRET=1 + fi + fi +} + +check_home_perm() { + # 750 or more restrictive + local home=$1 + HOME_PERM_ERRORS=0 + + debug "Exceptions : $HOME_PERM_EXCEPTIONS" + debug "echo \"$HOME_PERM_EXCEPTIONS\" | grep -q $home" + if echo "$HOME_PERM_EXCEPTIONS" | grep -q "$home"; then + debug "$home is confirmed as an exception" + # shellcheck disable=SC2001 + RESULT=$(sed "s!$home!!" <<<"$RESULT") + else + debug "$home not found in exceptions" + fi + if [ -d "$home" ]; then + dirperm=$(/bin/ls -ld "$home" | cut -f1 -d" ") + if [ "$(echo "$dirperm" | cut -c6)" != "-" ]; then + crit "Group Write permission set on directory $home" + HOME_PERM_ERRORS=$((HOME_PERM_ERRORS + 1)) + fi + if [ "$(echo "$dirperm" | cut -c8)" != "-" ]; then + crit "Other Read permission set on directory $home" + HOME_PERM_ERRORS=$((HOME_PERM_ERRORS + 1)) + fi + if [ "$(echo "$dirperm" | cut -c9)" != "-" ]; then + crit "Other Write permission set on directory $home" + HOME_PERM_ERRORS=$((HOME_PERM_ERRORS + 1)) + fi + if [ "$(echo "$dirperm" | cut -c10)" != "-" ]; then + crit "Other Execute permission set on directory $home" + HOME_PERM_ERRORS=$((HOME_PERM_ERRORS + 1)) + fi + fi +} + +# This function will be called if the script status is on enabled / audit mode +audit() { + RESULT=$(get_db passwd | awk -F: '{ print $1 ":" $3 ":" $6 }') + for LINE in $RESULT; do + debug "Working on $LINE" + USER=$(awk -F: '{print $1}' <<<"$LINE") + USERID=$(awk -F: '{print $2}' <<<"$LINE") + DIR=$(awk -F: '{print $3}' <<<"$LINE") + if [ "$USERID" -ge 1000 ]; then + if [ ! -d "$DIR" ] && [ "$USER" != "nfsnobody" ] && [ "$USER" != "nobody" ] && [ "$DIR" != "/nonexistent" ]; then + crit "The home directory ($DIR) of user $USER does not exist." + ERRORS=$((ERRORS + 1)) + fi + + if [ -d "$DIR" ] && [ "$USER" != "nfsnobody" ]; then + check_home_owner "$USER" "$DIR" + [ $FNRET -ne 0 ] && ERRORS=$((ERRORS + 1)) + fi + fi + + done + + for DIR in $(get_db passwd | grep -Ev '(root|halt|sync|shutdown)' | awk -F: '($7 != "/usr/sbin/nologin" && $7 != "/sbin/nologin" && $7 != "/bin/false" && $7 !="/nonexistent" ) { print $6 }'); do + check_home_perm "$DIR" + ERRORS=$((ERRORS + HOME_PERM_ERRORS)) + done + + if [ "$ERRORS" -eq 0 ]; then + ok "All home directories are correctly configured" + fi + +} + +# This function will be called if the script status is on enabled mode +apply() { + info "Modifying home directories may seriously harm your system, report only here" +} + +create_config() { + cat <:: +HOME_OWNER_EXCEPTIONS="" +# space separated list of path, where permissions are different than 0750 +HOME_PERM_EXCEPTIONS="" +EOF +} + +# This function will check config parameters required +check_config() { + if [ -z "$HOME_PERM_EXCEPTIONS" ]; then + HOME_PERM_EXCEPTIONS="@" + fi +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/lib/utils.sh b/lib/utils.sh index baa0029..84388d2 100644 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -100,6 +100,15 @@ does_file_exist() { fi } +is_file_empty() { + local FILE=$1 + if $SUDO_CMD [ -s "$FILE" ]; then + FNRET=1 + else + FNRET=0 + fi +} + has_file_correct_ownership() { local FILE=$1 local USER=$2 diff --git a/tests/hardening/apt_gpg_is_configured.sh b/tests/hardening/apt_gpg_is_configured.sh new file mode 100644 index 0000000..d85b8b0 --- /dev/null +++ b/tests/hardening/apt_gpg_is_configured.sh @@ -0,0 +1,40 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + local APT_KEY_FILE="/etc/apt/trusted.gpg" + local APT_KEY_PATH="/etc/apt/trusted.gpg.d" + local unsecure_source="/etc/apt/sources.list.d/unsecure.list" + local unsecure_conf_file="/etc/apt/apt.conf.d/unsecure" + + # make sure we don't have any key + [ -f "$APT_KEY_FILE" ] && mv "$APT_KEY_FILE" /tmp + [ -d "$APT_KEY_PATH" ] && mv "$APT_KEY_PATH" /tmp + + describe Running non compliant missing keys + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + # fix the situation + [ -d /tmp/trusted.gpg.d ] && mv /tmp/trusted.gpg.d /etc/apt/ + [ -f /tmp/trusted.gpg ] && mv /tmp/trusted.gpg /etc/apt/ + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + echo 'deb [allow-insecure=yes] http://deb.debian.org/debian bookworm main' >"$unsecure_source" + describe Running non compliant unsecure option in sources list + register_test retvalshouldbe 1 + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + rm -f "$unsecure_source" + + echo 'Acquire::AllowInsecureRepositories=true' >"$unsecure_conf_file" + describe Running non compliant unsecure option in apt conf + register_test retvalshouldbe 1 + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + rm -f "$unsecure_conf_file" + +} diff --git a/tests/hardening/dev_shm_separate_partition.sh b/tests/hardening/dev_shm_separate_partition.sh new file mode 100644 index 0000000..4ad9ef8 --- /dev/null +++ b/tests/hardening/dev_shm_separate_partition.sh @@ -0,0 +1,16 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + dismiss_count_for_test + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + ################################################################## + # For this test, we only check that it runs properly on a blank # + # host, and we check root/sudo consistency. But, we don't test # + # the apply function because it can't be automated or it is very # + # long to test and not very useful. # + ################################################################## +} diff --git a/tests/hardening/enable_libpam_pwquality.sh b/tests/hardening/enable_libpam_pwquality.sh new file mode 100644 index 0000000..559869b --- /dev/null +++ b/tests/hardening/enable_libpam_pwquality.sh @@ -0,0 +1,21 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + PATTERN_COMMON='pam_pwquality.so' + FILE_COMMON='/etc/pam.d/common-password' + + # create issue + sed -i '/'$PATTERN_COMMON'/d' "$FILE_COMMON" + + describe Running non compliant + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + sed -i 's/audit/enabled/' "${CIS_CONF_DIR}/conf.d/${script}.cfg" + "${CIS_CHECKS_DIR}/${script}.sh" || true + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all +} diff --git a/tests/hardening/install_iptables.sh b/tests/hardening/install_iptables.sh new file mode 100644 index 0000000..4ad9ef8 --- /dev/null +++ b/tests/hardening/install_iptables.sh @@ -0,0 +1,16 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + dismiss_count_for_test + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + ################################################################## + # For this test, we only check that it runs properly on a blank # + # host, and we check root/sudo consistency. But, we don't test # + # the apply function because it can't be automated or it is very # + # long to test and not very useful. # + ################################################################## +} diff --git a/tests/hardening/install_libpam_pwquality.sh b/tests/hardening/install_libpam_pwquality.sh new file mode 100644 index 0000000..4ad9ef8 --- /dev/null +++ b/tests/hardening/install_libpam_pwquality.sh @@ -0,0 +1,16 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + dismiss_count_for_test + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + ################################################################## + # For this test, we only check that it runs properly on a blank # + # host, and we check root/sudo consistency. But, we don't test # + # the apply function because it can't be automated or it is very # + # long to test and not very useful. # + ################################################################## +} diff --git a/tests/hardening/install_nftables.sh b/tests/hardening/install_nftables.sh new file mode 100644 index 0000000..4ad9ef8 --- /dev/null +++ b/tests/hardening/install_nftables.sh @@ -0,0 +1,16 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + dismiss_count_for_test + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + ################################################################## + # For this test, we only check that it runs properly on a blank # + # host, and we check root/sudo consistency. But, we don't test # + # the apply function because it can't be automated or it is very # + # long to test and not very useful. # + ################################################################## +} diff --git a/tests/hardening/password_complexity.sh b/tests/hardening/password_complexity.sh new file mode 100644 index 0000000..11c9202 --- /dev/null +++ b/tests/hardening/password_complexity.sh @@ -0,0 +1,28 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + local OPTIONS="minclass=3 dcredit=-1 ucredit=-2 ocredit=-1 lcredit=-1" + local FILE_QUALITY='/etc/security/pwquality.conf' + + # install dependencies + apt-get update + apt-get install -y libpam-pwquality + + # prepare to fail + describe Prepare on purpose failed test + sed -i '/minclass/d' $FILE_QUALITY + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe Correcting situation + echo "$OPTIONS" >>"$FILE_QUALITY" + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + +} diff --git a/tests/hardening/password_consecutive_characters.sh b/tests/hardening/password_consecutive_characters.sh new file mode 100644 index 0000000..bad826d --- /dev/null +++ b/tests/hardening/password_consecutive_characters.sh @@ -0,0 +1,28 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + local OPTIONS="maxrepeat=3" + local FILE_QUALITY='/etc/security/pwquality.conf' + + # install dependencies + apt-get update + apt-get install -y libpam-pwquality + + # prepare to fail + describe Prepare on purpose failed test + sed -i '/maxrepeat/d' $FILE_QUALITY + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe Correcting situation + echo "$OPTIONS" >>"$FILE_QUALITY" + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + +} diff --git a/tests/hardening/password_max_sequential_characters.sh b/tests/hardening/password_max_sequential_characters.sh new file mode 100644 index 0000000..df1dd88 --- /dev/null +++ b/tests/hardening/password_max_sequential_characters.sh @@ -0,0 +1,28 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + local OPTIONS="maxsequence=3" + local FILE_QUALITY='/etc/security/pwquality.conf' + + # install dependencies + apt-get update + apt-get install -y libpam-pwquality + + # prepare to fail + describe Prepare on purpose failed test + sed -i '/maxsequence/d' $FILE_QUALITY + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe Correcting situation + echo "$OPTIONS" >>"$FILE_QUALITY" + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + +} diff --git a/tests/hardening/password_min_length.sh b/tests/hardening/password_min_length.sh new file mode 100644 index 0000000..73a680b --- /dev/null +++ b/tests/hardening/password_min_length.sh @@ -0,0 +1,27 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + local OPTIONS="minlen=14" + local FILE_QUALITY='/etc/security/pwquality.conf' + + # install dependencies + apt-get update + apt-get install -y libpam-pwquality + + # prepare to fail + describe Prepare on purpose failed test + sed -i '/minlen/d' $FILE_QUALITY + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe Correcting situation + echo "$OPTIONS" >>"$FILE_QUALITY" + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all +} diff --git a/tests/hardening/users_homedir_is_configured.sh b/tests/hardening/users_homedir_is_configured.sh new file mode 100644 index 0000000..a7ebe5b --- /dev/null +++ b/tests/hardening/users_homedir_is_configured.sh @@ -0,0 +1,88 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + local no_home_test_user="userwithouthome" + local owner_test_user="testhomeuser" + local perm_test_user="testhomepermuser" + + describe Running on blank host + register_test retvalshouldbe 0 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + home_dir_missing "$no_home_test_user" + home_dir_ownership "$owner_test_user" + home_dir_perm "$perm_test_user" + + fix_home "$no_home_test_user" "$owner_test_user" "$perm_test_user" + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + cleanup "$no_home_test_user" "$owner_test_user" "$perm_test_user" +} + +home_dir_missing() { + local test_user="$1" + + useradd -d /home/"$test_user" "$test_user" + describe Tests purposely failing that a homdedir does not exists + register_test retvalshouldbe 1 + register_test contain "does not exist." + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all +} + +home_dir_ownership() { + local test_user="$1" + + describe Test purposely failing that a user does not own its home + useradd -d /home/"$test_user" -m "$test_user" + chown root:root /home/"$test_user" + chmod 0750 /home/"$test_user" + register_test retvalshouldbe 1 + register_test contain "[ KO ] The home directory (/home/$test_user) of user $test_user is owned by root" + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all +} + +home_dir_perm() { + local test_user="$1" + + describe Tests purposely failing for wrong permissions on home + useradd -d /home/"$test_user" --create-home "$test_user" + chmod 777 /home/"$test_user" + register_test retvalshouldbe 1 + register_test contain "Group Write permission set on directory" + register_test contain "Other Read permission set on directory" + register_test contain "Other Write permission set on directory" + register_test contain "Other Execute permission set on directory" + + run noncompliant "${CIS_CHECKS_DIR}/${script}.sh" --audit-all +} + +fix_home() { + local missing_home_test_user="$1" + local owner_test_user="$2" + local perm_test_user="$3" + + describe correcting situation for missing home + install -d -m 0750 -o "$missing_home_test_user" /home/"$missing_home_test_user" + + describe correcting situation for ownership + # we don't want to erase default configurations, or others checks could fail + # shellcheck disable=2086 + sed -i '/^HOME_OWNER_EXCEPTIONS/s|HOME_OWNER_EXCEPTIONS=\"|HOME_OWNER_EXCEPTIONS=\"/home/'$owner_test_user':'$owner_test_user':root |' ${CIS_CONF_DIR}/conf.d/${script}.cfg + + describe correcting situation for permissions + chmod 0750 /home/"$perm_test_user" + +} + +cleanup() { + local users="$*" + for user in $users; do + # owner_test_user del will fail as its home is owned by another user + userdel -r "$user" || true + rm -rf /home/"${user:?}" || true + done +}