debian-cis/bin/hardening.sh
2017-06-13 18:30:29 +02:00

256 lines
8.9 KiB
Bash
Executable File

#!/bin/bash
#
# CIS Debian 7/8 Hardening
# Authors : Thibault Dewailly, OVH <thibault.dewailly@corp.ovh.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
CIS_ROOT_DIR=''
usage() {
cat << EOF
$LONG_SCRIPT_NAME <RUN_MODE> [OPTIONS], where RUN_MODE is one of:
--help -h
Show this help
--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
--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.
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.
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
;;
-h|--help)
usage
;;
*)
usage
;;
esac
shift
done
# Source Root Dir Parameter
if [ ! -r /etc/default/cis-hardening ]; then
echo "There is no /etc/default/cis-hardening file, cannot source CIS_ROOT_DIR variable, aborting"
exit 128
else
. /etc/default/cis-hardening
if [ -z $CIS_ROOT_DIR ]; then
echo "No CIS_ROOT_DIR variable, aborting"
exit 128
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
# 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
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"
$SCRIPT --audit
elif [ $AUDIT_ALL = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all"
$SCRIPT --audit-all
elif [ $AUDIT_ALL_ENABLE_PASSED = 1 ]; then
debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all"
$SCRIPT --audit-all
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
TOTAL_CHECKS=$((TOTAL_CHECKS+1))
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