Я использую fail2ban на всех серверах с общедоступными сервисами, и мне интересно:
Я получил бесчисленное количество попыток входа в систему с первого дня настройки этого сервера.
Однажды я видел систему централизации данных fail2ban на этом сайте, и создал измененную версию. База данных такая же, но я изменил и создал несколько скриптов.
Моя система состоит из 4 компонентов:
база данных fail2ban
Это база данных MySQL, содержащая только одну таблицу: erp_core_fail2ban
:
CREATE TABLE IF NOT EXISTS 'erp_core_fail2ban' (
'id' bigint(20) unsigned NOT NULL AUTO_INCREMENT,
'hostname' varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
'created' datetime NOT NULL,
'name' text COLLATE utf8_unicode_ci NOT NULL,
'protocol' varchar(16) COLLATE utf8_unicode_ci NOT NULL,
'port' varchar(32) COLLATE utf8_unicode_ci NOT NULL,
'ip' varchar(64) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY ('id'),
KEY 'hostname' ('hostname','ip')
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
fail2ban.php
Каждый раз, когда хост блокируется, он заполняет базу данных:
<?php
require_once("/etc/fail2ban/phpconfig.php");
$name = $_SERVER["argv"][1];
$protocol = $_SERVER["argv"][2];
$port = $_SERVER["argv"][3];
if (!preg_match('/^\d{1,5}$/', $port))
$port = getservbyname($_SERVER["argv"][3], $protocol);
$ip = $_SERVER["argv"][4];
$hostname = gethostname();
$query = "INSERT INTO 'erp_core_fail2ban' set hostname='" . addslashes($hostname) . "', name='" . addslashes($name) ."', protocol='" . addslashes($protocol) . "', port='" . addslashes($port) . "', ip='" . addslashes($ip) . "', created=NOW()";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
mysql_close($link);
exit;
?>
cron2ban
Вы запускаете это на crontab каждую минуту. Он получит последние добавленные хосты и заблокирует их.
<?php
// phpconfig.php will have database configuration settings
require_once("/etc/fail2ban/phpconfig.php");
// file with only a line, containing the last id banned
$lastbanfile="/etc/fail2ban/lastban";
$lastban = file_get_contents($lastbanfile);
// select only hosts banned after last check
$sql = "select id, ip from erp_core_fail2ban where id > $lastban";
$result = mysql_query($sql) or die('Query failed: ' . mysql_error());
mysql_close($link);
while ($row = mysql_fetch_array($result)) {
//
$id = $row['id'];
$ip = $row['ip'];
exec("fail2ban-client set $jail banip $ip");
}
// $id contains the last banned host, add it to the config file
file_put_contents($lastbanfile, $id);
?>
phpconfig
Этот файл идет в / etc / fail2ban и содержит конфигурацию базы данных и выбор тюрьмы.
<?php
// jail to be used
$jail = "ssh";
// file to keep the last ban
$lastbanfile="/etc/fail2ban/lastban";
// database configuration
$dbserver="localhost";
$dbuser="root";
$dbpass="root";
$dbname="fail2ban";
// connect to database
$link = mysql_connect($dbserver, $dbuser, $dbpass) or die('Could not connect: ' . mysql_error());
mysql_select_db($dbname) or die('Could not select database');
?>
Создайте эти файлы и измените конфигурацию из fail2ban:
После строки с actionban = .....
вставлена новая строка для вызова PHP-скрипта:
/root/fail2ban.php <name> <protocol> <port> <ip>
Использование этой структуры на всех ваших серверах гарантирует, что каждый раз, когда один хост будет заблокирован на одном сервере, все остальные серверы также будут его запрещать.
Поэтому я провел кучу исследований о том, как это сделать, после того, как увидел, что один и тот же IP-адрес один за другим попадает в мой кластер веб-серверов. Поскольку я использую AWS, я подумал, что может быть простой способ, и он прекрасно работает в первые два дня тестирования 5 серверов.
Первое, что я рекомендую, это временно отключить SELinux, мы разберемся с этим в конце. Я не эксперт по SELinux, но то, что я сделал, пока работает.
Основное требование - общий источник файлов, я использую AWS EFS. После того, как новый диск подготовлен и смонтирован, я изменил logtarget внутри /etc/fail2ban/fail2ban.conf на подпапку на диске EFS.
logtarget = /efsmount/fail2ban/server1.log
Затем я написал простой фильтр и поместил его в /etc/fail2ban/filter.d/fail2ban-log.conf
[Definition]
failregex = .* Ban <HOST>
ignoreregex =
Добавлен фильтр в /etc/fail2ban/jail.local
[fail2ban-log]
enabled = true
port = http,https
findtime = 86400 ; 1 day
logpath = /efsmount/fail2ban/server1.log
/efsmount/fail2ban/server2.log
/efsmount/fail2ban/server3.log
/efsmount/fail2ban/server4.log
maxretry = 1
Затем перезапустили fail2ban
sudo fail2ban-client reload
Все идет нормально! Нет, самая болезненная часть - это SELinux. После того, как я немного поработал fail2ban, я выполнил эту команду, которая позволила бы fail2ban проходить через фильтры.
sudo grep fail2ban /var/log/audit/audit.log | sudo audit2allow -M fail2ban-nfs
Audit2allow скажет вам запустить эту команду
sudo semodule -i fail2ban-nfs.pp
Я все еще проверяю свои журналы SELinux здесь и там, чтобы узнать, есть ли еще отказы. Если у кого-то есть подсказка, как получить этот чистый SELinux другим методом, это было бы здорово.
sudo cat /var/log/audit/audit.log |grep fail2ban |grep denied
В этот момент я все еще получал ошибки при перезапуске fail2ban. Ошибка при использовании action = action_mwl в jail.local. После небольшого поиска в Google я обнаружил, что это работает до сих пор. Из того, что я прочитал, это из-за разрывов строк в директиве logpath, указывающей на несколько файлов. Я пробовал использовать запятые, пробелы и т. Д., С action_mwl больше ничего не работало.
action_mwm = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-matches[name=%(__name__)s, dest="%(destemail)s", chain="%(chain)s"]
action = %(action_mwm)s
Не забудьте снова включить SELinux!
Я только что реализовал это, и до сих пор, похоже, он работает хорошо. Однако мне пришлось обновить часть php, потому что сценарии в исходном ответе используют устаревшие функции.
Вот обновленные скрипты
phpconfig.php
#!/usr/bin/php
<?php
// jail to be used
$jail = "ssh";
// file to keep the last ban
$lastbanfile="/etc/fail2ban/lastban";
// database configuration
$dbserver="[your.mysql.hostname]";
$dbport="[sql.port.default.is.3306]";
$dbuser="[sql.user";
$dbpass="[sql.password]";
$dbname="[sql.table]";
// connect to database
$link = mysqli_connect($dbserver, $dbuser, $dbpass, $dbname, $dbport) or die('Could not connect: ' . mysqli_error());
mysqli_select_db($link,$dbname) or die('Could not select database');
?>
fail2ban.php
#!/usr/bin/php
<?php
require_once("/etc/fail2ban/phpconfig.php");
$name = $_SERVER["argv"][1];
$protocol = $_SERVER["argv"][2];
$port = $_SERVER["argv"][3];
if (!preg_match('/^\d{1,5}$/', $port))
$port = getservbyname($_SERVER["argv"][3], $protocol);
$ip = $_SERVER["argv"][4];
$hostname = gethostname();
$query = "INSERT INTO erp_core_fail2ban (hostname,created,name,protocol,port,ip) VALUES ('$hostname',NOW(),'$name','$protocol','$port','$ip')";
echo $query;
$result = mysqli_query($link,$query) or die('Query failed: ' . mysqli_error($link));
mysqli_close($link);
exit;
?>
cron2ban.php
#!/usr/bin/php
<?php
// phpconfig.php will have database configuration settings
require_once("/etc/fail2ban/phpconfig.php");
// file with only a line, containing the last id banned
$lastbanfile="/etc/fail2ban/lastban";
$lastban = file_get_contents($lastbanfile);
// select only hosts banned after last check
$sql = "SELECT id,ip FROM erp_core_fail2ban WHERE id > $lastban";
$result = mysqli_query($link,$sql) or die('Query failed: ' . mysqli_error($link));
mysqli_close($link);
while ($row = mysqli_fetch_array($result)) {
//
$id = $row['id'];
$ip = $row['ip'];
exec("fail2ban-client set $jail banip $ip");
}
// $id contains the last banned host, add it to the config file
file_put_contents($lastbanfile, $id);
?>
Кроме того, где бы вы ни разместили действие fail2ban.php, он должен иметь такой же отступ, как и строка над ним. Например:
actionban = ...
/etc/fail2ban/fail2ban.php
В противном случае fail2ban не запустится. Надеюсь, это поможет любому, кто пытается это развернуть.
Альтернатива fail2ban
является DenyHosts который поставляется с функцией синхронизации. Установка довольно похожа на fail2ban
, видеть Учебник Cyberciti для более подробной информации.
Проблема в том, что служба синхронизации является централизованным, и исходный код на стороне сервера кажется недоступным, поэтому вы не можете легко запустить свою собственную службу DenyHosts, и вам придется полагаться на стороннюю организацию (что может быть приемлемым для некоторых случаев использования).
Да и да. И то, и другое можно сделать.
Вам нужно найти подходящий механизм для обмена списком IP-адресов. Например, если вы используете AWS, вы можете воспользоваться преимуществами s3. Вы можете использовать rsync между хостами Linux или базу данных, общую для всех хостов. Вы можете создать сервис с любимым языком программирования, который предоставляет спокойный API, выбор за вами.
С точки зрения общего доступа к списку, вы можете создать веб-сайт и разместить на нем простой текстовый файл, некоторые уже предоставляют такие списки (а не краудсорсинг, о котором я знаю). Как создать свой собственный сайт / услугу будет выходить за рамки ответа, однако это не должно быть ужасно сложно.
Is there an easy way to share banned IPs between hosts I control?
Достаточно ручной настройкой было бы изменение конфигурации, которая вызывает iptables
для обновления правил, чтобы он вызывал сценарий, созданный вами, который просматривает список хостов (читается из файла?) и делает iptables
звонит по каждому через SSH. Вам понадобится аутентификация на основе ключей между всеми хостами, настроенными для этого. Инструменты автоматизации администрирования, такие как марионетка, могут упростить настройку и обслуживание. Это было бы не очень эффективно, но я уверен, что этого будет достаточно, если вы не увидите большой объем исследуемого трафика (и / или не имеете большого количества хостов). Если у вас всего несколько хостов, вам даже не нужно перебирать файл: настройте каждый так, чтобы он просто вызывал другие по порядку. Усилия по написанию сценариев будут минимальными.
Is there a way to share banned IPs publicly?
Несомненно, способов много. Попросите приведенный выше сценарий (-ы) перетащить данные в БД и попросить клиентов читать из нее, опрашивать новые правила и запускать их по мере их поступления. Простое «запускать правило, как вы его видите» не будет идеальным, если многие хосты отправляют информацию, например, в этом случае:
но это не должно быть серьезной проблемой, и если вы станете немного умнее с базой данных, вы сможете более аккуратно управлять несколькими отправками, если решите, что это того стоит.
Однако запуск этого как общедоступной службы откроет вам целый мир проблем с администратором: