diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d63185a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.sh] +indent_size = 4 +indent_style = tab diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index 3a9a88b..b337e44 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -42,6 +42,24 @@ MY_IP="94.199.214.20" # After this point, no editing is required. start=$(date +%s) +# Dependencies of this script. Simple array with the following structure: +# (command package [...]) +dependencies=( +"sudo" "app-admin/sudo" +"python" "dev-lang/python:3.8" +"fail2ban-client" "net-analyzer/fail2ban" +"cut" "sys-apps/coreutils" +"id" "sys-apps/coreutils" +"sort" "sys-apps/coreutils" +"touch" "sys-apps/coreutils" +"tr" "sys-apps/coreutils" +"uniq" "sys-apps/coreutils" +"grep" "sys-apps/grep" +"sponge" "sys-apps/moreutils" +"netstat" "sys-apps/net_tools" +"getopt" "sys-apps/util-linux" +) + # These suffixes must be appended to the respective addresses and subnets. suffix8="/8" suffix16="/16" @@ -61,18 +79,40 @@ bold="$(printf '\033[1m')" reset="$(printf '\033[0m')" # Clean up when the script exits. -trap 'sudo -k; rm -r ${tmpdir}' EXIT +trap 'sudo -k 2>/dev/null >&2; rm -r ${tmpdir}' EXIT -function check_installed() { +function is_installed() { + which "${1}" 2>/dev/null >&2 + return $? +} + +function print_missing_dependency() { local command="$1" local package="$2" - which "${command}" 2>/dev/null >&2 - local result=$? - if [[ "${result}" -ne 0 ]] ; then - echo "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}." - exit 1 - fi + echo "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}." >&2 +} + +function check_dependencies() { + local arraylength=${#dependencies[@]} + local res= + local command= + local package= + # 0: true, all installed; 1: false, at least one command/package missing + local all_installed=0 + + for (( i=0; i<${arraylength}; i+=2 )) ; do + command="${dependencies[$i]}" + package="${dependencies[$i+1]}" + is_installed "${command}" "${package}" + res=$? + if [[ $res -ne 0 ]] ; then + print_missing_dependency "${command}" "${package}" + all_installed=1 + fi + done + + return ${all_installed} } function print_help() { @@ -96,6 +136,12 @@ Usage: $(basename $0) -d FILE [OPTION...] comma-separated values; defaults to 'CN' (China). + -e, --dependencies Check if all required dependencies are + installed. If all dependencies are found, + exits with code 0. Otherwise, missing + dependencies are printed to stderr and + the program terminates with code 1. + -j, --jail=JAIL Specify the JAIL to use for banning the IP addresses. Defaults to 'apache-auth'. @@ -152,7 +198,7 @@ function filter() { } function parse_command_line_args() { - TEMP=$(getopt -o 'a::,c:,d:,j:,n:,p:,h' -l 'auto::,country:,database:,jail:,netmask:,port:,help' -- "$@") + TEMP=$(getopt -o 'a::,c:,d:,e,j:,n:,p:,h' -l 'auto::,country:,database:,dependencies,jail:,netmask:,port:,help' -- "$@") if [ $? -ne 0 ] ; then echo 'Error parsing command line options. Terminating. Invoke with --help for help.' >&2 @@ -191,6 +237,10 @@ function parse_command_line_args() { database="$2" shift ;; + '-e'|'--dependencies') + check_dependencies + exit $? + ;; '-j'|'--jail') jail="$2" shift @@ -408,19 +458,11 @@ port=443 parse_command_line_args "$@" -check_installed "sudo" "app-admin/sudo" -check_installed "python" "dev-lang/python:3.8" -check_installed "fail2ban-client" "net-analyzer/fail2ban" -check_installed "cut" "sys-apps/coreutils" -check_installed "id" "sys-apps/coreutils" -check_installed "sort" "sys-apps/coreutils" -check_installed "touch" "sys-apps/coreutils" -check_installed "tr" "sys-apps/coreutils" -check_installed "uniq" "sys-apps/coreutils" -check_installed "grep" "sys-apps/grep" -check_installed "sponge" "sys-apps/moreutils" -check_installed "netstat" "sys-apps/net_tools" -check_installed "getopt" "sys-apps/util-linux" +check_dependencies +dependencies_ok=$? +if [[ ${dependencies_ok} -ne 0 ]] ; then + exit ${dependencies_ok} +fi # List already banned addresses in the chosen jail banned="$(exec_as_root fail2ban-client get "${jail}" banip)"