RHEL 9 配置使用Fail2Ban 本文介绍怎样在RHEL9系统中使用Fail2Ban结合防火墙nftables来阻止来自网络的恶意访问,以保护计算机免受暴力破解。
Fail2Ban 能干什么 Fail2ban是由Python语言开发的程序,它通过监视服务器的各种系统日志,并对日志里的各种行为进行实时判断,以确定该行为是否为恶意攻击,并会统计该攻击的频次,然后根据用户配置的各种规则执行对应的屏蔽操作。
举例说明,一台Linux服务器正遭到远程暴力破解SSH登录,每一次用户名密码登录错误都会在ssh的登录日志里留下一条错误记录,Fail2ban程序从该日志文件里捕捉到一定频次的这些错误后,就可以及时采取应对措施,比如把这个IP加入防火墙的禁止访问(屏蔽)列表,从而阻断该IP的继续尝试。
我尽量把Fail2Ban的应用情景写的通俗些,但是限于我的知识面,可能不全。 Fail2Ban能处理如下各种问题:
阻断来自某IP地址的恶意多次尝试破解SSH登录密码;
阻断来自某IP地址的连续尝试访问各种路径下的用户敏感文件;
阻断来自某IP地址的爬虫程序尝试连续下载某个路径下的所有媒体文件,比如domain/video/1.mp4, domain/video/2.mp4,…, domain/video/999.mp4;
针对这些行为,Fail2Ban 可以自动采取各种应对措施,比如通过防火墙屏蔽该IP以禁止它连接一段时间。当然也可以采取其他动作,比如发送邮件给管理员,或者记录此类行为到日志等。
Fail2Ban 是如何工作的 下面具体展开它的一些技术细节。
Fail2Ban的名字就包含了2个最重要的部分: fail(失败) 和 ban(屏蔽)。另外还涉及到jails(监控),filters(过滤),actions(行动) 这几个词,后续会陆续介绍。
Fail2Ban的处理流程可以分为如下几个部分。
1. 监控系统日志文件
Fail2Ban的输入是来自用户指定的某个或某些日志文件(例如 Nginx 的 access.log 或 SSH 的认证日志 auth.log)。 它会持续监控这些文件, 类似 tail -F xx.log
的效果。
这些日志文件的位置是通过后面用户设置的每个 jail(监控规则)中的 logpath 参数定义的。
2. 从日志文件中尝试捕捉fail(失败)
用户在每个启用的监控 jail 里都可以配置一个或多个过滤规则(filter)。
每个filter里可以使用一个或多个正则表达式(failregex)来定义恶意或不合法行为的模式。
如果日志文件中有内容与 某个failregex 匹配,则该日志条目会被标记为一次fail“失败”。
举例说明: 如果用户在某个jail里定义了针对Nginx的access.log日志的如下一个failregex规则:
1 failregex = <HOST> -.*GET.*(\.env |phpinfo\.php|PHPINFO\.php).*HTTP.* 4\d{2}
则下面这一条错误访问日志就会被匹配到:
1 52.81.78.71 - - [18/Jan/2025:16:09:20 -0700] "GET /.env HTTP/1.1" 404 230 "-" "Go-http-client/1.1" "-"
此时,Fail2Ban就会把它标记为1个fail,并记录它的时间和访问IP。这就好比一个人犯了一次错被记录在案,注意此时还只是记录在案,并不会立即采取后续行动(处罚)。
3. 统计一定时间内失败次数来决定是否采取行动 Fail2Ban会记录来自同一个IP的各种错误,并和用户事先设好的处罚屏蔽标准进行比较,以决定是否应该屏蔽该IP。每个监控 jail指定的处罚屏蔽标准(filters) 都有以下参数:
maxretry:触发行动的最大失败次数。
findtime:统计失败次数的时间窗口(单位为秒)。
如果同一个 IP 在 findtime 时间窗口内触发的失败次数达到或超过了 maxretry,Fail2Ban 会采取响应的的动作,比如禁止(ban)。
举例说明,如果某个网页需要进行basic auth的登录认证,如果用户输入错误的用户名和密码,则会在Nginx里留下一条代码401的日志。而 如果用户针对这个401代码,设置了一个只允许在10分钟内,最多有3次401错误的代码的规则,那么在这个用户10分钟内连续输入3次错误信息后,Fail2Ban就会采取措施,把这个IP放入jail,也就是会采取下一步的屏蔽错误。打个比方,就好比一个小偷在1个月内连续3次被人抓住,而当地法律规定了如果一个人1个月内连续偷窃达到或超过3次就要被投进监狱,那么这个小偷就要被扔进监狱。
4. 采取行动以屏蔽该IP 如果前一步里一定时间内多个fail触发Fail2Ban将此IP标记为应该对该IP采取行动(比如放入jail)后,Fail2Ban 就会执行用户设置的相关操作(actions),一般是把此IP放入防火墙的封禁列表并屏蔽一定时长,比如1小时。1小时后才会恢复该IP的访问。
5. 总结 最后总结一下上面出现的这些词的关系:fail(失败) , ban(屏蔽),jails(监控),filters(过滤规则)和actions (动作)。
用户可以设定多个jails,每个jail 实现1个监控目标。比如创建1个以跟踪SSH日志用于监控SSH暴力登录,再创建1个以跟踪Nginx的访问日志用于监控Nginx网络爬虫等。另外,也可以根据实际需用对同一个日志设置多个jails,比如针对Nginx的访问日志,创建1个jail用来监控各种 .env/ .php等后门探测,再创建1个用来监控各种暴力破解网页上的basic auth的用户名密码暴力破解等。
每个监控目标jail里必须指定一个日志文件路径logpath,一个fail捕捉过滤规则filter, 以及一组触发该监控惩罚规则的条件参数:maxretry, findtime等
每个fail捕捉过滤规则filter文件里里可以包含一条或多条正则表达式, 用来判断该条记录是否应该被标记为fail。比如下面的这个filter就使用了两条规则来捕捉所有的404 或403 代码对应的请求: 1 2 3 4 5 6 7 [Definition] failregex = ^<HOST> - \S+ \[.*\] ".*" 404 failregex = ^<HOST> - \S+ \[.*\] ".*" 403
RHEL9上实际操作 1. 安装: 安装比较简单,一行命令解决:
1 sudo dnf install fail2ban
安装完毕后,它会在系统里添加fail2ban这个系统服务,因此,可以使用各种常规服务操作命令来启动,停止,重启此服务或查询它的运行状态:
1 sudo systemctl start/stop/restart/status fail2ban
2. 配置和使用 根据前面所述,针对1个监控目标就要设定一个jail,而每个jail里必须指定一个日志文件路径logpath,一个fail捕捉过滤规则filter, 以及一组触发该监控惩罚规则的条件参数:maxretry, findtime。 我们需要把这些item放在两个新建的文件里:
一个包含一条或多条正则表达式的filter 配置文件 *.conf
一个包含日志文件路径和惩罚触发规则的配置文件 *.local.
具体可以参见下面的示意图。系统里可以有多个jail,每个jail都需要创建两个文件:
应用实例 1: 阻断Nginx 后门探测 404 近期发现Nginx的访问日志 access.log里多了一些试图探测各种.env文件的访问:
1 2 3 4 5 6 7 8 9 78.153.140.224 - - [17/Jan/2025:15:05:34 -0700] "GET /temp/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/14.0.1" "-" 78.153.140.224 - - [17/Jan/2025:15:05:37 -0700] "GET /logs/.env HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Windows NT 6.0; Win128; x128) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.18 Safari/537.36 OPR/37.0.2178.54" "-" 78.153.140.224 - - [17/Jan/2025:15:05:41 -0700] "GET /setup/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.4) Gecko/20070622 Firefox/2.0.0.4" "-" 78.153.140.224 - - [17/Jan/2025:15:05:42 -0700] "GET /httpd/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.9.0 Safari/538.1" "-" 78.153.140.224 - - [17/Jan/2025:15:05:43 -0700] "GET /bin/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/58.0" "-" 78.153.140.224 - - [17/Jan/2025:15:05:45 -0700] "GET /twilio/.env HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 OPR/42.0.2393.94" "-" 78.153.140.224 - - [17/Jan/2025:15:05:46 -0700] "GET /legal/.env HTTP/1.1" 404 555 "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.3)" "-" 78.153.140.224 - - [17/Jan/2025:15:05:50 -0700] "GET /themes/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Galeon/2.0.6 (Ubuntu 2.0.6-2)" "-" 78.153.140.224 - - [17/Jan/2025:15:05:50 -0700] "GET /wwwroot/.env HTTP/1.1" 404 153 "-" "Mozilla/5.0 (iPad; CPU OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 Mobile/14G60 Safari/602.1" "-"
同一个IP交替使用各种客户端来查找各种可能路径下的.env文件,因为都不存在,所以访问日志里都是返回404。这种行为很可疑,所以建议加入IP屏蔽列表进行屏蔽。
1. 创建1个filter过滤规则文件 因此针对这些请求,我们可以在/etc/fail2ban/filter.d/
目录下创建1个包含两条简单正则表达式的filter配置文件,起名为nginx-sensitive.conf
:
1 sudo nano /etc/fail2ban/filter.d/nginx-sensitive.conf
里面写入如下内容:
1 2 3 [Definition] failregex = <HOST> -.*(?:GET|POST).*(\.env |phpinfo\.php|PHPINFO\.php|\.php).*HTTP.* 4\d{2} ignoreregex = "GET .*\.css.*" "GET .*\.js.*" "GET .*\.png.*" "GET .*\.gif.*" "GET .*\.jpeg.*"
第一条就是用来匹配各种试图使用GET或POST请求来访问/*/.env
, /*/.phpinfo.php
, /*/.PHPINFO.php
而导致服务器返回4xx错误代码的访问记录,找到一条就标记为1个fail。第二行的ignoreregex 的意图是让Fail2Ban对忽略静态资源的访问日志的处理。
2. 验证创建的filter过滤规则文件 写完这些规则后,在重启fail2ban服务使它生效前,可以对这个filter里的两个正则表达式进行一些模拟测试来验证是否正确。
首先创建1个模拟测试的日志文件 “~/test.log”,包含如下5条访问记录,可以看出4条应该被标记为fail,第4条应该被忽略(不处理):
1 2 3 4 5 193.189.100.204 - - [17/Jan/2025:21:42:25 -0700] "POST /test.php HTTP/1.1" 404 230 "-" "Mozilla/5.0" 192.168.1.1 - - [17/Jan/2025:21:42:30 -0700] "GET /wp-comments-post.php HTTP/1.1" 403 0 "-" "-" 192.168.1.2 - - [17/Jan/2025:21:42:31 -0700] "POST /wp-content/.env HTTP/1.1" 404 0 "-" "-" 192.168.1.3 - - [17/Jan/2025:21:42:32 -0700] "GET /static/style.css HTTP/1.1" 404 0 "-" "-" 192.168.1.4 - - [17/Jan/2025:21:42:33 -0700] "GET /admin/login.php HTTP/1.1" 404 0 "-" "-"
然后执行如下测试命令:sudo fail2ban-regex ~/test.log /etc/fail2ban/filter.d/nginx-sensitive.conf
运行后返回测试结果,我在里面加了解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Running tests ============= Use failregex filter file : nginx-sensitive, basedir: /etc/fail2ban Use log file : ~/test.log Use encoding : UTF-8 Results ======= Failregex: 4 total |- | 1) [4] <HOST> -.*(?:GET|POST).* (\.env |phpinfo\.php|PHPINFO\.php|\.php).*HTTP.* 4\d{2} `- Ignoreregex: 0 total Date template hits: |- [ | [5] Day(?P<_sep>[-/])MON(?P=_sep)ExYear[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)? `- Lines: 5 lines, 0 ignored, 4 matched, 1 missed [processed in 0.08 sec] |- Missed line(s): | 192.168.1.3 - - [17/Jan/2025:21:42:32 -0700] "GET /static/style.css HTTP/1.1" 404 0 "-" "-" `-
3. 配置jail监控设置 上面已经创建了一个filter的配置文件.conf, 接下来需要新建1个jail的配置文件来使用这个filter匹配规则。
在/etc/fail2ban/jail.d
目录下新建1个名为nginx-sensitive.local
的配置文件,并写入如下内容:
1 2 3 4 5 6 7 8 [nginx-sensitive] enabled = true filter = nginx-sensitive logpath = /var/log/nginx/access.log maxretry = 3 findtime = 600 bantime = 3600 action = nftables-multiport[name=nginx-sensitive, port="80,443" , protocol=tcp]
为了方便解释,我在每一行后添加了注释,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [nginx-sensitive] enabled = true filter = nginx-sensitive logpath = /var/log/nginx/access.log maxretry = 3 findtime = 600 bantime = 3600 action = nftables-multiport[name=nginx-sensitive, port="80,443" , protocol=tcp]
注意一点,RHEL9 已经默认使用nftables, 不再使用iptables了,所以对应的防火墙规则要使用nftables的规则。而且上面的atction里只屏蔽该IP访问80和443 端口,如果想完全禁止该IP访问所有端口,则可以修改为:
1 action = nftables-allports[name=nginx-auth]
配置完毕后,重启fail2ban 服务使它生效:
sudo systemctl restart fail2ban
4. 检查jail 配置以及运行日志 运行后,可以使用如下命令来检查当前的监控情况:
1 2 3 4 5 6 sudo fail2ban-client status Status |- Number of jail: 2 `- Jail list: nginx-basic-auth, nginx-sensitive.
还可以直接指定jail的名字来看它的具体内容:
1 2 3 4 5 6 7 8 9 10 11 sudo fail2ban-client status nginx-sensitive Status for the jail: nginx-sensitive |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- File list: /var/log/nginx/access.log `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list:
从我的电脑浏览器里输入https://zangchuantao.com/test/1.php
来访问一个不存在的php文件,以触发这个规则,试了3次后,被屏蔽了,此时我浏览器里显示:
1 2 3 4 5 6 7 This site can’t be reached zangchuantao.com refused to connect. Try: Checking the connection Checking the proxy and the firewall ERR_CONNECTION_REFUSED
再次查看该jail的状态,可以发现它已经把我的IP屏蔽了:
1 2 3 4 5 6 7 8 9 10 sudo fail2ban-client status nginx-sensitive Status for the jail: nginx-sensitive |- Filter | |- Currently failed: 0 | |- Total failed: 3 | `- File list: /var/log/nginx/access.log `- Actions |- Currently banned: 1 |- Total banned: 1 `- Banned IP list: 24.8.72.142
5. 手动解除屏蔽某个IP 1 sudo fail2ban-client unban <IP>
6. 关于fail2ban的日志 它的日志默认保存为 /var/log/fail2ban.log,可以通过tail —f/F 来实时查看 (推荐-F),例如刚才我的测试操作在日志里就对应如下记录:
1 2 3 4 5 6 7 8 9 10 11 12 sudo tail -f /var/log/fail2ban.log 2025-01-19 06:48:04,105 fail2ban.actions [1083892]: INFO banTime: 3600 2025-01-19 06:48:04,105 fail2ban.filter [1083892]: INFO encoding: UTF-8 2025-01-19 06:48:04,105 fail2ban.filter [1083892]: INFO Added logfile: '/var/log/nginx/access.log' (pos = 145355, hash = 381a097c0c4fa35a87df6d27c5e9a9037d6c4fa5) 2025-01-19 06:48:04,109 fail2ban.jail [1083892]: INFO Jail 'nginx-basic-auth' started 2025-01-19 06:48:04,112 fail2ban.jail [1083892]: INFO Jail 'nginx-sensitive' started 2025-01-19 07:30:35,247 fail2ban.filter [1083892]: INFO [nginx-sensitive] Found 24.8.72.142 - 2025-01-19 07:30:35 2025-01-19 07:30:37,342 fail2ban.filter [1083892]: INFO [nginx-sensitive] Found 24.8.72.142 - 2025-01-19 07:30:37 2025-01-19 07:30:38,135 fail2ban.filter [1083892]: INFO [nginx-sensitive] Found 24.8.72.142 - 2025-01-19 07:30:38 2025-01-19 07:30:38,421 fail2ban.actions [1083892]: NOTICE [nginx-sensitive] Ban 24.8.72.142 2025-01-19 07:34:53,385 fail2ban.actions [1083892]: NOTICE [nginx-sensitive] Unban 24.8.72.142
实例2: 阻止爬虫连续下载媒体文件 Fail2Ban 完全可以用于阻止爬虫连续下载你的服务器上的媒体文件。假如服务器的 /files/video/
路径下有大量*.mp4 文件,文件名使用了数字编号从1到10000,为了阻止爬虫连续下载以爬取你的媒体文件,可以为它设置一个jail,具体做法如下。
1. 创建filter过滤规则文件 创建一个新的 Fail2Ban 过滤规则文件,比如 /etc/fail2ban/filter.d/nginx-media-download.conf:
1 sudo nano /etc/fail2ban/filter.d/nginx-media-download.conf
在文件中添加以下正则规则:
1 2 3 4 5 6 7 8 9 [Definition] failregex = ^<HOST> -.*GET /files/video/.*\.mp4 HTTP/.* 200
2. 创建自定义 Jail 1 sudo nano /etc/fail2ban/jail.d/nginx-media-download.local
配置 Jail 添加以下内容:
1 2 3 4 5 6 7 8 9 [nginx-media-download] enabled = true filter = nginx-media-download logpath = /var/log/nginx/access.log maxretry = 50 findtime = 300 bantime = 3600 action = nftables-allports[name=nginx-media-download]
3. 重启 Fail2Ban 服务 重启 Fail2Ban 服务以应用新规则就可以阻断爬虫连续下载了:
1 sudo systemctl restart fail2ban