2020-06-23 02:16:06 +02:00
|
|
|
#!/bin/sh
|
2020-06-23 03:07:09 +02:00
|
|
|
################################################################################
|
|
|
|
# #
|
|
|
|
# Try and prevent apache overloads by banning IP addresses that have (too) #
|
|
|
|
# many open connections. #
|
2020-09-18 14:06:30 +02:00
|
|
|
# This script uses netstat to determine the connections to a configurable port #
|
|
|
|
# on the host machine and provides automated GeoIP information retrieval based #
|
|
|
|
# the address or the /24-, /16- or /8-subnet thereof. A GeoIP city- or country #
|
|
|
|
# database must be installed separately and is provided to the script via a #
|
|
|
|
# command line parameter. #
|
|
|
|
# Addresses (or subnets) are presented to the user in order of descending #
|
|
|
|
# connection count. For each address (or subnet), the user can choose to ban #
|
|
|
|
# or ignore it. Addresses (or subnets) chosen to be banned will be blocked by #
|
|
|
|
# a configurable jail of fail2ban. #
|
2020-06-23 03:07:09 +02:00
|
|
|
# Author: Manuel Friedli, <manuel@fritteli.ch> #
|
|
|
|
# This script is licenced under the GNU General Public Licence, version 3 or #
|
|
|
|
# later. #
|
|
|
|
# #
|
|
|
|
################################################################################
|
|
|
|
|
2020-08-03 13:19:18 +02:00
|
|
|
################################################################################
|
|
|
|
# #
|
|
|
|
# Prerequisites: #
|
|
|
|
# - app-admin/sudo (`sudo`) #
|
2020-08-04 03:52:33 +02:00
|
|
|
# - dev-lang/python:3.8 (`python`) #
|
2020-08-03 13:19:18 +02:00
|
|
|
# - net-analyzer/fail2ban (`fail2ban-client`) #
|
|
|
|
# - sys-apps/coreutils (`cut`, `id`, `sort`, `touch`, `tr`, `uniq`) #
|
|
|
|
# - sys-apps/grep (`grep`) #
|
|
|
|
# - sys-apps/moreutils (`sponge`) #
|
2020-09-18 00:38:38 +02:00
|
|
|
# - sys-apps/net-tools (`netstat`) #
|
2020-08-03 13:19:18 +02:00
|
|
|
# - sys-apps/util-linux (`getopt`) #
|
|
|
|
# #
|
|
|
|
################################################################################
|
2020-08-04 17:35:41 +02:00
|
|
|
# Here goes:
|
|
|
|
# ss -On state established '( sport = :https )'|tr -s '[:blank:]'|cut -d' ' -f5
|
2020-08-03 13:19:18 +02:00
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# Set the host's own IP address. So far, only an IPv4 address is supported.
|
2020-06-23 02:16:06 +02:00
|
|
|
MY_IP="94.199.214.20"
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# After this point, no editing is required.
|
2020-08-18 23:54:37 +02:00
|
|
|
start=$(date +%s)
|
|
|
|
|
2020-09-15 23:28:48 +02:00
|
|
|
# Dependencies of this script. Simple array with the following structure:
|
|
|
|
# (command package [...])
|
|
|
|
dependencies=(
|
2020-09-15 23:34:21 +02:00
|
|
|
"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"
|
2020-09-18 00:38:38 +02:00
|
|
|
"netstat" "sys-apps/net-tools"
|
2020-09-15 23:34:21 +02:00
|
|
|
"getopt" "sys-apps/util-linux"
|
2020-09-15 23:28:48 +02:00
|
|
|
)
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# These suffixes must be appended to the respective addresses and subnets.
|
2020-08-04 04:08:24 +02:00
|
|
|
suffix8="/8"
|
|
|
|
suffix16="/16"
|
|
|
|
suffix24="/24"
|
|
|
|
suffix32="/32"
|
|
|
|
ext8=".0.0.0"
|
|
|
|
ext16=".0.0"
|
|
|
|
ext24=".0"
|
|
|
|
ext32=""
|
2020-06-23 02:16:06 +02:00
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# Define some constants to format the output in a colorful way.
|
2020-08-04 03:49:21 +02:00
|
|
|
red="$(printf '\033[38;2;255;0;43m')"
|
|
|
|
yellow="$(printf '\033[38;2;255;204;0m')"
|
|
|
|
green="$(printf '\033[38;2;0;179;89m')"
|
|
|
|
blue="$(printf '\033[38;2;0;85;255m')"
|
|
|
|
bold="$(printf '\033[1m')"
|
|
|
|
reset="$(printf '\033[0m')"
|
2020-06-23 02:16:06 +02:00
|
|
|
|
2020-07-23 21:12:48 +02:00
|
|
|
# Clean up when the script exits.
|
2020-09-15 23:28:48 +02:00
|
|
|
trap 'sudo -k 2>/dev/null >&2; rm -r ${tmpdir}' EXIT
|
|
|
|
|
|
|
|
function is_installed() {
|
|
|
|
which "${1}" 2>/dev/null >&2
|
|
|
|
return $?
|
|
|
|
}
|
2020-07-23 21:12:48 +02:00
|
|
|
|
2020-09-15 23:28:48 +02:00
|
|
|
function print_missing_dependency() {
|
2020-08-03 13:32:32 +02:00
|
|
|
local command="$1"
|
|
|
|
local package="$2"
|
2020-08-03 13:19:18 +02:00
|
|
|
|
2020-09-15 23:28:48 +02:00
|
|
|
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}
|
2020-08-03 13:32:32 +02:00
|
|
|
}
|
2020-07-23 21:12:48 +02:00
|
|
|
|
2020-08-03 13:19:18 +02:00
|
|
|
function print_help() {
|
2020-07-23 21:12:48 +02:00
|
|
|
cat <<ENDOFHELP
|
2020-08-04 03:49:21 +02:00
|
|
|
Usage: $(basename $0) -d FILE [OPTION...]
|
|
|
|
|
|
|
|
${bold}Mandatory options:${reset}
|
2020-09-15 14:06:54 +02:00
|
|
|
-d, --database=FILE The path to the GeoIP2 database file (must
|
|
|
|
be either country or city database).
|
2020-07-23 21:12:48 +02:00
|
|
|
|
2020-08-04 03:49:21 +02:00
|
|
|
${bold}Optional options:${reset}
|
2020-09-15 14:06:54 +02:00
|
|
|
-a, --auto[=LIMIT] Enable the autopilot for automatically
|
|
|
|
banning IP addresses of the desired
|
|
|
|
countries (see also -c option).
|
|
|
|
When LIMIT is given, only auto-ban IP
|
|
|
|
addresses with at least LIMIT current
|
|
|
|
connections.
|
|
|
|
When LIMIT is omitted, assume LIMIT=1.
|
|
|
|
|
|
|
|
-c, --country=COUNTRY[,COUNTRY...] The country-codes to block as a list of
|
|
|
|
comma-separated values; defaults to 'CN'
|
|
|
|
(China).
|
|
|
|
|
2020-09-15 23:28:48 +02:00
|
|
|
-e, --dependencies Check if all required dependencies are
|
|
|
|
installed. If all dependencies are found,
|
2020-09-15 23:37:42 +02:00
|
|
|
the program terminates with exit code 0.
|
|
|
|
Otherwise, missing dependencies are
|
|
|
|
printed to stderr and the program
|
|
|
|
terminates with exit code 1.
|
2020-09-15 23:28:48 +02:00
|
|
|
|
2020-09-15 14:06:54 +02:00
|
|
|
-j, --jail=JAIL Specify the JAIL to use for banning the IP
|
|
|
|
addresses.
|
|
|
|
Defaults to 'apache-auth'.
|
|
|
|
|
|
|
|
-n, --netmask=SIZE SIZE defines the subnet size in bytes to
|
|
|
|
be analyzed.
|
|
|
|
Valid values are:
|
|
|
|
- 1 or 8 for class A networks (X.0.0.0/8)
|
|
|
|
- 2 or 16 for class B networks (X.X.0.0/16)
|
|
|
|
- 3 or 24 for class C networks (X.X.X.0/24)
|
|
|
|
- 4 or 32 for class D networks (X.X.X.X/32)
|
|
|
|
|
|
|
|
-p, --port=PORT The desired port to monitor.
|
|
|
|
Defaults to 443 (https).
|
|
|
|
|
|
|
|
-h, --help Show this help message
|
2020-07-23 21:12:48 +02:00
|
|
|
|
|
|
|
Mandatory or optional arguments to long options are also mandatory or optional
|
|
|
|
for any corresponding short options.
|
|
|
|
|
2020-08-04 03:49:21 +02:00
|
|
|
When invoked without optional options, the autopilot is disabled and the
|
|
|
|
netmask SIZE is inquired interactively.
|
2020-07-23 21:12:48 +02:00
|
|
|
ENDOFHELP
|
2020-07-23 20:45:19 +02:00
|
|
|
}
|
|
|
|
|
2020-08-03 13:19:18 +02:00
|
|
|
function exec_as_root() {
|
2020-07-30 13:19:03 +02:00
|
|
|
if [[ $(id -un) == "root" ]] ; then
|
|
|
|
"$@"
|
|
|
|
else
|
|
|
|
sudo "$@"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2020-08-03 13:32:32 +02:00
|
|
|
function filter() {
|
|
|
|
# list of current connections
|
|
|
|
file="$1"
|
2020-08-04 04:08:24 +02:00
|
|
|
# subnet extension, e.g. ".0.0"
|
2020-08-03 13:32:32 +02:00
|
|
|
ext="$2"
|
2020-08-04 04:08:24 +02:00
|
|
|
# subnet suffix, e.g. "/16"
|
|
|
|
suffix="$3"
|
2020-08-03 13:32:32 +02:00
|
|
|
rm -f "${filtered}"
|
2020-09-15 13:51:03 +02:00
|
|
|
touch "${filtered}"
|
2020-08-03 13:32:32 +02:00
|
|
|
|
|
|
|
# Reject already banned addresses
|
|
|
|
while read -r -u3 address ; do
|
2020-08-04 04:08:24 +02:00
|
|
|
if [[ "${banned}" != *"${address}${ext}${suffix}"* ]] ; then
|
2020-08-03 13:32:32 +02:00
|
|
|
echo "${address}" >> "${filtered}"
|
|
|
|
else
|
2020-08-04 04:22:16 +02:00
|
|
|
echo "IGNORING ${address}${ext}${suffix}, already banned."
|
2020-08-03 13:32:32 +02:00
|
|
|
fi
|
|
|
|
done 3< "${file}"
|
|
|
|
|
|
|
|
mv "${filtered}" "${file}"
|
|
|
|
}
|
|
|
|
|
2020-08-03 13:19:18 +02:00
|
|
|
function parse_command_line_args() {
|
2020-09-15 23:28:48 +02:00
|
|
|
TEMP=$(getopt -o 'a::,c:,d:,e,j:,n:,p:,h' -l 'auto::,country:,database:,dependencies,jail:,netmask:,port:,help' -- "$@")
|
2020-07-23 20:45:19 +02:00
|
|
|
|
|
|
|
if [ $? -ne 0 ] ; then
|
|
|
|
echo 'Error parsing command line options. Terminating. Invoke with --help for help.' >&2
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
eval set -- "${TEMP}"
|
|
|
|
unset TEMP
|
|
|
|
|
|
|
|
while true ; do
|
|
|
|
case "$1" in
|
|
|
|
'-a'|'--auto')
|
2020-07-25 00:06:48 +02:00
|
|
|
case "$2" in
|
2020-07-23 20:45:19 +02:00
|
|
|
'')
|
|
|
|
autopilot=1
|
|
|
|
;;
|
|
|
|
*[!0-9]*)
|
|
|
|
echo "Invalid argument for parameter 'auto': '$2'. Invoke with --help for help." >&2
|
|
|
|
exit 1
|
|
|
|
;;
|
|
|
|
*)
|
2020-07-25 00:06:48 +02:00
|
|
|
autopilot="$2"
|
2020-07-23 20:45:19 +02:00
|
|
|
;;
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
;;
|
2020-08-03 13:32:32 +02:00
|
|
|
'-c'|'--country')
|
2020-09-15 14:06:54 +02:00
|
|
|
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
|
2020-08-03 13:32:32 +02:00
|
|
|
shift
|
|
|
|
;;
|
2020-08-04 03:49:21 +02:00
|
|
|
'-d'|'--database')
|
|
|
|
database="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-09-15 23:28:48 +02:00
|
|
|
'-e'|'--dependencies')
|
|
|
|
check_dependencies
|
|
|
|
exit $?
|
|
|
|
;;
|
2020-07-25 00:06:48 +02:00
|
|
|
'-j'|'--jail')
|
|
|
|
jail="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-07-23 20:45:19 +02:00
|
|
|
'-n'|'--netmask')
|
|
|
|
case "$2" in
|
|
|
|
'1'|'8')
|
|
|
|
netmask=8
|
|
|
|
;;
|
|
|
|
'2'|'16')
|
|
|
|
netmask=16
|
|
|
|
;;
|
|
|
|
'3'|'24')
|
|
|
|
netmask=24
|
|
|
|
;;
|
|
|
|
'4'|'32')
|
|
|
|
netmask=32
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
echo "Invalid argument for parameter 'netmask': '$2'. Invoke with --help for help." >&2
|
|
|
|
exit 1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
;;
|
2020-09-15 13:51:03 +02:00
|
|
|
'-p'|'--port')
|
|
|
|
port="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-07-23 20:45:19 +02:00
|
|
|
'-h'|'--help')
|
2020-08-03 13:19:18 +02:00
|
|
|
print_help
|
2020-07-23 20:45:19 +02:00
|
|
|
exit
|
|
|
|
;;
|
|
|
|
'--')
|
|
|
|
shift
|
|
|
|
break
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
echo "Unknown error on command line argument '$1'. Terminating." >&2
|
|
|
|
exit 1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
done
|
2020-08-04 03:49:21 +02:00
|
|
|
|
|
|
|
if [[ -z "${database}" ]] ; then
|
|
|
|
echo "No GeoIP database specified. Invoke with --help for more information." >&2
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ ! -r "${database}" ]] ; then
|
|
|
|
echo "Database '${database}' is not accessible." >&2
|
|
|
|
exit 1
|
|
|
|
fi
|
2020-07-23 20:45:19 +02:00
|
|
|
}
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
################################################################################
|
|
|
|
# Set the highlighting color for the address count. The color goes from green
|
|
|
|
# (low count) over yellow (intermediate count) to red (high count). Depending
|
|
|
|
# on the size of the subnet (/8, /16, /24 or /32), the transitions from one
|
|
|
|
# color to the next happen at different values.
|
|
|
|
################################################################################
|
2020-08-03 13:32:32 +02:00
|
|
|
function set_highlight_color() {
|
2020-06-23 02:16:06 +02:00
|
|
|
local count=$1
|
|
|
|
case "${choice}" in
|
|
|
|
"1" )
|
2020-06-23 03:07:09 +02:00
|
|
|
# /32: 0 <= green < 3 <= yellow < 5 <= red
|
2020-06-23 02:16:06 +02:00
|
|
|
if [ $count -ge 5 ] ; then
|
|
|
|
hilite="${red}"
|
|
|
|
elif [ $count -ge 3 ] ; then
|
|
|
|
hilite="${yellow}"
|
|
|
|
else
|
|
|
|
hilite="${green}"
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
"2" )
|
2020-06-23 03:07:09 +02:00
|
|
|
# /24: 0 <= green < 7 <= yellow < 13 <= red
|
2020-06-23 02:16:06 +02:00
|
|
|
if [ $count -ge 13 ] ; then
|
|
|
|
hilite="${red}"
|
|
|
|
elif [ $count -ge 7 ] ; then
|
|
|
|
hilite="${yellow}"
|
|
|
|
else
|
|
|
|
hilite="${green}"
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
"3" )
|
2020-06-23 03:07:09 +02:00
|
|
|
# /16: 0 <= green < 13 <= yellow < 25 <= red
|
2020-06-23 02:16:06 +02:00
|
|
|
if [ $count -ge 25 ] ; then
|
|
|
|
hilite="${red}"
|
|
|
|
elif [ $count -ge 13 ] ; then
|
|
|
|
hilite="${yellow}"
|
|
|
|
else
|
|
|
|
hilite="${green}"
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
"4" )
|
2020-06-23 03:07:09 +02:00
|
|
|
# /8: 0 <= green < 21 <= yellow < 49 <= red
|
2020-06-23 02:16:06 +02:00
|
|
|
if [ $count -ge 49 ] ; then
|
|
|
|
hilite="${red}"
|
|
|
|
elif [ $count -ge 21 ] ; then
|
|
|
|
hilite="${yellow}"
|
|
|
|
else
|
|
|
|
hilite="${green}"
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
* )
|
2020-06-23 03:07:09 +02:00
|
|
|
# ???: We should never get here. As a fall-back, just use no
|
|
|
|
# highlighting.
|
2020-06-23 02:16:06 +02:00
|
|
|
hilite=""
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
}
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
################################################################################
|
|
|
|
# Process the file denoted by $1. For each line in the file, the count and the
|
2020-08-04 04:08:24 +02:00
|
|
|
# address are displayed and a lookup for the IP addresses country is made in the
|
|
|
|
# GeoIP database. The user can then choose to ban or ignore the address.
|
|
|
|
# Addresses chosen to be banned are appended to the $banlist.
|
2020-06-23 03:07:09 +02:00
|
|
|
################################################################################
|
2020-08-03 13:32:32 +02:00
|
|
|
function process_file () {
|
2020-06-23 02:16:06 +02:00
|
|
|
local file="${1}"
|
|
|
|
local line=''
|
|
|
|
local count=0
|
2020-08-04 04:08:24 +02:00
|
|
|
local addronly=''
|
|
|
|
local addrwithsuffix=''
|
2020-06-23 02:16:06 +02:00
|
|
|
local banaction=''
|
|
|
|
local nline=1
|
2020-08-04 04:08:24 +02:00
|
|
|
local country=
|
2020-06-23 03:07:09 +02:00
|
|
|
# Read the contents from filedescriptor 3 (important: Don's use the
|
|
|
|
# standard filedescriptor because we need to handle user input from
|
|
|
|
# within the loop).
|
2020-06-23 02:16:06 +02:00
|
|
|
while IFS= read -r -u3 line ; do
|
|
|
|
line="$(echo "${line}" | tr -s '[:blank:]')"
|
|
|
|
count="$(echo "${line}" | cut -d' ' -f2)"
|
2020-08-04 04:08:24 +02:00
|
|
|
addronly="$(echo "${line}" | cut -d' ' -f3-)${ext}"
|
|
|
|
addrwithsuffix="${addronly}${suffix}"
|
2020-08-03 13:32:32 +02:00
|
|
|
set_highlight_color "${count}"
|
2020-08-04 17:21:45 +02:00
|
|
|
country="$("${curdir}/geoip-lookup.py" -f "${database}" "${addronly}")"
|
2020-07-23 20:45:19 +02:00
|
|
|
if [[ autopilot -eq 0 ]] ; then
|
2020-09-15 14:06:54 +02:00
|
|
|
echo "Country: '${yellow}${country}${reset}'"
|
2020-07-23 20:45:19 +02:00
|
|
|
fi
|
2020-08-04 03:49:21 +02:00
|
|
|
echo -n "Address ${bold}$((nline++)) of ${nlines}${reset}: \
|
2020-08-04 04:08:24 +02:00
|
|
|
Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times."
|
2020-07-23 20:45:19 +02:00
|
|
|
|
|
|
|
if [[ ${autopilot} -eq 0 ]] ; then
|
2020-09-15 14:06:54 +02:00
|
|
|
echo -n " Ban [y/N/s=No, and skip remaining]? "
|
2020-07-23 20:45:19 +02:00
|
|
|
read banaction
|
|
|
|
else
|
2020-09-15 14:06:54 +02:00
|
|
|
if [[ " ${bancountries[@]} " =~ " ${country} " ]] ; then
|
2020-07-23 20:45:19 +02:00
|
|
|
if [[ $count -ge $autopilot ]] ; then
|
2020-07-23 21:36:50 +02:00
|
|
|
echo -en "\n${red}Autopilot active. ${reset}"
|
2020-07-23 20:45:19 +02:00
|
|
|
banaction=y
|
|
|
|
else
|
2020-07-23 21:36:50 +02:00
|
|
|
echo -e "\n${yellow}Autopilot active. Ignoring remaining addresses due to limit of ${autopilot}.${reset}"
|
|
|
|
return
|
2020-07-23 20:45:19 +02:00
|
|
|
fi
|
|
|
|
else
|
2020-07-23 21:36:50 +02:00
|
|
|
if [[ $count -ge $autopilot ]] ; then
|
|
|
|
echo -en "\n${green}Autopilot active. ${reset}"
|
|
|
|
banaction=n
|
|
|
|
else
|
|
|
|
echo -e "\n${green}Autopilot active.${reset} ${yellow}Ignoring remaining addresses due to limit of ${autopilot}.${reset}"
|
|
|
|
return
|
|
|
|
fi
|
2020-07-23 20:45:19 +02:00
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
2020-06-23 02:16:06 +02:00
|
|
|
case "${banaction}" in
|
2020-06-23 03:07:09 +02:00
|
|
|
"s" | "S" )
|
2020-08-04 04:08:24 +02:00
|
|
|
echo "Not banning '${blue}${addrwithsuffix}${reset}', \
|
2020-06-23 03:07:09 +02:00
|
|
|
skipping remaining addresses."
|
|
|
|
return
|
|
|
|
;;
|
|
|
|
"y" | "Y" )
|
2020-08-04 04:08:24 +02:00
|
|
|
echo "Adding '${blue}${addrwithsuffix}${reset}' to \
|
2020-09-15 14:06:54 +02:00
|
|
|
banlist (country=${yellow}${country}${reset})."
|
2020-08-04 04:08:24 +02:00
|
|
|
echo "${addrwithsuffix}" >> "${banlist}"
|
2020-06-23 03:07:09 +02:00
|
|
|
;;
|
|
|
|
"n" | "N" | * )
|
2020-09-15 14:06:54 +02:00
|
|
|
echo "Not banning '${blue}${addrwithsuffix}${reset}' (country=${yellow}${country}${reset})."
|
2020-06-23 03:07:09 +02:00
|
|
|
;;
|
2020-06-23 02:16:06 +02:00
|
|
|
esac
|
2020-06-23 03:07:09 +02:00
|
|
|
# Here goes: Pipe the file contents via filedescriptor 3.
|
2020-06-23 02:16:06 +02:00
|
|
|
done 3< "${file}"
|
2020-06-23 03:07:09 +02:00
|
|
|
echo "Processed all entries in ${file}."
|
2020-06-23 02:16:06 +02:00
|
|
|
}
|
|
|
|
|
2020-08-03 13:32:32 +02:00
|
|
|
# Create a temp directory, chdir into it and create the (initially empty)
|
|
|
|
# banlist file.
|
|
|
|
tmpdir=$(mktemp -d)
|
2020-08-04 03:49:21 +02:00
|
|
|
|
|
|
|
# Set up all file paths
|
2020-08-04 17:21:45 +02:00
|
|
|
curdir="$(dirname "$0")"
|
2020-08-04 03:49:21 +02:00
|
|
|
# Define the files that will contain the addresses an subnets.
|
|
|
|
fileraw="${tmpdir}/raw-http.txt"
|
|
|
|
filtered="${tmpdir}/filtered-http.txt"
|
|
|
|
file8="${tmpdir}/sorted-http-8.txt"
|
|
|
|
file16="${tmpdir}/sorted-http-16.txt"
|
|
|
|
file24="${tmpdir}/sorted-http-24.txt"
|
|
|
|
file32="${tmpdir}/sorted-http-32.txt"
|
|
|
|
# This file will contain the addresses to be banned.
|
|
|
|
banlist="${tmpdir}/banlist.txt"
|
|
|
|
# This file contains the output of the last invocation of whois
|
|
|
|
whoisoutput="${tmpdir}/whois.txt"
|
|
|
|
|
2020-08-03 13:32:32 +02:00
|
|
|
touch "${banlist}"
|
|
|
|
|
2020-08-04 03:49:21 +02:00
|
|
|
# Parse the command line options
|
|
|
|
autopilot=0
|
|
|
|
netmask=0
|
|
|
|
jail="apache-auth"
|
2020-09-15 14:06:54 +02:00
|
|
|
bancountries=("CN")
|
2020-08-04 03:49:21 +02:00
|
|
|
database=
|
2020-09-15 13:51:03 +02:00
|
|
|
port=443
|
2020-08-04 03:49:21 +02:00
|
|
|
|
|
|
|
parse_command_line_args "$@"
|
|
|
|
|
2020-09-15 23:28:48 +02:00
|
|
|
check_dependencies
|
|
|
|
dependencies_ok=$?
|
|
|
|
if [[ ${dependencies_ok} -ne 0 ]] ; then
|
|
|
|
exit ${dependencies_ok}
|
|
|
|
fi
|
2020-08-03 13:32:32 +02:00
|
|
|
|
|
|
|
# List already banned addresses in the chosen jail
|
|
|
|
banned="$(exec_as_root fail2ban-client get "${jail}" banip)"
|
|
|
|
|
|
|
|
# Determine the current connections to the desired port; store the raw data in
|
|
|
|
# $fileraw.
|
2020-09-15 13:51:03 +02:00
|
|
|
netstat -nt | grep "${MY_IP}:${port}" | tr -s '[:blank:]' | cut -d' ' -f5 \
|
2020-08-03 13:32:32 +02:00
|
|
|
| cut -d: -f1 | sort > "${fileraw}"
|
|
|
|
|
|
|
|
# Group and sort the data into the subnet-specific files.
|
|
|
|
cp "${fileraw}" "${file32}"
|
|
|
|
cut -d. -f1-3 "${fileraw}" | sort > "${file24}"
|
|
|
|
cut -d. -f1-2 "${fileraw}" | sort > "${file16}"
|
|
|
|
cut -d. -f1 "${fileraw}" | sort > "${file8}"
|
|
|
|
|
|
|
|
# Filter already banned addresses
|
2020-08-04 04:08:24 +02:00
|
|
|
filter "${file32}" "${ext32}" "${suffix32}"
|
|
|
|
filter "${file24}" "${ext24}" "${suffix24}"
|
|
|
|
filter "${file16}" "${ext16}" "${suffix16}"
|
|
|
|
filter "${file8}" "${ext8}" "${suffix8}"
|
2020-08-03 13:32:32 +02:00
|
|
|
|
|
|
|
# Determine the number of connections per address
|
|
|
|
uniq -c "${file32}" | sort -rn | sponge "${file32}"
|
|
|
|
uniq -c "${file24}" | sort -rn | sponge "${file24}"
|
|
|
|
uniq -c "${file16}" | sort -rn | sponge "${file16}"
|
|
|
|
uniq -c "${file8}" | sort -rn | sponge "${file8}"
|
|
|
|
|
|
|
|
# Determine the number of entries per file.
|
|
|
|
nlines32=$(cat "${file32}" | wc -l)
|
|
|
|
nlines24=$(cat "${file24}" | wc -l)
|
|
|
|
nlines16=$(cat "${file16}" | wc -l)
|
|
|
|
nlines8=$(cat "${file8}" | wc -l)
|
|
|
|
|
|
|
|
if [ ${netmask} -eq 0 ] ; then
|
|
|
|
# Now let the user choose which file to process.
|
|
|
|
echo "We've got:"
|
|
|
|
echo "[1] 8bit: ${nlines8} entries"
|
|
|
|
echo "[2] 16bit: ${nlines16} entries"
|
|
|
|
echo "[3] 24bit: ${nlines24} entries"
|
|
|
|
echo "[4] 32bit: ${nlines32} entries"
|
|
|
|
read -p 'Which one do you want to work with (q=Quit) [1-4]? ' choice
|
|
|
|
|
|
|
|
# Based on the user's choice, initialize the variables $file, $ext and
|
|
|
|
# $nlines, which will be used after this point. Also, $choice will be
|
|
|
|
# used to color the output based on subnet-type.
|
|
|
|
case "${choice}" in
|
|
|
|
"1" )
|
|
|
|
netmask=8
|
|
|
|
;;
|
|
|
|
"2" )
|
|
|
|
netmask=16
|
|
|
|
;;
|
|
|
|
"3" )
|
|
|
|
netmask=24
|
|
|
|
;;
|
|
|
|
"4" )
|
|
|
|
netmask=32
|
|
|
|
;;
|
|
|
|
"Q" | "q" )
|
|
|
|
echo "You chose to abort. That's fine! Have a nice day!"
|
|
|
|
exit
|
|
|
|
;;
|
|
|
|
* )
|
|
|
|
echo "Invalid input: ${choice}. I'm out of here."
|
|
|
|
exit 1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Now initialize the variables $file, $ext and $nlines based on the chosen $netmask
|
|
|
|
TEMP="file${netmask}"
|
|
|
|
file="${!TEMP}"
|
|
|
|
TEMP="ext${netmask}"
|
|
|
|
ext="${!TEMP}"
|
2020-08-04 04:08:24 +02:00
|
|
|
TEMP="suffix${netmask}"
|
|
|
|
suffix="${!TEMP}"
|
2020-08-03 13:32:32 +02:00
|
|
|
TEMP="nlines${netmask}"
|
|
|
|
nlines="${!TEMP}"
|
|
|
|
unset TEMP
|
|
|
|
|
|
|
|
echo "Processing ${file}."
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# Invoke the processing function on the chosen file.
|
2020-08-03 13:32:32 +02:00
|
|
|
process_file "${file}"
|
2020-06-23 02:16:06 +02:00
|
|
|
|
|
|
|
echo "These are the addresses to be banned:"
|
|
|
|
cat "${banlist}"
|
|
|
|
|
2020-06-23 03:07:09 +02:00
|
|
|
# Make sure the user has to (re)-identify him- or herself before actually
|
|
|
|
# banning anything.
|
|
|
|
sudo -k
|
|
|
|
|
|
|
|
# Iterate over all addresses in $banlist and invoke fail2ban-client on each
|
|
|
|
# one of them.
|
2020-08-04 04:08:24 +02:00
|
|
|
while read -r addrwithsuffix ; do
|
|
|
|
echo "Banning ${addrwithsuffix} ..."
|
|
|
|
exec_as_root fail2ban-client set "${jail}" banip "${addrwithsuffix}"
|
2020-06-23 02:16:06 +02:00
|
|
|
done < "${banlist}"
|
2020-06-23 03:07:09 +02:00
|
|
|
|
2020-08-18 23:54:37 +02:00
|
|
|
end=$(date +%s)
|
|
|
|
|
|
|
|
echo "${green}All done in $((end - start)) seconds!${reset}"
|