Fail2ban 结合 Firewalld 与 iptables 的防御实践

Fail2ban 是 Python 开发的入侵检测框架:监视系统日志,按配置规则执行对应屏蔽动作。SSH 被暴力破解时,让它调用 firewalld 拉黑源 IP,比手动 iptables 一条条加省事。这里记一套 Fail2ban 结合 Firewalld 与 iptables 的防御配置,含 Nginx 的 CC 防护。

缘起:服务器被 SSH 爆破

有段时间服务器执行命令异常卡顿,排除了网络问题,内存、CPU、磁盘 IO 也都正常,怀疑被扫描。统计登录失败次数最多的源 IP:

lastb | awk '{ print $3}' | sort | uniq -c | sort -n 

输出显示居然有 IP 尝试 SSH 登录了几万次。起初手动用 iptables 拉黑,但每次手堵不现实,于是上 Fail2ban。

惊讶gif动图

安装

Fail2ban 包含在 epel 源里:CentOS 先装 epel-release 再装 fail2ban,Ubuntu 直接 apt-get。装完配置位于 /etc/fail2ban,jail.conf 是主配置,匹配规则在 filter.d 目录。

#包含在epel-release源中,所以需要首先安装epel-release源
yum install epel*

#CentOS安装
yum install fail2ban

#Ubuntu
apt-get install fail2ban

配置四步

一、防火墙先放通必要端口——CentOS 7 默认 firewalld,封禁逻辑不能建在自己都进不去的机器上:

#查看状态
firewall-cmd --state
#启动
systemctl start firewalld
#设置自动启动
systemctl enable firewalld

#开启一些必要的端口
firewall-cmd --zone=public --add-port=22/tcp --permanent
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --reload

#查看已经放通的端口
firewall-cmd --list-all

二、新建 jail.local 覆盖默认规则(别直接改主配置)。[DEFAULT] 段:ignoreip 白名单、bantime 封禁时长 24h、findtime 统计窗口 10m、maxretry 最大失败次数 5,动作走 firewallcmd-ipset;若改用 iptables 就取消对应注释并删掉 firewalld 配置。[sshd] 段:开启、指定 filter.d 里的 sshd 规则、port 22、action %(action_mwl)s 即禁 IP 加写日志,监视 /var/log/secure。不显式配 port 则封禁该 IP 访问所有端口:

~]# vim /etc/fail2ban/jail.local
# 默认配置
[DEFAULT]
# 指定哪些地址可以忽略 fail2ban 屏蔽(白名单)
ignoreip = 127.0.0.1/8
# 客户端IP被封禁时长
bantime  = 24h
# 如果在findtime秒内未找到匹配项,则计数器设置为零
findtime = 10m
# 允许客户端最大尝试失败的次数
maxretry = 5
# 在达到maxretry阈值时使用的默认动作;如果使用iptables将下面的注释取消,并将firewalld配置注释或者删除
banaction = firewallcmd-ipset
# 允许你对每个端口的IP地址进行标记和封锁
banaction_allports = firewallcmd-ipset
#banaction = iptables-multiport
#banaction_allports = iptables-allports
action = %(action_mwl)s

#名称自定义
[sshd]
#true表示开启
enabled = true
#规则名称(也就是文件名),必须填写位于filter.d目录里面的规则
filter  = sshd
#对应的端口或者协议;不显式配置port参数则封禁IP访问服务器所有端口
port  = 22 
#采取的行动,下面表示禁IP+写入日志
action = %(action_mwl)s
#需要监视的日志路径
logpath = /var/log/secure

三、封禁策略改成 DROP。注意不要修改默认配置文件:在 action.d 下复制对应策略文件、把 .conf 后缀换成 .local 再改:

# iptables
[root@localhost action.d]# cp iptables.conf iptables.conf_bak
[root@localhost action.d]# vim iptables.conf
blocktype = DROP

# firewalld
[root@localhost action.d]# cp firewallcmd-common.conf firewallcmd-common.local
[root@localhost action.d]# vim firewallcmd-common.local
blocktype = DROP

四、启动并开机自启,日志在 /var/log/fail2ban.log:

systemctl start fail2ban.service 
systemctl enable fail2ban.service 
systemctl status fail2ban.service

# 启动和服务日志查看
tailf /var/log/fail2ban.log

测试 SSH 防护

方便验证的话把策略调松:10 秒内连接失败 3 次就封。先看当前 sshd jail 状态:

~]# fail2ban-client status sshd
fail2ban01

用 SSH 客户端故意连续输错三次密码:

fail2ban02

回到服务器再执行同一条 status 命令,测试 IP 已进封禁列表:

fail2ban03

误封了解除:

[root@localhost ~]# fail2ban-client set sshd unbanip 192.168.66.61
1
[root@localhost ~]# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	0
|  `- Journal matches:	_SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned:	0
   |- Total banned:	1
   `- Banned IP list:	

防御配套建议:关闭 Root 远程登录、更改 SSH 默认端口;Fail2ban 也能配合 Nginx 做应用层防御。

Nginx CC 防护与被封 IP 的查看

这里 Nginx 日志用默认格式,匹配规则基于该格式写正则,实际要按自己的日志格式调整:

192.168.66.1 - - [27/June/2022:21:40:41 +0800] "GET / HTTP/1.1" 403 3308 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"

思路:用 fail2ban 监视 nginx 访问日志,匹配短时间内频繁请求的 IP,交给 firewalld 屏蔽。先建匹配规则 /etc/fail2ban/filter.d/nginx-cc.conf——正则命中返回 4xx(如 404、403)的 GET 请求来源 IP,ignoreregex 放行 css/js/png/gif/jpeg 等静态请求:

# 需要先新建一个nginx日志匹配规则
[root@localhost ~]# vi /etc/fail2ban/filter.d/nginx-cc.conf
[Definition]
# 此处的正则匹配规则是根据 nginx 的访问日志进行撰写,不同的恶意扫描有不同的日志特征
failregex = ^<HOST> .*"GET .* HTTP.*" 4d{2} d+ ".*"$

# 忽略对静态内容的请求
ignoreregex = "GET .*.css.*" "GET .*.js.*" "GET .*.png.*" "GET .*.gif.*" "GET .*.jpeg.*"

再在 jail.local 里加 [nginx-cc]:60 秒内同一 IP 达 20 次请求,封 1 小时,盯 /var/log/nginx/access.log。这是测试数值,正式用按实际访问量级修改:

[root@localhost ~]# vim /etc/fail2ban/jail.local
#名称自定义
[nginx-cc] 
#true表示开启
enabled = true 
#对应的端口或者协议
port = http,https
#规则名称(也就是文件名),必须填写位于filter.d目录里面的规则
filter = nginx-cc
#采取的行动,下面表示禁IP+写入日志
action = %(action_mwl)s
# 允许客户端尝试失败的次数
maxretry = 20
# 在findtime时间内允许尝试maxretry次
findtime = 60
# 客户端IP被封禁时长;单位默认为秒(s),可以用分m,时h,天d,月mo,年y, -1为永久屏蔽
bantime = 1h
#需要监视的日志路径
logpath = /var/log/nginx/access.log

配置变更用 fail2ban-client reload 加载;不生效就重启服务:

[root@localhost ~]# fail2ban-client reload

# 查看启用的规则
[root@localhost ~]# fail2ban-client status
fail2ban04
fail2ban05

然后快速刷新某个页面,正常情况会被拒访,再看 nginx-cc 这个 jail 的状态:

[root@localhost ~]# fail2ban-client status nginx-cc
fail2ban06

用 Firewalld 时,Fail2ban 通过 ipset 列表管理被封 IP 并应用到防火墙规则,每个 jail 一个列表,命名形如 f2b-sshd。查看被封 IP 分几步:先列出当前所有 ipset 列表,找 f2b- 开头的:

sudo ipset list

确认名称后直接看列表内容:

sudo ipset list f2b-sshd

注意 ipset 里有记录不等于全部被拦——防火墙规则必须引用这个集合并指定动作才生效,可用 direct 规则核查 firewalld 是否引用:

sudo firewall-cmd --direct --get-all-rules
© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容