fail2banでpostfixの認証失敗回数に応じて恒久的に送信元IPアドレスをbanする

## fail2banとは
[公式](http://www.fail2ban.org/wiki/index.php/Main_Page) / github: https://github.com/fail2ban/fail2ban

> Fail2Ban: ban hosts that cause multiple authentication errors

ログファイルを定期的に見て送信元IPアドレスをiptables等で一時的および恒久的に拒否するツールのこと。

ログの形式に対して対応できるので自作可能だが
標準で各種アプリケーションのログ監査と対処するルールがすでに用意されている。
例えば以下のとおり(0.11を参考）

- [apache-auth.conf](https://github.com/fail2ban/fail2ban/blob/0.11/config/filter.d/apache-auth.conf) : 認証を試みるアクセスに対応
- [sshd.conf](https://github.com/fail2ban/fail2ban/blob/0.11/config/filter.d/sshd.conf) : SSHブルートフォースアタックを検知
- [postfix.conf](https://github.com/fail2ban/fail2ban/blob/0.11/config/filter.d/postfix.conf) : エラーコードに対応。また認証失敗も検知可能。

他にも様々なルールがデフォルトで用意されている。

### インストール
導入は簡単。
Centos系なら `yum install fail2ban-server` をepelで。

### 設定
主に設定するファイルは以下。 `/etc/fail2ban`配下。

- `fail2ban.conf` ... メイン設定。ログレベルなど。データベースはsqlite3を使用
    - `/var/lib/fail2ban/fail2ban.sqlite3` に保存。
- `jail.conf` ... [INCLUDE]に注目。Centosならfedora系とみなして ` /etc/fail2ban/paths-fedora.conf` を参照している。
    - ここでログのデフォルトの設定を定義しているのでルール設定によっては参照される
    - `paths-common.conf`も参照される
    - `ignoreip`の設定は必要。送信元IPアドレスで除外したい対象を指定。カスタマイズルールで用意可能だが、基本参照として用意するのはあり。
    - ただしカスタマイズは別途jailを用意して上書き設定するのが基本

### 設定方法
今回の目的はpostfixのsasl認証に対してfail2banで制御したい。
ポート番号を開いてsasl認証を許しているとそれなりに認証を試みる動きはある。
その行動に対して動的にiptablesでREJECTしたい時のやり方です。

```:/etc/fail2ban/jail.d/postfix-sasl.conf
[postfix-sasl]
enabled = true
logpath = %(postfix_log)s
maxretry = 50
findtime = 600
bantime = 1800
backend = %(postfix_backend)s
```

基本IP単位で見るので、送信元IPアドレスが上記の条件を満たしたらfail2banのban候補になる。

- `enable` ... ture or falseで有効/無効。
- `logpath` ... これは `postfix_log` の定義を見る。望む設定と違うパスなら直接記載に変更
- `maxretry` ... パターンの試行回数の閾値。今回だと50回に達したとき
- `findtime` ... maxretryの回数の時間幅。今回だと600秒=10分。よって10分で50回パターンにひっかかれば、という条件になる。
- `bantime` ... この設定がきも。実際にアクセスを拒否する時間。1800なら秒なので30分一時的に拒否する。

この設定を起動時に読み込むようにしている。

今回は設定していないが通知先をメールアドレス宛に指定することも可能なのでslack連携などもできる。

#### /etc/fail2ban/filter.d/postfix-sasl.conf
今回用いたルールはfilter.d配下にあるpostfix-sasl.confを参照している。
一部抜粋。

```shell-session:/etc/fail2ban/filter.d/postfix-sasl.conf
[Definition]

_daemon = postfix(-\w+)?/(?:submission/|smtps/)?smtp[ds]

failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(:[ A-Za-z0-9+/:]*={0,2})?\s*$

ignoreregex = authentication failed: Connection lost to authentication server$
```

今回使用したルールより、エラーログパターンは
`warning: SASL authentication failure: Password verification failed` に該当する送信元IPが対象。
`ignoreregex` の設定により除外ルールも設定されている。
その数をカウントすることができる。


### puppetで構成管理
<a href="https://github.com/lelutin/puppet-fail2ban">lelutin/puppet-fail2ban: Manage fail2ban and its jails with puppet</a>
今回はこちらを用いたでの設定が容易であった。
以下の設定を用意することで先ほどの設定を簡易にコントールすることができる。

```puppet

class mail::fail2ban {

  class { 'fail2ban':
    ignoreip => ['127.0.0.1'],
    loglvl   => 'NOTICE',
  }

  $postfix_sasl_extra_params = {
    'bantime'  => 1800,
    'maxretry' => 50,
    'findtime' => 600,
  }

  $postfix_sasl_params = lookup('fail2ban::jail::postfix-sasl') + $postfix_sasl_extra_params
  fail2ban::jail { 'postfix-sasl':
    * => $postfix_sasl_params,
  }
}
```

### 観測
インストールし設定後起動。
`/var/log/fail2ban.log` を見る。
※ ログレベルのデフォルトは `INFO` だが `NOTICE` と最初はしぼらないほうが動きが見えてよい。

#### 検知
```
2019-08-15 22:34:53,085 fail2ban.filter         [950]: INFO    [postfix-sasl] Found 91.xxx.yyy.zzz
2019-08-15 22:35:01,752 fail2ban.filter         [950]: INFO    [postfix-sasl] Found 126.xx.yyy.zzz
2019-08-15 22:35:01,768 fail2ban.filter         [950]: INFO    [postfix-sasl] Found 126.xx.yyy.zzz
2019-08-15 22:35:01,798 fail2ban.filter         [950]: INFO    [postfix-sasl] Found 126.xx.yyy.zzz
```

INFOレベルであれば検知した時点でログに記録される。
たくさん流れるようであればそれだけ試行回数を稼がれているということ。
その流量によって、さきほど用意した設定の値を書き換えるとよい。

このとき、bantimeが短ければ誤検知を防ぎやすいので調整するとよい。
`fail2ban.filter` の動きを見る。

### 実際のban
banするとログは以下。
`fail2ban.actions` を見る。

```
2019-08-22 10:18:05,975 fail2ban.actions        [950]: NOTICE  [postfix-sasl] Ban 188.xxx.yyy.zzz
2019-08-22 10:48:06,624 fail2ban.actions        [950]: NOTICE  [postfix-sasl] Unban 188.xxx.yyy.zzz
```

bantimeによるのでban解除も自動的に実行される。
bantimeが30分だったのでその時間の分だけ発動。
解除されれば`unban`と表記される。

ただし、しつこく同様の対象がいた場合はban/unbanが繰り返される。

#### 恒久的にbanする
`bantime -1` を使う。
これで恒久的に対象をbanすることができる。
基本的には解除はコマンドで可能なので様子見後導入するのはありだろう。

### fail2ban-client
`fail2ban-client status`で現在のbanのIPアドレスを調査可能。
引数は`jail名`を指定する。
例えば今回用意した設定だと以下の稼働を得る。

```shell-session
$ sudo fail2ban-client status postfix-sasl
Status for the jail: postfix-sasl
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     439
|  `- Journal matches:  _SYSTEMD_UNIT=postfix.service
`- Actions
   |- Currently banned: 2
   |- Total banned:     2
   `- Banned IP list:   103.xx.y.zzz 113.xxx.yy.zz
```

`Banned IP list` を見る。この値が実際にbanされた対象。
bantimeに応じてunbanされる。

また、設定の反映として`fail2ban-client reload` もある。
恒久的にIPは残るので個別削除も検討するときもある。
その場合は `fail2ban-client set <JAIL> unbanip <IP>`

ただし、上記は古い方法。
今はIPそのものを全てのjailに対して実行可能。
[公式のトラブルシューティング](https://github.com/fail2ban/fail2ban/wiki/Troubleshooting)より `fail2ban-client unban <IP>`

### 送信元IPアドレスを制御した状態とは
iptablesでデフォルトでREJECTをしているがDROPがよい場合はそちらも設定で変更可能。
今回の設定だと以下の表示をiptablesとして独自に用意される。

```shell-session
$ sudo iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
f2b-postfix-sasl  tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 0:65535

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain f2b-postfix-sasl (1 references)
target     prot opt source               destination
reject-with icmp-port-unreachable
REJECT     all  --  113.xxx.yy.zz　      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  103.xx.y.zzz         0.0.0.0/0            reject-with icmp-port-unreachable
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
```

これが動的に変化する。
停止すればiptablesの上記の設定は一時的に消える。
fail2banを再起動してもiptalbesの設定は復活する。

### まとめ
流れるログファイルさえあれば、どんなログでもban対象作成可能。
正規表現を使えるがルールが複雑化するので今あるルール設定でできることがないかという視点で十分と思われる。

### 参考サイト
<a href="https://qiita.com/pypypyo14/items/cfcaad2783debfafe505">CentOS7 fail2banでSSH, SMTPへの攻撃からサーバを守る - Qiita</a>
<a href="https://qiita.com/azakazu/items/6e26d37e851265be17c3">Fail2BanでRainLoopへの不正アクセスをBANする - Qiita</a>
<a href="https://qiita.com/yosusk/items/da8196cc1f3ef618e928">fail2ban で Wordpress のログイン攻撃を防ぐ - Qiita</a>
<a href="https://qiita.com/osamu1203/items/4872842cd689a78fdeaa">fail2banでslackに通知する - Qiita</a>