Недавно упоминал, как злобные хакеры воспользовались моим остатком кредита на счету VoIP оператора.
После перенастройки астериска угроза исчезла, но проблема атак на мою телефонную станцию осталась.
Поискал в интернете, как же заблокировать таких настырных, и наткнулся на неплохую систему, которая входит в состав некоторых дистрибутивов линукса -
Fail2Ban К сожалению она предназначена для блокировок на локальном сервере. Поэтому, посмотрев, что атаки происходят по 1-2 в день или реже, причём атакующие отваливаются уже после пары попыток, я решил, что проблема несущественная и закрыл на неё глаза.
Как оказалось зря. Глянув недавно в логи, на всякий случай, увидел, что атаки изменились по своей характеристике. Атакующие просто напросто решили побрутфорсить пароли для рандомных учётных записей, которые состоят только из цифр (многие оставляют логин учётки равным экстэншэну телефона, как в примерах конфигурационных файлов). В результате, логи астериска разрослись до ужасающих размеров, почти по 3ГБ на логфайл.
Тогда я решил всё-таки сделать что-нибудь похожее на Fail2Ban, но для блокировки IP адресов на удалённом рутере с линуксом (например
DD-WRT).
В результати получился перловый скриптик, который умеет:
- искать нужные строчки в логах,
- вытаскивать из них адреса атакующих,
- проверять не были ли они уже забанены (скрипт сканирует лог каждые 2 минуты до ротации),
- банить новых через телнет доступ на рутере,
- проверять, не был ли рутер перезагружен и не надо ли восстановить таблицу бана,
- и отправлять электронные письма при перезагрузке рутера и при добавлении новых адресов в бан.
При необходимости можно легко модифицировать скрипт под собственные нужды или под локальное применение, заменив коммуникацию с рутером на исполнение комманд в шелле.
UPD: поправил форматирование в HTML и опечатку.
#!/usr/bin/perl -w
use strict;
use warnings;
################################################################################
# INSTRUCTIONS #
# #
# 1. Place script into the convenient directory at your server where the log #
# file is located #
# 2. See the CONFIG SECTION below, make and save changes as needed #
# 3. Add script into crontab, by running 'crontab -e' command and inserting #
# the following line, modifying paths as needed: #
# */2 * * * * perl /opt/ban_ips_failed_asterisk_auth/ban.pl #
# If you have configured it properly, script should be working and you should #
# receive first 2 emails, about router restart and banned IPs #
################################################################################
# CONFIG SECTION #
################################################################################
my $log_file = "/var/log/asterisk/messages"; # you need read access here
my $data_dir = '/opt/ban_ips_failed_asterisk_auth'; # you need r/w access here
my $dbd_error_log = $data_dir . '/dbd_error.log';
my $bdb_messg_log = $data_dir . '/bdb_messg.log';
my $shmem_key = 1111; # 4 numbers
my $bdb_datafile = $data_dir . '/offending_ips.dbf';
my $telnet_log_file = $data_dir . '/router_telnet.log';
my $router_host = 'router.localnet'; # '10.0.0.136'
my $router_login = 'root';
my $router_passwd = 'r)65^)FVrtOVt';
my $iptables_chain_name = 'asterisk';
my $mailto_address = 'Admin ';
my $mailfrom_address = 'Banscript ';
my $smtp_host = 'mail.homeserver.localnet'# '10.0.0.56'
################################################################################
use BerkeleyDB;
use List::MoreUtils qw{uniq};
use Net::Telnet;
use Mail::Sendmail;
open (MYINPUTFILE, $log_file) or die "\n", $!, "Does log file $log_file exist\?\n\n";
my %offending_ips;
my $dbd_env = new BerkeleyDB::Env
-Home => $data_dir,
-ErrFile => $dbd_error_log,
-MsgFile => $bdb_messg_log,
-Flags => DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL,
-SharedMemKey => $shmem_key,
-Verbose => 1;
die "Could not init BDB env: $!; " . $BerkeleyDB::Error if !$dbd_env;
my $bdbh = tie %offending_ips, 'BerkeleyDB::Btree',
-Filename => $bdb_datafile,
-Flags => DB_CREATE,
-Env => $dbd_env;
die "Could not open database: $!; " . $BerkeleyDB::Error if !$bdbh;
my @new_offenders;
while () {
my ($line) = $_;
chomp($line);
################################################################################
# Configure your text to match in log files here. Remember to place brackets #
# around IP address expression #
################################################################################
if ($line =~ m/\' failed for \'(.*?)\' - (?:No matching peer found|Wrong password)/) {
push @new_offenders, $1 if (!exists $offending_ips{$1});
}
}
@new_offenders = uniq @new_offenders;
if (@new_offenders) {
#block
#establish telnet connection to router
my $router = new Net::Telnet(
Dump_Log => $telnet_log_file,
Host => $router_host,
Input_Log => $telnet_log_file,
Output_Log => $telnet_log_file
);
die "Could not connect to router" if !$router;
#login
$router->login($router_login, $router_passwd);
#check if asterisk chain exists on router fw (hasn't router been restarted)
my @chain_check_res = $router->cmd('iptables -L asterisk');
#if not
if (@chain_check_res < 2) {
#create chain
$router->cmd(
'iptables -N ' . $iptables_chain_name . ' && iptables -I INPUT 1 -j ' . $iptables_chain_name .
' && iptables -I FORWARD 1 -j ' . $iptables_chain_name
);
#add old offending ips to the chain
foreach my $ip_to_block (keys %offending_ips) {
$router->cmd('iptables -I ' . $iptables_chain_name . ' -s ' . $ip_to_block . ' -j DROP');
}
#e-mail admin about router restart
my %mail = (
To => $mailto_address,
From => $mailfrom_address,
Subject => '[BAN_IP] router has been restarted',
'X-Mailer' => 'Mail::Sendmail ' . $Mail::Sendmail::VERSION,
Smtp => $smtp_host,
Message => 'router has been restarted at ' . Mail::Sendmail::time_to_date(time()) . '+/- 2 minutes',
Date => Mail::Sendmail::time_to_date(time()),
);
die "Could not send router restart e-mail to administrator: " . $Mail::Sendmail::error if !sendmail %mail;
}
#block new offenders
foreach my $ip_to_block (@new_offenders) {
$router->cmd('iptables -I ' . $iptables_chain_name . ' -s ' . $ip_to_block . ' -j DROP');
$offending_ips{$ip_to_block} = 1;
}
#e-mail admin about new block
my %mail = (
To => $mailto_address,
From => $mailfrom_address,
Subject => '[BAN_IP] new IPs has been banned',
'X-Mailer' => 'Mail::Sendmail ' . $Mail::Sendmail::VERSION,
Smtp => $smtp_host,
Message => "New IPs has been banned from accessing asterisk.\n\nIP list: " . join(', ', @new_offenders),
Date => Mail::Sendmail::time_to_date(time()),
);
die "Could not send new banned ips e-mail to administrator: " . $Mail::Sendmail::error if !sendmail %mail;
$router->close();
$bdbh->db_sync();
}
exit(0);