Apache の DoS攻撃対策モジュール mod_evasive

# mod_evasive (mod_dosevasive)
[mod_evasive](https://github.com/jzdziarski/mod_evasive) は [Jonathan Zdziarski](https://www.zdziarski.com/blog/?page_id=202) によって [mod_dosevasive](https://web.archive.org/web/20050828190834/http://www.nuclearelephant.com/projects/dosevasive/) として開発され、2002年10月に 1.0.0 がリリースされた。 [^changelog]
2005年10月の 1.10.1 のリリースで「発音を簡単にするためと皆の舌の寿命を保つため」[^mod_dosevasive]に mod_evasive に名称を変更したのを最後に更新されていない。 [^changelog]

[^changelog]: <q cite="https://github.com/jzdziarski/mod_evasive/blob/master/CHANGELOG">Version 1.0.0 [20021015] jonz: Initial Release</q> - <cite>[mod_evasive/CHANGELOG at master · jzdziarski/mod_evasive](https://github.com/jzdziarski/mod_evasive/blob/master/CHANGELOG)</cite>

[^mod_dosevasive]: <q cite="https://web.archive.org/web/20050908131021/http://www.nuclearelephant.com/projects/dosevasive/">For the sake of pronunciation's simplicity and to preserve the life of your tongue, mod_dosevasive is now known simply as mod_evasive</q> - <cite>[http://www.nuclearelephant.com/projects/dosevasive/](https://web.archive.org/web/20050908131021/http://www.nuclearelephant.com/projects/dosevasive/)</cite>

最終リリース日は2005年10月8日であるものの、 [EPEL 7 リポジトリ](https://fedoraproject.org/wiki/EPEL/ja)にパッケージが2015年6月16日付けでビルドされていたり、セキュリティ監査ツール [Lynis](http://qiita.com/bezeklik/items/cf9be2deb546c69123e7) に DoS 攻撃対策としてインストールするようサジェストされるなど、依然として知名度が高い。

同様の Apache モジュールとして、元はてな CTO の田中慎司氏による [mod_dosdetector](https://github.com/stanaka/mod_dosdetector) や黒沢剛氏による [doshelper](https://github.com/kurosawatsuyoshi/doshelper)、David Jao による [mod_limitipconn](https://dominia.org/djao/limitipconn.html) などがある。

## evasive
evasive とはあまりなじみのない単語だが、発音は [ɪvéɪsɪv] で「回避的な」という意味。

## インストール
```bash
yum install epel-release
yum install mod_evasive
systemctl restart httpd
```
オリジナルの mod_evasive 1.10.1 は Apache 2.2 までしか対応していないが、 EPEL リポジトリのパッケージでは Apache 2.4 に対応されている。

### 確認
```apache:/etc/httpd/conf.d/mod_evasive.conf
LoadModule evasive20_module modules/mod_evasive24.so
```
/usr/lib64/httpd 配下の modules/mod_evasive24.so が `evasive20_module` としてロードされる。

```bash
httpd -M | grep evasive
```
```text:出力例
 evasive20_module (shared)
```
Apache の `-M` オプションでモジュールがロードできているか確認できる。 [^httpd-m]

[^httpd-m]: <q cite="https://linux.die.net/man/8/httpd">-M     Dump a list of loaded Static and Shared Modules.</q> - <cite>[httpd(8)](https://linux.die.net/man/8/httpd)</cite>


## 設定
設定ファイルは /etc/httpd/conf.d/mod_evasive.conf になる。

#### デフォルト値
|設定項目              |デフォルト値|内容|
|:--              |:-:|:--|
|DOSHashTableSize |3097|ハッシュテーブルサイズ|
|DOSPageCount     |2   |最大ページヒット数/間隔|
|DOSSiteCount     |50  |最大サイトヒット数/間隔|
|DOSPageInterval  |1   |page interval|
|DOSSiteInterval  |1   |site interval|
|DOSBlockingPeriod|10  |ブロック時間(秒)|
|DOSEmailNotify   |    |通知先メールアドレス|
|DOSSystemCommand |    |連動実行コマンド|
|DOSLogDir        |/tmp|ログ保存ディレクトリ|
|DOSWhitelist     |    |ホワイトリスト(IPアドレス)|

デフォルト値は [mod_evasive.c](https://github.com/jzdziarski/mod_evasive/blob/master/mod_evasive.c#L50) で定義されている。

#### 設定項目

##### DOSHashTableSize
ハッシュテーブルサイズは各子供のハッシュテーブルのトップレベルノードの数を定義する。
この数を増やすと、レコードに到達するために必要な反復回数を減らすことで高速なパフォーマンスが得られるが、テーブルスペースとしてより多くのメモリを消費する。
ビジー状態の Web サーバを使用している場合は、この値を大きくする必要がある。

指定した値は、自動的に素数リストの次の素数まで階層化される (使用される素数のリストについては [mod_evasive.c](https://github.com/jzdziarski/mod_evasive/blob/master/mod_evasive.c#L259) を参照)。

##### DOSPageCount
`DOSPageInterval` ごとの同じページ（またはURI）に対するリクエスト数の閾値。
その間隔の閾値を超えると、そのクライアントのIPアドレスはブロックリストに追加される。

##### DOSSiteCount
`DOSSiteInterval` ごとに、同じリスナー上の同じクライアントによる、任意のオブジェクトに対するリクエストの合計数の閾値。 
その間隔の閾値を超えると、そのクライアントのIPアドレスがブロックリストに追加される。

##### DOSPageInterval
そのページでの閾値を設定する間隔。デフォルトは1秒。

##### DOSSiteInterval
そのサイトでの閾値を設定する間隔。デフォルトは1秒。

##### DOSBlockingPeriod
ブロック期間 (blocking period) とは、クライアントがブロックリストに追加された場合にブロックされる時間（秒）のこと。
この間、クライアントからのそれ以降のリクエストはすべて 403 (Forbidden) となり、タイマーはリセットされる (例: さらに10秒)。
タイマーは後続のリクエストごとにリセットされるので、長いブロック期間を設ける必要はない。DoS 攻撃の場合、このタイマーはリセットされ続ける。

##### DOSEmailNotify
この値を設定すると、IPアドレスがブラックリストに登録されるたびに、指定したアドレスにメールが送信されるようになる。
`/tmp` を利用したロック機構により、連続したメールの送信を防ぐことができる。

注意： mailx の `/bin/mail` が必要。

```
DOSEmailNotify      root
```

###### メールの件名の変更
```
DOSEmailNotify      "-s \"mod_evasive: Added IP address to the blacklist on ${HOSTNAME}\" root"
```
デフォルトのメールの件名は「HTTP BLACKLIST \<IPアドレス\>」で固定だが、 `DOSEmailNotify` は `"/bin/mail -t %s"` に代入されるためバッドノウハウとして mailx の `-s` オプションを追加することで件名を任意のものに変更することができる。ただし、本文にも出力されてしまう。

ちなみにハードコーディングされている `-t` オプションは余計なメールヘッダーの破棄を指示するもので `To:` の意味ではない。 [^mailx-t]

[^mailx-t]: <q cite="https://linux.die.net/man/1/mailx">-t     The message to be sent is expected to contain a message header with `To:`, `Cc:`, or `Bcc:` fields giving its recipients.  Recipients specified on the command line are ignored.</q> - <cite>[mailx(1)](https://linux.die.net/man/1/mailx)</cite>

##### DOSSystemCommand
この値を設定すると、IPアドレスがブラックリストに登録されるたびに、指定されたシステムコマンドが実行される。これは、IPフィルターや他のツールのシステムコールを可能にするために設計されている。
`/tmp` を用いたロック機構により、連続したシステムコールを防ぐことができる。
ブラックリストに登録されたIPアドレスを表すには `%s` を使用する。

```
DOSSystemCommand    "su - someuser -c '/sbin/... %s ...'"
```

###### 独自のメールアラートを送信する
`DOSEmailNotify` ではメールの本文をコントロールできないが、例えば以下のように指定することで IP アドレスに関する情報を付加して自前でメールを送ることができる。この場合 `DOSEmailNotify` を指定する必要はない。
```
DOSSystemCommand    "export IP=%s API=http://ip-api.com/line/${IP} ; /bin/echo -e \"mod_evasive HTTP Blacklisted ${IP}\n\n$(/bin/curl -s ${API})\" | /bin/mail -s \"mod_evasive: Added IP address ${IP} ($(/bin/curl -s ${API}?fields=country)) to the blacklist on ${HOSTNAME}\" root"
```

##### DOSLogDir
デフォルトでは `/tmp` がロック機構に使用されるが、システムがシェルユーザーに開かれている場合、いくつかのセキュリティ上の問題が発生する。
[http://security.lss.hr/index.php?page=details&ID=LSS-2005-01-01](https://web.archive.org/web/20090629015125/http://security.lss.hr:80/index.php?page=details&ID=LSS-2005-01-01)

非特権シェルユーザーがいる場合、Apache が実行されているユーザーにのみ書き込み可能なディレクトリを作成し、httpd.conf で設定する。

```
DOSLogDir           "/var/lock/mod_evasive"
```

保存先として設定ファイルのコメントで例示されている `/var/lock` は、 [FHS 3.0](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s09.html) でロックファイルの保存先として規定されている。

```bash
mkdir --parents --verbose /var/lock/mod_evasive
chown --changes apache:apache /var/lock/mod_evasive
```

##### DOSWhitelist
ホワイトリストを使用して特定の範囲の IP に対してモジュールを無効にすることができる。
必要であれば最後の3オクテットまでワイルドカードを使用することができる。
複数の `DOSWhitelist`を設定に使用することができる。

## 動作テスト
mod_evasive の有効性をテストするための小さなスクリプト [test.pl](https://github.com/jzdziarski/mod_evasive/blob/master/test.pl) が付属している。

```bash
perl /usr/share/doc/mod_evasive-1.10.1/test.pl
```
```text:出力例
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 403 Forbidden
HTTP/1.1 403 Forbidden
HTTP/1.1 403 Forbidden
```

## ログ
##### /var/log/messages
```bash
grep 'mod_evasive' /var/log/messages*
```
```
/var/log/messages:Jan  1 00:00:00 localhost yum[2705]: Installed: mod_evasive-1.10.1-22.el7.x86_64
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[12385]: Blacklisting address 192.0.2.1: possible DoS attack.
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[14201]: Blacklisting address 192.0.2.1: possible DoS attack.
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[14222]: Blacklisting address 192.0.2.1: possible DoS attack.
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[14303]: Blacklisting address 192.0.2.1: possible DoS attack.
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[14325]: Blacklisting address 192.0.2.1: possible DoS attack.
/var/log/messages:Jan  1 00:00:00 localhost mod_evasive[14360]: Blacklisting address 192.0.2.1: possible DoS attack.
```
/var/log/messages にブロックしたIPアドレスのログが出力される。

## Fail2ban
Fail2ban をインストールしている場合、mod_evasive のログでフィルターを作成できる。

```ini:/etc/fail2ban/filter.d/apache-mod_evasive.conf 
# Fail2Ban apache-mod_evasive filter
#

[Definition]
failregex   = Blacklisting address <HOST>: possible DoS attack.
ignoreregex =
```
```ini:/etc/fail2ban/jail.local
[apache-mod_evasive]
enabled  = true
port     = http,https
logpath  = %(syslog_local0)s
maxretry = 1
```
```bash
fail2ban-regex /var/log/messages /etc/fail2ban/filter.d/apache-mod_evasive.conf --print-all-matched
fail2ban-client status apache-mod_evasive
```
`syslog_local0` は /etc/fail2ban/paths-common.conf で `/var/log/messages` と定義されている。
https://briansnelson.com/Howto_install_mod_evasive_with_fail2ban