harbian-audit/bin/hardening.sh

378 lines
13 KiB
Bash
Executable File

#!/bin/bash
#
# harbian audit Debian 9 / CentOS Hardening
# Authors : Thibault Dewailly, OVH <thibault.dewailly@corp.ovh.com>
# Authors : Samson wen, Samson <sccxboy@gmail.com>
#
# Main script : Execute hardening considering configuration
#
LONG_SCRIPT_NAME=$(basename $0)
SCRIPT_NAME=${LONG_SCRIPT_NAME%.sh}
DISABLED_CHECKS=0
PASSED_CHECKS=0
FAILED_CHECKS=0
TOTAL_CHECKS=0
TOTAL_TREATED_CHECKS=0
AUDIT=0
APPLY=0
AUDIT_ALL=0
AUDIT_ALL_ENABLE_PASSED=0
ALLOW_SERVICE_LIST=0
SET_HARDENING_LEVEL=0
SUDO_MODE=''
INIT_G_CONFIG=0
FINAL_G_CONFIG=0
DONT_BY_UID_G_CONFIG=127
usage() {
cat << EOF
$LONG_SCRIPT_NAME <RUN_MODE> [OPTIONS], where RUN_MODE is one of:
--help -h
Show this help
--init
Initialize the global configuration file(/etc/default/cis-hardening) based
on the release version number.
--apply
Apply hardening for enabled scripts.
Beware that NO confirmation is asked whatsoever, which is why you're warmly
advised to use --audit before, which can be regarded as a dry-run mode.
--audit
Audit configuration for enabled scripts.
No modification will be made on the system, we'll only report on your system
compliance for each script.
--audit-all
Same as --audit, but for *all* scripts, even disabled ones.
This is a good way to peek at your compliance level if all scripts were enabled,
and might be a good starting point.
--audit-all-enable-passed
Same as --audit-all, but in addition, will *modify* the individual scripts
configurations to enable those which passed for your system.
This is an easy way to enable scripts for which you're already compliant.
However, please always review each activated script afterwards, this option
should only be regarded as a way to kickstart a configuration from scratch.
Don't run this if you have already customized the scripts enable/disable
configurations, obviously.
--set-hardening-level <level>
Modifies the configuration to enable/disable tests given an hardening level,
between 1 to 5. Don't run this if you have already customized the scripts
enable/disable configurations.
1: very basic policy, failure to pass tests at this level indicates severe
misconfiguration of the machine that can have a huge security impact
2: basic policy, some good practice rules that, once applied, shouldn't
break anything on most systems
3: best practices policy, passing all tests might need some configuration
modifications (such as specific partitioning, etc.)
4: high security policy, passing all tests might be time-consuming and
require high adaptation of your workflow
5: placebo, policy rules that might be very difficult to apply and maintain,
with questionable security benefits, need to confirm manually
--allow-service <service>
Use with --set-hardening-level.
Modifies the policy to allow a certain kind of services on the machine, such
as http, mail, etc. Can be specified multiple times to allow multiple services.
Use --allow-service-list to get a list of supported services.
Example:
bin/hardening.sh --set-hardening-level 5 --allow-service dns,http
--final
The final action that needs to be done when all repairs are completed. The action items are:
1. Use passwd to change the password of the regular and root user to update the user
password strength and robustness;
2. Aide reinitializes.
--dont-auditd-by-uid <1/0>
Auditd rules do not use uid parameter, for all user to auditd. If set 1 will not use uid, else if
set 0 will use uid. Default is 0.
OPTIONS:
--only <test_number>
Modifies the RUN_MODE to only work on the test_number script.
Can be specified multiple times to work only on several scripts.
The test number is the numbered prefix of the script,
i.e. the test number of 1.2_script_name.sh is 1.2.
--sudo
This option lets you audit your system as a normal user, but allows sudo
escalation to gain read-only access to root files. Note that you need to
provide a sudoers file with NOPASSWD option in /etc/sudoers.d/ because
the '-n' option instructs sudo not to prompt for a password.
Finally note that '--sudo' mode only works for audit mode.
EOF
exit 0
}
if [ $# = 0 ]; then
usage
fi
declare -a TEST_LIST ALLOWED_SERVICES_LIST
# Arguments parsing
while [[ $# > 0 ]]; do
ARG="$1"
case $ARG in
--audit)
AUDIT=1
;;
--audit-all)
AUDIT_ALL=1
;;
--audit-all-enable-passed)
AUDIT_ALL_ENABLE_PASSED=1
;;
--apply)
APPLY=1
;;
--allow-service-list)
ALLOW_SERVICE_LIST=1
;;
--allow-service)
ALLOWED_SERVICES_LIST[${#ALLOWED_SERVICES_LIST[@]}]="$2"
shift
;;
--set-hardening-level)
SET_HARDENING_LEVEL="$2"
shift
;;
--only)
TEST_LIST[${#TEST_LIST[@]}]="$2"
shift
;;
--sudo)
SUDO_MODE='--sudo'
;;
-h|--help)
usage
;;
--init)
INIT_G_CONFIG=1
;;
--final)
FINAL_G_CONFIG=1
;;
--dont-auditd-by-uid)
DONT_BY_UID_G_CONFIG="$2"
shift
;;
*)
usage
;;
esac
shift
done
# 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
# For --dont-auditd-by-uid
if [ -z "$DONT_BY_UID_G_CONFIG" ]; then
usage
else
if [ $DONT_BY_UID_G_CONFIG -ne 127 ]; then
if [ $DONT_BY_UID_G_CONFIG -eq 1 ]; then
echo "Set dont use uid for auditd rules"
sed -i 's/^DONT_AUDITD_BY_UID=.*/DONT_AUDITD_BY_UID=1/g' $CIS_ROOT_DIR/etc/hardening.cfg
else
echo "Set use uid for auditd rules"
sed -i 's/^DONT_AUDITD_BY_UID=.*/DONT_AUDITD_BY_UID=0/g' $CIS_ROOT_DIR/etc/hardening.cfg
fi
exit 0
fi
fi
[ -r $CIS_ROOT_DIR/lib/constants.sh ] && . $CIS_ROOT_DIR/lib/constants.sh
[ -r $CIS_ROOT_DIR/etc/hardening.cfg ] && . $CIS_ROOT_DIR/etc/hardening.cfg
[ -r $CIS_ROOT_DIR/lib/common.sh ] && . $CIS_ROOT_DIR/lib/common.sh
[ -r $CIS_ROOT_DIR/lib/utils.sh ] && . $CIS_ROOT_DIR/lib/utils.sh
# For --init
if [ $INIT_G_CONFIG -eq 1 ]; then
if [ -r /etc/redhat-release ]; then
info "This OS is redhat/CentOS."
sed -i 's/^OS_RELEASE=.*/OS_RELEASE=2/g' /etc/default/cis-hardening
. /etc/default/cis-hardening
elif [ -r /etc/debian_version ]; then
info "This OS is Debian."
:
else
crit "This OS not support!"
exit 128
fi
exit 0
fi
if [ $OS_RELEASE -eq 1 ]; then
info "Start auditing for Debian."
elif [ $OS_RELEASE -eq 2 ]; then
info "Start auditing for redhat/CentOS."
else
crit "This OS not support!"
exit 128
fi
# For --final
if [ $FINAL_G_CONFIG -eq 1 ]; then
# Reset passwd for regular and root user
USERSNAME=$(cat /etc/passwd | awk -F':' '{if($3>=1000 && $3<65534) {print $1}}')
for USER in $USERSNAME; do
RESETCONTIN="n"
read -p "Will password of $USER be reset, are you sure to continue?(y/N)" RESETCONTIN
if [ "$RESETCONTIN" == "y" ]; then
passwd $USER
else
continue
fi
done
RESETCONTIN="n"
read -p "Will password of root be reset, are you sure to continue?(y/N)" RESETCONTIN
if [ "$RESETCONTIN" == "y" ]; then
passwd
fi
# Reinit aide database
info "Will reinitialize the AIDE database"
if [ $OS_RELEASE -eq 1 ]; then
aideinit
elif [ $OS_RELEASE -eq 2 ]; then
aide --init
mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
fi
exit 0
fi
# If --allow-service-list is specified, don't run anything, just list the supported services
if [ "$ALLOW_SERVICE_LIST" = 1 ] ; then
declare -a HARDENING_EXCEPTIONS_LIST
for SCRIPT in $(ls $CIS_ROOT_DIR/bin/hardening/*.sh -v); do
template=$(grep "^HARDENING_EXCEPTION=" "$SCRIPT" | cut -d= -f2)
[ -n "$template" ] && HARDENING_EXCEPTIONS_LIST[${#HARDENING_EXCEPTIONS_LIST[@]}]="$template"
done
echo "Supported services are: "$(echo "${HARDENING_EXCEPTIONS_LIST[@]}" | tr " " "\n" | sort -u | tr "\n" " ")
exit 0
fi
# If --set-hardening-level is specified, don't run anything, just apply config for each script
if [ -n "$SET_HARDENING_LEVEL" -a "$SET_HARDENING_LEVEL" != 0 ] ; then
if ! grep -q "^[12345]$" <<< "$SET_HARDENING_LEVEL" ; then
echo "Bad --set-hardening-level specified ('$SET_HARDENING_LEVEL'), expected 1 to 5"
exit 1
fi
for SCRIPT in $(ls $CIS_ROOT_DIR/bin/hardening/*.sh -v); do
SCRIPT_BASENAME=$(basename $SCRIPT .sh)
script_level=$(grep "^HARDENING_LEVEL=" "$SCRIPT" | cut -d= -f2)
if [ -z "$script_level" ] ; then
echo "The script $SCRIPT_BASENAME doesn't have a hardening level, configuration untouched for it"
continue
fi
wantedstatus=disabled
[ "$script_level" -le "$SET_HARDENING_LEVEL" ] && wantedstatus=enabled
sed -i -re "s/^status=.+/status=$wantedstatus/" $CIS_ROOT_DIR/etc/conf.d/$SCRIPT_BASENAME.cfg
# If use --allow-service to set, add ISEXCEPTION=1 to SCRTPT_BASENAME.cfg
template=$(grep "^HARDENING_EXCEPTION=" "$SCRIPT" | cut -d= -f2)
if [ -n "$template" -a $(echo "${ALLOWED_SERVICES_LIST[@]}" | grep -wc "$template") -eq 1 ]; then
sed -i "s/^ISEXCEPTION=./ISEXCEPTION=1/" $CIS_ROOT_DIR/etc/conf.d/$SCRIPT_BASENAME.cfg
fi
done
echo "Configuration modified to enable scripts for hardening level at or below $SET_HARDENING_LEVEL"
exit 0
fi
# Parse every scripts and execute them in the required mode
for SCRIPT in $(ls $CIS_ROOT_DIR/bin/hardening/*.sh -v); do
if [ ${#TEST_LIST[@]} -gt 0 ] ; then
# --only X has been specified at least once, is this script in my list ?
SCRIPT_PREFIX=$(grep -Eo '^[0-9.]+' <<< "$(basename $SCRIPT)")
SCRIPT_PREFIX_RE=$(sed -e 's/\./\\./g' <<< "$SCRIPT_PREFIX")
if ! grep -qEw "^$SCRIPT_PREFIX_RE" <<< "${TEST_LIST[@]}"; then
# not in the list
continue
fi
fi
info "Treating $SCRIPT"
if [ $AUDIT = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit $SUDO_MODE"
$SCRIPT --audit $SUDO_MODE
elif [ $AUDIT_ALL = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE"
$SCRIPT --audit-all $SUDO_MODE
elif [ $AUDIT_ALL_ENABLE_PASSED = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE"
$SCRIPT --audit-all $SUDO_MODE
elif [ $APPLY = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT"
$SCRIPT
fi
SCRIPT_EXITCODE=$?
debug "Script $SCRIPT finished with exit code $SCRIPT_EXITCODE"
case $SCRIPT_EXITCODE in
0)
debug "$SCRIPT passed"
PASSED_CHECKS=$((PASSED_CHECKS+1))
if [ $AUDIT_ALL_ENABLE_PASSED = 1 ] ; then
SCRIPT_BASENAME=$(basename $SCRIPT .sh)
sed -i -re 's/^status=.+/status=enabled/' $CIS_ROOT_DIR/etc/conf.d/$SCRIPT_BASENAME.cfg
info "Status set to enabled in $CIS_ROOT_DIR/etc/conf.d/$SCRIPT_BASENAME.cfg"
fi
;;
1)
debug "$SCRIPT failed"
FAILED_CHECKS=$((FAILED_CHECKS+1))
;;
2)
debug "$SCRIPT is disabled"
DISABLED_CHECKS=$((DISABLED_CHECKS+1))
;;
esac
if [ $SCRIPT_EXITCODE -eq 3 ]; then
{
warn "$SCRIPT maybe is nonexist service or nonexist file in this system"
TOTAL_CHECKS=$((TOTAL_CHECKS+1))
}
else
{
TOTAL_CHECKS=$((TOTAL_CHECKS+1))
}
fi
done
TOTAL_TREATED_CHECKS=$((TOTAL_CHECKS-DISABLED_CHECKS))
printf "%40s\n" "################### SUMMARY ###################"
printf "%30s %s\n" "Total Available Checks :" "$TOTAL_CHECKS"
printf "%30s %s\n" "Total Runned Checks :" "$TOTAL_TREATED_CHECKS"
printf "%30s [ %7s ]\n" "Total Passed Checks :" "$PASSED_CHECKS/$TOTAL_TREATED_CHECKS"
printf "%30s [ %7s ]\n" "Total Failed Checks :" "$FAILED_CHECKS/$TOTAL_TREATED_CHECKS"
printf "%30s %.2f %%\n" "Enabled Checks Percentage :" "$( echo "($TOTAL_TREATED_CHECKS/$TOTAL_CHECKS) * 100" | bc -l)"
if [ $TOTAL_TREATED_CHECKS != 0 ]; then
printf "%30s %.2f %%\n" "Conformity Percentage :" "$( echo "($PASSED_CHECKS/$TOTAL_TREATED_CHECKS) * 100" | bc -l)"
else
printf "%30s %s %%\n" "Conformity Percentage :" "N.A" # No check runned, avoid division by 0
fi