公開されているサービスを備えたすべてのサーバーでfail2banを使用しています。
このサーバーをセットアップした1日目から、私は無数のログイン試行を繰り返してきました。
Fail2banデータを集中化するシステム このサイト上 を見て、修正バージョンを作成したことがあります。データベースは同じですが、いくつかのスクリプトを変更して作成しました。
私のシステムには4つのコンポーネントがあります。
fail2banデータベース
これは、1つのテーブルのみを含む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");
</ code>
} // $ idは最後に禁止されたホストを含み、それを設定ファイルに追加します file_put_contents($ lastbanfile、$ id) ; ?>
phpconfig
このファイルは/ etc/fail2banに移動し、データベース構成とjailの選択があります。
<?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>
すべてのサーバーでこの構造を使用すると、1つのホストが1つのサーバーで禁止されるたびに、他のすべてのサーバーでも禁止されます。
そのため、同じIPアドレスがWebサーバーのクラスターに次々とヒットするのを見た後、これを行う方法について多くの調査を行いました。私はAWSを使用しているので、簡単な方法があり、5台のサーバーをテストする最初の2日間でうまく機能する可能性があると考えました。
私が最初にお勧めするのは、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は開始されません。これがこれをデプロイしようとしている人を助けることを願っています。
fail2ban
の代替は、同期機能に付属する DenyHosts です。インストールはfail2ban
とかなり似ています。詳細については、 Cybercitiのチュートリアルを参照してください 。
問題は、 同期サービス が集中化されており、サーバー側のソースコードが利用できないように見えるので、簡単に開始できませんDenyHostsサービスを所有していて、サードパーティに依存する必要がある(一部のユースケースでは問題ない場合があります)。
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?
間違いなく多くの方法があります。上記のスクリプトでデータをDBにドロップし、クライアントからデータを読み取って、新しいルールをポーリングし、それらが実行されたときに実行します。単純な「見たとおりにルールを実行する」というのは、多くの場合完璧ではありません。ホストは情報を送信しています、たとえばこの場合:
しかし、これは重大な問題ではないはずです。データベースをもう少し巧妙にすると、努力する価値があると判断した場合、複数の送信をよりきれいに管理できます。
しかし、それをパブリックサービスとして実行すると、管理者の煩わしさの世界が開かれます。
はい、はい。どちらも行うことができます。
IPのリストを共有するには、適切なメカニズムを見つける必要があります。たとえばAWSを使用している場合は、s3を利用できます。 Linuxホスト間、またはすべてのホストに共通のデータベース間でrsyncを使用できます。お気に入りのプログラミング言語を使用して、安らかなAPIを提供するサービスを利用して、選択肢を選ぶことができます。
リストを公に同盟国と共有する場合、ウェブサイトを作成して簡単なテキストファイルをホストすることができます。一部はすでにそのようなリストを提供しています(私が知っているクラウドソースではありません)。独自のサイト/サービスを作成する方法は回答の範囲外ですが、実行するのはそれほど難しくありません。