我已正确安装fail2ban
在我的机器上,激活了ssh
,ssh-dos
和的规则recidive
;一切正常。
最近,我看到越来越多的来自不同主机的重复攻击模式形成相同的网络,通过在禁止后切换 IP 来规避“recidive”规则:
2015-01-25 11:12:11,976 fail2ban.actions: WARNING [ssh] Ban XXX.41.124.29
2015-01-25 11:12:13,165 fail2ban.actions: WARNING [ssh] Ban XXX.41.124.42
2015-01-25 11:12:16,297 fail2ban.actions: WARNING [ssh] Ban XXX.41.124.28
2015-01-25 11:12:20,446 fail2ban.actions: WARNING [ssh] Ban XXX.41.124.104
Run Code Online (Sandbox Code Playgroud)
我想检测它并制定一个“recidive24”规则来阻止所有这些禁止整个/24
块的攻击。
我在debian 错误档案中找到了一个关于 fail2ban的建议,我已经应用了它,但是:
如果我/24
在ssh
监狱被触发时应用完全禁令,我会遇到一个问题,我的同一网络上的某个人很容易通过从一个 IP 进行攻击来阻止我;
该recidive
监狱将是完美的,但它不是由风暴改变IP地址触发...
所以我想更改recidive
过滤器规范,以便它只查看 IP 的前三个字节,但我在这里不知所措......执行禁令的正则表达式是 (from /etc/fail2ban/recidive.conf
)
# The name of the jail that this filter is used for. In jail.conf, name the
# jail using this filter 'recidive', or change this line!
_jailname = recidive
failregex = ^(%(__prefix_line)s|,\d{3} fail2ban.actions:\s+)WARNING\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$
Run Code Online (Sandbox Code Playgroud)
...它将匹配一个完整的 IP。
问题:如何更改此故障正则表达式,使其仅匹配主机 IP 的前三个字节?
请注意,当检测到垃圾邮件 IP 时,问题不会阻塞整个子网——这相对容易。问题是触发一种情况subnet-recidive
,例如,recidive
同一子网有五个或更多点击......
我想用另一个守护进程过滤fail2ban日志文件并写入第二个文件,其中最后一个字节每次都是0,并使用它触发recidive jail,但它看起来真的很笨拙......
Fail2ban 没有自动阻止来自整个子网的攻击的简洁功能。不过,使用最新版本的fail2ban(我使用v0.11)、一些简单的fail2ban脚本和一个小型的纯python3脚本是可以做到的。
注意:问题涉及“整个子网”(我将其称为 CIDR 块或 IP 范围)。这是一件困难的事情,因为我们不知道攻击者控制了多大的地址块。攻击者甚至可能偶然控制同一块中的少数地址,而中间地址是合法的。
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))
Run Code Online (Sandbox Code Playgroud)
如果我们有动态 CIDR 确定,这可能会变得有点复杂,因为我们必须更改我们所禁止的内容。但通过 whois 查找,我们可以根据我们认为合适的 maxretry 和 findtime 简单地禁止我们找到的 CIDR 块。这是我使用的监狱:
[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
Run Code Online (Sandbox Code Playgroud)
以及附带的过滤器
[Definition]
failregex = \[suspectrange\] .* has CIDR <SUBNET>
Run Code Online (Sandbox Code Playgroud)
您可能已经注意到,我使用 action.d/nft-common.conf。nftables 允许阻止 CIDR 块而不是单个主机。这需要对操作脚本的 actionstart 部分的第一行进行一些小更改:
actionstart = nft add set netdev f2b <set_name> \{ type ipv4_addr\; \}
Run Code Online (Sandbox Code Playgroud)
应修改为:
actionstart = nft add set netdev f2b <set_name> \{ type ipv4_addr\; flags interval\; \}
Run Code Online (Sandbox Code Playgroud)