# 使用`Fail2Ban`防止sip攻击`FreeSWITCH`
`FreeSWITCH`在公网运行容易遭受sip攻击,解决的办法有很多种,而`Fail2Ban`安装配置以及调试比较简单,不失为一种好的选择。
`Fail2Ban`版本很多,配置方式略有差别。
本次测试是基于`Fail2Ban 0.9.6`版本,其他相关信息如下:
- Debian9
- FreeSWITCH 1.10.3,`base_dir`是`/usr/local/freeswitch`
## 安装`Fail2Ban`
```shell
cd /usr/local/src;
git clone https://github.com/fail2ban/fail2ban.git -b 0.11.1
cd /usr/local/src/fail2ban;
python3 setup.py install
#配置成服务
ubuntu
cp files/debian-initd /etc/init.d/fail2ban
update-rc.d fail2ban defaults
centos
cp files/redhat-initd /etc/init.d/fail2ban
chkconfig fail2ban on
yum install redhat-lsb -y
配置之后操作
service fail2ban start
服务器如果重启可能导致fail2ban启动失败,需要执行以下命令
sed -i 's@Starting fail2ban.*@&\n [ ! -e "/var/run/fail2ban" ] \&\& mkdir /var/run/fail2ban@' /etc/init.d/fail2ban
systemctl daemon-reload
service fail2ban start
```
## 配置`iptables`
# iptables -D INPUT -s 120.5.142.230 -j DROP
iptables -D INPUT -s 120.5.5.158 -j DROP
iptables -A INPUT -s 101.17.88.2 -j ACCEPT
120.5.5.158
ACCEPT
```shell
iptables -A INPUT -p tcp --dport 5060:5061 -j ACCEPT
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5080 -j ACCEPT
iptables -A INPUT -p udp --dport 5080 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 9922 -j ACCEPT
iptables -A INPUT -p udp --dport 6660 -j ACCEPT
iptables -A INPUT -p udp --dport 6880 -j ACCEPT
iptables -A INPUT -p tcp --dport 8831 -j ACCEPT
iptables -A INPUT -p tcp --dport 8899 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 7443 -j ACCEPT
iptables -A INPUT -p udp --dport 16384:32768 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
```
禁止被扫描
```
iptables -I INPUT -j DROP -p tcp --dport 5060 --string "friendly-scanner" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5080 --string "friendly-scanner" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5060 --string "friendly-scanner" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5080 --string "friendly-scanner" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5060 --string "sipcli" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5080 --string "sipcli" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5060 -m string --string "sipcli" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5080 -m string --string "sipcli" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5060 -m string --string "Sippy" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5080 -m string --string "Sippy" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5060 -m string --string "Sippy" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5080 -m string --string "Sippy" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5060 -m string --string "VaxSIPUserAgent" --algo bm
iptables -I INPUT -j DROP -p tcp --dport 5080 -m string --string "VaxSIPUserAgent" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5060 -m string --string "VaxSIPUserAgent" --algo bm
iptables -I INPUT -j DROP -p udp --dport 5080 -m string --string "VaxSIPUserAgent" --algo bm
```
## 配置 `FreeSWITCH`
1. sip_profiles
```
<param name="log-auth-failures" value="true"/>
```
2. switch.conf.xml 要修改一个配置项目
```
<param name="threaded-system-exec" value="true"/>
```
## 配置 fail2ban
### 配置 freeswitch jail
找到 `/etc/fail2ban/jail.conf` 的 freeswitch 段,修改成下面这样:
注意日志文件的路径
```
[freeswitch]
enabled = true
* port = 5060,5061,5080 # sip profile 的端口
* action = iptables-allports[name=freeswitch, protocol=all] # 这里不用改动
* logpath = /usr/local/freeswitch/log/freeswitch.log # freeswitch.log的全路径
* filter = freeswitch #这里不用改动
* maxretry = 5 # 尝试次数
* bantime = -1 # -1 永久 ban(禁止)
* findtime = 3600 # 发现的时间,这几个参数合起来的意思就是,如果 1 小时内检查到 哪个 IP 地址,做了 5 次尝试,那么永久禁止他
* ignoreip = 127.0.0.1/8 192.168.0.0/16 10.0.0.0/8 172.16.0.0/16 # ip 白名单
### 配置 freeswitch filter
修改`/etc/fail2ban/filter.d/freeswitch.conf`,改成下面这样:
```
# Fail2Ban configuration file
#
# Enable "log-auth-failures" on each Sofia profile to monitor
# <param name="log-auth-failures" value="true"/>
# -- this requires a high enough loglevel on your logs to save these messages.
#
# In the fail2ban jail.local file for this filter set ignoreip to the internal
# IP addresses on your LAN.
#
[Definition]
#failregex = ^\.\d+ \[WARNING\] sofia_reg\.c:\d+ SIP auth (failure|challenge) \((REGISTER|INVITE)\) on sofia profile \'[^']+\' for \[.*\] from ip <HOST>$
# ^\.\d+ \[WARNING\] sofia_reg\.c:\d+ Can't find user \[\d+@\d+\.\d+\.\d+\.\d+\] from <HOST>$
failregex = .*? \[WARNING\] sofia_reg\.c:\d+ SIP auth (failure|challenge) \((REGISTER|INVITE)\) on sofia profile \'[^']+\' for \[.*\] from ip <HOST>$
.*? \[WARNING\] sofia_reg\.c:\d+ Can't find user \[\d+@\d+\.\d+\.\d+\.\d+\] from <HOST>$
.*? \[WARNING\] sofia\.c:\d+ IP <HOST> Rejected by acl \"domains"$
ignoreregex =
# Author: Rupa SChomaker, soapee01, Daniel Black
# https://freeswitch.org/confluence/display/FREESWITCH/Fail2Ban
# Thanks to Jim on mailing list of samples and guidance
#
# No need to match the following. Its a duplicate of the SIP auth regex.
# ^\.\d+ \[DEBUG\] sofia\.c:\d+ IP <HOST> Rejected by acl "\S+"\. Falling back to Digest auth\.$
```
[WARNING] sofia.c:10487 IP 62.138.3.130 Rejected by acl "domains"
``
现在运行`systemctl restart fail2ban`重启服务
再运行 `fail2ban-client status`,输出如下:
```
Status
|- Number of jail: 1
`- Jail list: freeswitch
```
运行`fail2ban-client status freeswitch`,输出如下:
```
Status for the jail: freeswitch
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /usr/local/freeswitch/log/freeswitch.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
```
现在试着ban一个ip,执行这个命令:
```
fail2ban-client set freeswitch banip 192.168.1.11
```
然后用`fail2ban-client status freeswitch`查看
```
Status for the jail: freeswitch
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /usr/local/freeswitch/log/freeswitch.log
`- Actions
|- Currently banned: 1
|- Total banned: 3
`- Banned IP list: 192.168.1.11
```
可以看到, `192.168.1.11` 这个地址已经被ban
执行这个命令`iptables -nvL --line-numbers`
输出如下:
```
Chain INPUT (policy DROP 9 packets, 2952 bytes)
num pkts bytes target prot opt in out source destination
1 1057 132K f2b-freeswitch all -- * * 0.0.0.0/0 0.0.0.0/0
2 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
3 13 808 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
4 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
5 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
6 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
7 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5066
8 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:7443
9 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpts:5060:5061
10 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060
11 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5080
12 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:5080
13 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpts:16384:32768
14 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 8
Chain FORWARD (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 6 packets, 496 bytes)
num pkts bytes target prot opt in out source destination
Chain f2b-freeswitch (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT all -- * * 113.113.113.113 0.0.0.0/0 reject-with icmp-port-unreachable
2 1057 132K RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
```
用这个命令解除,`fail2ban-client set freeswitch unbanip 192.168.1.11`
## 把 FreeSWITCH 运行起来,运行`tail -f /var/log/fail2ban.log`进行观察,再结合`/usr/local/freeswitch/log/freeswitch.log`的日志内容,进行调试,不断优化。
## fail2ban.lua
一般情况下`Fail2Ban`工作的很好,但还是有特殊的呼叫流程`Fail2Ban`抓不到。为此,笔者写了个`fail2ban.lua`,弥补`Fail2Ban`的不足
1. 修改`lua.conf.xml`,增加下面俩个配置项目:
```
<hook event="CUSTOM" subclass="sofia::wrong_call_state" script="fail2ban.lua"/>
<hook event="CUSTOM" subclass="sofia::register_failure" script="fail2ban.lua"/>
```
下面是`fail2ban.lua`的内容(代码比较简单,不再解释了):
```
function OnEvent(e)
local subclass = e:getHeader("Event-Subclass") or ""
if string.find(subclass, "sofia::") ~= 1 then return end
local ip = e:getHeader("network_ip") or e:getHeader("network-ip")
if not ip then return end
local ua = e:getHeader("user-agent") or ""
local to_user = e:getHeader("to-user") or ""
local from_user = e:getHeader("from-user") or ""
local auth_result = e:getHeader("auth-result") or ""
local registration_type = e:getHeader("registration-type") or ""
local s = string.format("*** %s, ip = %s, ua = %s, to = %s, from = %s, result = %s, type = %s\n", subclass, ip, ua, to_user, from_user, auth_result, registration_type)
freeswitch.consoleLog("NOTICE", s)
if subclass == "sofia::wrong_call_state" or subclass == "sofia::register_failure" then
local cmd = "fail2ban-client set freeswitch banip " .. ip
freeswitch.consoleLog("ERR", cmd .. "\n")
os.execute(cmd)
end
end
freeswitch.consoleLog("INFO", "fail2ban.lua, ===\n" .. event:serialize() .. "===\n")
OnEvent(event)
```
以上如果freeswitch在docker里边,可以通过freeswitch的事件通知方式,在宿主机中将注册失败和拨打失败相关的消息的ip用fail2ban拦截,后续我会写一些rabbitmq事件相关的文章,可以供大家参考