Скрипт блокирования brute-force атак в .htaccess

Ваш сайт атакуют. Если не прямо сейчас, то несколько раз в день. Подбирают пароли, нащупывают уязвимости. В логе ошибок это выглядит примерно так:

[Thu Jul 13 05:28:49 2017] [error] [client 196.64.59.18] ModSecurity: Access denied with code 401 (phase 2). Operator GT matched 0 at USER:bf_block. [file "/etc/apache2/mod_security/custom/wpbrute.conf"] [line "20"] [id "9999001"] [msg "ip address blocked for 5 minutes, more than 15 login attempts in 3 minutes."] [hostname "giraffesdoexist.com"] [uri "/wp-login.php"] [unique_id "WWdngc26uBEAAGIsOAEAAABG"]

Сервер, в моём случае, Апач, справляется с этими атаками, но говноеды возвращаются снова и снова. Помимо лишней нагрузки на сервер, есть небольшой шанс, что в какой-то момент атака будет успешной, поэтому я сделал простенький скрипт, который блокирует адреса взломщиков на уровне .htaccess.

Этот скрипт запускается через cron с тремя параметрами:

blockbruteforcers.sh ../logs/error_log ../domains/.htaccess 200

В процессе выполнения он ищет в файле error_log ip-адреса, которые встречаются там более 200 раз и записывает их в .htaccess с префиксом "deny from". Вот полный текст скрипта, не забудьте поставить ему атрибут "Execute" при создании на сервере.

#!/bin/bash
if [ $# -ne 3 ]; then
  echo "Incorrect number of arguments. Should be 3: apache error log path, .htaccess file path, number of entries."
  exit
fi

filename=$2
num_entries=$3
error_log=$1

#If .htaccess file does not exist, create it and add 2 first lines
if [ ! -f "$filename" ]; then
  echo order allow,deny >> "$filename"
  echo allow from all >> "$filename"
fi

#Check if .htaccess file has new line at the end. If not - add it.
c=`tail -c 1 "$filename"`
if [ "$c" != "" ]; then
  echo >> "$filename"
fi

msg=""
arr=($(grep -o 'client [0-9.]*' "$error_log"| sort -r | uniq -c | sort -nr | awk -v pNum=$num_entries '$1>pNum {print $3}'))
for i in "${arr[@]}"
do
  if ! grep -q -P "$i$" "$filename"; then
    if [ ! -z "$msg" ]; then
	  msg+="\n"
	fi
    msg+="deny from ${i}"
  fi
done

if [ ! -z "$msg" ]; then
  echo -e "$msg" >> "$filename"
  echo "$msg"
else
  echo "no matching records found"
fi