# Fail2Ban

Fail2Ban 是一個入侵檢測系統框架，它可以保護電腦伺服器免受蠻力攻擊。以Python程式設計語言編寫，並能夠在類Unix系統上運行，這些系統具有本地安裝的封包控制系統或防火牆的介面，例如Iptables或TCP Wrapper。

# Install Fail2Ban on Debian

##### Installation

Download: [https://www.fail2ban.org/wiki/index.php/Downloads](https://www.fail2ban.org/wiki/index.php/Downloads)

```shell
# Debian 7.x
tar xzf 0.9.2.tar.gz
cd fail2ban-0.9.2/
python setup.py install
```

Verify

```
fail2ban-client -h
```

##### Configuration for Asterisk

On Asterisk)

 /etc/asterisk/logger.conf:

```
...
[logfiles]
...
fail2ban => notice,warning,security
```

Restart the logger on Asterisk

```
asterisk -rx "logger reload"
asterisk -rx "logger show channels"
```

On Fail2Ban)

/etc/fail2ban/jail.d/asterisk.conf

```
[asterisk]
enabled = true
logpath = /var/log/asterisk/fail2ban
maxretry = 5
bantime = 259200
```

##### Configuration for SSH

/etc/fail2ban/jail.d/sshd.conf

```
[sshd]
enabled = true
bantime = 7200
findtime = 900
maxretry = 4
```

##### Auto startup

```shell
cd fail2ban-0.9.2/
cp files/debian-initd /etc/init.d/fail2ban
chmod 0755 /etc/init.d/fail2ban 
update-rc.d fail2ban defaults
```

Service start

```
service fail2ban start
```

##### Setup Logrotate

/etc/logrotate.d/fail2ban

```
/var/log/fail2ban.log {
	missingok
	notifempty
	size 30k
	create 0600 root root
	postrotate
		/usr/bin/fail2ban-client set logtarget /var/log/fail2ban.log 1>/dev/null || true
		#/usr/bin/fail2ban-client reload 2> /dev/null || true
	endscript
}
```

##### FAQ

Q:以下 Call Log 無法被偵測到

> Call from '' (195.154.134.116:5071) to extension '8011441295298642' rejected because extension not found in context 'public'.

Ans：設定 Asterisk 的 allowguest=no

編輯 sip.conf

```
allowguest=no
```

Apply the changes

```
#> asterisk -rx "sip reload"
#> asterisk -rx "sip show settings" | grep -i "Allow unknown access"
  Allow unknown access:   No 
```

# Learning Fail2Ban

[Fail2ban](https://www.fail2ban.org/) 是一套以 Python 語言所撰寫的 GPLv2 授權軟體，藉由分析系統紀錄檔，並透過設定過濾條件 (filter) 及動作 (action)，當符合我們所設定的過濾條件時，將觸發相對動作來達到自動化反應的效果 (如封鎖來源 IP、寄信通知管理者、查詢來源 IP 資訊等)。因其架構相當彈性，我們可以針對自己的需求，設計不同的過濾條件與動作來達到伺服器防護的功能，或是及時的反應某些異常資訊。常見應用有：

- 阻擋 SSH、FTP 多次嘗試錯誤連線；
- 阻擋特定的瀏覽器或網路爬蟲；
- 提供管理者了解異常伺服器服務要求 (如 apache、bind、postfix、vsftpd、proftpd…)。

常見的像是 SSH 服務，當使用者嘗試輸入帳號密碼進行登入時，如發生驗證錯誤，系統將紀錄事件於記錄檔中。藉由即時的分析系統紀錄檔，我們可以過濾出一些有用的資訊，再加以判斷此類事件是否對伺服器服務有害。

GitHub: [https://github.com/fail2ban/fail2ban](https://github.com/fail2ban/fail2ban)

##### Tutorials

- [Configure fail2ban to use route instead of iptables to block connections](https://www.faqforge.com/linux/controlpanels/ispconfig3/configure-fail2ban-to-use-route-instead-of-iptables-to-block-connections/)
- [How to Create a Simple IP Blocker Script Using iptables and Fail2Ban](https://www.tecmint.com/bash-script-to-block-ip-with-iptables/)
- [Fail2Ban Prometheus Exporter](https://github.com/hctrdev/fail2ban-prometheus-exporter)

# Fail2Ban FAQ

##### Q:服務啟動後出現錯誤

> WARNING Determined IP using DNS Lookup:

Ans: 編輯 /etc/fail2ban/jail.conf

```
usedns = no
```

##### Q:\[v0.10.0\] 啟動後出現錯誤

> iptables v1.4.14: unknown option "-w"

Ans：如果你的 iptables 版本不是 v1.4.20 以上，可能發生啟動錯誤，解決方式如下：

新增 /etc/fail2ban/action.d/iptables-common.local

```
[Init]
lockingopt =
```

##### Q:\[Asterisk\] 以下 Call Log 無法被偵測到

> Call from '' (195.154.134.116:5071) to extension '8011441295298642' rejected because extension not found in context 'public'.

Ans：設定 Asterisk 的 allowguest=no

編輯 sip.conf

```
allowguest=no
```

套用新設定

```
#> asterisk -rx "sip reload"
#> asterisk -rx "sip show settings" | grep -i "Allow unknown access"
  Allow unknown access:   No
```

##### Q:\[Asterisk\] 如何驗證一條新規則

Ans: 對於特定的 log 內容，要增加一條新規則，該如何驗證是否有效。

需要過濾的 log 內容：

```
[2015-01-28 05:40:16] NOTICE[-1] Ext. 9015448702956577: Incoming SIP connection from unknown peer failed for 31.3.244.234 - Unknown connection from peer
```

在 /etc/fail2ban/filter.d/asterisk.conf 增加一條新規則

```
NOTICE.* .*: Incoming SIP connection from unknown peer failed for <HOST> - Unknown connection from peer
```

要驗證此規則是否有效，可以使用以下指令

```
fail2ban-regex /var/log/asterisk/fail2ban "NOTICE.* .*: Incoming SIP connection from unknown peer failed for <HOST> - Unknown connection from peer"
```

> Tips: fail2ban-regex &lt;path/to/log&gt; &lt;failregex or /etc/fail2ban/filter.s/XXX.conf&gt;

##### Q:為何沒有 /var/log/fail2ban.log

Ans: 檢查 /etc/fail2ban/fail2ban.conf

```
#logtarget = SYSLOG
logtarget = /var/log/fail2ban.log
```

重啟 fail2ban 服務

##### Q:\[Asterisk\] 在 Elastix/CentOS 5.3 始終無法 ban IP

使用 fail2ban-client 檢查，明明 log 內有異常連線，但卻無法 ban IP

```
# fail2ban-client status
Status
|- Number of jail:      1
`- Jail list:           asterisk-iptables

# fail2ban-client status asterisk-iptables
|- filter
|  |- File list:        /var/log/asterisk/fail2ban
|  |- Currently failed: 0
|  `- Total failed:     0
`- action
   |- Currently banned: 0
   |  `- IP list:       
   `- Total banned:     0
```

解決方法：

編輯 /etc/asterisk/logger.conf

```
;syslog keyword : This special keyword logs to syslog facility
;將下行註解拿掉
syslog.local0 => notice,warning,error
```

Reload Asterisk

##### Q:如何將 IP 從阻擋清單移除

```
# iptables -D fail2ban-ASTERISK -s 123.123.123.123 -j DROP
或
# iptables -L fail2ban-ASTERISK -nv --line-number
Chain fail2ban-ASTERISK (1 references)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 DROP       all  --  *      *       134.213.134.172      0.0.0.0/0
2        0     0 DROP       all  --  *      *       46.105.127.222       0.0.0.0/0
3        0     0 DROP       all  --  *      *       116.255.152.101      0.0.0.0/0
4     1364  363K RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0

# iptables -D fail2ban-ASTERISK 2     ;刪除第 2 規則
```

##### Q:\[Asterisk\] 如何阻擋不會紀錄來源 IP 的 DDoS 攻擊 Received incoming SIP connection

> CLI Log：
> 
> Received incoming SIP connection from unknown peer to 003333002972597886748"

以上訊息只有在 sip\_general.conf 的 allowguest=yes (by default)，才會出現。  
此篇也適合解決 Sending fake auth rejection for device 100&lt;sip:100@123.123.123.123&gt; ，這類的攻擊手法。

方法是先 註解 allowguest=no，然後完成下面的步驟。  
注意：比較安全的做法應該是將 allowguest=no，然後在 Asterisk 11 以後版本，Log 就可以紀錄到惡意的來源 IP，然後就可以用 Fail2ban 來將其阻擋。

Ans: 編輯 /etc/asterisk/extensions.conf

```
[from-sip-external]
; 註解原有內容，加上以下內容
exten => _.,1,NoOp(Received incoming SIP connection from unknown peer to ${EXTEN})
exten => _.,n,Set(DID=${IF($["${EXTEN:1:2}"=""]?s:${EXTEN})})
exten => _.,n,Set(foo=${SIPCHANINFO(recvip)})
exten => _.,n,Log(NOTICE,Incoming SIP connection from unknown peer failed for ${foo} - Unknown connection from peer)
exten => _.,n,Hangup
exten => h,1,Hangup
exten => i,1,Hangup
exten => t,1,Hangup 
```

編輯 /etc/fail2ban/filter.d/asterisk.conf

```
...
failregex = NOTICE.* .*: Registration from '.*' failed for '<HOST>' - Wrong password
            ...
            ...
            NOTICE.* .*: Incoming SIP connection from unknown peer failed for <HOST> - Unknown connection from peer
```

##### Q:\[Asterisk\] 如何阻擋 Sending fake auth rejection

在 Asterisk 1.11+)

```
Failed to authenticate device 1005<sip:1005@123.123.123.123>;tag=2071f8ca
```

在 Asterisk 1.8)

```
Sending fake auth rejection for device 100<sip:100@123.123.123.123>;tag=99fdd5d7
```

Ans：不同 Asterisk 版本的解決方法

Asterisk 11)  
此版新增一個 Security Log Level 的功能，透過啟用這個，可以記錄攻擊者的來源 IP，然後再透過 fail2ban 去阻擋。

編輯 /etc/fail2ban/filter.d/asterisk.conf

```
# 加上 SECURITY 那行
failregex = Registration from '.*' failed for '<HOST>:.*' - Wrong password
...
...
            SECURITY.* .*: SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="[\d-]+",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="\d+",SessionID="0x[\da-f]+",LocalAddress="IPV[46]/(UD|TC)P/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UD|TC)P/<HOST>/\d+"(,Challenge="\w+",ReceivedChallenge="\w+")?(,ReceivedHash="[\da-f]+")?$
```

Asterisk 1.8/1.6)  
舊版的 Asterisk 要阻擋這類行為，只能修改原始檔 channels/chan\_sip.c，使系統可以記錄攻擊者的來源 IP，修改後必須重新編譯 Asterisk 才能被套用，再設定 fail2abn 去阻擋。

# Fail2ban Setup

#### 內建白名單

##### 方法一：修改設定

 /etc/fail2an/jail.conf

```
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
ignoreip = 127.0.0.1/8 ::1 192.168.9.0/24 192.168.31.0/24
```

##### 方法二：指令模式

 `fail2ban-client`

```bash
# set <JAIL> addignoreip <IP>
# set <JAIL> delignoreip <IP>
fail2ban-client set sshd addignoreip 123.123.123.123
fail2ban-client set sshd delignoreip 123.123.123.123
```

驗證結果

```bash
fail2ban-client get <JAIL> ignoreip
fail2ban-client get asterisk ignoreip
fail2ban-client get sshd ignoreip
```

#### 黑名單功能客製  


- [Persistent Banning of IP Addresses with Fail2Ban](https://dev-notes.eu/2018/04/persistent-banning-of-ip-addresses-with-fail2ban/)
- [Fail2Ban Blacklist JAIL for Repeat Offenders](https://github.com/mitchellkrogza/Fail2Ban-Blacklist-JAIL-for-Repeat-Offenders-with-Perma-Extended-Banning)

使用方法：

- 新增要封鎖的 IP：`fail2ban-client set blacklist banip xxx.xxx.xxx.xxx`
- 解除已封鎖的 IP：`fail2ban-client set blacklist unbanip xxx.xxx.xxx.xxx`
- 檢視已封鎖 IP：`fail2ban-client status blacklist`

設定步驟：

`/etc/fail2ban/filter.d/blacklist.conf` :

```
# /etc/fail2ban/filter.d/blacklist.conf
# Fail2Ban Blacklist for Repeat Offenders (filter.d)

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition] 
# The name of the jail that this filter is used for. In jail.conf, name the 
# jail using this filter 'blacklist', or change this line!
_jailname = blacklist

failregex = 
ignoreregex = 
```

`/etc/fail2ban/action.d/blacklist.conf` :

```
# /etc/fail2ban/action.d/blacklist.conf
# Fail2Ban Blacklist for Repeat Offenders (action.d)

[Definition]
# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#

actionstart = iptables -N f2b-<name>
              iptables -A f2b-<name> -j RETURN
              iptables -I <chain> -j f2b-<name>
              # Sort and Check for Duplicate IPs in our text file and Remove Them
              sort -u /etc/fail2ban/ip.blacklist -o /etc/fail2ban/ip.blacklist
              # Persistent banning of IPs reading from our ip.blacklist text file
              # and adding them to IPTables on our jail startup command
              cat /etc/fail2ban/ip.blacklist | while read IP; do iptables -I f2b-<name> 1 -s $IP -j DROP; done
              
# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#

actionstop = iptables -D <chain> -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> -j DROP
          # Add the new IP ban to our ip.blacklist file
          echo '<ip>' >> /etc/fail2ban/ip.blacklist
# I don't want reporting on any badboys service
# curl http://www.badips.com/add/badbots/<ip>/

# 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> -j DROP
            # Remove IP from our ip.blacklist file
            sed -i -e '/<ip>/d' /etc/fail2ban/ip.blacklist

[Init]
# Chain to insert the f2b-<name> jump rule into
chain = INPUT
```

`/etc/fail2ban/jail.d/blacklist.conf` :

- bantime 與 findtime 可以依需要做調整，單位：秒

```
# Usage: 
# Add a bad IP - fail2ban-client set blacklist banip xxx.xxx.xxx.xxx
# Remove an IP - fail2ban-client set blacklist unbanip xxx.xxx.xxx.xxx

[blacklist]
enabled   = true
banaction = blacklist
bantime   = 2592000   ; 1 month
findtime  = 2592000   ; 1 month
```

##### DROP vs REJECT

參數語法：

- DROP: `-j DROP`
- REJECT: `-j REJECT --reject-with icmp-port-unreachable`

<div id="bkmrk-%E7%94%A8-drop-%E7%9A%84%E5%A0%B4%E6%99%AF%EF%BC%9A">用 DROP 的場景：</div><div id="bkmrk-blacklist-%2F-%E6%83%A1%E6%84%8F-ip-%E2%80%94-">- Blacklist / 惡意 IP — 不要讓對方知道這個 IP 有在運作，浪費攻擊者的 timeout 時間
- SSH / 敏感服務的暴力攻擊來源 — 讓攻擊者摸不著到底主機在不在
- 防火牆外網 — 降低 footprint，不暴露主機存在
- 大量攻擊 (DDoS) — DROP 比 REJECT 省資源（不用產生 ICMP 封包）

</div><div id="bkmrk-%E7%94%A8-reject-%E7%9A%84%E5%A0%B4%E6%99%AF%EF%BC%9A">用 REJECT 的場景：</div><div id="bkmrk-%E5%85%A7%E9%83%A8%E6%9C%8D%E5%8B%99%E3%80%81%E6%AD%A3%E5%B8%B8%E4%BD%BF%E7%94%A8%E8%80%85-%E2%80%94-%E5%BF%AB%E9%80%9F%E8%AE%93-cli">- 內部服務、正常使用者 — 快速讓 client 知道此路不通，避免卡在 timeout
- 你希望 client 端有良好的 UX — 例如封鎖特定 port 但對方應立即知道被擋而不是等到 timeout
- Debug — 區分是「防火牆擋了」還是「服務沒開」
- 合法但未授權的流量 — 例如只允許內網連的服務，外部 IP 連過來給 REJECT 而非 DROP

</div><div id="bkmrk-reject-%E7%9A%84%E5%B9%BE%E7%A8%AE%E5%9B%9E%E8%A6%86%E9%A1%9E%E5%9E%8B">REJECT 的幾種回覆類型</div><div id="bkmrk-icmp-port-unreachabl">- icmp-port-unreachable（預設值）— 最常見，告訴對方「這個 port 沒在聽」
- icmp-host-unreachable — 告訴對方「這台主機不存在」
- icmp-net-unreachable — 告訴對方「這個網段不存在」
- tcp-reset — 直接發 TCP RST，對 TCP 連線來說就像远端關閉連線，比 ICMP 更乾淨

</div>

# fail2ban command

##### 常用指令表

- [Commands - Fail2ban](https://www.fail2ban.org/wiki/index.php/Commands)

<table id="bkmrk-cmd-description-serv" style="width:90%;table-layout:fixed;"><tbody><tr><td>Cmd</td><td>Description</td></tr><tr><td>service fail2ban restart

systemctl restart fail2ban

</td><td>restart fail2ban service (after edit configuration)</td></tr><tr><td>fail2ban-client reload</td><td>restart fail2ban client</td></tr><tr><td>fail2ban-client status</td><td>get list activated jail</td></tr><tr><td>fail2ban-client status &lt;JAIL&gt;  
example: fail2ban-client status wplogin  
example: fail2ban-client status sshd</td><td>get &lt;JAIL&gt; status (the number of unsuccessful attempts and the list of banned IPs)</td></tr><tr><td>fail2ban-regex /var/lib/docker/containers/&lt;CONTAINERID&gt;/&lt;CONTAINERID&gt;-json.log /etc/fail2ban/filter.d/wplogin.conf</td><td>test regex wplogin</td></tr><tr><td>fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf</td><td>test regex sshd</td></tr><tr><td>fail2ban-regex "line" "failregex"</td><td>test regex</td></tr><tr><td>fail2ban-client set &lt;JAIL-NAME&gt; unbanip &lt;IP-ADDRESS&gt;</td><td>manually unban IP</td></tr><tr><td>fail2ban-client set &lt;JAIL-NAME&gt; banip &lt;IP-ADDRESS&gt;</td><td>manually Ban IP</td></tr><tr><td>tail -f /var/log/fail2ban.log</td><td>view fail2ban logs</td></tr><tr><td>iptables -L –line-numbers</td><td>list IP blocked with line numbers</td></tr><tr><td>iptables -D &lt;Jail-Name&gt; -s &lt;IP-ADDRESS&gt; -j DROP  
Example: Jail-Name =f2b-wplogin  
Jail-Name =f2b-sshd</td><td>Unban IP</td></tr><tr><td>fail2ban-server -b</td><td>start fail2ban server</td></tr><tr><td>docker inspect –format='{{.LogPath}}' $INSTANCE\_ID</td><td>return instance log file path</td></tr><tr><td>fail2ban-client get &lt;JAIL-NAME&gt; ignoreip  
</td><td>Test ignoreip for JAIL  
</td></tr></tbody></table>

Check the version

```bash
fail2ban-client version
```

Check the Help

```bash
fail2ban-client -h
```