Приветствую всех администраторов FreeBSD!
Настроив свой второй сервер на FreeBSD и перенеся туда важную корпоративную информацию, я задумался о защите. Не буду повторяться про антивирусы, брандмауэры и дополнительные полезные комплексы — ни один из этих инструментов не решал мою задачу.
Задача возникла сама собой, при просмотре логов:
в них постоянно попадала информация о неудачных попытках подобрать пароль к exim, к серверу и к веб-почте соответственно. Рано или поздно злоумышленники могут пароль подобрать, поэтому их нужно как-то остановить, например, добавив их IP-адрес в правила ipfw. А на веб-сервере еще и пытались найти несуществующие каталоги и файлы, относящиеся к администрированию, типа phpmyadmin, очевидно, чтобы проверить их на существующие уязвимости.
Google и статья lissyara о некоторых аспектах безопасности подсказали направление.
Решение в итоге выглядит просто: cron раз в минуту запускает sh-скрипт, который сканирует указанные логи и добавляет незадачливых нарушителей в правила ipfw в виде:
Cкрипты от lissyara мне удалось доработать напильником:
Для каждого лог-файла я сделал соответственно свой sh-скрипт:
т.к. у них немного разный подход к решению задачи, но общая концепция аналогична.
Скрипты стабильно отрабатывают свою электроэнергию, делая практически невозможным брутфорс и защищая от излишне любопытных. По накопленной статистике, за минуту у самых быстрых получалось сделать не более 100 попыток подбора. До ввода в эксплуатацию скриптов, попытки были круглосуточными и бесчисленными.
Думаю, что подобные скрипты должны стоять на каждом сервере, т.к. защиты никогда не бывает много.
Надеюсь, мои наработки помогут Вам в этом. Привожу код. Готов ответить не вопросы.
Возможно специалисты по sh-скриптам смогут помочь оптимизировать код.
Настроив свой второй сервер на FreeBSD и перенеся туда важную корпоративную информацию, я задумался о защите. Не буду повторяться про антивирусы, брандмауэры и дополнительные полезные комплексы — ни один из этих инструментов не решал мою задачу.
Задача возникла сама собой, при просмотре логов:
/var/log/exim/rejectlog
/var/log/auth.log
/var/log/apache22/httpd-error.log
в них постоянно попадала информация о неудачных попытках подобрать пароль к exim, к серверу и к веб-почте соответственно. Рано или поздно злоумышленники могут пароль подобрать, поэтому их нужно как-то остановить, например, добавив их IP-адрес в правила ipfw. А на веб-сервере еще и пытались найти несуществующие каталоги и файлы, относящиеся к администрированию, типа phpmyadmin, очевидно, чтобы проверить их на существующие уязвимости.
Google и статья lissyara о некоторых аспектах безопасности подсказали направление.
Решение в итоге выглядит просто: cron раз в минуту запускает sh-скрипт, который сканирует указанные логи и добавляет незадачливых нарушителей в правила ipfw в виде:
deny ip from 123.123.123.123 to me
Cкрипты от lissyara мне удалось доработать напильником:
- в правила не добавляются пользователи доверенных подсетей
- в правила не добавляются одни и те же IP
- скрипт может обрабатывать один и тот-же лог файл без его копирования (основываясь на дате событий)
- скрипт пишет свои логи
- скрипт исправляет некоторые ошибки в логах
Для каждого лог-файла я сделал соответственно свой sh-скрипт:
mailsec.sh
nixsec.sh
websec.sh
т.к. у них немного разный подход к решению задачи, но общая концепция аналогична.
Скрипты стабильно отрабатывают свою электроэнергию, делая практически невозможным брутфорс и защищая от излишне любопытных. По накопленной статистике, за минуту у самых быстрых получалось сделать не более 100 попыток подбора. До ввода в эксплуатацию скриптов, попытки были круглосуточными и бесчисленными.
Думаю, что подобные скрипты должны стоять на каждом сервере, т.к. защиты никогда не бывает много.
Надеюсь, мои наработки помогут Вам в этом. Привожу код. Готов ответить не вопросы.
mailsec.sh
#!/bin/sh
# Настройки
trusted_net='192.168.'
debug_log="/var/log/mailsec.log"
cur_log_file="rejectlog"
cur_log_dir="/var/log/exim/"
#echo "Hi!" > /dev/ttyv0
# если два часа четыре минуты удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 04 ]
then {
# пишем статистику использования накопленных правил
echo "-= RULES STATS =-" >> ${debug_log}
/sbin/ipfw show 3 >> ${debug_log}
/sbin/ipfw delete 3 >/dev/null 2>&1
echo "-= RESET RULES =-" >> ${debug_log}
date >> ${debug_log}
}
fi
# блокируем тех кто пробует логиниться на несуществующих пользователей
cat ${cur_log_dir}${cur_log_file} | grep "auth_login authenticator failed for" | sed 's/ (/--/' | sed 's/failed for--/failed for (/' | sort | awk '{print $1,$8}' | sed 's/\[/ /' | sed 's/\]:/ /' | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
logday=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 3`
logmonth=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 2`
logyear=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 1`
IP=`echo ${count_IP} | awk '{print $3}'`
day=`date +%d`
mon=`date +%m`
yea=`date +%Y`
ippresent=`/sbin/ipfw show | grep ${IP} >/dev/null 2>&1`
if [ ${day} -eq ${logday} ] && [ ${mon} -eq ${logmonth} ] && [ ${yea} -eq ${logyear} ] && [ ! ${ippresent} ]
then {
if [ ${count_deny} -ge 6 ] && echo $IP | grep ${trusted_net} >/dev/null 2>&1
then echo "MAILSEC: (bruteforce) IP address = ${IP} attempts count = ${count_deny}. Правило не применяется (доверенный IP)" >> ${debug_log}
else {
if [ ${count_deny} -ge 6 ]
then {
echo "MAILSEC: (bruteforce) IP address = ${IP} attempts count = ${count_deny}" >> ${debug_log}
/sbin/ipfw add 3 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
}
fi
}
fi
done
}
nixsec.sh
#!/bin/sh
#echo "Hi!" > /dev/ttyv0
# в два часа ночи удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 00 ]
then {
/sbin/ipfw delete 1 >/dev/null 2>&1
echo "-= RESET RULES =-" >> /var/log/nixsec.log
date >> /var/log/nixsec.log
}
fi
#
day=`date +%d`
month=`date +%m`
year=`date +%Y`
log_dir="/var/old_log/${year}/${month}"
# создаём папку для логов
mkdir -p ${log_dir}
log_file="${log_dir}/${day}_auth.log"
# переносим логи
cat /var/log/auth.log > /tmp/auth.log
cat /dev/null > /var/log/auth.log
cat /tmp/auth.log >> ${log_file}
# Вначале отлавливаем IP с которых пытаются залогинится
# под несуществующими пользователями
cat /tmp/auth.log | grep "Invalid user" | awk '{print $10}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (invalid user) IP address = ${IP} deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me #>/dev/null 2>&1
}
fi
done
}
# Вылавливаем пытающихся ломать сервант
cat /tmp/auth.log | grep "Did not receive identification string" | awk '{print $12}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (not ident) IP address = ${IP} deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}
# Вылавливаем пытающихся ломать сервант c несуществующих адресов
cat /tmp/auth.log | grep "but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT" | awk '{print $7}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (break) IP address = ${IP} deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}
# Вылавливаем тех, кто лезет в root
cat /tmp/auth.log | grep "User root from" | awk '{print $9}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (root attempt) IP address = ${IP} deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}
websec.sh
#!/bin/sh
# Настройки
trusted_nets='192.168. 127.0. 10.0.'
debug_log="/var/log/websec.log"
cur_log_file="httpd-error.log"
cur_log_dir="/var/log/apache22"
old_log_dir="/var/log/apache22/old"
#echo "Hi!" > /dev/ttyv0
# в два часа две минуты удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 02 ]
then {
echo "-= RULES STATS =-" >> ${debug_log}
/sbin/ipfw show 2 >> ${debug_log}
/sbin/ipfw delete 2 >/dev/null 2>&1
echo "-= RESET RULES =-" >> ${debug_log}
date >> ${debug_log}
}
fi
# создаем папку для архивных логов
day=`date +%d`
month=`date +%m`
year=`date +%Y`
log_dir="${old_log_dir}/${year}/${month}"
# создаЈм папку для логов
mkdir -p ${log_dir}
log_file="${log_dir}/${day}_${cur_log_file}"
# копируем логи
cat ${cur_log_dir}/${cur_log_file} > /tmp/${cur_log_file}
# очищаем исходный логфайл
cat /dev/null > ${cur_log_dir}/${cur_log_file}
# обновляем архивный логфайл
cat /tmp/${cur_log_file} >> ${log_file}
# блокируем тех кто ищет несуществующие файлы
cat /tmp/${cur_log_file} | grep "File does not exist" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 5 ] && echo $IP | grep 192.168. >/dev/null 2>&1
then echo "WEBSEC: (files) IP address = ${IP} attempts count = ${count_deny}. Правило не применяется (доверенный IP)" >> ${debug_log}
else {
if [ ${count_deny} -ge 5 ]
then {
echo "WEBSEC: (files) IP address = ${IP} attempts count = ${count_deny}" >> ${debug_log}
/sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
}
fi
done
}
# Вылавливаем спецов по составлению неправильных URI
cat /tmp/${cur_log_file} | grep "Invalid URI in request" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "WEBSEC: (URI) IP address = ${IP} attempts count = ${count_deny}" >> ${debug_log}
/sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}
# Вылавливаем спецов по нарушению RFC
cat /tmp/${cur_log_file} | grep "request without hostname" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "WEBSEC: (RFC) IP address = ${IP} attempts count = ${count_deny}" >> ${debug_log}
/sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}
Возможно специалисты по sh-скриптам смогут помочь оптимизировать код.