From 0871a25bf76aca1ff72a8defb8f9532afd935d78 Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
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
-- 
2.49.0


From 8e0f22da8f646df2a888b759597109b1b910d892 Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
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 "$@"
 
-- 
2.49.0


From 11ad1b471a7aecdb30b60e8390fa26328f86f7d8 Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
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=$?
-- 
2.49.0


From f5180a5e57f36c3b2687f7e3c18a52df03870c57 Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
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
 }
 
 ################################################################################
-- 
2.49.0


From 5078363348241ede1a9b75d1cf0e67d8313cac28 Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
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
-- 
2.49.0