fail2ban で対象 IP アドレスの第 2 オクテット(Class B)から遮断する

# 概要

某国からの DDoS 攻撃を fail2ban を使って防御したので、その時の設定をメモしておきます。
（設定は鵜呑みにせずに検証はしてください。）

当初 IP アドレス単位で Ban してたのですが、アクセスが多すぎて fail2ban がメモリを使いすぎて、
SWAP が発生してしまったので、対象 IP アドレスの第 2 オクテット(Class B)から遮断しました。

# 環境

-   OS: CentOS release 6.9 (Final)
-   Python: 2.6.6
-   Apache: 2.2.15
-   fail2ban: 0.9.6

# fail2ban のインストール

```
# sudo su -
# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum install --enablerepo=epel fail2ban
```

# fail2ban の用語の定義

-   filter : 対象のログパターンを正規表現で定義する
-   action : filter にマッチした際に実行されるコマンドを定義する
-   jail : 1つの filter と 1つ以上の action を組み合わせ

# fail2ban の典型的なフォルダ構成

公式のドキュメントにも書いてありますが、 
**`.conf` は編集せず `.local` ファイルにカスタマイズしたい内容だけを設定するようにしましょう**。
そうすることで、アップグレード時のマージ問題を避けられるそうです。

> Every .conf file can be overridden with a file named .local. The .conf file is read first, then .local, with later settings overriding earlier ones. Thus, a .local file doesn't have to include everything in the corresponding .conf file, only those settings that you wish to override.
> 
> Modifications should take place in the .local and not in the .conf. This avoids merging problem when upgrading. These files are well documented and detailed information should be available there. 

```
/etc/fail2ban/
├── action.d
│   ├── dummy.conf
│   ├── hostsdeny.conf
│   ├── iptables.conf
│   ├── mail-whois.conf
│   ├── mail.conf
│   └── shorewall.conf
├── fail2ban.conf ← メイン設定。編集しないこと。
├── fail2ban.local ← メイン設定をカスタマイズする場合に作成する。
├── filter.d
│   ├── apache-auth.conf
│   ├── apache-noscript.conf
│   ├── couriersmtp.conf
│   ├── postfix.conf
│   ├── proftpd.conf
│   ├── qmail.conf
│   ├── sasl.conf
│   ├── sshd.conf
│   └── vsftpd.conf
├── jail.conf ← ジェイル設定。編集しないこと。
└── jail.local ← ジェイル設定をカスタマイズする場合に作成する。
```

# DDos 攻撃を受けた時にやったこと

## 1. filter の作成

テンプレートがいくつか用意されているので、似た条件の filter ファイルを探してコピーするのが間違いが少ないと思います。
今回は Apache を使用した Web サーバへのアタックだったので `apache-badbots.conf` をコピーして filter を作成しました。

`failregex` にマッチさせる条件を正規表現で設定します。
下のサンプルは Apache のアクセスログを想定してマッチ条件を書いてるので適宜置き換えてください。

```/etc/fail2ban/filter.d/apache-test.conf
[Definition]

failregex = ^<HOST> -.*"GET /icons/poweredby.png.*HTTP.*".*$
ignoreregex =
```

## 2. jail.local の作成

`jail.conf` の設定からカスタマイズしたい項目だけを設定します。
逆に言うと、ここで設定しない項目は `jail.conf` の設定が反映されてしまうので、
`jail.conf` を読みながら `jail.local` を作っていくのが良いかと思います。

`jail.local` の中で更に DEFAULT の設定を個別の jail の設定が上書きします。

まずは 1. で作成したマッチ条件が想定した通りかを dummy アクションを使用して、
動作確認することがおすすめです。

```/etc/fail2ban/jail.local
# デフォルトの設定
[DEFAULT]
# ここに設定された IP アドレスは Ban 対象外
ignoreip = 127.0.0.1/8

# filter 条件に何回マッチしたら Ban をするか
maxretry = 5

# ここで設定された時間内に "maxretry" で設定した回数マッチすると Ban される
# あまり長い時間設定するとメモリが枯渇するので 600 秒程度から開始するのが良さそう
findtime = 600

# Ban される時間を秒単位で設定
bantime  = 600

# 基本的には auto で問題ない
# 気になる人は jail.conf を読みましょう
backend  = auto

# jail 個別の設定
[apache-test]

# true にしなければ jail は開始されない
enabled  = true

# jail.conf で全てのポートが指定されているので、
# 個別に指定しないと全ポートが対象になる
port     = http,https

# フィルタ名を指定。ここでは 1. で設定したフィルタを設定。
filter   = apache-test

# アクションを設定。まずは dummy でフィルタを試す。
action   = dummy

# 対象をログを指定
logpath  = /var/log/httpd/access_log
```

## 3. fail2ban の起動

-   起動

```
# service fail2ban
```

-   起動している jail を確認

```
# fail2ban-client status
Status
|- Number of jail:	1
`- Jail list:	apache-test
```

-   jail 名を指定すると詳細が確認できます

```
# fail2ban-client status apache-test
Status for the jail: apache-test
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	0
|  `- File list:	/var/log/httpd/access_log
`- Actions
   |- Currently banned:	0
   |- Total banned:	0
   `- Banned IP list:
```

## 4. ログ確認

dummy アクションは、マッチした場合はログに出力のみ行うので、filter が正しく設定されているかを確認できます。
ログが出ていないようであれば、何かしら設定が間違っているので見直ししましょう。

```
2018-09-16 13:55:37,229 fail2ban.filter         [3505]: INFO    Log rotation detected for /var/log/httpd/access_log
2018-09-16 13:55:37,283 fail2ban.filter         [3505]: INFO    [apache-test] Found 192.168.xxx.xxx
2018-09-16 13:55:37,571 fail2ban.actions        [3505]: NOTICE  [apache-test] Ban 192.168.xxx.xxx
```

ちなみにログの場所はデフォルトは SYSLOG になっているので `/var/log/messages` に出力されます。
ただ、それだと可読性が悪いので個別のログに出力するのが良いと思います。

rsyslog を使用しているのであれば、rsyslog の設定を追加して反映してあげましょう。
それから、**ログローテートの設定は忘れずに**。

```/etc/rsyslog.d/fail2ban.conf
if $programname startswith 'fail2ban' then /var/log/fail2ban.log
if $programname startswith 'fail2ban' then stop
```

## 5. action の作成

filter の動作確認が出来たら action を作っていきます。
filter と同様にテンプレートがいくつか用意されているので、似た条件の action ファイルを探してコピーします。

今回は `iptables-allports.conf` をコピーして作成しました。
今回は対象 IP アドレスの第 2 オクテット(Class B)まで遮断したかったのと、
http, https のみ Ban したかったので、actionban と actionunban を変更してます。

```iptables-http-ports-class-b.conf
[INCLUDES]

before = iptables-common.conf


[Definition]

# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart = <iptables> -N f2b-<name>
              <iptables> -A f2b-<name> -j <returntype>
              <iptables> -I <chain> -p <protocol> -j f2b-<name>

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
             <iptables> -F f2b-<name>
             <iptables> -X f2b-<name>

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionban = <iptables> -I f2b-<name> 1 -s <ip>/16 -m multiport -p tcp --dports 80,443 -j <blocktype>

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionunban = <iptables> -D f2b-<name> -s <ip>/16 -m multiport -p tcp --dports 80,443 -j <blocktype>

[Init]
```

## 6. jail.local の action 変更

`jail.local` の action 部分を書き換えます。
引数の name は iptables の CHAIN 名になるので、判別しやすい名前にしましょう。

```/etc/fail2ban/jail.local
action = dummy

↓↓↓ 書き換え

action = iptables-http-ports-class-b[name=ApacheTest]
```

## 7. fail2ban-client のリロード

設定に問題がなければリロードして反映します。

```
# fail2ban-client reload
```

正常に遮断が出来ているか確認を忘れずに。

```
# iptables -nvL
# tailf /var/log/fail2ban/fail2ban.log
```

## 8. fail2ban の自動起動 ON

正常に遮断されていることを確認できたら fail2ban の自動起動を ON にしておきます。

```
# chkconfig fail2ban on
# chkconfig --list fail2ban
fail2ban       	0:off	1:off	2:on	3:on	4:on	5:on	6:off
```