Приветствую всех администраторов 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 meCкрипты от 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-скриптам смогут помочь оптимизировать код.