From d2bbf754ac967858ccb7a2541588d6b882a2ae29 Mon Sep 17 00:00:00 2001 From: Charles Herlin Date: Wed, 20 Dec 2017 15:14:30 +0100 Subject: [PATCH] Migrate generic checks from secaudit to cis-hardening new file: 99.3.1_acc_shadow_sha512.sh new file: 99.3.2_acc_sudoers_no_all.sh new file: 99.4_net_fw_default_policy_drop.sh new file: 99.5.1_ssh_auth_pubk_only.sh new file: 99.5.2.1_ssh_cry_kex.sh new file: 99.5.2.2_ssh_cry_mac.sh new file: 99.5.2.3_ssh_cry_rekey.sh new file: 99.5.3_ssh_disable_features.sh new file: 99.5.4_ssh_keys_from.sh new file: 99.5.5_ssh_strict_modes.sh new file: 99.5.6_ssh_sys_accept_env.sh new file: 99.5.7_ssh_sys_no_legacy.sh new file: 99.5.8_ssh_sys_sandbox.sh new file: 99.5.9_ssh_log_level.sh Fix descriptions in comment section for 99.* secaudit checks Remove duplicated legacy services that are already taken care of by vanilla cis Enable custom configuration of checks in config-file, no more hard coded conf Add test to disable check if debian version is too old Add excused IPs while checking "from" field of authorized_keys Escaping dots in IPs Manage Kex for different debian versions Add tests for generic checks and add apply for ssh config Apply shellcheck recommendations on audit/hardening scripts Update script to check for allowed IPs only, remove bastion related Fill `apply` func for ssh config related scripts Add and update tests scenarii Disable shellcheck test for external source 1091 As of today, the entire project is not shellcheck compliant, I prefer disabling the test that warns about not finding external source (that arent compliant). I will enable it again when the project library will be shellchecked https://github.com/koalaman/shellcheck/wiki/SC1091 Refactor password policy check with one check by feature Previous file will now only look for bad passwords in /etc/shadow I added two checks that look for the compliant configuration lines in conf files /etc/logins.defs and /etc/pam.d/common-passwords FIX: merge chained sed and fix regex FIX: update regex to capture more output FIX: fix pattern to ignore commented lines, add apply Also add tests to ensure that commented lines are not detected as valid configuration CHORE: cleanup test situation with file and users removal IMP: add case insensitive option when looking for patterns in files CHORE: removed duplicated line in test file --- bin/hardening/99.3.1_acc_shadow_sha512.sh | 79 ++++++++ bin/hardening/99.3.2_acc_sudoers_no_all.sh | 99 ++++++++++ bin/hardening/99.3.3_acc_pam_sha512.sh | 62 +++++++ bin/hardening/99.3.4_acc_logindefs_sha512.sh | 76 ++++++++ .../99.4_net_fw_default_policy_drop.sh | 78 ++++++++ bin/hardening/99.5.1_ssh_auth_pubk_only.sh | 97 ++++++++++ bin/hardening/99.5.2.1_ssh_cry_kex.sh | 117 ++++++++++++ bin/hardening/99.5.2.2_ssh_cry_mac.sh | 107 +++++++++++ bin/hardening/99.5.2.3_ssh_cry_rekey.sh | 113 ++++++++++++ bin/hardening/99.5.3_ssh_disable_features.sh | 96 ++++++++++ bin/hardening/99.5.4_ssh_keys_from.sh | 174 ++++++++++++++++++ bin/hardening/99.5.5_ssh_strict_modes.sh | 96 ++++++++++ bin/hardening/99.5.6_ssh_sys_accept_env.sh | 88 +++++++++ bin/hardening/99.5.7_ssh_sys_no_legacy.sh | 66 +++++++ bin/hardening/99.5.8_ssh_sys_sandbox.sh | 98 ++++++++++ bin/hardening/99.5.9_ssh_loglevel.sh | 97 ++++++++++ lib/utils.sh | 34 +++- shellcheck/launch_shellcheck.sh | 2 +- tests/hardening/99.3.1_acc_shadow_sha512.sh | 25 +++ tests/hardening/99.3.2_acc_sudoers_no_all.sh | 28 +++ tests/hardening/99.3.3_acc_pam_sha512.sh | 9 + .../hardening/99.3.4_acc_logindefs_sha512.sh | 38 ++++ tests/hardening/99.5.1_ssh_auth_pubk_only.sh | 29 +++ tests/hardening/99.5.2.1_ssh_cry_kex.sh | 29 +++ tests/hardening/99.5.2.2_ssh_cry_mac.sh | 22 +++ tests/hardening/99.5.2.3_ssh_cry_rekey.sh | 22 +++ .../hardening/99.5.3_ssh_disable_features.sh | 28 +++ tests/hardening/99.5.4_ssh_keys_from.sh | 50 +++++ tests/hardening/99.5.5_ssh_strict_modes.sh | 23 +++ tests/hardening/99.5.6_ssh_sys_accept_env.sh | 30 +++ tests/hardening/99.5.7_ssh_sys_no_legacy.sh | 8 + tests/hardening/99.5.8_ssh_sys_sandbox.sh | 22 +++ tests/hardening/99.5.9_ssh_loglevel.sh | 22 +++ tests/launch_tests.sh | 2 - 34 files changed, 1959 insertions(+), 7 deletions(-) create mode 100755 bin/hardening/99.3.1_acc_shadow_sha512.sh create mode 100755 bin/hardening/99.3.2_acc_sudoers_no_all.sh create mode 100755 bin/hardening/99.3.3_acc_pam_sha512.sh create mode 100755 bin/hardening/99.3.4_acc_logindefs_sha512.sh create mode 100755 bin/hardening/99.4_net_fw_default_policy_drop.sh create mode 100755 bin/hardening/99.5.1_ssh_auth_pubk_only.sh create mode 100755 bin/hardening/99.5.2.1_ssh_cry_kex.sh create mode 100755 bin/hardening/99.5.2.2_ssh_cry_mac.sh create mode 100755 bin/hardening/99.5.2.3_ssh_cry_rekey.sh create mode 100755 bin/hardening/99.5.3_ssh_disable_features.sh create mode 100755 bin/hardening/99.5.4_ssh_keys_from.sh create mode 100755 bin/hardening/99.5.5_ssh_strict_modes.sh create mode 100755 bin/hardening/99.5.6_ssh_sys_accept_env.sh create mode 100755 bin/hardening/99.5.7_ssh_sys_no_legacy.sh create mode 100755 bin/hardening/99.5.8_ssh_sys_sandbox.sh create mode 100755 bin/hardening/99.5.9_ssh_loglevel.sh create mode 100644 tests/hardening/99.3.1_acc_shadow_sha512.sh create mode 100644 tests/hardening/99.3.2_acc_sudoers_no_all.sh create mode 100644 tests/hardening/99.3.3_acc_pam_sha512.sh create mode 100644 tests/hardening/99.3.4_acc_logindefs_sha512.sh create mode 100644 tests/hardening/99.5.1_ssh_auth_pubk_only.sh create mode 100644 tests/hardening/99.5.2.1_ssh_cry_kex.sh create mode 100644 tests/hardening/99.5.2.2_ssh_cry_mac.sh create mode 100644 tests/hardening/99.5.2.3_ssh_cry_rekey.sh create mode 100644 tests/hardening/99.5.3_ssh_disable_features.sh create mode 100644 tests/hardening/99.5.4_ssh_keys_from.sh create mode 100644 tests/hardening/99.5.5_ssh_strict_modes.sh create mode 100644 tests/hardening/99.5.6_ssh_sys_accept_env.sh create mode 100644 tests/hardening/99.5.7_ssh_sys_no_legacy.sh create mode 100644 tests/hardening/99.5.8_ssh_sys_sandbox.sh create mode 100644 tests/hardening/99.5.9_ssh_loglevel.sh diff --git a/bin/hardening/99.3.1_acc_shadow_sha512.sh b/bin/hardening/99.3.1_acc_shadow_sha512.sh new file mode 100755 index 0000000..a1ca79c --- /dev/null +++ b/bin/hardening/99.3.1_acc_shadow_sha512.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# run-shellcheck +# +# OVH Security audit +# + +# +# Check that any password that may exist in /etc/shadow is SHA512 hashed and salted +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +DESCRIPTION="Check that any password that may exist in /etc/shadow is SHA512 hashed and salted" +FILE="/etc/shadow" + + +# This function will be called if the script status is on enabled / audit mode +audit () { +# Review shadow file for existing passwords + pw_found="" + users_reviewed="" + if $SUDO_CMD [ ! -r "$FILE" ]; then + crit "$FILE is not readable" + return + fi + for line in $($SUDO_CMD cut -d ":" -f 1,2 /etc/shadow); do + users_reviewed+="$line " + user=$(echo "$line" | cut -d ":" -f 1) + passwd=$(echo "$line" | cut -d ":" -f 2) + if [[ $passwd = '!' || $passwd = '*' ]]; then + continue + # Check password against $6$$, see `man 3 crypt` + elif [[ $passwd =~ ^\$6\$[a-zA-Z0-9./]{2,16}\$[a-zA-Z0-9./]{86}$ ]]; then + pw_found+="$user " + ok "User $user has suitable SHA512 hashed password." + else + pw_found+="$user " + crit "User $user has a password that is not SHA512 hashed." + fi + done + if [[ -z "$users_reviewed" ]]; then + crit "No users were reviewed in $FILE !" + return + fi + if [[ -z "$pw_found" ]]; then + ok "There is no password in $FILE" + fi +} + +# This function will be called if the script status is on enabled mode +apply () { + : +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.3.2_acc_sudoers_no_all.sh b/bin/hardening/99.3.2_acc_sudoers_no_all.sh new file mode 100755 index 0000000..233076c --- /dev/null +++ b/bin/hardening/99.3.2_acc_sudoers_no_all.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# run-shellcheck +# +# OVH Security audit +# + +# +# Checks there are no carte-blanche authorization in sudoers file(s). +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +DESCRIPTION="Checks there are no carte-blanche authorization in sudoers file(s)." + +FILE="/etc/sudoers" +DIRECTORY="/etc/sudoers.d" +# spaces will be expanded to [:space:]* when using the regex +# improves readability in audit report +REGEX="ALL = \( ALL( : ALL)? \)( NOPASSWD:)? ALL" +EXCEPT="" + +# This function will be called if the script status is on enabled / audit mode +audit () { + FILES="" + if $SUDO_CMD [ ! -r "$FILE" ]; then + crit "$FILE is not readable" + return + fi + FILES="$FILE" + if $SUDO_CMD [ ! -d "$DIRECTORY" ]; then + debug "$DIRECTORY does not exist" + elif $SUDO_CMD [ ! -x "$DIRECTORY" ]; then + crit "Cannot browse $DIRECTORY" + else + FILES="$FILES $($SUDO_CMD ls -1 $DIRECTORY | sed s=^=$DIRECTORY/= )" + fi + for file in $FILES; do + if $SUDO_CMD [ ! -r "$file" ]; then + crit "$file is not readable" + else + # shellcheck disable=2001 + if ! $SUDO_CMD grep -E "$(echo "$REGEX" | sed 's/ /[[:space:]]*/g')" "$file" &> /dev/null ; then + ok "There is no carte-blanche sudo permission in $file" + else + # shellcheck disable=2001 + RET=$($SUDO_CMD grep -E "$(echo "$REGEX" | sed 's/ /[[:space:]]*/g')" "$file" | sed 's/\t/#/g;s/ /#/g' ) + for line in $RET; do + if grep -q "$(echo "$line" | cut -d '#' -f 1)" <<< "$EXCEPT" ; then + # shellcheck disable=2001 + ok "$(echo "$line" | sed 's/#/ /g') is present in $file but was EXCUSED because $(echo "$line" | cut -d '#' -f 1) is part of exceptions." + continue + fi + # shellcheck disable=2001 + crit "$(echo "$line" | sed 's/#/ /g') is present in $file" + done + fi + fi + done + +} + +# This function will be called if the script status is on enabled mode +apply () { + : +} + +# This function will create the config file for this check with default values +create_config() { + cat < /dev/null 2>&1 + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi + diff --git a/bin/hardening/99.4_net_fw_default_policy_drop.sh b/bin/hardening/99.4_net_fw_default_policy_drop.sh new file mode 100755 index 0000000..104be24 --- /dev/null +++ b/bin/hardening/99.4_net_fw_default_policy_drop.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# run-shellcheck +# +# OVH Security audit +# + +# +# Check iptables firewall default policy for DROP on INPUT and FORWARD. +# + +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="Check iptables firewall default policy for DROP on INPUT and FORWARD." + +PACKAGE="iptables" +FW_CHAINS="INPUT FORWARD" +FW_POLICY="DROP" + +# 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 + ipt=$($SUDO_CMD $PACKAGE -nL || true ) + if [[ -z $ipt ]]; then + crit "Empty return from $PACKAGE command. Aborting..." + return + fi + for chain in $FW_CHAINS; do + regex="Chain $chain \(policy ([A-Z]+)\)" + # previous line will capture actual policy + if [[ $ipt =~ $regex ]]; then + actual_policy=${BASH_REMATCH[1]} + if [[ $actual_policy == "$FW_POLICY" ]]; then + ok "Policy correctly set to $FW_POLICY for chain $chain" + else + crit "Policy set to $actual_policy for chain $chain, should be ${FW_POLICY}." + fi + else + echo "cant find chain $chain" + fi + done + fi +} + +# This function will be called if the script status is on enabled mode +apply () { + : +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.1_ssh_auth_pubk_only.sh b/bin/hardening/99.5.1_ssh_auth_pubk_only.sh new file mode 100755 index 0000000..c2f0427 --- /dev/null +++ b/bin/hardening/99.5.1_ssh_auth_pubk_only.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# run-shellcheck + +# +# OVH Security audit +# + +# +# Ensure that sshd only allows authentication through public key. +# + +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 that sshd only allows authentication through public key." + +PACKAGE='openssh-server' +FILE='/etc/ssh/sshd_config' +OPTIONS='PubkeyAuthentication=yes PasswordAuthentication=no KbdInteractiveAuthentication=no KerberosAuthentication=no ChallengeResponseAuthentication=no HostbasedAuthentication=no GSSAPIAuthentication=no GSSAPIKeyExchange=no ' + +# 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" + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo "$SSH_OPTION" | cut -d= -f 1) + SSH_VALUE=$(echo "$SSH_OPTION" | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]+$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + done + 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 + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo "$SSH_OPTION" | cut -d= -f 1) + SSH_VALUE=$(echo "$SSH_OPTION" | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]+$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$SSH_PARAM" + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$SSH_PARAM $SSH_VALUE" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]+.*" "$SSH_PARAM $SSH_VALUE" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi + done +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.2.1_ssh_cry_kex.sh b/bin/hardening/99.5.2.1_ssh_cry_kex.sh new file mode 100755 index 0000000..baf3953 --- /dev/null +++ b/bin/hardening/99.5.2.1_ssh_cry_kex.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# run-shellcheck + +# +# CIS Debian 7/8 Hardening +# + +# +# Checking key exchange ciphers. +# + +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="Checking key exchange ciphers." + +PACKAGE='openssh-server' +OPTIONS='' +FILE='/etc/ssh/sshd_config' + +# 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" + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + done + 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 + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$SSH_PARAM" + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$SSH_PARAM $SSH_VALUE" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]*.*" "$SSH_PARAM $SSH_VALUE" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi + done +} + +create_config() { + get_debian_major_version + set +u + debug "Debian version : $DEB_MAJ_VER " + if [[ -z $DEB_MAJ_VER ]] || [[ 7 -eq $DEB_MAJ_VER ]]; then + KEX='diffie-hellman-group-exchange-sha256' + elif [[ 8 -eq $DEB_MAJ_VER ]] || [[ 9 -eq $DEB_MAJ_VER ]]; then + KEX='curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256' + else + KEX='diffie-hellman-group-exchange-sha256' + fi + set -u + cat < /dev/null 2>&1 + fi + done + +} + +# This function will create the config file for this check with default values +create_config() { + cat < /dev/null 2>&1 + fi + done + +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.3_ssh_disable_features.sh b/bin/hardening/99.5.3_ssh_disable_features.sh new file mode 100755 index 0000000..f7367fb --- /dev/null +++ b/bin/hardening/99.5.3_ssh_disable_features.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# run-shellcheck + +# +# OVH Security audit +# + +# +# Check all special features in sshd_config are disabled +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +DESCRIPTION="Check all special features in sshd_config are disabled" + +PACKAGE='openssh-server' +FILE='/etc/ssh/sshd_config' +OPTIONS='AllowAgentForwarding=no AllowTcpForwarding=no AllowStreamLocalForwarding=no PermitTunnel=no PermitUserRC=no GatewayPorts=no' + +# 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" + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo "$SSH_OPTION" | cut -d= -f 1) + SSH_VALUE=$(echo "$SSH_OPTION" | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + done + 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 + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo "$SSH_OPTION" | cut -d= -f 1) + SSH_VALUE=$(echo "$SSH_OPTION" | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$SSH_PARAM" + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$SSH_PARAM $SSH_VALUE" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]*.*" "$SSH_PARAM $SSH_VALUE" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi + done + +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.4_ssh_keys_from.sh b/bin/hardening/99.5.4_ssh_keys_from.sh new file mode 100755 index 0000000..f504708 --- /dev/null +++ b/bin/hardening/99.5.4_ssh_keys_from.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# run-shellcheck + +# +# OVH Security audit +# + +# +# Check field in ssh authorized keys files for users with login shell, and bastions IP if available. +# + +set -e # One error, it is over +set -u # One variable unset, it is over + +# shellcheck disable=2034 +DESCRIPTION="Check field in ssh authorized keys files for users with login shell, and allowed IP if available." + +# Regex looking for empty, hash starting lines, or 'from="127.127.127,127.127.127" ssh' +# shellcheck disable=2089 +REGEX="^(from=(?:'|\")(,?(\d{1,3}(\.\d{1,3}){3}))+(?:'|\")\s+ssh|#|$)" +AUTHKEYFILE_PATTERN="" +AUTHKEYFILE_PATTERN_DEFAULT=".ssh/authorized_keys .ssh/authorized_keys2" + +ALLOWED_IPS="" + +ALLOWED_NOLOGIN_SHELLS="/bin/false /usr/sbin/nologin" + +# Check functions +check_ip() { + file=$1 + if [ -z "$ALLOWED_IPS" ]; then + warn "No allowed IPs to treat"; + return ; + fi + for line in $($SUDO_CMD grep -ne "from" "$file" | tr -s " " | sed 's/ /_/g' ); do + linum=$(echo "$line" | cut -d ':' -f 1) + ips=$(echo "$line" | cut -d '"' -f 2 | tr ',' ' ') + ok_ips_allowed="" + bad_ips="" + for ip in $ips; do + ip_escaped=$(sed 's/\./\\./g' <<< "$ip") + if grep -qw "$ip_escaped" <<< $ALLOWED_IPS ; then + debug "Line $linum of $file allows access from exused IP (${ip})." + ok_ips_allowed+="$ip " + else + debug "Line $linum of $file allows access from ip ($ip) that is not allowed." + bad_ips+="$ip " + fi + done + ok_ips=$( sed 's/ $//' <<< "${ok_ips_allowed}") + bad_ips=$( sed 's/ $//' <<< "${bad_ips}") + if [[ -z $bad_ips ]]; then + if [[ ! -z $ok_ips ]]; then + ok "Line $linum of $file allows ssh access only from allowed IPs ($ok_ips)." + fi + else + crit "Line $linum of $file allows ssh access from (${bad_ips}) that are not allowed." + if [[ ! -z $ok_ips ]]; then + ok "Line $linum of $file allows ssh access from at least allowed IPs ($ok_ips)." + fi + fi + done +} + +check_file() { + file=$1 + if $SUDO_CMD [ ! -e "$file" ]; then debug "$file does not exist"; return; fi + if $SUDO_CMD [ -r "$file" ]; then + debug "Treating $file" + FOUND_AUTHKF=1 + if $SUDO_CMD grep -vqP "$REGEX" "${file}" ; then + bad_lines="$(grep -vnP "$REGEX" "${file}" | cut -d ':' -f 1 | tr '\n' ' ' | sed 's/ $//' )" + crit "There are anywhere access keys in ${file} at lines (${bad_lines})." + else + ok "File ${file} is cleared from anywhere access keys." + check_ip "$file" + fi + else + crit "Cannot read ${file} for ${user}." + fi +} + +check_dir() { + directory=$1 + if $SUDO_CMD [ ! -x "$directory" ]; then + crit "Cannot read ${directory}." + return + fi + for file in $AUTHKEYFILE_PATTERN; do + check_file "${directory}"/${file} + done +} + +# This function will be called if the script status is on enabled / audit mode +audit () { + # Retrieve authorized_key file pattern from sshd_config + if $SUDO_CMD [ ! -r /etc/ssh/sshd_config ]; then + crit "/etc/ssh/sshd_config is not readable." + else + ret=$($SUDO_CMD grep -iP "^AuthorizedKeysFile" /etc/ssh/sshd_config || echo '#KO' ) + if [ "x$ret" = "x#KO" ]; then + debug "No AuthorizedKeysFile defined in sshd_config." + else + AUTHKEYFILE_PATTERN=$(echo "$ret" | sed 's/AuthorizedKeysFile//i' | sed 's#%h/##' | tr -s "[:space:]") + debug "Found pattern in sshdconfig : ${AUTHKEYFILE_PATTERN}." + fi + fi + + if [ -z "$AUTHKEYFILE_PATTERN" ] ; then + AUTHKEYFILE_PATTERN=$AUTHKEYFILE_PATTERN_DEFAULT + debug "Set default pattern for authorized_keys file." + fi + + for line in $($SUDO_CMD cat /etc/passwd | cut -d ":" -f 1,7); do + # Checking if at least one AuthKeyFile has been found for this user + FOUND_AUTHKF=0 + user=$(echo "$line" | cut -d ":" -f 1); + shell=$(echo "$line" | cut -d ':' -f 2); + if grep -q "$shell" <<< "$ALLOWED_NOLOGIN_SHELLS" ; then + continue + else + info "User $user has a valid shell."; + if [ "x$user" = "xroot" ]; then + check_dir /root + continue + elif $SUDO_CMD [ ! -d /home/"$user" ]; then + info "User $user has no home directory." + continue + fi + check_dir /home/"${user}" + if [ $FOUND_AUTHKF = 0 ]; then + warn "$user has a valid shell but no authorized_keys file" + fi + fi + + done +} + +# This function will be called if the script status is on enabled mode +apply () { + : +} + +create_config() { + cat < /dev/null 2>&1 + fi + done + +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.6_ssh_sys_accept_env.sh b/bin/hardening/99.5.6_ssh_sys_accept_env.sh new file mode 100755 index 0000000..68221ba --- /dev/null +++ b/bin/hardening/99.5.6_ssh_sys_accept_env.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# run-shellcheck + +# +# CIS Debian 7/8 Hardening +# + +# +# 9Restrict which user's variables are accepted by ssh daemon +# + +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="Restrict which user's variables are accepted by ssh daemon" + +PACKAGE='openssh-server' +PATTERN='^\s*AcceptEnv\s+LANG LC_\*' +FILE='/etc/ssh/sshd_config' + +# 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" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + 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 + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$PATTERN" + PATTERN=$( sed 's/\^//' <<< "$PATTERN" | sed -r 's/\\s\*//' | sed -r 's/\\s\+/ /g' | sed 's/\\//g') + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$PATTERN" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]*.*" "$PATTERN" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.7_ssh_sys_no_legacy.sh b/bin/hardening/99.5.7_ssh_sys_no_legacy.sh new file mode 100755 index 0000000..002f675 --- /dev/null +++ b/bin/hardening/99.5.7_ssh_sys_no_legacy.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# run-shellcheck + +# CIS Debian 7 Hardening +# + +# +# Ensure that legacy services rlogin, rlogind and rcp are disabled and not installed +# +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +DESCRIPTION="Ensure that legacy services rlogin, rlogind and rcp are disabled and not installed" +# shellcheck disable=2034 +SERVICES="rlogin rlogind rcp" + +# This function will be called if the script status is on enabled / audit mode +audit () { + for SERVICE in $SERVICES + do + info "Checking if $SERVICE is enabled and installed" + is_service_enabled "$SERVICE" + if [ "$FNRET" != 0 ]; then + ok "$SERVICE is disabled" + else + crit "$SERVICE is enabled" + fi + is_pkg_installed "$SERVICE" + if [ "$FNRET" != 0 ]; then + ok "$SERVICE is not installed" + else + warn "$SERVICE is installed" + fi + done +} + +# This function will be called if the script status is on enabled mode +apply () { + : +} + + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment, cannot source CIS_ROOT_DIR variable, aborting" + exit 128 +fi + + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.8_ssh_sys_sandbox.sh b/bin/hardening/99.5.8_ssh_sys_sandbox.sh new file mode 100755 index 0000000..aac06ef --- /dev/null +++ b/bin/hardening/99.5.8_ssh_sys_sandbox.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# run-shellcheck + +# +# CIS Debian 7/8 Hardening +# + +# +# Check UsePrivilegeSeparation set to sandbox. +# + +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="Check UsePrivilegeSeparation set to sandbox." + +PACKAGE='openssh-server' +OPTIONS='UsePrivilegeSeparation=sandbox' +FILE='/etc/ssh/sshd_config' + +# 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" + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + done + 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 + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$SSH_PARAM" + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$SSH_PARAM $SSH_VALUE" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]*.*" "$SSH_PARAM $SSH_VALUE" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi + done +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/99.5.9_ssh_loglevel.sh b/bin/hardening/99.5.9_ssh_loglevel.sh new file mode 100755 index 0000000..ddc32d6 --- /dev/null +++ b/bin/hardening/99.5.9_ssh_loglevel.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# run-shellcheck + +# +# CIS Debian 7/8 Hardening +# + +# +# SSH log level is set to VERBOSE +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +DESCRIPTION="SSH log level is set to VERBOSE" +# shellcheck disable=2034 +HARDENING_LEVEL=2 + +PACKAGE='openssh-server' +OPTIONS='LogLevel=VERBOSE' +FILE='/etc/ssh/sshd_config' + +# 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" + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + crit "$PATTERN is not present in $FILE" + fi + done + 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 + for SSH_OPTION in $OPTIONS; do + SSH_PARAM=$(echo $SSH_OPTION | cut -d= -f 1) + SSH_VALUE=$(echo $SSH_OPTION | cut -d= -f 2) + PATTERN="^$SSH_PARAM[[:space:]]*$SSH_VALUE" + does_pattern_exist_in_file_nocase $FILE "$PATTERN" + if [ "$FNRET" = 0 ]; then + ok "$PATTERN is present in $FILE" + else + warn "$PATTERN is not present in $FILE, adding it" + does_pattern_exist_in_file_nocase $FILE "^$SSH_PARAM" + if [ "$FNRET" != 0 ]; then + add_end_of_file $FILE "$SSH_PARAM $SSH_VALUE" + else + info "Parameter $SSH_PARAM is present but with the wrong value -- Fixing" + replace_in_file $FILE "^$SSH_PARAM[[:space:]]*.*" "$SSH_PARAM $SSH_VALUE" + fi + /etc/init.d/ssh reload > /dev/null 2>&1 + fi + done +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + . /etc/default/cis-hardening +fi +if [ -z "$CIS_ROOT_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_ROOT_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "$CIS_ROOT_DIR"/lib/main.sh ]; then + # shellcheck source=/opt/debian-cis/lib/main.sh + . "$CIS_ROOT_DIR"/lib/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_ROOT_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/lib/utils.sh b/lib/utils.sh index 22e1439..aa6ae09 100644 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -95,14 +95,25 @@ has_file_correct_permissions() { fi } +does_pattern_exist_in_file_nocase() { + _does_pattern_exist_in_file "-Ei" $* +} + does_pattern_exist_in_file() { - local FILE=$1 - local PATTERN=$2 + _does_pattern_exist_in_file "-E" $* +} + +_does_pattern_exist_in_file() { + local OPTIONS="$1" + shift + local FILE="$1" + shift + local PATTERN="$*" debug "Checking if $PATTERN is present in $FILE" if $SUDO_CMD [ -r "$FILE" ] ; then - debug "$SUDO_CMD grep -qE -- '$PATTERN' $FILE" - if $($SUDO_CMD grep -qE -- "$PATTERN" $FILE); then + debug "$SUDO_CMD grep -q $OPTIONS -- '$PATTERN' $FILE" + if $($SUDO_CMD grep -q $OPTIONS -- "$PATTERN" $FILE); then FNRET=0 else FNRET=1 @@ -382,3 +393,18 @@ is_pkg_installed() FNRET=1 fi } + + +# Returns Debian major version + +get_debian_major_version() +{ + DEB_MAJ_VER="" + does_file_exist /etc/debian_version + if [ $FNRET ]; then + DEB_MAJ_VER=$(cut -d '.' -f1 /etc/debian_version) + else + DEB_MAJ_VER=$(lsb_release -r | cut -f2 | cut -d '.' -f 1) + fi +} + diff --git a/shellcheck/launch_shellcheck.sh b/shellcheck/launch_shellcheck.sh index 4d761d5..68d0659 100755 --- a/shellcheck/launch_shellcheck.sh +++ b/shellcheck/launch_shellcheck.sh @@ -14,7 +14,7 @@ fi for f in $files; do if head "$f" | grep -qE "^# run-shellcheck$"; then printf "\e[1;36mRunning shellcheck on: %s \e[0m\n" "$f" - if ! /usr/bin/shellcheck --color=always --shell=bash "$f"; then + if ! /usr/bin/shellcheck --color=always --shell=bash -e SC1091 "$f"; then retval=$((retval + 1)) fi fi diff --git a/tests/hardening/99.3.1_acc_shadow_sha512.sh b/tests/hardening/99.3.1_acc_shadow_sha512.sh new file mode 100644 index 0000000..546d24b --- /dev/null +++ b/tests/hardening/99.3.1_acc_shadow_sha512.sh @@ -0,0 +1,25 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "There is no password in /etc/shadow" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + cp -a /etc/shadow /tmp/shadow.bak + sed -i 's/secaudit:!/secaudit:mypassword/' /etc/shadow + describe Fail: Found unsecure password + register_test retvalshouldbe 1 + register_test contain "User secaudit has a password that is not SHA512 hashed" + run unsecpasswd /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + mv /tmp/shadow.bak /etc/shadow + chpasswd << EOF +secaudit:mypassword +EOF + describe Pass: Found properly hashed password + register_test retvalshouldbe 0 + register_test contain "User secaudit has suitable SHA512 hashed password" + run sha512pass /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.3.2_acc_sudoers_no_all.sh b/tests/hardening/99.3.2_acc_sudoers_no_all.sh new file mode 100644 index 0000000..699bd26 --- /dev/null +++ b/tests/hardening/99.3.2_acc_sudoers_no_all.sh @@ -0,0 +1,28 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "There is no carte-blanche sudo permission in" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + # Proceed to operation that will end up to a non compliant system + useradd -s /bin/bash jeantestuser + echo 'jeantestuser ALL = (ALL) NOPASSWD:ALL' >> /etc/sudoers.d/jeantestuser + describe Fail: Not compliant system + register_test retvalshouldbe 1 + register_test contain "[ KO ] jeantestuser ALL = (ALL) NOPASSWD:ALL is present in /etc/sudoers.d/jeantestuser" + run userallcmd /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + + # shellcheck disable=2016 + echo 'EXCEPT="$EXCEPT jeantestuser"' >> /opt/debian-cis/etc/conf.d/"${script}".cfg + describe Adding jeantestuser to exceptions + register_test retvalshouldbe 0 + register_test contain "[ OK ] jeantestuser ALL = (ALL) NOPASSWD:ALL is present in /etc/sudoers.d/jeantestuser but was EXCUSED because jeantestuser is part of exceptions" + run userexcept /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + rm -f /etc/sudoers.d/jeantestuser + userdel jeantestuser +} + diff --git a/tests/hardening/99.3.3_acc_pam_sha512.sh b/tests/hardening/99.3.3_acc_pam_sha512.sh new file mode 100644 index 0000000..421786f --- /dev/null +++ b/tests/hardening/99.3.3_acc_pam_sha512.sh @@ -0,0 +1,9 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^\s*password\s.+\s+pam_unix\.so\s+.*sha512 is present in /etc/pam.d/common-password" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.3.4_acc_logindefs_sha512.sh b/tests/hardening/99.3.4_acc_logindefs_sha512.sh new file mode 100644 index 0000000..c584b6d --- /dev/null +++ b/tests/hardening/99.3.4_acc_logindefs_sha512.sh @@ -0,0 +1,38 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "ENCRYPT_METHOD SHA512 is present in /etc/login.defs" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + cp /etc/login.defs /tmp/login.defs.bak + describe Line as comment + sed -i 's/\(ENCRYPT_METHOD SHA512\)/# \1/' /etc/login.defs + register_test retvalshouldbe 1 + register_test contain "SHA512 is not present" + run commented /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + rm /etc/login.defs + describe Fail: missing conf file + register_test retvalshouldbe 1 + register_test contain "/etc/login.defs is not readable" + run missconffile /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + cp /tmp/login.defs.bak /etc/login.defs + sed -ir 's/ENCRYPT_METHOD[[:space:]]\+SHA512/ENCRYPT_METHOD MD5/' /etc/login.defs + describe Fail: wrong hash function configuration + register_test retvalshouldbe 1 + register_test contain "SHA512 is not present" + run wrongconf /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + mv /tmp/login.defs.bak /etc/login.defs + register_test retvalshouldbe 0 + run sha512pass /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.1_ssh_auth_pubk_only.sh b/tests/hardening/99.5.1_ssh_auth_pubk_only.sh new file mode 100644 index 0000000..84cc1c3 --- /dev/null +++ b/tests/hardening/99.5.1_ssh_auth_pubk_only.sh @@ -0,0 +1,29 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^PubkeyAuthentication[[:space:]]+yes is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^PasswordAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^KbdInteractiveAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^KerberosAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^ChallengeResponseAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^HostbasedAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^GSSAPIAuthentication[[:space:]]+no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^GSSAPIKeyExchange[[:space:]]+no is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.2.1_ssh_cry_kex.sh b/tests/hardening/99.5.2.1_ssh_cry_kex.sh new file mode 100644 index 0000000..0ae79bb --- /dev/null +++ b/tests/hardening/99.5.2.1_ssh_cry_kex.sh @@ -0,0 +1,29 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + cp -a /etc/ssh/sshd_config /tmp/sshd_config.bak + + describe Change case of config line + sed -i 's/\(KexAlgorithms\)/\U\1/' /etc/ssh/sshd_config + register_test retvalshouldbe 0 + run uppercase /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + mv /tmp/sshd_config.bak /etc/ssh/sshd_config + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.2.2_ssh_cry_mac.sh b/tests/hardening/99.5.2.2_ssh_cry_mac.sh new file mode 100644 index 0000000..c193573 --- /dev/null +++ b/tests/hardening/99.5.2.2_ssh_cry_mac.sh @@ -0,0 +1,22 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^MACs[[:space:]]*umac-128-etm@openssh.com,umac-64-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128@openssh.com,umac-64@openssh.com,hmac-sha2-512,hmac-sha2-256 is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.2.3_ssh_cry_rekey.sh b/tests/hardening/99.5.2.3_ssh_cry_rekey.sh new file mode 100644 index 0000000..e9a1e50 --- /dev/null +++ b/tests/hardening/99.5.2.3_ssh_cry_rekey.sh @@ -0,0 +1,22 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^RekeyLimit[[:space:]]*512M\s+6h is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.3_ssh_disable_features.sh b/tests/hardening/99.5.3_ssh_disable_features.sh new file mode 100644 index 0000000..9fe24b3 --- /dev/null +++ b/tests/hardening/99.5.3_ssh_disable_features.sh @@ -0,0 +1,28 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^AllowAgentForwarding[[:space:]]*no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^AllowTcpForwarding[[:space:]]*no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^AllowStreamLocalForwarding[[:space:]]*no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^PermitTunnel[[:space:]]*no is present in /etc/ssh/sshd_config + " + register_test contain "[ OK ] ^PermitUserRC[[:space:]]*no is present in /etc/ssh/sshd_config" + register_test contain "[ OK ] ^GatewayPorts[[:space:]]*no is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.4_ssh_keys_from.sh b/tests/hardening/99.5.4_ssh_keys_from.sh new file mode 100644 index 0000000..b694527 --- /dev/null +++ b/tests/hardening/99.5.4_ssh_keys_from.sh @@ -0,0 +1,50 @@ +# run-shellcheck +test_audit() { + dismiss_test + # shellcheck disable=2154 + run genconf /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + useradd -s /bin/bash jeantestuser + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "[WARN] secaudit has a valid shell but no authorized_keys file" + register_test contain "[INFO] User jeantestuser has a valid shell" + register_test contain "[INFO] User jeantestuser has no home directory" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + mkdir -p /home/secaudit/.ssh + touch /home/secaudit/.ssh/authorized_keys2 + describe empty authorized keys file + register_test retvalshouldbe 0 + run emptyauthkey /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + ssh-keygen -t ed25519 -f /tmp/key1 + cat /tmp/key1.pub >> /home/secaudit/.ssh/authorized_keys2 + describe Key without from field + register_test retvalshouldbe 1 + run keynofrom /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + echo -n 'from="127.0.0.1" ' > /home/secaudit/.ssh/authorized_keys2 + cat /tmp/key1.pub >> /home/secaudit/.ssh/authorized_keys2 + describe Key with from, no ip check + register_test retvalshouldbe 0 + run keyfrom /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + # shellcheck disable=2016 + echo 'ALLOWED_IPS="$ALLOWED_IPS 127.0.0.1"' >> /opt/debian-cis/etc/conf.d/"${script}".cfg + echo -n 'from="10.0.1.2" ' >> /home/secaudit/.ssh/authorized_keys2 + cat /tmp/key1.pub >> /home/secaudit/.ssh/authorized_keys2 + describe Key with from, filled allowed IPs, one bad ip + register_test retvalshouldbe 1 + run badfromip /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + # shellcheck disable=2016 + echo 'ALLOWED_IPS="$ALLOWED_IPS 10.0.1.2"' >> /opt/debian-cis/etc/conf.d/"${script}".cfg + describe Key with from, filled allowed IPs, all IPs allowed + register_test retvalshouldbe 0 + run allwdfromip /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + userdel jeantestuser +} + diff --git a/tests/hardening/99.5.5_ssh_strict_modes.sh b/tests/hardening/99.5.5_ssh_strict_modes.sh new file mode 100644 index 0000000..2e2b48a --- /dev/null +++ b/tests/hardening/99.5.5_ssh_strict_modes.sh @@ -0,0 +1,23 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + dismiss_count_for_test + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^StrictModes[[:space:]]*yes is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.6_ssh_sys_accept_env.sh b/tests/hardening/99.5.6_ssh_sys_accept_env.sh new file mode 100644 index 0000000..8bdd26c --- /dev/null +++ b/tests/hardening/99.5.6_ssh_sys_accept_env.sh @@ -0,0 +1,30 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + # Proceed to operation that will end up to a non compliant system + describe Tests purposely failing + sed -ri 's/^\s*AcceptEnv\s+LANG LC_\*//' /etc/ssh/sshd_config + register_test retvalshouldbe 1 + register_test contain "[ KO ] ^\s*AcceptEnv\s+LANG LC_\* is not present in /etc/ssh/sshd_config" + run noncompliant /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^\s*AcceptEnv\s+LANG LC_\* is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.7_ssh_sys_no_legacy.sh b/tests/hardening/99.5.7_ssh_sys_no_legacy.sh new file mode 100644 index 0000000..46ed25a --- /dev/null +++ b/tests/hardening/99.5.7_ssh_sys_no_legacy.sh @@ -0,0 +1,8 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 0 + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.8_ssh_sys_sandbox.sh b/tests/hardening/99.5.8_ssh_sys_sandbox.sh new file mode 100644 index 0000000..9b48509 --- /dev/null +++ b/tests/hardening/99.5.8_ssh_sys_sandbox.sh @@ -0,0 +1,22 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^UsePrivilegeSeparation[[:space:]]*sandbox is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/hardening/99.5.9_ssh_loglevel.sh b/tests/hardening/99.5.9_ssh_loglevel.sh new file mode 100644 index 0000000..b51b66e --- /dev/null +++ b/tests/hardening/99.5.9_ssh_loglevel.sh @@ -0,0 +1,22 @@ +# run-shellcheck +test_audit() { + describe Running on blank host + register_test retvalshouldbe 1 + register_test contain "openssh-server is installed" + # shellcheck disable=2154 + run blank /opt/debian-cis/bin/hardening/"${script}".sh --audit-all + + describe Correcting situation + # `apply` performs a service reload after each change in the config file + # the service needs to be started for the reload to succeed + service ssh start + # if the audit script provides "apply" option, enable and run it + sed -i 's/disabled/enabled/' /opt/debian-cis/etc/conf.d/"${script}".cfg + /opt/debian-cis/bin/hardening/"${script}".sh || true + + describe Checking resolved state + register_test retvalshouldbe 0 + register_test contain "[ OK ] ^LogLevel[[:space:]]*VERBOSE is present in /etc/ssh/sshd_config" + run resolved /opt/debian-cis/bin/hardening/"${script}".sh --audit-all +} + diff --git a/tests/launch_tests.sh b/tests/launch_tests.sh index 5793841..5e41633 100755 --- a/tests/launch_tests.sh +++ b/tests/launch_tests.sh @@ -4,8 +4,6 @@ set -e # stop on undefined variable set -u -# debug -#set -x mytmpdir=$(mktemp -d -t debian-cis-test.XXXXXX) totalerrors=255