fail2banでBotからsshdとNginxを守る

久しぶりにfail2banを使ったら設定方法を調べるのに苦戦したのでメモ

やりたいこと

  • 以下のようなアクセスを行なったIPアドレスをBotとみなす
    • SSHで認証に失敗したアクセスの送信元アドレス
    • HTTPやHTTPSでよくある攻撃対象のパスへアクセスしてきた送信元アドレス
  • BotとみなしたIPアドレスからのアクセスは以後すべてのポートでDROPするようにする
  • 一度BotとみなしたIPアドレスは永久にブロックする

環境

# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
# yum list fail2ban
fail2ban.noarch     0.9.7-1.el7     @epel

情報源

いろいろ調べたが結局manが一番参考になるし信用できる.

man fail2ban
man jail.conf
man ipset

インストール

# yum install -y fail2ban

設定ファイル類

設定ファイルは/etc/fail2banに集まっている.

  • fail2ban.conf
    fail2ban全般の設定を書く.変更するとしたらログレベルやログファイルのパスくらい.
  • jail.conf
    filterとactionの組み合わせ(この記事ではルールと呼ぶことにする)をここで定義していく.デフォルトでいくつか定義されているがすべて無効化されている.
    またjail.d/*.confも読み込むようになっている.
  • filter.d/*.conf
    どのログをどんな正規表現で監視するか(filter)を定義する
  • action.d/*.conf
    IPをブロックする方法(action)を定義する

設定ファイルの書き方

デフォルトでは.confファイルが作成されているが,これらのファイルは編集せずに同名の.localファイルを同じディレクトリに作る方法が推奨されている. .confのうち上書きしたい部分だけを.localファイルに書けばその部分だけ上書きされる.

jailの設定は各ルール共通の設定をjail.localに書き,各ルールの設定と有効化をjail.d/*.localに書くことにするときれいに管理できそう.

fail2banのしくみ

  1. jailのlogpathに設定したログファイルをtail
  2. jailのfilterに設定した条件(ログイン失敗など)に一致したログをカウント
    このときログに含まれる「アクセス元IPアドレス」も認識しておく
  3. 設定した頻度に逹したIPアドレスをブロック
    ブロックの手段はjailのbanactionで設定する

設定

上に書いたやりたいことを実現すべく設定していく.

SSHで認証に失敗したアクセスの送信元アドレスをBotとみなす

jail.confにははじめから以下の内容が書かれている(抜粋)

jail.conf:

[DEFAULT]
enabled = false
filter = %(__name__)s

[sshd]
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
  • sshdjailは定義されているが無効化されている
  • filter名はsshd(fail名と同名)
  • sshd_logの値は別ファイルで定義されている

パラメータはこのままでよさそうなのでこのjailを有効化する

jail.d/sshd.local:

[sshd]
enabled = true

HTTPやHTTPSでよくある攻撃対象のパスへアクセスしてきた送信元アドレスをBotとみなす

自分の環境ではNginxでこれらのポートをリッスンしている.これもやりたいことを実現できそうなjailがデフォルトで定義されている.

jail.conf:

[nginx-botsearch]
port     = http,https
logpath  = %(nginx_error_log)s
maxretry = 2

error¥_logを見るようになっているが自分の場合はaccess¥_logを見てリクエストのパスから判断したかったのでいくつか設定を変更した.filterも上書きしている.

filter.d/nginx-botsearch.local:

[Definition]
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" .+$
            ^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$
            ^<HOST> -.*"(GET|POST|PUT|HEAD) .*\.(php|jsp) HTTP\/.*$
            ^<HOST> -.*"GET https?\:\/\/.*/latest/meta-data HTTP\/.*$
ignoreregex =

jail.d/nginx-botsearch.local:

[nginx-botsearch]
enabled  = true
logpath  = %(nginx_access_log)s

BotとみなしたIPアドレスからのアクセスは以後すべてのポートでDROPするようにする

これはactionで設定する. このサーバではFirewalldを使用しているので,fail2banでもFirwalldを通して設定するようにしたい.また今回の設定では大量のIPアドレスをブロックすることになるのでipsetを使いたい.(iptablesに大量のルールを設定するとパフォーマンスに影響がありそうだったため) これを実現できそうなactionはデフォルトでは存在しなかったため,一番近そうなfirewallcmd-ipsetjailを改変することにした. fail2ban起動時にipsetを作成しDROPするようなルールをFirewalldで設定している(ダイレクトルールを使用). 検知時にipsetへIPアドレスを追加するようにする.

action.d/firewallcmd-ipset.local:

[Definition]
actionstart = ipset create fail2ban-<name> hash:ip
              firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -m set --match-set fail2ban-<name> src -j <blocktype>

actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m set --match-set fail2ban-<name> src -j <blocktype>
             ipset flush fail2ban-<name>
             ipset destroy fail2ban-<name>

actionban = ipset add fail2ban-<name> <ip> -exist

[Init]
blocktype = DROP

jail.local:

(抜粋)
[DEFAULT]
banaction = firewallcmd-ipset

一度BotとみなしたIPアドレスは永久にブロックする

jail.conf(jail.local)にはbantimeというパラメータがある

bantime
    effective ban duration (in seconds).

永久にBANしたい場合の値が読みとれなかったが,試してみたところ0にするとBANした瞬間にunBANの処理が実行されてしまった.よってここの値は便宜上-1にしておく.

jail.local:

(抜粋)
[DEFAULT]
bantime = -1

ここの値はactionの中で<bantime>のように書くことで使用できる.今回の場合はactionを自分で定義したが,上で書いた定義で永久BANが実現できている. ipsetはtimeoutオプションでエントリの寿命を設定できるが,何も指定しないと永久にエントリが残るようだった.

以上.

関連記事

comments powered by Disqus