CentOS7 繰り返される不正アクセスをfail2banで排除

毎日毎日性懲りもなくあちらの国から来る不正アクセスを自動的に排除したい！という時に助かるfail2banというソフトの導入手順を記載します。
https://github.com/fail2ban/fail2ban
ライセンスはGNU GPL v2です。

(2020.03.13)[recidive]ルールの`enabled`指定が`enable`になっていたので修正

fail2banは、超ざっくり説明すると各デーモンのログファイルを監視して、排除したいホストに対するIPフィルタルール等を動的に作成してくれるソフトです。

少し詳しく説明すると以下のような感じです。

（１）アタックと思しきログ（↓こんなの）に対して
　postfix/smtpd[xxxxxx]: warning: unknown[xxx.xxx.xxx.xxx]: SASL LOGIN authentication failed

（２）ログレコードにマッチする正規表現（↓こんなの）を登録する
　failregex = ^%(__prefix_line)swarning: [-._\w]+\[\<HOST\>\]: SASL (?:LOGIN|Login|login|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$

（３）ログを監視して正規表現とマッチしたら、アタック間隔(1分間に何回来てるか等)に基づき、BAN対象か判別する

（４）BAN対象と判断したら、アタック元IPアドレス（上記（２）における\<HOST\>）をIPフィルタルール(iptablesとか)に自動登録する

（５）BAN対象期間（30分等）が過ぎたらIPフィルタルールを削除する



sshやメール認証だけでなく、exploit目的で存在しないurlを叩きまくってくる奴なんかもBANできます。

今回はそのようなhttp/httpsによる大量の403/404を弾く設定をします。
ログの書式が同一であれば、webサーバはnginx、apacheどちらも（リバースプロキシでも）同じような設定で良いと思われます。

参考にさせて頂いたサイト：
[EC2でnginxの過剰な404|403に対しfail2banをかける](https://qiita.com/osamu1203/items/e7b1718caf4fa59dca4f)

---
###インストール

CentOS7での手順です。
epelリポジトリを追加する必要があります。

```
# yum install epel-release -y
# yum install fail2ban -y
```

以下のバージョンがインストールされました

```
fail2ban.noarch      0.10.5-2.el7
```

---
###設定用ファイルの作成

```
# cd /etc/fail2ban
```

https://github.com/fail2ban/fail2ban/wiki/Proper-fail2ban-configuration
> [Q] Should I make my configuration directly in jail.conf and fail2ban.conf?
> [A] No. You should avoid to change .conf files, created by fail2ban installation. Instead, you'll write new files having .local extension.

との事なので、（アプデで上書きされる恐れのある）`fail2ban.conf`と`jail.conf`はいじらず、`.conf`をコピーした`.local`を作成して設定していきます。

```
# cp jail.conf jail.local
```

基本的には`jail.local`にBAN定義を追加していきます。
`./jail.d/`配下に、ルール毎の`.local`を作って管理しても良いです。

`.conf`と`.local`とサブディレクトリの詳細な優先順位は、
https://manpages.debian.org/experimental/fail2ban/jail.conf.5.en.html

> ./jail.conf
> ./jail.d/\*.conf (in alphabetical order)
> ./jail.local
> ./jail.d/\*.local (in alphabetical order)

の順で、上書きされていくとの事です。`./jail.d/*.local`が最優先という事ですね。

---
###正規表現フィルタ定義の作成

まずは、監視対象のログファイル（不正アクセスをしてきたホストIPが記載されているもの）に基づき、アタックと認識する為の正規表現を定義します。

今回はhttp/httpsの403/404エラーを弾きたいので、以下のようなログが、正規表現でマッチしたいログとなります（nginxのアクセスログですがapacheでも同じような書式かと）。

```/var/log/nginx/access.log
xxx.xxx.xxx.xxx - - [12/Mar/2020:17:03:10 +0900] "GET /wordpress/license.txt HTTP/1.1" 404 295 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" "-"
```

このログにマッチする正規表現を記載した定義ファイルを、`./filter.d/`の配下に拡張子`.conf`として作成します。ファイル名は後でも使用するルール名になりますので忘れず控えてください。ここでは`web-404`としました。

```../filter.d/web-404.conf
[Definition]
failregex = ^<HOST>.*"(GET|POST).*" (403|404) .*$
ignoreregex = \.(?i)(jpe?g|gif|png|bmp|pdf|js|css|woff|eot|ttf|ico|txt|xml|swf|xlsx?|docx?|pptx?)
```
**failregex**
アタックと認識したいレコードにマッチする正規表現を定義します。

**ignoreregex**
ホワイトリストです。この正規表現にマッチするとBAN対象にしません。上記では画像やドキュメントファイル等の拡張子を対象としたnot foundに関してはアタックとみなしません。
`favicon.ico`や`robots.txt`がない場合に404を吐いてしまい正規のアクセスまで弾いてしまうという事を防止できますが、例えば上記のようなwordpressの`license.txt`を取得しようとするアクセスもOKとみなしてしまう為、慎重に検討してください。


---
###BAN定義ファイルの作成

フィルタ定義を作成したら、次はフィルタにマッチしたホストをどのようにBANするかを設定に追加します。

```../jail.local
[web-404]
enabled  = true
filter   = web-404
action   = iptables-multiport[name=web-404, port="http,https", protocol=tcp]
           sendmail-whois[name=web-404, dest=root, sender=fail2ban, sendername="Fail2Ban"]
logpath  = /var/log/nginx/access.log
bantime  = 30m
findtime = 10m
maxretry = 5
```
**[ルール名]**
他と重複のないルール名を指定
**enabled**
trueで有効化
**filter**
ここで指定した名称の`./filter.d/*.conf`を使用してログファイルを監視しなさいという設定です。
**action**
BANの処理についてはここで指定した名称の`./action.d/*.conf`を使用しなさいという設定です。上記の例では`./action.d/iptables-multiport.conf`という、複数のポートを対象としたiptables用の設定を使ってIPフィルタを作成します。二行目はBANした事をメールで通知を受けたい場合の設定です。`./action.d/sendmail-whois.conf`が使われます。
**logpath**
監視するログファイルのパスを指定します。不正アクセス元ホストIPが記載されているものである必要があります。
**findtime**
**maxretry**
findtime時間中にmaxretry回のフィルタマッチでBAN対象とみなす設定。例では1分間に5回の403/404を検出したらBAN（上記のactionを実行）となります。
**bantime**
どのくらいの期間BANするか、例では30分。

上記の例をまとめると、
http/httpsによる接続で10分間に5回以上の403/404エラーを検出したホストをiptablesにより30分間アクセス禁止にし、rootにメールするという設定になります。
(2020.03.18変更)1分間に5回でBANを例としていましたがデフォルトの設定通り10分間に5回へと変更

iptablesではなくfirewalldを使用している場合は、`./action.d/firewallcmd-multiport.conf`を使用する書式に書き換えて下さい。


尚、v0.10では以下のデフォルト定義を元に、上記のiptables-multiportとsendmailへのactionを1行で書けるようになっていました。

```
[DEFAULT]
destemail = root@localhost
sender = fail2ban@<fq-hostname>
mta = sendmail
protocol = tcp
banaction = iptables-multiport
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
             %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]

[web-404]
enabled  = true
port     = http,https
action   = %(action_mwl)s
logpath  = /var/log/nginx/access.log
bantime  = 30m
findtime = 10m
maxretry = 5
```

filter項を省略しても、ルール名から`./filter.d/web-404.conf`が参照されるようです。

---
###悪質なホストに対する長期BAN設定

設定ファイルの下の方に、一日に何度もBANされている悪質なホストを長期間BANする[recidive]というルールのひな形があります。
必要ならenableにしましょう。fail2ban.log自体を監視しているルールとなります。

```
[recidive]
enabled = true
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime  = 1w
findtime = 1d
maxretry = 5
```

上記の例では、何かのルールで1日に5回BANされたホストを1週間BANします。
逆に言うと1日に5回BANされなければルールに当てはまらない為、
bantimeを中途半端に長くしてしまっていると（例えば10hとか）recidiveルールが適用されません。

正規表現フィルタの定義は`./filter.d/recidive.conf`が使われます。

---
###正規表現フィルタ定義のテスト

ここまで終わるとfail2banを起動できますが、その前に作成した正規表現フィルタが正しくアタックログにマッチするかテストしておきます。
`fail2ban-regex`というツールに`-v`オプションを付けて実行すると、マッチした行が見えるテストができます。

```
# fail2ban-regex --help
Usage: /usr/bin/fail2ban-regex [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]

# fail2ban-regex -v /var/log/nginx/access.log /etc/fail2ban/filter.d/web-404.conf

Results
=======

Failregex: 2 total
|-  #) [# of hits] regular expression
|   1) [2] ^<HOST>.*"(GET|POST).*" (403|404) .*$
|      xxx.xxx.xxx.xxx  Thu Mar 12 03:52:39 2020
|      xxx.xxx.xxx.xxx  Thu Mar 12 10:16:45 2020
`-

Ignoreregex: 67 total
|-  #) [# of hits] regular expression
|   1) [67] \.(?i)(jpe?g|gif|png|bmp|pdf|js|css|woff|eot|ttf|ico|xml|swf|xlsx?|docx?|pptx?)
`-
`-

(省略)

Lines: 588 lines, 67 ignored, 2 matched, 519 missed
```

Failregexの項目に、フィルタがマッチした行が表示されます。最後の行には、ログファイルのレコード数、除外したレコード数、マッチしたレコード数、マッチしなかったレコード数がわかります。上記のテストでは588行中、2行のログが不正アクセスとして検出されました。

---
###サービス起動と動作テスト


問題がなければサービスに登録して起動します。

```
# systemctl enable fail2ban 
# systemctl start fail2ban
```


本当にBANされるかテストしてみます。試しに存在しないURLを連続5回叩き、fail2banのログを見てみます。

```
# tail /var/log/fail2ban.log
2020-03-12 17:02:53,398 fail2ban.jail           [19885]: INFO    Jail 'web-404' started
2020-03-12 17:03:08,651 fail2ban.filter         [19885]: INFO    [web-404] Found xxx.xxx.xxx.xxx - 2020-03-12 17:03:08
2020-03-12 17:03:09,254 fail2ban.filter         [19885]: INFO    [web-404] Found xxx.xxx.xxx.xxx - 2020-03-12 17:03:08
2020-03-12 17:03:09,456 fail2ban.filter         [19885]: INFO    [web-404] Found xxx.xxx.xxx.xxx - 2020-03-12 17:03:09
2020-03-12 17:03:10,060 fail2ban.filter         [19885]: INFO    [web-404] Found xxx.xxx.xxx.xxx - 2020-03-12 17:03:09
2020-03-12 17:03:10,262 fail2ban.filter         [19885]: INFO    [web-404] Found xxx.xxx.xxx.xxx - 2020-03-12 17:03:10
2020-03-12 17:03:10,641 fail2ban.actions        [19885]: NOTICE  [web-404] Ban xxx.xxx.xxx.xxx
```

5回の検出の後、BANされたログが確認できました。という事はiptablesにIPフィルタルールが追加されている筈なので確認してみます。

```
# iptables -nvL
Chain f2b-web-404 (1 references)
 pkts bytes target     prot opt in     out     source               destination
   76  4276 REJECT     all  --  *      *       xxx.xxx.xxx.xxx      0.0.0.0/0            reject-with icmp-port-unreachable
    6   240 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0
```

`f2b-web-404`というチェインのsourceにBANしたIPが登録されていました。


最後に、BANを解除する手順です。
`fail2ban-client`というツールに対象ルール名と、unbanipというコマンドを発行するとルールが消えます。

```
# fail2ban-client set web-404 unbanip xxx.xxx.xxx.xxx
1
```

結果が「1」とそっけないので確認しておきます。

```
# iptables -nvL
Chain f2b-web-404 (1 references)
 pkts bytes target     prot opt in     out     source               destination
  160 16886 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0
```

ちゃんと消えてました。


以上で終わりです。繰り返しになりますが、sshやメール認証への不正アクセスも同様の手順で検知・BANできます。正規表現フィルタを公開して下さっている方もいらっしゃいますので自作が困難であれば探してみて下さい。