From 0871a25bf76aca1ff72a8defb8f9532afd935d78 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 24 Nov 2020 21:28:15 +0100 Subject: [PATCH 1/5] Add the possibility of using a config file instead of CLI parameters. --- ddos-mitigator.conf | 30 +++++++++++++++++++++++++++ ddos-mitigator.sh | 49 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 ddos-mitigator.conf diff --git a/ddos-mitigator.conf b/ddos-mitigator.conf new file mode 100644 index 0000000..a7ebc54 --- /dev/null +++ b/ddos-mitigator.conf @@ -0,0 +1,30 @@ +# Example configuration file for ddos-mitigator.sh. +# PLEASE TAKE CARE not to put any whitespace around the '=' signs, as this file is directly sourced by the script +# file and this needs to conform to the BASH syntax. Also, make sure to declare the COUNTRIES variable with the +# correct array syntax: COUNTRIES=("AA" "BB" "CC"), or to comment it out altogether. + +# The path to the GeoIP2 database file (must be either country or city database). This parameter is mandatory. If it is +# not specified here, it must be given on the command line (through the -d option). +DATABASE_FILE="/path/to/geoip/country-or-city-database.mmdb" + +# Enable the autopilot for automatically banning IP addresses of the desired countries (see also COUNTRIES option). +# Only ban IP addresses with at least AUTOPILOT current connections. If the value is not specified or 0, don't +# automatically ban IP addresses, but run in interactive mode. +AUTOPILOT="1" + +# Defines the subnet size in bytes to be analyzed. Valid values are: +# - 8 for class A networks (X.0.0.0/8) +# - 16 for class B networks (X.X.0.0/16) +# - 24 for class C networks (X.X.X.0/24) +# - 32 for class D networks (X.X.X.X/32) +# If not specified, run in interactive mode and prompt for the netmask size. +NETMASK="8" + +# The country-codes to block as an array. Defaults to "CN" (China). +#COUNTRIES=("CN" "HK" "TW") + +# Specify the JAIL to use for banning the IP addresses. Defaults to 'apache-auth'. +#JAIL="apache-auth" + +# The desired port to monitor. Defaults to 443 (https). +#PORT="443" diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index 8270584..01689ea 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -32,10 +32,7 @@ # # ################################################################################ -# Set the host's own IP address. So far, only an IPv4 address is supported. -MY_IP="94.199.214.20" - -# After this point, no editing is required. +# Store the start time; this enables us to output the total runtime at the end. start="$(date +%s)" # Dependencies of this script. Simple array with the following structure: @@ -139,6 +136,13 @@ Usage: $(basename $0) -d FILE [OPTION...] printed to stderr and the program terminates with exit code 1. + -f, --config-file=FILENAME Specify the full path to the configuration + file. If omitted, all options are read + from the command line. + Any parameter specified on the command + line takes precedence over configuration + option read from the file. + -j, --jail=JAIL Specify the JAIL to use for banning the IP addresses. Defaults to 'apache-auth'. @@ -194,8 +198,30 @@ function filter() { mv "${filtered}" "${file}" } +function parse_config_file() { + source "${configfile}" + if [[ -z "${autopilot+x}" ]]; then + autopilot="${AUTOPILOT}" + fi + if [[ -z "${bancountries}" ]]; then + bancountries=()${COUNTRIES[@]}) + fi + if [[ -z "${database+x}" ]]; then + database="${DATABASE_FILE}" + fi + if [[ -z "${jail+x}" ]]; then + jail="${JAIL}" + fi + if [[ -z "${netmask+x}" ]]; then + netmask="${NETMASK}" + fi + if [[ -z "${port+x}" ]]; then + port="${PORT}" + fi +} + function parse_command_line_args() { - TEMP=$(getopt -o 'a::,c:,d:,e,j:,n:,p:,h' -l 'auto::,country:,database:,dependencies,jail:,netmask:,port:,help' -- "$@") + TEMP=$(getopt -o 'a::,c:,d:,e,f:,j:,n:,p:,h' -l 'auto::,country:,database:,dependencies,config-file:,jail:,netmask:,port:,help' -- "$@") if [ $? -ne 0 ]; then echo 'Error parsing command line options. Terminating. Invoke with --help for help.' >&2 @@ -238,6 +264,14 @@ function parse_command_line_args() { check_dependencies exit $? ;; + '-f' | '--config-file') + configfile="${2}" + if [[ ! -f "${configfile}" || ! -r "${configfile}" ]]; then + echo "Can not read configuration file '${2}'. Invoke with --help for help." >&2 + exit 1 + fi + shift + ;; '-j' | '--jail') jail="${2}" shift @@ -283,6 +317,11 @@ function parse_command_line_args() { shift done + # If the config file option is set, parse the config file. + if [[ ! -z ${configfile+x} ]]; then + parse_config_file + fi + if [[ -z "${database}" ]]; then echo "No GeoIP database specified. Invoke with --help for more information." >&2 exit 1 From 8e0f22da8f646df2a888b759597109b1b910d892 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 24 Nov 2020 20:42:11 +0100 Subject: [PATCH 2/5] Make it actually work. To do: validate input from config file. --- ddos-mitigator.conf | 2 +- ddos-mitigator.sh | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/ddos-mitigator.conf b/ddos-mitigator.conf index a7ebc54..abcd59e 100644 --- a/ddos-mitigator.conf +++ b/ddos-mitigator.conf @@ -10,7 +10,7 @@ DATABASE_FILE="/path/to/geoip/country-or-city-database.mmdb" # Enable the autopilot for automatically banning IP addresses of the desired countries (see also COUNTRIES option). # Only ban IP addresses with at least AUTOPILOT current connections. If the value is not specified or 0, don't # automatically ban IP addresses, but run in interactive mode. -AUTOPILOT="1" +AUTOPILOT="0" # Defines the subnet size in bytes to be analyzed. Valid values are: # - 8 for class A networks (X.0.0.0/8) diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index 01689ea..a5b4b80 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -198,24 +198,42 @@ function filter() { mv "${filtered}" "${file}" } +function set_default_values() { + if [[ -z "${autopilot}" ]]; then + autopilot=0 + fi + if [[ -z "${netmask}" ]]; then + netmask=0 + fi + if [[ -z "${jail}" ]]; then + jail="apache-auth" + fi + if [[ -z "${bancountries}" ]]; then + bancountries=("CN") + fi + if [[ -z "${port}" ]]; then + port=443 + fi +} + function parse_config_file() { source "${configfile}" - if [[ -z "${autopilot+x}" ]]; then + if [[ -z "${autopilot}" ]]; then autopilot="${AUTOPILOT}" fi if [[ -z "${bancountries}" ]]; then - bancountries=()${COUNTRIES[@]}) + bancountries=(${COUNTRIES[@]}) fi - if [[ -z "${database+x}" ]]; then + if [[ -z "${database}" ]]; then database="${DATABASE_FILE}" fi - if [[ -z "${jail+x}" ]]; then + if [[ -z "${jail}" ]]; then jail="${JAIL}" fi - if [[ -z "${netmask+x}" ]]; then + if [[ -z "${netmask}" ]]; then netmask="${NETMASK}" fi - if [[ -z "${port+x}" ]]; then + if [[ -z "${port}" ]]; then port="${PORT}" fi } @@ -331,6 +349,9 @@ function parse_command_line_args() { echo "Database '${database}' is not accessible." >&2 exit 1 fi + + # Here, we set the default values for all options that have not been set yet. + set_default_values } ################################################################################ @@ -483,12 +504,12 @@ banlist="${tmpdir}/banlist.txt" touch "${banlist}" # Parse the command line options -autopilot=0 -netmask=0 -jail="apache-auth" -bancountries=("CN") +autopilot= +netmask= +jail= +bancountries= database= -port=443 +port= parse_command_line_args "$@" From 11ad1b471a7aecdb30b60e8390fa26328f86f7d8 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 24 Nov 2020 21:28:28 +0100 Subject: [PATCH 3/5] Change the way default parameters are set. --- ddos-mitigator.sh | 76 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index a5b4b80..abfdf31 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -256,10 +256,10 @@ function parse_command_line_args() { '') autopilot=1 ;; - *[!0-9]*) - echo "Invalid argument for parameter 'auto': '${2}'. Invoke with --help for help." >&2 - exit 1 - ;; + # *[!0-9]*) + # echo "Invalid argument for parameter 'auto': '${2}'. Invoke with --help for help." >&2 + # exit 1 + # ;; *) autopilot="$2" ;; @@ -268,10 +268,10 @@ function parse_command_line_args() { ;; '-c' | '--country') IFS=',' read -ra bancountries <<<"${2}" - if [[ -z ${bancountries[@]// /} ]]; then - echo "Invalid argument for parameter 'country': '${2}'. Invoke with --help for help." >&2 - exit 1 - fi + # if [[ -z ${bancountries[@]// /} ]]; then + # echo "Invalid argument for parameter 'country': '${2}'. Invoke with --help for help." >&2 + # exit 1 + # fi shift ;; '-d' | '--database') @@ -284,10 +284,6 @@ function parse_command_line_args() { ;; '-f' | '--config-file') configfile="${2}" - if [[ ! -f "${configfile}" || ! -r "${configfile}" ]]; then - echo "Can not read configuration file '${2}'. Invoke with --help for help." >&2 - exit 1 - fi shift ;; '-j' | '--jail') @@ -296,21 +292,20 @@ function parse_command_line_args() { ;; '-n' | '--netmask') case "${2}" in - '1' | '8') + '1') netmask=8 ;; - '2' | '16') + '2') netmask=16 ;; - '3' | '24') + '3') netmask=24 ;; - '4' | '32') + '4') netmask=32 ;; *) - echo "Invalid argument for parameter 'netmask': '${2}'. Invoke with --help for help." >&2 - exit 1 + netmask="${2}" ;; esac shift @@ -337,21 +332,57 @@ function parse_command_line_args() { # If the config file option is set, parse the config file. if [[ ! -z ${configfile+x} ]]; then + if [[ ! -f "${configfile}" || ! -r "${configfile}" ]]; then + echo "Can not read configuration file '${2}'. Invoke with --help for help." >&2 + exit 1 + fi + parse_config_file fi + # Here, we set the default values for all options that have not been set yet. + set_default_values +} + +function validate_parameter_values() { + # Autopilot + case "${autopilot}" in + *[!0-9]*) + echo "Invalid value argument for parameter 'auto' / 'AUTOPILOT': '${autopilot}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + esac + + # Countries + if [[ -z ${bancountries[@]// /} ]]; then + echo "Invalid argument for parameter 'country' / 'COUNTRIES': '${bancountries[*]}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + fi + + # Netmask + case "${netmask}" in + '8' | '16' | '24' | '32') + # Everything OK. + ;; + *) + echo "Invalid argument for parameter 'netmask': '${2}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + esac + + # GeoIP-Database if [[ -z "${database}" ]]; then echo "No GeoIP database specified. Invoke with --help for more information." >&2 exit 1 fi - if [[ ! -r "${database}" ]]; then + if [[ ! -f "${database}" || ! -r "${database}" ]]; then echo "Database '${database}' is not accessible." >&2 exit 1 fi - - # Here, we set the default values for all options that have not been set yet. - set_default_values } ################################################################################ @@ -512,6 +543,7 @@ database= port= parse_command_line_args "$@" +validate_parameter_values check_dependencies dependencies_ok=$? From f5180a5e57f36c3b2687f7e3c18a52df03870c57 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 24 Nov 2020 21:15:37 +0100 Subject: [PATCH 4/5] Improve parameter validation. --- ddos-mitigator.sh | 87 +++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index abfdf31..adcf837 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -256,10 +256,6 @@ function parse_command_line_args() { '') autopilot=1 ;; - # *[!0-9]*) - # echo "Invalid argument for parameter 'auto': '${2}'. Invoke with --help for help." >&2 - # exit 1 - # ;; *) autopilot="$2" ;; @@ -268,10 +264,6 @@ function parse_command_line_args() { ;; '-c' | '--country') IFS=',' read -ra bancountries <<<"${2}" - # if [[ -z ${bancountries[@]// /} ]]; then - # echo "Invalid argument for parameter 'country': '${2}'. Invoke with --help for help." >&2 - # exit 1 - # fi shift ;; '-d' | '--database') @@ -345,34 +337,6 @@ function parse_command_line_args() { } function validate_parameter_values() { - # Autopilot - case "${autopilot}" in - *[!0-9]*) - echo "Invalid value argument for parameter 'auto' / 'AUTOPILOT': '${autopilot}'." >&2 - echo "Invoke with --help for help." >&2 - exit 1 - ;; - esac - - # Countries - if [[ -z ${bancountries[@]// /} ]]; then - echo "Invalid argument for parameter 'country' / 'COUNTRIES': '${bancountries[*]}'." >&2 - echo "Invoke with --help for help." >&2 - exit 1 - fi - - # Netmask - case "${netmask}" in - '8' | '16' | '24' | '32') - # Everything OK. - ;; - *) - echo "Invalid argument for parameter 'netmask': '${2}'." >&2 - echo "Invoke with --help for help." >&2 - exit 1 - ;; - esac - # GeoIP-Database if [[ -z "${database}" ]]; then echo "No GeoIP database specified. Invoke with --help for more information." >&2 @@ -383,6 +347,57 @@ function validate_parameter_values() { echo "Database '${database}' is not accessible." >&2 exit 1 fi + + # Autopilot + case "${autopilot}" in + *[!0-9]*) + echo "Invalid value for parameter 'auto' / 'AUTOPILOT': '${autopilot}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + esac + + # Countries + if [[ -z ${bancountries[@]// /} ]]; then + echo "Invalid value for parameter 'country' / 'COUNTRIES': '${bancountries[*]}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + fi + + # Jail + if [[ -z "${jail}" ]]; then + echo "Invalid value for parameter 'jail' / 'JAIL': '${jail}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + fi + + # Netmask + case "${netmask}" in + '0' | '8' | '16' | '24' | '32') + # Everything OK. + ;; + *) + echo "Invalid value for parameter 'netmask': '${2}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + esac + + # Port + case "${port}" in + *[!0-9]*) + echo "Invalid value for parameter 'port' / 'PORT': '${autopilot}'." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + esac + if [[ ${port} -lt 0 || ${port} -gt 65535 ]]; then + echo "Invalid value for parameter 'port' / 'PORT': '${autopilot}'." >&2 + echo "Value must be between 0 ... 65535 (inclusive)." >&2 + echo "Invoke with --help for help." >&2 + exit 1 + ;; + fi } ################################################################################ From 5078363348241ede1a9b75d1cf0e67d8313cac28 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 24 Nov 2020 21:21:44 +0100 Subject: [PATCH 5/5] Harmonize parameter processing order. --- ddos-mitigator.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ddos-mitigator.sh b/ddos-mitigator.sh index adcf837..f90c0e1 100755 --- a/ddos-mitigator.sh +++ b/ddos-mitigator.sh @@ -202,14 +202,14 @@ function set_default_values() { if [[ -z "${autopilot}" ]]; then autopilot=0 fi - if [[ -z "${netmask}" ]]; then - netmask=0 + if [[ -z "${bancountries}" ]]; then + bancountries=("CN") fi if [[ -z "${jail}" ]]; then jail="apache-auth" fi - if [[ -z "${bancountries}" ]]; then - bancountries=("CN") + if [[ -z "${netmask}" ]]; then + netmask=0 fi if [[ -z "${port}" ]]; then port=443