我在具有公开可见服务的所有服务器上使用了fail2ban,我想知道:
- 是否有一种简单的方法可以在我控制的主机之间共享禁止的IP?
- 那里有收集和发布数据的服务吗?
自设置此服务器的第一天以来,我一直在进行无数次登录尝试。
我在具有公开可见服务的所有服务器上使用了fail2ban,我想知道:
自设置此服务器的第一天以来,我一直在进行无数次登录尝试。
Answers:
我曾经在该站点上看到过一个用于集中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地址一个接一个击中我的Web服务器群集之后,我进行了很多研究。由于我使用的是AWS,因此我发现在测试5台服务器的前两天,可能会有一种简便的方法,并且可以很好地运行。
我建议的第一件事是暂时禁用SELinux,最后我们将对其进行处理。我不是SELinux专家,但是到目前为止,我的工作仍然有效。
主要要求是共享文件源,我使用AWS EFS。设置并装入新驱动器后,我将/etc/fail2ban/fail2ban.conf内的logtarget更改为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时仍然出现错误。在jail.local中使用action = action_mwl时存在错误。经过一番谷歌搜索后,我发现这是到目前为止。从我读到的内容来看,因为指向多个文件的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将不会启动。希望这对尝试部署此功能的人有所帮助。
是的,是的。两者都可以做到。
您需要找到一种合适的机制来共享IP列表。例如,如果您使用的是AWS,则可以利用s3。您可以在Linux主机之间或所有主机共用的数据库之间使用rsync。您可以使用自己喜欢的编程语言来提供一项服务,该语言提供一个宁静的API,供您选择。
用术语来说,如果与公众共享列表,则可以创建一个网站并托管一个简单的文本文件,其中一些已经提供了此类列表(据我所知并非来自众包)。如何创建自己的站点/服务将超出答案的范围,但是并不难做到。
Is there an easy way to share banned IPs between hosts I control?
相当手动的设置是更改调用iptables
更新规则的配置,以便它调用您自己设计的脚本,该脚本循环遍历主机列表(从文件中读取?),并iptables
通过SSH在每个主机上进行调用。您需要在所有为此配置的主机之间进行基于密钥的身份验证。诸如puppet之类的管理自动化工具可以使设置和维护变得更容易。这并不是非常有效,但是除非您看到大量的探测流量(和/或拥有大量的主机),否则我相信它已经足够了。如果只有几个主机,那么您甚至不需要遍历文件:将每个主机配置为仅按顺序调用其他主机。脚本编写工作将是最少的。
Is there a way to share banned IPs publicly?
毫无疑问,有很多方法。让上面的脚本将数据拖放到数据库中,并让客户端从中读取数据,轮询新规则并在新规则运行时运行它们。如果有很多规则,那么简单的“按需运行规则”就不完美主机正在提交信息,例如这种情况:
但这不是一个严重的问题,如果您对数据库有了更多的了解,并且认为值得付出努力,则可以更清晰地管理多个提交。
但是,将其作为公共服务运行会给您带来管理麻烦的世界: