fail2ban 可以检测并阻止 IP 范围吗?

fail2ban 可以检测并阻止 IP 范围吗?

我每天都会在我的 mail.log 中收到数百行这样的信息:

Apr 28 11:10:28 servername amavis[30077]: (30077-08) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.16] <[email protected]> -> <[email protected]>, quarantine: F/spam-FaGlty0PIZMS.gz, Message-ID: <[email protected]>, mail_id: FaGlty0PIZMS, Hits: 7.544, size: 5136, 7444 ms
Apr 28 11:44:53 servername amavis[30074]: (30074-10) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.25] <[email protected]> -> <[email protected]>, quarantine: H/spam-H4sMG6EC6q-I.gz, Message-ID: <[email protected]>, mail_id: H4sMG6EC6q-I, Hits: 12.405, size: 5209, 3816 ms
Apr 28 11:45:53 servername amavis[30077]: (30077-10) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.30] <[email protected]> -> <[email protected]>, quarantine: q/spam-qNkRyAnBW5ul.gz, Message-ID: <[email protected]>, mail_id: qNkRyAnBW5ul, Hits: 12.405, size: 5217, 4456 ms
Apr 28 12:05:22 servername amavis[30074]: (30074-12) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.11] <[email protected]> -> <[email protected]>, quarantine: z/spam-zaKH80IIImbj.gz, Message-ID: <[email protected]>, mail_id: zaKH80IIImbj, Hits: 11.155, size: 5163, 6837 ms
Apr 28 12:06:41 servername amavis[30074]: (30074-13) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.40] <[email protected]> -> <[email protected]>, quarantine: j/spam-jgw8hoOtyeSf.gz, Message-ID: <[email protected]>, mail_id: jgw8hoOtyeSf, Hits: 9.546, size: 4749, 3844 ms
Apr 28 12:07:50 servername amavis[30077]: (30077-13) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.95] <[email protected]> -> <[email protected]>, quarantine: w/spam-wYu7sNla0_BX.gz, Message-ID: <[email protected]>, mail_id: wYu7sNla0_BX, Hits: 8.87, size: 4729, 3889 ms
Apr 28 12:58:32 servername amavis[30077]: (30077-16) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.46] <[email protected]> -> <[email protected]>, quarantine: 5/spam-52iE_rnYAkaF.gz, Message-ID: <[email protected]>, mail_id: 52iE_rnYAkaF, Hits: 19.628, size: 5032, 7830 ms
Apr 28 13:39:12 servername amavis[30077]: (30077-20) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.62] <[email protected]> -> <[email protected]>, quarantine: 8/spam-8zKenB5I3mjS.gz, Message-ID: <[email protected]>, mail_id: 8zKenB5I3mjS, Hits: 11.211, size: 5106, 3928 ms
Apr 28 14:22:34 servername amavis[14260]: (14260-04) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.64] <[email protected]> -> <[email protected]>, quarantine: S/spam-SLdyUkN0XFpi.gz, Message-ID: <[email protected]>, mail_id: SLdyUkN0XFpi, Hits: 12.405, size: 5146, 3869 ms
Apr 28 14:58:44 servername amavis[14260]: (14260-06) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.47] <[email protected]> -> <[email protected]>, quarantine: M/spam-MEimd4Bg1bE3.gz, Message-ID: <[email protected]>, mail_id: MEimd4Bg1bE3, Hits: 11.231, size: 5064, 3838 ms
Apr 28 15:16:17 servername amavis[15052]: (15052-08) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.91] <[email protected]> -> <[email protected]>, quarantine: M/spam-MVHz2AB6fJWo.gz, Message-ID: <[email protected]>, mail_id: MVHz2AB6fJWo, Hits: 10.805, size: 5071, 3764 ms
Apr 28 15:16:38 servername amavis[14260]: (14260-09) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.95] <[email protected]> -> <[email protected]>, quarantine: P/spam-P_vgm1aE0UvA.gz, Message-ID: <[email protected]>, mail_id: P_vgm1aE0UvA, Hits: 9.555, si 6.694, size: 5656, 2536 ms
Apr 28 15:57:55 servername amavis[14260]: (14260-15) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.108.104] <[email protected]> -> <[email protected]>, quarantine: 8/spam-8hnRkMDQmj4E.gz, Message-ID: <[email protected]>, mail_id: 8hnRkMDQmj4E, Hits: 9its: 7.772, size: 8343, 6229 ms
Apr 28 16:36:12 servername amavis[14260]: (14260-20) Blocked SPAM {DiscardedInbound,Quarantined}, [185.140.110.64] <[email protected]> -> <[email protected]>, quarantine: J/spam-JAzp8lAdYrqB.gz, Message-ID: <[email protected]>, mail_id: JAzp8lAdYrqB, Hits: 18.228, size: 4938, 4849 ms

如您所见,这些邮件来自不同但相似的 IP。在此示例中,来自 185.140.110.xxx 和 185.140.108.xxx(或网络掩码语法中的 185.140.110.0/24 和 185.140.108.0/24)。

Fail2ban 擅长识别来自相同 IP 地址的日志行,但这里我们有所有不同的地址,但都来自一些小的范围。

有没有办法告诉 fail2ban,不要查看相同的 IP 地址,而是查看范围?

我希望 fail2ban 在几小时内检测到 3 行包含此范围内的 IP 地址后,立即阻止从 185.140.110.0 到 185.140.110.255 的所有 IP。

答案1

Fail2ban 没有自动检测和阻止 IP 范围的简洁功能。不过,可以使用较新版 fail2ban(我使用的是 v0.11)、一些简单的 fail2ban 脚本和一个小型纯 python3 脚本来实现。

注意:问题涉及 IP 范围(我将其称为 CIDR 块,因为我发现 CIDR 表示法在 nftables 中易于使用)。这是一件困难的事情,因为我们不知道攻击者控制的地址块有多大。甚至可能攻击者偶然控制了同一块中的少数地址,而中间的地址是合法的。

步骤 1. 获取主机的 CIDR

fail2ban 监控的日志文件通常显示主机(例如 127.0.0.1)而不是 CIDR 块(127.0.0.0/24)或 IP 范围(127.0.0.0 - 127.0.0.255)。

一种解决方案可能是首先假设一个较小的 CIDR 块,然后随着日志报告更多行为不当的主机而增大它。显然,只有当这些主机来自相邻地址时,它才应该增大 CIDR。但这很复杂,无论算法多么复杂,合法地址都可能被卷入其中。

相反,我们也可以简单地在 whois 中查找 CIDR。这会导致 whois 查找出现一些延迟,并产生一些流量。但解析 whois 的脚本可以将 CIDR 写入 syslog,然后 fail2ban 可以再次捕获它。

注意:不要忘记将此脚本挂接到您首选的 action.d/lorem-ipsum.conf 脚本的 actionban 中。请注意,如果其 maxretry > 1,那么您将无法捕获主机仅发生一次故障的 CIDR 块!

#!/usr/bin/python

import sys, subprocess, ipaddress, syslog

def narrowest_cidr(cidrA, cidrB):
    _ip, subA = cidrA.split('/')
    _ip, subB = cidrB.split('/')

    if int(subA) > int(subB):
        return cidrA
    else:
        return cidrB

bad_ip = sys.argv[1]
cidrs = []
inetnums = []
ret = None

whois = subprocess.run(['whois', bad_ip], text=True,
        stdout=subprocess.PIPE, check=True)

for line in whois.stdout.split('\n'):
    if 'CIDR:' in line:
        cidrs.append(line.replace('CIDR:', '').strip())
    if 'inetnum:' in line:
        inetnums.append(line.replace('inetnum:', '').strip())

if len(cidrs) >= 1:
    if len(cidrs) == 1:
        cidr = cidrs[0]
    else:
        cidr = narrowest_cidr(cidrs[0], cidrs[-1])
elif len(inetnums) > 0:
    if len(inetnums) == 1:
        inetnum = inetnums[0]
        startip, endip = inetnum.split(' - ')
        cidrs = [ipaddr for ipaddr in ipaddress.summarize_address_range(ipaddress.IPv4Address(startip), ipaddress.IPv4Address(endip))]
        if len(cidrs) == 1:
            cidr = cidrs[0]
        else:
            cidr = narrowest_cidr(cidrs[0], cidrs[-1])
else:
    cidr = "no CIDR found"

syslog.openlog(ident="suspectrange")
syslog.syslog("{} has CIDR {}".format(bad_ip, cidr))

第 2 步:确定何时阻止 CIDR

如果我们有动态 CIDR 确定,这可能会变得有点复杂,因为我们必须更改要禁止的内容。但是使用 whois 查询,我们可以根据我们认为合适的 maxretry 和 findtime 简单地禁止我们找到的 CIDR 块。这是我使用的 jail:

[fail2ban-cidr-recidive]
filter = fail2ban-cidr-recidive
action = nftables-common[name=BADRANGE]
logpath = /var/log/everything/current

#5 bans in 12hrs is 48hr ban
maxretry = 5
findtime = 12h
bantime = 2d

及配套过滤器

[Definition]

failregex = \[suspectrange\] .* has CIDR <SUBNET>

步骤 3. 实际阻止 CIDR

您可能已经注意到,我使用了 action.d/nft-common.conf。nftables 允许阻止 CIDR 块而不是单个主机。这需要对操作脚本的 actionstart 部分的第一行进行一些小改动:

actionstart = nft add set netdev f2b <set_name> \{ type ipv4_addr\; \}

应修改为:

actionstart = nft add set netdev f2b <set_name> \{ type ipv4_addr\; flags interval\; \}

相关内容