原理介绍

Fail2ban 是一款用于防止暴力破解和恶意登录尝试的开源工具,广泛应用于 Linux 系统。它通过监控日志文件来检测失败的登录尝试或其他可疑活动,并自动更新防火墙规则以阻止恶意 IP 地址。

它通过监控日志文件(如 /var/log/auth.log)来检测失败的登录尝试,当某个 IP 地址在短时间内多次尝试失败后,Fail2ban 会自动将其添加到防火墙的黑名单中,阻止其进一步访问。

现代的Fail2ban通常可同时过滤IPv4和IPv6攻击,配置没有区别,非常方便。

实现过程

1、监控日志文件:Fail2ban 通过读取指定的日志文件来检测恶意活动。

2、检测失败尝试:它使用正则表达式来匹配日志中的失败登录尝试或其他可疑活动。

3、更新防火墙规则:当检测到恶意 IP 地址时,Fail2ban 会自动更新防火墙(如 iptables 或 nftables)规则,阻止该 IP 的进一步访问。

4、自动解封:在预设的时间后,被封锁的 IP 地址会被自动从黑名单中移除

主要配置文件

Fail2Ban的主要配置文件位于 /etc/fail2ban 目录下。关键的配置文件包括:

jail.confjail.local文件:这个是主要的配置文件,用于定义各种jail的设置,如SSH、Apach、Nginx等,并包含该服务的监控规则、封禁时间等参数。通常我们会修改的是jail.local文件,jail.conf文件为默认配置文件,当jail.localjail.conf有冲突配置时,以

jail.local配置为主,当jail.local文件中不存在某些配置时才会读取默认的jail.conf文件;

action.d:该目录包含各种定义的action,这写action定义了在遇到攻击时需要采取的措施。如更新防火墙规则等。

filter.d:该目录包含各种过滤器的定义文件,这些过滤器用于从日志文件中提取出攻击模式,通常用户可自定义过滤规则。

安装及常用配置

1、Ubuntu/Debain安装Fail2ban

apt update && apt install fail2ban -y

Centos/RHEL安装

yum install epel-release -y
yum update && yum install fail2ban -y

2、配置示例(SSH)

避免直接修改默认配置文件 jail.conf,创建一个 jail.local 文件

vim /etc/fail2ban/jail.local

添加如下配置(这里使用的是nftables防火墙,默认为iptables,如果不想改去掉banaction即可)

[DEFAULT]											# 全局模板
ignoreip = 127.0.0.1/8								# 白名单
backend=auto										# 自动选择后端日志服务,最好写到全局下,否则可能导致某些规则不生效
banaction = nftables								# 默认封禁工具,仅封禁单个端口
banaction_allports = nftables[type=allports]		# 全局下全端口封禁模板
action = %(action_mwl)s								# 全局下启用组合动作(封禁 + 通知 + 日志记录)

[sshd]
enabled  = true
port    = 22
filter = sshd
maxretry= 3											# 在指定过滤时间内最大重试次数
findtime= 600										# 间隔时间,单位为 s
bantime = 7d										# 封禁时间,最终会计算为s
logpath = /var/log/auth.log  						# 日志路径(Ubuntu 默认为 /var/log/auth.log)

默认fail2ban使用防火墙阻止客户端访问时使用的是Reject,会提示入侵者,为了安全需要改为Drop

vim /etc/fail2ban/action.d/nftables.conf
# 找到blocktype = reject,修改为以下内容
blocktype = drop

重启fail2ban,使得配置生效

systemctl restart fail2ban
systemctl enable fail2ban
fail2ban-client status sshd
----------------------------------------------------------------------------------------------------------
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0												# 当前失败数
|  |- Total failed:     3												# 失败总数
|  `- File list:        /var/log/auth.log								# 日志匹配项
`- Actions
   |- Currently banned: 1												# 目前禁止
   |- Total banned:     1												# 被禁总数
   `- Banned IP list:   192.168.10.1									# 被禁IP列表

当看到 Total failed下方存在File listJournal matches且其中存在内容时,表明规则已经正常开始监听日志文件,否则就是后端日志服务没选择正确,建议全局下配置backend=auto自动选择后端日志服务!

这里我尝试三次输入错误SSH密码,可以看到sshd的jail模块已经检测到进行阻止,并且已记录攻击者IP。需要注意的是:当封禁结束后,该IP信息将会被移除,可以通过以下查看日志的命令进行查看

cat /var/log/fail2ban.log | grep Ban			# 查看封禁IP记录
cat /var/log/fail2ban.log | grep Unban			# 查看解封禁IP记录

image

3、常用运维命令

fail2ban-client status								# 查看生效的jail数量和名称
fail2ban-client status <jail-name>					# 查看某个jail的详细信息
fail2ban-client banned								# 列出所有被禁的IP
fail2ban-client set <jail-name> unbanip <被禁IP>		# 解封某个jail封禁的ip
fail2ban-client set <jail-name> unbanip 0.0.0.0/0	# 解封某个jail封禁的所有ip

image

#如果SSHD使用的是密钥登录,默认会阻止无效的ssh用户密钥登录失败,如果需要也阻止有效的ssh用户登录,则需要在Fail2ban的sshd.conf的filter规则中进行以下修改,否则无法正常匹配密钥登录失败的日志

vim /etc/fail2ban/filter.d/sshd.conf
# 将publickey = nofail改为以下内容
publickey = any

重启fail2ban后即可。同时建议ssh配置文件中增加白名单,只运行某些用户登录,这样更安全,fail2ban也可以更好的匹配攻击者

AllowUsers root

4、Nginx相关配置

① 防CC、防目录扫描、爬虫配置

先在filter.d目录下创建预定过滤规则

block_cc.conf

[Definition]
failregex = <HOST> -.*- .*HTTP/(1|2).* .* .*$
ignoreregex = .*(robots.txt|ttf|ico|jpg|svg|png|css|js)

说明

  • 匹配HTTP1/2的所有访问状态码

  • 忽略部分常用静态文件,防止误封正常用户

block_scan.conf

[Definition]
failregex =
    # 针对敏感路径的未授权访问 (状态码 4xx/5xx)
    ^<HOST> - - \[.*?\] "(GET|POST) /(web-admin|wp-admin|.git|.env|backup|admin|phpmyadmin|sql|home|login|manage|config|conf|\.bak|\.old|\.config|\.conf|uc|user|users|console)(/.*)? HTTP.*?" (40\d|50\d) \d+
ignoreregex =

说明

  • 拦截访问敏感路径(如 wp-admin.git.env 等)的请求。

  • 支持自定义添加其他敏感关键词(如 loginconfig)。

说明

  • 正则表达式匹配常见恶意爬虫的 User-Agent(如 curlsqlmapnmap 等)。

随后在jail.local文件中加入以下内容

[DEFAULT]
ignoreip = 127.0.0.1/8
banaction = nftables
banaction_allports = nftables[type=allports]
action = %(action_mwl)s

[block_cc]
enabled = true
filter = block_cc
port = 9443
maxretry = 300
findtime = 240
bantime = 3600
logpath = /www/wwwlogs/xxxx.cn.log

[block_scan]
enabled = true
filter = block-scan
port = 80,443
maxretry = 5
findtime = 3600
bantime = 43200
logpath = /www/wwwlogs/xxxx.cn.log

重启Fail2ban即可

② Nginx反向代理内部的Vaultwarden防爆破

Nginx反向代理配置

http {
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    server {
        listen 80;
        server_name vault.xxx.xxx;
        access_log /var/log/nginx/vaultwarden.access.log main;  # 日志路径

        location / {
            proxy_pass https://xxx.xxxx.xx:9443;  # Vaultwarden实际端口
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            # 重要:确保日志记录真实客户端IP(尤其在使用CDN时)
            # 若前端有CDN,需使用 $http_x_forwarded_for
        }
    }
}

自定义过滤规则

vim /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^<HOST> -.*POST (/identity/connect/token|/notifications/hub/negotiate).* 4[0-9]{2}.*$
ignoreregex =

说明

  • 匹配POST请求到Vaultwarden登录端点(Bitwarden API路径)

  • 状态码 4xx(如401/400表示登录失败)

  • <HOST> 自动捕获客户端IP

编辑 /etc/fail2ban/jail.local

vim /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8
backend=auto
banaction = nftables
banaction_allports = nftables[type=allports]
action = %(action_mwl)s

[vaultwarden]
enabled = true
port = 9443
filter = vaultwarden
logpath = /var/log/nginx/vaultwarden.access.log		# 匹配Nginx日志路径
maxretry = 3                                    	# 3次错误后封禁
findtime = 1800                                  	# 30分钟内触发
bantime = 7d                                  		# 封禁7200秒(2小时)
action = %(action_mwl)s

重启Fail2ban

systemctl restart fail2ban

此时我尝试输入错误一次密码,使用fail2ban-client检测工具检测后输出如下内容,可以看到已经检测到了一次错误。
且经过测试,即使是输入不存在的邮箱,或是使用app或浏览器扩展通过api连接也会被封禁;

imageimage

CC攻击和目录扫描测试

# 安装使用Siege(100个并发,持续30秒),-c 为并发连接数,-t 为持续时间
apt install siege -y
siege -c 100 -t 30s "http://你的域名+端口"

# Nikto 扫描(模拟专业攻击),x 为扫描常见的危险文件, 6为扫描所有目录(已知的超2000个目录)
apt install nikto -y
nikto -h http://your-domain.com -Tuning x 6

随后查看记录是否存在被封禁IP

5、对接mail在受到攻击时发送邮件进行提醒,这里使用的是qq邮箱,注意:授权码不是用户密码,需要在网页版qq邮箱的设置中进行申请!!!

# Ubuntu或Deabin
apt install bsd-mailx ssmtp -y
# Centos或RHEL
yum install mainx -y

#Centos和RHEL配置方法

vim /etc/mail.rc
#发件人邮箱
set from="Fail2ban <xxxx@qq.com>"
#发件人邮箱的SMTP地址
set smtp=smtps://smtp.qq.com:465
#发件人邮箱登陆账号
set smtp-auth-user=xxxx@qq.com
#发件人邮箱的授权码
set smtp-auth-password=xxxx
#认证方式
set smtp-auth=login
#忽略证书警告
set ssl-verify=ignore
#证书存放路径
set nss-config-dir=etc/pki/nssdb

#Ubuntu和Debain配置方法

vim /etc/ssmtp/ssmtp.conf
# 清空原有配置后加入以下配置
mailhub=smtp.qq.com:465					# qq邮箱地址
AuthUser=xxxx@qq.com					# qq邮箱用户
AuthPass=xxxx							# qq邮箱授权码
UseTLS=YES								# 使用TLS
UseSTARTTLS=NO							# 465端口的邮箱需要关闭改配置
FromLineOverride=YES
vim /etc/mail.rc
# 清空原有配置后加入以下配置
set from="Fail2ban <xxxx@qq.com>"		# 设置发件人信息

#会有这种差异的主要原因在于:mail命令只是一个调取发送邮件程序的一个工具,Centos和RHEL系统自带有发送邮件的应用。而Ubuntu和Debain并不自带,需要自己配置,尝试了Sendmail,Postfix,Exim4均不生效,但是ssmtp可以生效,且配置简单,因此使用ssmtp;

#debain中设置了mail后可能会在jail开启和暂停时不断发送邮件,将mail-whois-lines.conf文件内相关配置删除后,暂停服务或者重启服务器后即可解决(重启服务不行)

进行邮箱测试

echo 'Test Mail' | mail -v -s 'Test' xxxx@qq.com
######################################################################
Resolving host smtp.qq.com . . . done.
Connecting to 183.47.101.192:465 . . . connected.
220 newxmesmtplogicsvrszc13-0.qq.com XMail Esmtp QQ Mail Server.
>>> EHLO AliYun
250-newxmesmtplogicsvrszc13-0.qq.com
250-PIPELINING
250-SIZE 73400320
250-AUTH LOGIN PLAIN XOAUTH XOAUTH2
250-AUTH=LOGIN
250-MAILCOMPRESS
250-SMTPUTF8
250 8BITMIME
>>> AUTH LOGIN
334 VXNlcm5hbWU6
>>> MTE3OTcxMjc3MkBxcS5jb20=
334 UGFzc3dvcmQ6
>>> YmFqbWVqbGdpdHR2amhmZA==
235 Authentication successful
>>> MAIL FROM:<xxxx@qq.com>
250 OK
>>> RCPT TO:<xxxx@qq.com>
250 OK
>>> DATA
354 End data with <CR><LF>.<CR><LF>.
>>> .
250 OK: queued as.
>>> QUIT
smtp-server: "/root/dead.letter" 0/0
. . . message not sent.

Fail2ban配置

vim /etc/fail2ban/jail.local
[DEFAULT]										# 全局模板
backend=auto
ignoreip = 127.0.0.1/8							# 白名单
banaction = nftables							# 默认封禁工具,仅封禁单个端口
banaction_allports = nftables[type=allports]	# 全局下全端口封禁模板
action = %(action_mwl)s
mta = mail
destemail = xxx@qq.com							# 接收告警邮件的地址 (改成你自己的邮箱)
sendername = Fail2ban Server					# 发件人名称
sender = xxxx@qq.com							# 发件人邮件地址(写入刚才mail中配置的邮箱即可,可以收发人相同)

自定义邮件内容

vim /etc/fail2ban/action.d/mail-whois-lines.conf
actionban = printf "警告!!!\n被攻击机器名:`uname -n` \n被攻击机器IP:`/bin/curl ifconfig.co` \n攻击服务:<name> \n时间范围:<findtime> 秒内 \n被攻击次数:<failures> 次 \n攻击者IP:<ip> \n攻击方式:暴力破解、尝试弱口令或洪水攻击。\n处理方式:已将<ip>加入防火墙黑名单,封禁时间为 <bantime> 秒。\n封禁时间:`date`\n日志信息摘要:\n`tail -5 <logpath>` \n\n\t                                                       ——来自fail2ban邮箱"|mail -v -s "攻城狮请注意,服务器遭到来自 <ip> 的暴力攻击!" <dest>

对于actionstartactionstop如果觉得没必要发那么多邮件,可将其注释

重启Fail2ban

systemctl restart fail2ban

尝试登录失败三次,收到邮件

imageimage

补充

Vaultwarden的原生日志中,防止爆破的filter规则(日志路径可自定义)

[Definition]
failregex = ^\[[^\]]*\]\[vaultwarden::api::identity\]\[ERROR\] Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
ignoreregex =

飞牛用户防爆破filter规则(日志路径为/var/log/syslog

[Definition]
failregex = MAINEVENT:.*"template":"LoginFail".*"IP":"<HOST>"
ignoreregex =