"Cherry-pick" (manually) some of the refactoring.
This commit is contained in:
parent
c5dc76f8eb
commit
e46a378460
1 changed files with 204 additions and 187 deletions
|
@ -72,46 +72,40 @@ reset="\033[0m"
|
|||
trap 'sudo -k; popd >/dev/null; rm -r ${tmpdir}' EXIT
|
||||
|
||||
function check_installed() {
|
||||
local command="$1"
|
||||
local package="$2"
|
||||
which "${command}" 2>/dev/null >&2
|
||||
local result=$?
|
||||
local command="$1"
|
||||
local package="$2"
|
||||
which "${command}" 2>/dev/null >&2
|
||||
local result=$?
|
||||
|
||||
if [[ "${result}" -ne 0 ]] ; then
|
||||
echo -e "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${result}" -ne 0 ]] ; then
|
||||
echo -e "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Create a temp directory, chdir into it and create the (initially empty)
|
||||
# banlist file.
|
||||
tmpdir=$(mktemp -d)
|
||||
# Suppress output of dir stack.
|
||||
pushd "${tmpdir}" > /dev/null
|
||||
touch "${banlist}"
|
||||
|
||||
function print_help() {
|
||||
cat <<ENDOFHELP
|
||||
Usage: $(basename $0) [OPTION...]
|
||||
|
||||
-a, --auto[=LIMIT] Enable the autopilot for automatically banning chinese IP
|
||||
addresses (whois output must contain "source: APNIC" and
|
||||
"country: CN").
|
||||
When LIMIT is given, only auto-ban IP addresses with at
|
||||
least LIMIT current connections.
|
||||
When LIMIT is omitted, assume LIMIT=1.
|
||||
-a, --auto[=LIMIT] Enable the autopilot for automatically banning IP
|
||||
addresses of the desired country (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.
|
||||
|
||||
-j, --jail=JAIL Specify the JAIL to use for banning the IP addresses. If
|
||||
not set, uses 'apache-auth'.
|
||||
-c, --country=COUNTRY The country-code to block; defaults to 'CN' (China).
|
||||
|
||||
-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)
|
||||
-j, --jail=JAIL Specify the JAIL to use for banning the IP addresses.
|
||||
Defaults to 'apache-auth'.
|
||||
|
||||
-h, --help Show this help message
|
||||
-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)
|
||||
|
||||
-h, --help Show this help message
|
||||
|
||||
Mandatory or optional arguments to long options are also mandatory or optional
|
||||
for any corresponding short options.
|
||||
|
@ -129,8 +123,28 @@ function exec_as_root() {
|
|||
fi
|
||||
}
|
||||
|
||||
function filter() {
|
||||
# list of current connections
|
||||
file="$1"
|
||||
# subnet extension, e.g. ".0.0/16"
|
||||
ext="$2"
|
||||
rm -f "${filtered}"
|
||||
|
||||
# Reject already banned addresses
|
||||
while read -r -u3 address ; do
|
||||
if [[ "${banned}" != *"${address}${ext}"* ]] ; then
|
||||
echo "Considering ${address}."
|
||||
echo "${address}" >> "${filtered}"
|
||||
else
|
||||
echo "IGNORING ${address}, already banned."
|
||||
fi
|
||||
done 3< "${file}"
|
||||
|
||||
mv "${filtered}" "${file}"
|
||||
}
|
||||
|
||||
function parse_command_line_args() {
|
||||
TEMP=$(getopt -o 'a::,j:,n:,h' -l 'auto::,jail:,netmask:,help' -- "$@")
|
||||
TEMP=$(getopt -o 'a::,c:,j:,n:,h' -l 'auto::,country:,jail:,netmask:,help' -- "$@")
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo 'Error parsing command line options. Terminating. Invoke with --help for help.' >&2
|
||||
|
@ -157,6 +171,10 @@ function parse_command_line_args() {
|
|||
esac
|
||||
shift
|
||||
;;
|
||||
'-c'|'--country')
|
||||
bancountry="$2"
|
||||
shift
|
||||
;;
|
||||
'-j'|'--jail')
|
||||
jail="$2"
|
||||
shift
|
||||
|
@ -199,10 +217,164 @@ function parse_command_line_args() {
|
|||
done
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# 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.
|
||||
################################################################################
|
||||
function set_highlight_color() {
|
||||
local count=$1
|
||||
case "${choice}" in
|
||||
"1" )
|
||||
# /32: 0 <= green < 3 <= yellow < 5 <= red
|
||||
if [ $count -ge 5 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 3 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"2" )
|
||||
# /24: 0 <= green < 7 <= yellow < 13 <= red
|
||||
if [ $count -ge 13 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 7 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"3" )
|
||||
# /16: 0 <= green < 13 <= yellow < 25 <= red
|
||||
if [ $count -ge 25 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 13 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"4" )
|
||||
# /8: 0 <= green < 21 <= yellow < 49 <= red
|
||||
if [ $count -ge 49 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 21 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
# ???: We should never get here. As a fall-back, just use no
|
||||
# highlighting.
|
||||
hilite=""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Process the file denoted by $1. For each line in the file, the count and the
|
||||
# address are displayed and a whois request is made. The user can then choose to
|
||||
# ban or ignore the address. Addresses chosen to be banned are appended to the
|
||||
# $banlist.
|
||||
################################################################################
|
||||
function process_file () {
|
||||
local file="${1}"
|
||||
local line=''
|
||||
local count=0
|
||||
local addr=''
|
||||
local banaction=''
|
||||
local nline=1
|
||||
local country_cn=1
|
||||
# Read the contents from filedescriptor 3 (important: Don's use the
|
||||
# standard filedescriptor because we need to handle user input from
|
||||
# within the loop).
|
||||
while IFS= read -r -u3 line ; do
|
||||
line="$(echo "${line}" | tr -s '[:blank:]')"
|
||||
count="$(echo "${line}" | cut -d' ' -f2)"
|
||||
addr="$(echo "${line}" | cut -d' ' -f3-)${ext}"
|
||||
set_highlight_color "${count}"
|
||||
if [[ autopilot -eq 0 ]] ; then
|
||||
whois "${addr}" | tee "${whoisoutput}"
|
||||
else
|
||||
whois "${addr}" > "${whoisoutput}"
|
||||
fi
|
||||
grep -iq "^country: *${bancountry}$" "${whoisoutput}"
|
||||
country_cn=$?
|
||||
echo -en "Address ${bold}$((nline++)) of ${nlines}${reset}: \
|
||||
Found '${blue}${addr}${reset}' ${hilite}${count}${reset} times."
|
||||
|
||||
if [[ ${autopilot} -eq 0 ]] ; then
|
||||
echo -en "Ban [y/N/s=No, and skip remaining]? "
|
||||
read banaction
|
||||
else
|
||||
if [[ ${country_cn} -eq 0 ]] ; then
|
||||
if [[ $count -ge $autopilot ]] ; then
|
||||
echo -en "\n${red}Autopilot active. ${reset}"
|
||||
banaction=y
|
||||
else
|
||||
echo -e "\n${yellow}Autopilot active. Ignoring remaining addresses due to limit of ${autopilot}.${reset}"
|
||||
return
|
||||
fi
|
||||
else
|
||||
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
|
||||
fi
|
||||
fi
|
||||
|
||||
case "${banaction}" in
|
||||
"s" | "S" )
|
||||
echo -e "Not banning '${blue}${addr}${reset}', \
|
||||
skipping remaining addresses."
|
||||
return
|
||||
;;
|
||||
"y" | "Y" )
|
||||
echo -e "Adding '${blue}${addr}${reset}' to \
|
||||
banlist."
|
||||
echo "${addr}" >> "${banlist}"
|
||||
;;
|
||||
"n" | "N" | * )
|
||||
echo -e "Not banning '${blue}${addr}${reset}'."
|
||||
;;
|
||||
esac
|
||||
# Here goes: Pipe the file contents via filedescriptor 3.
|
||||
done 3< "${file}"
|
||||
echo "Processed all entries in ${file}."
|
||||
}
|
||||
|
||||
# Create a temp directory, chdir into it and create the (initially empty)
|
||||
# banlist file.
|
||||
tmpdir=$(mktemp -d)
|
||||
# Suppress output of dir stack.
|
||||
pushd "${tmpdir}" > /dev/null
|
||||
touch "${banlist}"
|
||||
|
||||
check_installed "sudo" "app-admin/sudo"
|
||||
check_installed "fail2ban-client" "net-analyzer/fail2ban"
|
||||
check_installed "whois" "net-misc/whois"
|
||||
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"
|
||||
|
||||
# Parse the command line options
|
||||
autopilot=0
|
||||
netmask=0
|
||||
jail="apache-auth"
|
||||
bancountry="CN"
|
||||
|
||||
parse_command_line_args "$@"
|
||||
|
||||
|
@ -220,26 +392,6 @@ cut -d. -f1-3 "${fileraw}" | sort > "${file24}"
|
|||
cut -d. -f1-2 "${fileraw}" | sort > "${file16}"
|
||||
cut -d. -f1 "${fileraw}" | sort > "${file8}"
|
||||
|
||||
function filter() {
|
||||
# list of current connections
|
||||
file="$1"
|
||||
# subnet extension, e.g. ".0.0/16"
|
||||
ext="$2"
|
||||
rm -f "${filtered}"
|
||||
|
||||
# Reject already banned addresses
|
||||
while read -r -u3 address ; do
|
||||
if [[ "${banned}" != *"${address}${ext}"* ]] ; then
|
||||
echo "Considering ${address}."
|
||||
echo "${address}" >> "${filtered}"
|
||||
else
|
||||
echo "IGNORING ${address}, already banned."
|
||||
fi
|
||||
done 3< "${file}"
|
||||
|
||||
mv "${filtered}" "${file}"
|
||||
}
|
||||
|
||||
# Filter already banned addresses
|
||||
filter "${file32}" "${ext32}"
|
||||
filter "${file24}" "${ext24}"
|
||||
|
@ -305,143 +457,8 @@ unset TEMP
|
|||
|
||||
echo "Processing ${file}."
|
||||
|
||||
################################################################################
|
||||
# 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.
|
||||
################################################################################
|
||||
function setHilite() {
|
||||
local count=$1
|
||||
case "${choice}" in
|
||||
"1" )
|
||||
# /32: 0 <= green < 3 <= yellow < 5 <= red
|
||||
if [ $count -ge 5 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 3 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"2" )
|
||||
# /24: 0 <= green < 7 <= yellow < 13 <= red
|
||||
if [ $count -ge 13 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 7 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"3" )
|
||||
# /16: 0 <= green < 13 <= yellow < 25 <= red
|
||||
if [ $count -ge 25 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 13 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
"4" )
|
||||
# /8: 0 <= green < 21 <= yellow < 49 <= red
|
||||
if [ $count -ge 49 ] ; then
|
||||
hilite="${red}"
|
||||
elif [ $count -ge 21 ] ; then
|
||||
hilite="${yellow}"
|
||||
else
|
||||
hilite="${green}"
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
# ???: We should never get here. As a fall-back, just use no
|
||||
# highlighting.
|
||||
hilite=""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Process the file denoted by $1. For each line in the file, the count and the
|
||||
# address are displayed and a whois request is made. The user can then choose to
|
||||
# ban or ignore the address. Addresses chosen to be banned are appended to the
|
||||
# $banlist.
|
||||
################################################################################
|
||||
function processFile () {
|
||||
local file="${1}"
|
||||
local line=''
|
||||
local count=0
|
||||
local addr=''
|
||||
local banaction=''
|
||||
local nline=1
|
||||
local country_cn=1
|
||||
local source_apnic=1
|
||||
# Read the contents from filedescriptor 3 (important: Don's use the
|
||||
# standard filedescriptor because we need to handle user input from
|
||||
# within the loop).
|
||||
while IFS= read -r -u3 line ; do
|
||||
line="$(echo "${line}" | tr -s '[:blank:]')"
|
||||
count="$(echo "${line}" | cut -d' ' -f2)"
|
||||
addr="$(echo "${line}" | cut -d' ' -f3-)${ext}"
|
||||
setHilite "${count}"
|
||||
if [[ autopilot -eq 0 ]] ; then
|
||||
whois "${addr}" | tee "${whoisoutput}"
|
||||
else
|
||||
whois "${addr}" > "${whoisoutput}"
|
||||
fi
|
||||
grep -iq "^country: *cn$" "${whoisoutput}"
|
||||
country_cn=$?
|
||||
grep -iq "^source: *apnic$" "${whoisoutput}"
|
||||
source_apnic=$?
|
||||
echo -en "Address ${bold}$((nline++)) of ${nlines}${reset}: \
|
||||
Found '${blue}${addr}${reset}' ${hilite}${count}${reset} times."
|
||||
|
||||
if [[ ${autopilot} -eq 0 ]] ; then
|
||||
echo -en "Ban [y/N/s=No, and skip remaining]? "
|
||||
read banaction
|
||||
else
|
||||
if [[ ${country_cn} -eq 0 && ${source_apnic} -eq 0 ]] ; then
|
||||
if [[ $count -ge $autopilot ]] ; then
|
||||
echo -en "\n${red}Autopilot active. ${reset}"
|
||||
banaction=y
|
||||
else
|
||||
echo -e "\n${yellow}Autopilot active. Ignoring remaining addresses due to limit of ${autopilot}.${reset}"
|
||||
return
|
||||
fi
|
||||
else
|
||||
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
|
||||
fi
|
||||
fi
|
||||
|
||||
case "${banaction}" in
|
||||
"s" | "S" )
|
||||
echo -e "Not banning '${blue}${addr}${reset}', \
|
||||
skipping remaining addresses."
|
||||
return
|
||||
;;
|
||||
"y" | "Y" )
|
||||
echo -e "Adding '${blue}${addr}${reset}' to \
|
||||
banlist."
|
||||
echo "${addr}" >> "${banlist}"
|
||||
;;
|
||||
"n" | "N" | * )
|
||||
echo -e "Not banning '${blue}${addr}${reset}'."
|
||||
;;
|
||||
esac
|
||||
# Here goes: Pipe the file contents via filedescriptor 3.
|
||||
done 3< "${file}"
|
||||
echo "Processed all entries in ${file}."
|
||||
}
|
||||
|
||||
# Invoke the processing function on the chosen file.
|
||||
processFile "${file}"
|
||||
process_file "${file}"
|
||||
|
||||
echo "These are the addresses to be banned:"
|
||||
cat "${banlist}"
|
||||
|
|
Loading…
Reference in a new issue