feature/ss-instead-of-netstat #2
					 1 changed files with 170 additions and 170 deletions
				
			
		|  | @ -38,7 +38,7 @@ | |||
| MY_IP="94.199.214.20" | ||||
| 
 | ||||
| # After this point, no editing is required. | ||||
| start=$(date +%s) | ||||
| start="$(date +%s)" | ||||
| 
 | ||||
| # Dependencies of this script. Simple array with the following structure: | ||||
| # (command package [...]) | ||||
|  | @ -53,8 +53,8 @@ dependencies=( | |||
| 	"tr" "sys-apps/coreutils" | ||||
| 	"uniq" "sys-apps/coreutils" | ||||
| 	"grep" "sys-apps/grep" | ||||
| 	"sponge" "sys-apps/moreutils" | ||||
| 	"ss" "sys-apps/iproute2" | ||||
| 	"sponge" "sys-apps/moreutils" | ||||
| 	"getopt" "sys-apps/util-linux" | ||||
| ) | ||||
| 
 | ||||
|  | @ -85,8 +85,8 @@ function is_installed() { | |||
| } | ||||
| 
 | ||||
| function print_missing_dependency() { | ||||
| 	local command="$1" | ||||
| 	local package="$2" | ||||
| 	local command="${1}" | ||||
| 	local package="${2}" | ||||
| 
 | ||||
| 	echo "${red}Command ${bold}${command}${reset}${red} not found.${reset} Please install package ${blue}${package}${reset}." >&2 | ||||
| } | ||||
|  | @ -99,12 +99,12 @@ function check_dependencies() { | |||
| 	# 0: true, all installed; 1: false, at least one command/package missing | ||||
| 	local all_installed=0 | ||||
| 
 | ||||
| 	for (( i=0; i<${arraylength}; i+=2 )) ; do | ||||
| 	for ((i = 0; i < ${arraylength}; i += 2)); do | ||||
| 		command="${dependencies[$i]}" | ||||
| 		package="${dependencies[$i+1]}" | ||||
| 		package="${dependencies[$i + 1]}" | ||||
| 		is_installed "${command}" "${package}" | ||||
| 		res=$? | ||||
| 		if [[ $res -ne 0 ]] ; then | ||||
| 		if [[ ${res} -ne 0 ]]; then | ||||
| 			print_missing_dependency "${command}" "${package}" | ||||
| 			all_installed=1 | ||||
| 		fi | ||||
|  | @ -167,7 +167,7 @@ ENDOFHELP | |||
| } | ||||
| 
 | ||||
| function exec_as_root() { | ||||
| 	if [[ $(id -un) == "root" ]] ; then | ||||
| 	if [[ $(id -un) == "root" ]]; then | ||||
| 		"$@" | ||||
| 	else | ||||
| 		sudo "$@" | ||||
|  | @ -176,22 +176,22 @@ function exec_as_root() { | |||
| 
 | ||||
| function filter() { | ||||
| 	# list of current connections | ||||
| 	file="$1" | ||||
| 	file="${1}" | ||||
| 	# subnet extension, e.g. ".0.0" | ||||
| 	ext="$2" | ||||
| 	ext="${2}" | ||||
| 	# subnet suffix, e.g. "/16" | ||||
| 	suffix="$3" | ||||
| 	suffix="${3}" | ||||
| 	rm -f "${filtered}" | ||||
| 	touch "${filtered}" | ||||
| 
 | ||||
| 	# Reject already banned addresses | ||||
| 	while read -r -u3 address ; do | ||||
| 		if [[ "${banned}" != *"${address}${ext}${suffix}"* ]] ; then | ||||
| 			echo "${address}" >> "${filtered}" | ||||
| 	while read -r -u3 address; do | ||||
| 		if [[ "${banned}" != *"${address}${ext}${suffix}"* ]]; then | ||||
| 			echo "${address}" >>"${filtered}" | ||||
| 		else | ||||
| 			echo "IGNORING ${address}${ext}${suffix}, already banned." | ||||
| 		fi | ||||
| 	done 3< "${file}" | ||||
| 	done 3<"${file}" | ||||
| 
 | ||||
| 	mv "${filtered}" "${file}" | ||||
| } | ||||
|  | @ -199,7 +199,7 @@ function filter() { | |||
| 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' -- "$@") | ||||
| 
 | ||||
| 	if [ $? -ne 0 ] ; then | ||||
| 	if [ $? -ne 0 ]; then | ||||
| 		echo 'Error parsing command line options. Terminating. Invoke with --help for help.' >&2 | ||||
| 		exit 1 | ||||
| 	fi | ||||
|  | @ -207,91 +207,91 @@ function parse_command_line_args() { | |||
| 	eval set -- "${TEMP}" | ||||
| 	unset TEMP | ||||
| 
 | ||||
| 	while true ; do | ||||
| 		case "$1" in | ||||
| 			'-a'|'--auto') | ||||
| 				case "$2" in | ||||
| 					'') | ||||
| 						autopilot=1 | ||||
| 					;; | ||||
| 					*[!0-9]*) | ||||
| 						echo "Invalid argument for parameter 'auto': '$2'. Invoke with --help for help." >&2 | ||||
| 						exit 1 | ||||
| 					;; | ||||
| 					*) | ||||
| 						autopilot="$2" | ||||
| 					;; | ||||
| 				esac | ||||
| 				shift | ||||
| 			;; | ||||
| 			'-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') | ||||
| 				database="$2" | ||||
| 				shift | ||||
| 			;; | ||||
| 			'-e'|'--dependencies') | ||||
| 				check_dependencies | ||||
| 				exit $? | ||||
| 			;; | ||||
| 			'-j'|'--jail') | ||||
| 				jail="$2" | ||||
| 				shift | ||||
| 			;; | ||||
| 			'-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 | ||||
| 			;; | ||||
| 			'-p'|'--port') | ||||
| 				port="$2" | ||||
| 				shift | ||||
| 			;; | ||||
| 			'-h'|'--help') | ||||
| 				print_help | ||||
| 				exit | ||||
| 			;; | ||||
| 			'--') | ||||
| 				shift | ||||
| 				break | ||||
| 			;; | ||||
| 			*) | ||||
| 				echo "Unknown error on command line argument '$1'. Terminating." >&2 | ||||
| 	while true; do | ||||
| 		case "${1}" in | ||||
| 		'-a' | '--auto') | ||||
| 			case "${2}" in | ||||
| 			'') | ||||
| 				autopilot=1 | ||||
| 				;; | ||||
| 			*[!0-9]*) | ||||
| 				echo "Invalid argument for parameter 'auto': '${2}'. Invoke with --help for help." >&2 | ||||
| 				exit 1 | ||||
| 				;; | ||||
| 			*) | ||||
| 				autopilot="$2" | ||||
| 				;; | ||||
| 			esac | ||||
| 			shift | ||||
| 			;; | ||||
| 		'-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') | ||||
| 			database="${2}" | ||||
| 			shift | ||||
| 			;; | ||||
| 		'-e' | '--dependencies') | ||||
| 			check_dependencies | ||||
| 			exit $? | ||||
| 			;; | ||||
| 		'-j' | '--jail') | ||||
| 			jail="${2}" | ||||
| 			shift | ||||
| 			;; | ||||
| 		'-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 | ||||
| 			;; | ||||
| 		'-p' | '--port') | ||||
| 			port="${2}" | ||||
| 			shift | ||||
| 			;; | ||||
| 		'-h' | '--help') | ||||
| 			print_help | ||||
| 			exit | ||||
| 			;; | ||||
| 		'--') | ||||
| 			shift | ||||
| 			break | ||||
| 			;; | ||||
| 		*) | ||||
| 			echo "Unknown error on command line argument '${1}'. Terminating." >&2 | ||||
| 			exit 1 | ||||
| 			;; | ||||
| 		esac | ||||
| 		shift | ||||
| 	done | ||||
| 
 | ||||
| 	if [[ -z "${database}" ]] ; then | ||||
| 	  echo "No GeoIP database specified. Invoke with --help for more information." >&2 | ||||
| 	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 | ||||
| 	if [[ ! -r "${database}" ]]; then | ||||
| 		echo "Database '${database}' is not accessible." >&2 | ||||
| 		exit 1 | ||||
| 	fi | ||||
| } | ||||
|  | @ -303,52 +303,52 @@ function parse_command_line_args() { | |||
| # color to the next happen at different values. | ||||
| ################################################################################ | ||||
| function set_highlight_color() { | ||||
| 	local count=$1 | ||||
| 	local count=${1} | ||||
| 	case "${choice}" in | ||||
| 		"1" ) | ||||
| 	"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 | ||||
| 		if [ ${count} -ge 5 ]; then | ||||
| 			hilite="${red}" | ||||
| 		elif [ ${count} -ge 3 ]; then | ||||
| 			hilite="${yellow}" | ||||
| 		else | ||||
| 			hilite="${green}" | ||||
| 		fi | ||||
| 		;; | ||||
| 		"2" ) | ||||
| 	"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 | ||||
| 		if [ ${count} -ge 13 ]; then | ||||
| 			hilite="${red}" | ||||
| 		elif [ ${count} -ge 7 ]; then | ||||
| 			hilite="${yellow}" | ||||
| 		else | ||||
| 			hilite="${green}" | ||||
| 		fi | ||||
| 		;; | ||||
| 		"3" ) | ||||
| 	"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 | ||||
| 		if [ ${count} -ge 25 ]; then | ||||
| 			hilite="${red}" | ||||
| 		elif [ ${count} -ge 13 ]; then | ||||
| 			hilite="${yellow}" | ||||
| 		else | ||||
| 			hilite="${green}" | ||||
| 		fi | ||||
| 		;; | ||||
| 		"4" ) | ||||
| 	"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 | ||||
| 		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="" | ||||
| 		hilite="" | ||||
| 		;; | ||||
| 	esac | ||||
| } | ||||
|  | @ -359,7 +359,7 @@ function set_highlight_color() { | |||
| # GeoIP database. The user can then choose to ban or ignore the address. | ||||
| # Addresses chosen to be banned are appended to the $banlist. | ||||
| ################################################################################ | ||||
| function process_file () { | ||||
| function process_file() { | ||||
| 	local file="${1}" | ||||
| 	local line='' | ||||
| 	local count=0 | ||||
|  | @ -371,25 +371,25 @@ function process_file () { | |||
| 	# 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 | ||||
| 	while IFS= read -r -u3 line; do | ||||
| 		line="$(echo "${line}" | tr -s '[:blank:]')" | ||||
| 		count="$(echo "${line}" | cut -d' ' -f2)" | ||||
| 		addronly="$(echo "${line}" | cut -d' ' -f3-)${ext}" | ||||
| 		addrwithsuffix="${addronly}${suffix}" | ||||
| 		set_highlight_color "${count}" | ||||
| 		country="$("${curdir}/geoip-lookup.py" -f "${database}" "${addronly}")" | ||||
| 		if [[ autopilot -eq 0 ]] ; then | ||||
| 		if [[ ${autopilot} -eq 0 ]]; then | ||||
| 			echo "Country: '${yellow}${country}${reset}'" | ||||
| 		fi | ||||
| 		echo -n "Address ${bold}$((nline++)) of ${nlines}${reset}: \ | ||||
| Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times." | ||||
| 
 | ||||
| 		if [[ ${autopilot} -eq 0 ]] ; then | ||||
| 		if [[ ${autopilot} -eq 0 ]]; then | ||||
| 			echo -n " Ban [y/N/s=No, and skip remaining]? " | ||||
| 			read banaction | ||||
| 		else | ||||
| 			if [[ " ${bancountries[@]} " =~ " ${country} " ]] ; then | ||||
| 				if [[ $count -ge $autopilot ]] ; then | ||||
| 			if [[ " ${bancountries[@]} " =~ " ${country} " ]]; then | ||||
| 				if [[ ${count} -ge ${autopilot} ]]; then | ||||
| 					echo -en "\n${red}Autopilot active. ${reset}" | ||||
| 					banaction=y | ||||
| 				else | ||||
|  | @ -397,7 +397,7 @@ Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times." | |||
| 					return | ||||
| 				fi | ||||
| 			else | ||||
| 				if [[ $count -ge $autopilot ]] ; then | ||||
| 				if [[ ${count} -ge ${autopilot} ]]; then | ||||
| 					echo -en "\n${green}Autopilot active. ${reset}" | ||||
| 					banaction=n | ||||
| 				else | ||||
|  | @ -408,28 +408,28 @@ Found '${blue}${addrwithsuffix}${reset}' ${hilite}${count}${reset} times." | |||
| 		fi | ||||
| 
 | ||||
| 		case "${banaction}" in | ||||
| 			"s" | "S" ) | ||||
| 				echo "Not banning '${blue}${addrwithsuffix}${reset}', \ | ||||
| 		"s" | "S") | ||||
| 			echo "Not banning '${blue}${addrwithsuffix}${reset}', \ | ||||
| skipping remaining addresses." | ||||
| 				return | ||||
| 			return | ||||
| 			;; | ||||
| 			"y" | "Y" ) | ||||
| 				echo "Adding '${blue}${addrwithsuffix}${reset}' to \ | ||||
| 		"y" | "Y") | ||||
| 			echo "Adding '${blue}${addrwithsuffix}${reset}' to \ | ||||
| banlist (country=${yellow}${country}${reset})." | ||||
| 				echo "${addrwithsuffix}" >> "${banlist}" | ||||
| 			echo "${addrwithsuffix}" >>"${banlist}" | ||||
| 			;; | ||||
| 			"n" | "N" | * ) | ||||
| 				echo "Not banning '${blue}${addrwithsuffix}${reset}' (country=${yellow}${country}${reset})." | ||||
| 		"n" | "N" | *) | ||||
| 			echo "Not banning '${blue}${addrwithsuffix}${reset}' (country=${yellow}${country}${reset})." | ||||
| 			;; | ||||
| 		esac | ||||
| 	# Here goes: Pipe the file contents via filedescriptor 3. | ||||
| 	done 3< "${file}" | ||||
| 		# 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) | ||||
| tmpdir="$(mktemp -d)" | ||||
| 
 | ||||
| # Set up all file paths | ||||
| curdir="$(dirname "$0")" | ||||
|  | @ -457,7 +457,7 @@ parse_command_line_args "$@" | |||
| 
 | ||||
| check_dependencies | ||||
| dependencies_ok=$? | ||||
| if [[ ${dependencies_ok} -ne 0 ]] ; then | ||||
| if [[ ${dependencies_ok} -ne 0 ]]; then | ||||
| 	exit ${dependencies_ok} | ||||
| fi | ||||
| 
 | ||||
|  | @ -469,15 +469,15 @@ banned="$(exec_as_root fail2ban-client get "${jail}" banip)" | |||
| connections=$(ss -HOn state established "( sport = :${port} )" | tr -s '[:blank:]' | cut -d' ' -f5) | ||||
| 
 | ||||
| # IPv6-mapped-IPv4: [::ffff:192.168.0.1]:443 | ||||
| echo "${connections}" | grep '^\[::ffff:' - | cut -d: -f4 | cut -d] -f1 | grep -v '^$' > "${fileraw}" | ||||
| 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}" | ||||
| echo "${connections}" | grep -v '^\[' - | cut -d: -f1 | grep -v '^$' >>"${fileraw}" | ||||
| 
 | ||||
| # Group and sort the data into the subnet-specific files. | ||||
| sort "${fileraw}" > "${file32}" | ||||
| cut -d. -f1-3 "${fileraw}" | sort > "${file24}" | ||||
| cut -d. -f1-2 "${fileraw}" | sort > "${file16}" | ||||
| cut -d. -f1 "${fileraw}" | sort > "${file8}" | ||||
| sort "${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 | ||||
| filter "${file32}" "${ext32}" "${suffix32}" | ||||
|  | @ -497,7 +497,7 @@ nlines24=$(cat "${file24}" | wc -l) | |||
| nlines16=$(cat "${file16}" | wc -l) | ||||
| nlines8=$(cat "${file8}" | wc -l) | ||||
| 
 | ||||
| if [ ${netmask} -eq 0 ] ; then | ||||
| if [ ${netmask} -eq 0 ]; then | ||||
| 	# Now let the user choose which file to process. | ||||
| 	echo "We've got:" | ||||
| 	echo "[1] 8bit: ${nlines8} entries" | ||||
|  | @ -510,25 +510,25 @@ if [ ${netmask} -eq 0 ] ; then | |||
| 	# $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 | ||||
| 	"1") | ||||
| 		netmask=8 | ||||
| 		;; | ||||
| 		"2" ) | ||||
| 			netmask=16 | ||||
| 	"2") | ||||
| 		netmask=16 | ||||
| 		;; | ||||
| 		"3" ) | ||||
| 			netmask=24 | ||||
| 	"3") | ||||
| 		netmask=24 | ||||
| 		;; | ||||
| 		"4" ) | ||||
| 			netmask=32 | ||||
| 	"4") | ||||
| 		netmask=32 | ||||
| 		;; | ||||
| 		"Q" | "q" ) | ||||
| 			echo "You chose to abort. That's fine! Have a nice day!" | ||||
| 			exit | ||||
| 	"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 | ||||
| 	*) | ||||
| 		echo "Invalid input: ${choice}. I'm out of here." | ||||
| 		exit 1 | ||||
| 		;; | ||||
| 	esac | ||||
| fi | ||||
|  | @ -558,11 +558,11 @@ sudo -k | |||
| 
 | ||||
| # Iterate over all addresses in $banlist and invoke fail2ban-client on each | ||||
| # one of them. | ||||
| while read -r addrwithsuffix ; do | ||||
| while read -r addrwithsuffix; do | ||||
| 	echo "Banning ${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}" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue