0x01 前言
最近遭受大量扫描与各种渗透攻击,因为我的服务器配置了modsecurity保护nginx,所以目前还没有造成影响。
虽然通过waf可以阻隔恶意访问,但这个并不是长久的解决办法。为此,我决定配置fail2ban拦截恶意IP,以提高安全性。
0x02 准备
fail2ban简单来说就是通过不断读取设定的日志文件,并通过正则校验每条日志是否符合规则。
一旦符合,则提取日志中的IP地址与时间戳,然后写入到fail2ban的数据库中。写库的同时进行计数,如果该IP在设定的时间间隔内被匹配的次数超过阈值,则调用iptables,将其的请求reject。
为此,再继续之前需要准备以下内容:
- iptables
- 日志路径
- 拦截内容
0x02.1
在绝大部分的云服务商中,因为没有安全组的概念,所以iptables都是启用的,这时候就可以跳过此项。
但在诸如腾讯云、阿里云或亚马逊云等大型云服务商中,可以通过安全组控制公网连入的IP与端口。这种情况下,iptables一般是禁用的,这时候就需要检查相关服务:
[root@web-dev ~]# systemctl status iptables.service ● iptables.service - IPv4 firewall with iptables Loaded: loaded (/usr/lib/systemd/system/iptables.service; disable; vendor preset: disabled) Active: inactive (dead) since Mon 2018-04-02 16:54:05 CST; 1 weeks 0 days ago Process: 32056 ExecStop=/usr/libexec/iptables/iptables.init stop (code=exited, status=0/SUCCESS) Main PID: 29668 (code=exited, status=0/SUCCESS)
从上方可以看到服务处于停止inactive (dead)的状态,通过以下命令可以看到并没有规则处于启用状态:
[root@web-dev ~]# iptables -L -vn Chain INPUT (policy ACCEPT 209 packets, 14257 bytes) pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 115 packets, 11222 bytes) pkts bytes target prot opt in out source destination
这时候就需要要启用并删除2条默认的规则:
#开机启动iptables [root@web-dev ~]# systemctl enable iptables.service Created symlink from /etc/systemd/system/basic.target.wants/iptables.service to /usr/lib/systemd/system/iptables.service. #记录启动iptables [root@web-dev ~]# systemctl start iptables.service #删除INPUT链中的默认reject规则 [root@web-dev ~]# iptables -D INPUT 5 #删除FORWARD链中的默认reject规则 [root@web-dev ~]# iptables -D FORWARD 1 #保存修改内容 [root@web-dev ~]# service iptables save iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ] #重启iptables [root@web-dev ~]# systemctl restart iptables.service
0x02.2 日志路径
fail2ban支持使用通配符匹配日志路径,如:
/usr/local/html/ngx.hk/logs/ngx_access.log /usr/local/html/enginx.cn/logs/ngx_access.log /usr/local/html/enginx.org/logs/ngx_access.log /usr/local/html/enginx.uk/logs/ngx_access.log
如果需要匹配上面的日志,则需要写成以下形式即可:
/usr/local/html/*/logs/ngx_access.log
当然,也可以写完成的路径。
0x02.3 拦截内容
因为是针对HTTP服务,所以只需要匹配HTTP响应码即可。
正常情况下只会返回2种响应码:200与404;如果后端无响应的话,会返还50x;但被modsecurity拦截的请求会返回403,一些需要授权的页面则会返还400。
所以在我的环境里主要针对产生403与400这2种响应码的IP进行拦截。
0x03 安装配置
安装非常简单,建议使用epel的源进行安装:
[root@web-dev ~]# yum install fail2ban -y
安装完成后先不要启动,首先进入软件的目录:
[root@web-dev ~]# cd /etc/fail2ban/
0x03.1 jail
先复制一份jail文件:
[root@web-dev fail2ban]# cp jail.conf jail.local
默认的jail.conf不要修改,以备日后参考。
在fail2ban中,会先读取 .conf 后缀名的文件,然后读取 .local 后缀名的文件;而且后读取的配置可以覆盖先读取的配置。
然后在jail.local最后添加以下内容:
[ngx-log] enabled = true port = http,https filter = ngx-40x logpath = /var/log/nginx/access.log action = iptables-multiport[name=ModsecWaf, port="http,https", protocol=tcp] %(action_mwl)s maxretry = 10 findtime = 1800 bantime = 3600 ignoreip = 127.0.0.1 103.15.217.210 193.112.151.220
以上每行内容的大致意义如下:
- [ngx-log]:定义jail名称
- enabled:是否启用该jail,默认的所有规则都没有该项,需要手动添加
- port:指定封禁的端口,默认为0:65535,也就是所有端口,但可以在jail中设定
- filter:指定过滤器名称
- logpath:日志路径
- action:达到阈值后的动作
- maxretry:阈值
- findtime:时间间隔
- bantime:封禁时长
- ignoreip:忽略的IP
在这里有几点要注意的:
- logpath与action可以有多行,如action中的设定:
- 调用iptables-multiport封禁目标IP访问的多个端口
- 调用sendmail发送告警邮件
- findtime不是检查日志的时间间隔,日志的检查是实时的。因为fail2ban自带数据库,所以可以在设定的时间内统计匹配次数
- ignoreip添加后端服务器的IP或CDN的IP
0x03.2 action
自带的action完全可以满足我们的需求,如果需要手动编写,也非常简单。我们来看看上面jail.local中[ngx-log]这个jail的action:
iptables-multiport[name=ModsecWaf, port="http,https", protocol=tcp]
在action.d目录中我们可以找到相应的内容,把注释去掉之后的内容如下:
[root@web-dev fail2ban]# cat action.d/iptables-multiport.conf [INCLUDES] before = iptables-common.conf [Definition] actionstart = <iptables> -N f2b-<name> <iptables> -A f2b-<name> -j <returntype> <iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name> actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name> <iptables> -F f2b-<name> <iptables> -X f2b-<name> actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]' actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype> actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype> [Init]
从上面的内容可以发现该action定义了5种不同的动作,其中actionstart与actionstop为自动调用时所用的,另外3种则可以通过fail2ban-client调用。
为了方便自动化调用,上面的各种action都包含用“<>”符号包裹的变量,调用的时候只需要指定即可。
0x03.3 filter
过滤器也是很重要的一部分,主要提供匹配服务,使用正则寻找IP。在jail中指定需要调用的过滤器文件名即可完成调用工作:
filter = ngx-40x
如上面的配置,调用了filter.d目录下ngx-40x.conf文件中的过滤器:
[Definition] failregex = <HOST> - - \[.*\] \".*(403|400).*\" ignoreregex =
我需要匹配的日志如下:
#403 123.126.113.184 - - [09/Apr/2018:22:51:11 +0800] "GET /category/%e8%99%9a%e6%8b%9f%e5%8c%96/kvm%e8%99%9a%e6%8b%9f%e5%8c%96/page/2 HTTP/1.0" ngx.hk 403 162 "-" "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)" "123.126.113.184" - - - "-" - > 0.000 #400 123.126.113.184 - - [09/Apr/2018:22:51:06 +0800] "GET /robots.txt HTTP/1.1" ngx.hk 400 264 "-" "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)" "-" - - - "-" - > 0.000
题外话:我屏蔽了sogou等一系列的爬虫,因为这些爬虫不但没有给我带来流量,而且每次爬行都像CC攻击一样,都不知道他们是怎样开发的
定义过滤模块有两个需要注意的点:
- 无需匹配完整日志
- 日志中需要有标准的日期格式字段
其实fail2ban只关心1个要素:日志中的IP地址。
所以可以在生成日志的时候可以将正常与不正常的日志分开存储,然后匹配规则这样写:
failregex = <HOST> - -
但我的并不是这样,而是所有日志都在同一个文件中,所以需要写成这样:
failregex = <HOST> - - \[.*\] \".*(403|400).*\"
“<>”符号为固定的变量名称,用于匹配IP地址,往后需要匹配HTTP响应码,这里匹配403与400两种情况,至此已经完成匹配工作,fail2ban并不关心后面的内容,所以可以忽略。
如果有多条正则,可以换行添加。
建立完过滤文件后可以使用以下命令检查匹配情况:
[root@web-dev fail2ban]# fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/ngx-40x.conf Running tests ============= Use failregex filter file : ngx-40x, basedir: /etc/fail2ban Use log file : /var/log/nginx/access.log Use encoding : UTF-8 Results ======= Failregex: 1143 total |- #) [# of hits] regular expression | 1) [1143] <HOST> - - \[.*\] \".*(403|400).*\" `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format | [42889] Day(?P<_sep>[-/])MON(?P=_sep)Year[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)? `- Lines: 42889 lines, 0 ignored, 1143 matched, 41746 missed [processed in 14.49 sec] Missed line(s): too many to print. Use --print-all-missed to print all 41746 lines
在Results中的Failregex字段显示出有多少条日志符合要求;在Date template hits字段中可以看到有多少条日志被默认的时间正则匹配上。
而Lines字段则显示出该日志文件的行数等情况。
fail2ban对日志中的时间格式有要求,如果匹配不上则无法计算findtime,在这里需要考虑时间格式不标准的情况。
因此,需要手动编写时间的匹配规则,如以下日志:
c4.hk 119.139.198.243 - "GET /admin HTTP/2.0" 403 0 - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" 152316870061.619255 - /var/log/modsecurity//20180408/20180408-1425/20180408-142500-152316870061.619255 0 1269.000000 md5:d6859189629af76f03b4f75bc0e49b15
比较明显的时间格式如下:
20180408-1425
那么时间的匹配规则应该是这样的:
%Y%m%d-%H%M%S
不过在写入过滤器的时候需要多加一个“%”符号,用于转义:
[root@web-dev fail2ban]# cat filter.d/ngx-enginx-net.conf [Definition] failregex = .* <HOST> - .*(403|400) ignoreregex = [Init] datepattern = %%Y%%m%%d-%%H%%M%%S
运行fail2ban-regex检查结果:
[root@web-dev fail2ban]# fail2ban-regex 'c4.hk 119.139.198.243 - "GET /admin HTTP/2.0" 403 0 - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" 152316870061.619255 - /var/log/modsecurity//20180408/20180408-1425/20180408-142500-152316870061.619255 0 1269.000000 md5:d6859189629af76f03b4f75bc0e49b15' /etc/fail2ban/filter.d/ngx-enginx-net.conf -v Running tests ============= Use failregex filter file : ngx-enginx-net, basedir: /etc/fail2ban Use datepattern : YearMonthDay-24hourMinuteSecond Use single line : c4.hk 119.139.198.243 - "GET /admin HTTP/2.0" 403 ... Results ======= Failregex: 1 total |- #) [# of hits] regular expression | 1) [1] .* <HOST> - .*(403|400) | 119.139.198.243 Sun Apr 08 14:02:05 2018 `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format | [1] YearMonthDay-24hourMinuteSecond `- Lines: 1 lines, 0 ignored, 1 matched, 0 missed [processed in 0.00 sec]
从上面的结果可以看出,自定义的时间匹配规则已经生效。
0x04 运行
完成陪之后即可启动fail2ban:
#设定开机启动 [root@web-dev fail2ban]# systemctl enable fail2ban.service Created symlink from /etc/systemd/system/multi-user.target.wants/fail2ban.service to /usr/lib/systemd/system/fail2ban.service. #立即启动 [root@web-dev fail2ban]# systemctl start fail2ban.service #检查状态 [root@web-dev fail2ban]# systemctl status fail2ban.service ● fail2ban.service - Fail2Ban Service Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2018-04-09 23:47:36 CST; 6s ago Docs: man:fail2ban(1) Process: 19718 ExecStart=/usr/bin/fail2ban-client -x start (code=exited, status=0/SUCCESS) Main PID: 19721 (fail2ban-server) CGroup: /system.slice/fail2ban.service └─19721 /usr/bin/python2 -s /usr/bin/fail2ban-server -s /var/run/fail2ban/fail2ban.sock -p /var/run/fail2ban/fail2ban.pid -x -b Apr 09 23:47:36 web-dev systemd[1]: Starting Fail2Ban Service... Apr 09 23:47:36 web-dev fail2ban-client[19718]: 2018-04-09 23:47:36,662 fail2ban.server [19719]: INFO Starting Fail2ban v0.9.7 Apr 09 23:47:36 web-dev fail2ban-client[19718]: 2018-04-09 23:47:36,662 fail2ban.server [19719]: INFO Starting in daemon mode Apr 09 23:47:36 web-dev systemd[1]: Started Fail2Ban Service.
通过fail2ban-client可以进行一些简单的操作,如:封禁IP、解封IP与检查状态等:
#检查所有jail的状态 [root@hk1 ~]# fail2ban-client status Status |- Number of jail: 1 `- Jail list: ngx-log #检查特定jail的状态 [root@hk1 ~]# fail2ban-client status ngx-log Status for the jail: ngx-log |- Filter | |- Currently failed: 5 | |- Total failed: 240 | `- File list: /var/log/nginx/access.log `- Actions |- Currently banned: 2 |- Total banned: 5 `- Banned IP list: 58.250.143.116 58.243.74.153 #解封IP [root@hk1 ~]# fail2ban-client set ngx-log unbanip 127.0.0.1 #封禁IP [root@hk1 ~]# fail2ban-client set ngx-log banip 127.0.0.1
其他更多操作请参考使用手册。
0x05 结语
如果需要使用邮件告警,请修改jail.local文件中的destemail字段为你的电子邮箱地址,然后在jail的action中添加:
%(action_mwl)s
fail2ban是建立在日志基础上的封禁软件,只有在日志足够详细的情况下才能发挥应有的作用。
在过去一个月的使用后发现,fail2ban可以很好地提高安全防护水平。