我在所有具有公共可见服务的服务器上使用fail2ban,我想知道:
自从设置此服务器的第1天以来,我一直在进行无数的login尝试。
我曾经看到过在这个网站上集中fail2ban数据的系统,并且创build了一个修改版本。 数据库是一样的,我改变并创build了一些脚本。
我的系统有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包含最后被禁止的主机,将其添加到configuration文件中
file_put_contents($ lastbanfile,$ id);
?>
phpconfig
这个文件到/ etc / fail2ban,并有数据库configuration和监狱select。
<?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'); ?>
创build这些文件并从fail2ban更改configuration:
在actionban = .....
行之后插入一行来调用PHP脚本:
/root/fail2ban.php <name> <protocol> <port> <ip>
在你所有的服务器上使用这个结构将确保每次一个主机被禁止在一台服务器上时,所有其他的服务器也将禁止它。
我刚刚实施这个,到目前为止似乎运作良好。 但是,我不得不更新一些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地址打到我的集群的web服务器之后,做了大量的研究。 由于我使用的是AWS,所以在testing5台服务器的头两天里,可能会有一个简单的方法,它的工作很好。
我build议的第一件事是暂时禁用SELinux,我们将在最后处理它。 我不是SELinux的专家,但是我所做的工作到目前为止。
主要要求是共享文件源,我使用AWS EFS。 一旦新的驱动器被configuration和安装,我将/etc/fail2ban/fail2ban.conf中的logtarget改为EFS驱动器中的一个子文件夹。
logtarget = /efsmount/fail2ban/server1.log
然后我写了一个简单的filter,并把它放在/etc/fail2ban/filter.d/fail2ban-log.conf
[Definition] failregex = .* Ban <HOST> ignoreregex =
将filter添加到/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通过filter的命令。
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时有一个错误。 经过一番谷歌search,我发现这是迄今为止工作。 从我所读到的,因为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!
是的,是的。 两者都可以做到。
您需要find合适的机制来共享IP列表。 如果您使用AWS,您可以利用s3。 您可以在Linux主机之间使用rsync,也可以在所有主机上使用通用数据库。 你可以用你最喜欢的编程语言来打造一个服务,提供一个宁静的APIselect是你的。
就如果公开共享列表,你可以创build一个网站和一个简单的文本文件,一些已经提供了这样的名单(不是我所知道的来源)。 如何创build自己的网站/服务将超出答案的范围,但不应该是非常困难的。
Is there an easy way to share banned IPs between hosts I control?
一个相当手动的设置是改变调用iptables
的configuration来更新规则,以便它调用你自己devise的脚本,它通过主机列表(从文件读取?)循环,并通过SSH对每个iptables
调用。 您需要在为此工作的所有主机之间进行基于密钥的身份validation。 像puppet这样的pipe理自动化工具可以使这个设置起来,并使其更容易维护。 这不会是非常有效的,但除非你看到大量的探测stream量(和/或有大量的主机),那么我相信这将是足够好的。 如果你只有几个主机,那么你甚至不需要遍历一个文件:configuration每个只需要调用其他的。 脚本的努力将是最小的。
Is there a way to share banned IPs publicly?
毫无疑问,有很多方法。 让上面的脚本将数据放到数据库中,然后让客户端读取数据,轮询新规则并在进入时运行它们。简单的“如你所见,运行规则”将不会是完美的主机正在提交信息,例如这种情况:
但是这不应该是一个重大的问题,如果你对数据库更聪明一些,如果你认为这是值得的努力,你可以更干净地pipe理多个提交。
作为一个公共服务运行,会让你走向pipe理麻烦的世界:
另一种fail2ban
是DenyHosts ,它具有同步function。 安装与fail2ban
非常相似,请参阅Cyberciti的教程了解更多详情 。
问题是, 同步服务是集中的,服务器端的源代码似乎不可用,所以你不能轻易地启动你自己的DenyHosts服务,你必须依靠第三方(这可能是一些罚款用例)。