Merge pull request 'feature/ss-instead-of-netstat' (#2) from feature/ss-instead-of-netstat into master
Reviewed-on: #2
This commit is contained in:
commit
b65bd6ea55
1 changed files with 176 additions and 174 deletions
|
@ -3,7 +3,7 @@
|
||||||
# #
|
# #
|
||||||
# Try and prevent apache overloads by banning IP addresses that have (too) #
|
# Try and prevent apache overloads by banning IP addresses that have (too) #
|
||||||
# many open connections. #
|
# many open connections. #
|
||||||
# This script uses netstat to determine the connections to a configurable port #
|
# This script uses ss to determine the connections to a configurable port #
|
||||||
# on the host machine and provides automated GeoIP information retrieval based #
|
# 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 #
|
# 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 #
|
# database must be installed separately and is provided to the script via a #
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
# - net-analyzer/fail2ban (`fail2ban-client`) #
|
# - net-analyzer/fail2ban (`fail2ban-client`) #
|
||||||
# - sys-apps/coreutils (`cut`, `id`, `sort`, `touch`, `tr`, `uniq`) #
|
# - sys-apps/coreutils (`cut`, `id`, `sort`, `touch`, `tr`, `uniq`) #
|
||||||
# - sys-apps/grep (`grep`) #
|
# - sys-apps/grep (`grep`) #
|
||||||
|
# - sys-apps/iproute2 (`ss`) #
|
||||||
# - sys-apps/moreutils (`sponge`) #
|
# - sys-apps/moreutils (`sponge`) #
|
||||||
# - sys-apps/net-tools (`netstat`) #
|
|
||||||
# - sys-apps/util-linux (`getopt`) #
|
# - sys-apps/util-linux (`getopt`) #
|
||||||
# #
|
# #
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
MY_IP="94.199.214.20"
|
MY_IP="94.199.214.20"
|
||||||
|
|
||||||
# After this point, no editing is required.
|
# After this point, no editing is required.
|
||||||
start=$(date +%s)
|
start="$(date +%s)"
|
||||||
|
|
||||||
# Dependencies of this script. Simple array with the following structure:
|
# Dependencies of this script. Simple array with the following structure:
|
||||||
# (command package [...])
|
# (command package [...])
|
||||||
|
@ -51,8 +51,8 @@ dependencies=(
|
||||||
"tr" "sys-apps/coreutils"
|
"tr" "sys-apps/coreutils"
|
||||||
"uniq" "sys-apps/coreutils"
|
"uniq" "sys-apps/coreutils"
|
||||||
"grep" "sys-apps/grep"
|
"grep" "sys-apps/grep"
|
||||||
|
"ss" "sys-apps/iproute2"
|
||||||
"sponge" "sys-apps/moreutils"
|
"sponge" "sys-apps/moreutils"
|
||||||
"netstat" "sys-apps/net-tools"
|
|
||||||
"getopt" "sys-apps/util-linux"
|
"getopt" "sys-apps/util-linux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ function is_installed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function print_missing_dependency() {
|
function print_missing_dependency() {
|
||||||
local command="$1"
|
local command="${1}"
|
||||||
local package="$2"
|
local package="${2}"
|
||||||
|
|
||||||
echo "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}." >&2
|
echo "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}." >&2
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ function check_dependencies() {
|
||||||
package="${dependencies[$i + 1]}"
|
package="${dependencies[$i + 1]}"
|
||||||
is_installed "${command}" "${package}"
|
is_installed "${command}" "${package}"
|
||||||
res=$?
|
res=$?
|
||||||
if [[ $res -ne 0 ]] ; then
|
if [[ ${res} -ne 0 ]]; then
|
||||||
print_missing_dependency "${command}" "${package}"
|
print_missing_dependency "${command}" "${package}"
|
||||||
all_installed=1
|
all_installed=1
|
||||||
fi
|
fi
|
||||||
|
@ -174,11 +174,11 @@ function exec_as_root() {
|
||||||
|
|
||||||
function filter() {
|
function filter() {
|
||||||
# list of current connections
|
# list of current connections
|
||||||
file="$1"
|
file="${1}"
|
||||||
# subnet extension, e.g. ".0.0"
|
# subnet extension, e.g. ".0.0"
|
||||||
ext="$2"
|
ext="${2}"
|
||||||
# subnet suffix, e.g. "/16"
|
# subnet suffix, e.g. "/16"
|
||||||
suffix="$3"
|
suffix="${3}"
|
||||||
rm -f "${filtered}"
|
rm -f "${filtered}"
|
||||||
touch "${filtered}"
|
touch "${filtered}"
|
||||||
|
|
||||||
|
@ -206,14 +206,14 @@ function parse_command_line_args() {
|
||||||
unset TEMP
|
unset TEMP
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
case "$1" in
|
case "${1}" in
|
||||||
'-a' | '--auto')
|
'-a' | '--auto')
|
||||||
case "$2" in
|
case "${2}" in
|
||||||
'')
|
'')
|
||||||
autopilot=1
|
autopilot=1
|
||||||
;;
|
;;
|
||||||
*[!0-9]*)
|
*[!0-9]*)
|
||||||
echo "Invalid argument for parameter 'auto': '$2'. Invoke with --help for help." >&2
|
echo "Invalid argument for parameter 'auto': '${2}'. Invoke with --help for help." >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -223,15 +223,15 @@ function parse_command_line_args() {
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-c' | '--country')
|
'-c' | '--country')
|
||||||
IFS=',' read -ra bancountries <<< "$2"
|
IFS=',' read -ra bancountries <<<"${2}"
|
||||||
if [[ -z ${bancountries[@]// } ]] ; then
|
if [[ -z ${bancountries[@]// /} ]]; then
|
||||||
echo "Invalid argument for parameter 'country': '$2'. Invoke with --help for help." >&2
|
echo "Invalid argument for parameter 'country': '${2}'. Invoke with --help for help." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-d' | '--database')
|
'-d' | '--database')
|
||||||
database="$2"
|
database="${2}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-e' | '--dependencies')
|
'-e' | '--dependencies')
|
||||||
|
@ -239,11 +239,11 @@ function parse_command_line_args() {
|
||||||
exit $?
|
exit $?
|
||||||
;;
|
;;
|
||||||
'-j' | '--jail')
|
'-j' | '--jail')
|
||||||
jail="$2"
|
jail="${2}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-n' | '--netmask')
|
'-n' | '--netmask')
|
||||||
case "$2" in
|
case "${2}" in
|
||||||
'1' | '8')
|
'1' | '8')
|
||||||
netmask=8
|
netmask=8
|
||||||
;;
|
;;
|
||||||
|
@ -257,14 +257,14 @@ function parse_command_line_args() {
|
||||||
netmask=32
|
netmask=32
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid argument for parameter 'netmask': '$2'. Invoke with --help for help." >&2
|
echo "Invalid argument for parameter 'netmask': '${2}'. Invoke with --help for help." >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-p' | '--port')
|
'-p' | '--port')
|
||||||
port="$2"
|
port="${2}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'-h' | '--help')
|
'-h' | '--help')
|
||||||
|
@ -276,7 +276,7 @@ function parse_command_line_args() {
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown error on command line argument '$1'. Terminating." >&2
|
echo "Unknown error on command line argument '${1}'. Terminating." >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -301,13 +301,13 @@ function parse_command_line_args() {
|
||||||
# color to the next happen at different values.
|
# color to the next happen at different values.
|
||||||
################################################################################
|
################################################################################
|
||||||
function set_highlight_color() {
|
function set_highlight_color() {
|
||||||
local count=$1
|
local count=${1}
|
||||||
case "${choice}" in
|
case "${choice}" in
|
||||||
"1")
|
"1")
|
||||||
# /32: 0 <= green < 3 <= yellow < 5 <= red
|
# /32: 0 <= green < 3 <= yellow < 5 <= red
|
||||||
if [ $count -ge 5 ] ; then
|
if [ ${count} -ge 5 ]; then
|
||||||
hilite="${red}"
|
hilite="${red}"
|
||||||
elif [ $count -ge 3 ] ; then
|
elif [ ${count} -ge 3 ]; then
|
||||||
hilite="${yellow}"
|
hilite="${yellow}"
|
||||||
else
|
else
|
||||||
hilite="${green}"
|
hilite="${green}"
|
||||||
|
@ -315,9 +315,9 @@ function set_highlight_color() {
|
||||||
;;
|
;;
|
||||||
"2")
|
"2")
|
||||||
# /24: 0 <= green < 7 <= yellow < 13 <= red
|
# /24: 0 <= green < 7 <= yellow < 13 <= red
|
||||||
if [ $count -ge 13 ] ; then
|
if [ ${count} -ge 13 ]; then
|
||||||
hilite="${red}"
|
hilite="${red}"
|
||||||
elif [ $count -ge 7 ] ; then
|
elif [ ${count} -ge 7 ]; then
|
||||||
hilite="${yellow}"
|
hilite="${yellow}"
|
||||||
else
|
else
|
||||||
hilite="${green}"
|
hilite="${green}"
|
||||||
|
@ -325,9 +325,9 @@ function set_highlight_color() {
|
||||||
;;
|
;;
|
||||||
"3")
|
"3")
|
||||||
# /16: 0 <= green < 13 <= yellow < 25 <= red
|
# /16: 0 <= green < 13 <= yellow < 25 <= red
|
||||||
if [ $count -ge 25 ] ; then
|
if [ ${count} -ge 25 ]; then
|
||||||
hilite="${red}"
|
hilite="${red}"
|
||||||
elif [ $count -ge 13 ] ; then
|
elif [ ${count} -ge 13 ]; then
|
||||||
hilite="${yellow}"
|
hilite="${yellow}"
|
||||||
else
|
else
|
||||||
hilite="${green}"
|
hilite="${green}"
|
||||||
|
@ -335,9 +335,9 @@ function set_highlight_color() {
|
||||||
;;
|
;;
|
||||||
"4")
|
"4")
|
||||||
# /8: 0 <= green < 21 <= yellow < 49 <= red
|
# /8: 0 <= green < 21 <= yellow < 49 <= red
|
||||||
if [ $count -ge 49 ] ; then
|
if [ ${count} -ge 49 ]; then
|
||||||
hilite="${red}"
|
hilite="${red}"
|
||||||
elif [ $count -ge 21 ] ; then
|
elif [ ${count} -ge 21 ]; then
|
||||||
hilite="${yellow}"
|
hilite="${yellow}"
|
||||||
else
|
else
|
||||||
hilite="${green}"
|
hilite="${green}"
|
||||||
|
@ -376,7 +376,7 @@ function process_file () {
|
||||||
addrwithsuffix="${addronly}${suffix}"
|
addrwithsuffix="${addronly}${suffix}"
|
||||||
set_highlight_color "${count}"
|
set_highlight_color "${count}"
|
||||||
country="$("${curdir}/geoip-lookup.py" -f "${database}" "${addronly}")"
|
country="$("${curdir}/geoip-lookup.py" -f "${database}" "${addronly}")"
|
||||||
if [[ autopilot -eq 0 ]] ; then
|
if [[ ${autopilot} -eq 0 ]]; then
|
||||||
echo "Country: '${yellow}${country}${reset}'"
|
echo "Country: '${yellow}${country}${reset}'"
|
||||||
fi
|
fi
|
||||||
echo -n "Address ${bold}$((nline++)) of ${nlines}${reset}: \
|
echo -n "Address ${bold}$((nline++)) of ${nlines}${reset}: \
|
||||||
|
@ -387,7 +387,7 @@ Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times."
|
||||||
read banaction
|
read banaction
|
||||||
else
|
else
|
||||||
if [[ " ${bancountries[@]} " =~ " ${country} " ]]; then
|
if [[ " ${bancountries[@]} " =~ " ${country} " ]]; then
|
||||||
if [[ $count -ge $autopilot ]] ; then
|
if [[ ${count} -ge ${autopilot} ]]; then
|
||||||
echo -en "\n${red}Autopilot active. ${reset}"
|
echo -en "\n${red}Autopilot active. ${reset}"
|
||||||
banaction=y
|
banaction=y
|
||||||
else
|
else
|
||||||
|
@ -395,7 +395,7 @@ Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ $count -ge $autopilot ]] ; then
|
if [[ ${count} -ge ${autopilot} ]]; then
|
||||||
echo -en "\n${green}Autopilot active. ${reset}"
|
echo -en "\n${green}Autopilot active. ${reset}"
|
||||||
banaction=n
|
banaction=n
|
||||||
else
|
else
|
||||||
|
@ -427,7 +427,7 @@ banlist (country=${yellow}${country}${reset})."
|
||||||
|
|
||||||
# Create a temp directory, chdir into it and create the (initially empty)
|
# Create a temp directory, chdir into it and create the (initially empty)
|
||||||
# banlist file.
|
# banlist file.
|
||||||
tmpdir=$(mktemp -d)
|
tmpdir="$(mktemp -d)"
|
||||||
|
|
||||||
# Set up all file paths
|
# Set up all file paths
|
||||||
curdir="$(dirname "$0")"
|
curdir="$(dirname "$0")"
|
||||||
|
@ -440,8 +440,6 @@ file24="${tmpdir}/sorted-http-24.txt"
|
||||||
file32="${tmpdir}/sorted-http-32.txt"
|
file32="${tmpdir}/sorted-http-32.txt"
|
||||||
# This file will contain the addresses to be banned.
|
# This file will contain the addresses to be banned.
|
||||||
banlist="${tmpdir}/banlist.txt"
|
banlist="${tmpdir}/banlist.txt"
|
||||||
# This file contains the output of the last invocation of whois
|
|
||||||
whoisoutput="${tmpdir}/whois.txt"
|
|
||||||
|
|
||||||
touch "${banlist}"
|
touch "${banlist}"
|
||||||
|
|
||||||
|
@ -466,11 +464,15 @@ banned="$(exec_as_root fail2ban-client get "${jail}" banip)"
|
||||||
|
|
||||||
# Determine the current connections to the desired port; store the raw data in
|
# Determine the current connections to the desired port; store the raw data in
|
||||||
# $fileraw.
|
# $fileraw.
|
||||||
netstat -nt | grep "${MY_IP}:${port}" | tr -s '[:blank:]' | cut -d' ' -f5 \
|
connections=$(ss -HOn state established "( sport = :${port} )" | tr -s '[:blank:]' | cut -d' ' -f5)
|
||||||
| cut -d: -f1 | sort > "${fileraw}"
|
|
||||||
|
# IPv6-mapped-IPv4: [::ffff:192.168.0.1]:443
|
||||||
|
echo "${connections}" | grep '^\[::ffff:' - | cut -d: -f4 | cut -d] -f1 | grep -v '^$' >"${fileraw}"
|
||||||
|
# Pure IPv4: 192.168.0.1:443
|
||||||
|
echo "${connections}" | grep -v '^\[' - | cut -d: -f1 | grep -v '^$' >>"${fileraw}"
|
||||||
|
|
||||||
# Group and sort the data into the subnet-specific files.
|
# Group and sort the data into the subnet-specific files.
|
||||||
cp "${fileraw}" "${file32}"
|
sort "${fileraw}" >"${file32}"
|
||||||
cut -d. -f1-3 "${fileraw}" | sort >"${file24}"
|
cut -d. -f1-3 "${fileraw}" | sort >"${file24}"
|
||||||
cut -d. -f1-2 "${fileraw}" | sort >"${file16}"
|
cut -d. -f1-2 "${fileraw}" | sort >"${file16}"
|
||||||
cut -d. -f1 "${fileraw}" | sort >"${file8}"
|
cut -d. -f1 "${fileraw}" | sort >"${file8}"
|
||||||
|
@ -559,6 +561,6 @@ while read -r addrwithsuffix ; do
|
||||||
exec_as_root fail2ban-client set "${jail}" banip "${addrwithsuffix}"
|
exec_as_root fail2ban-client set "${jail}" banip "${addrwithsuffix}"
|
||||||
done <"${banlist}"
|
done <"${banlist}"
|
||||||
|
|
||||||
end=$(date +%s)
|
end="$(date +%s)"
|
||||||
|
|
||||||
echo "${green}All done in $((end - start)) seconds!${reset}"
|
echo "${green}All done in $((end - start)) seconds!${reset}"
|
||||||
|
|
Loading…
Reference in a new issue