我如何教 fail2ban 檢測和阻止來自整個網路塊的攻擊?
我已經正確安裝
fail2ban
在我的機器上,啟動了ssh
,ssh-dos
和recidive
;的規則 一切正常。最近,我看到越來越多的來自不同主機的重複攻擊來自同一網路,它們通過在禁止後切換 IP 來規避“重複”規則:
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
我想檢測它並製定一個“recidive24”規則來阻止所有這些禁止整個
/24
區塊的攻擊。我在debian 錯誤檔案中找到了一個關於 fail2ban 的建議,我已經應用了它,但是:
- 如果我
/24
在監獄被觸發時應用完全禁令ssh
,我會遇到一個問題,即我的同一網路上的某人很容易阻止我,只需從一個 IP 進行攻擊;- 監獄將
recidive
是完美的,但它不是由更改 IP 引發的風暴……所以我想更改
recidive
過濾器規範,使其只查看IP的前三個字節,但我在這裡不知所措……執行禁令的正則表達式是(來自/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*$
…它將匹配一個完整的IP。
問題:如何更改此失敗正則表達式,使其僅匹配主機 IP 的前三個字節?
請注意,當檢測到垃圾郵件 IP 時,問題不是阻塞整個子網 — 這相對容易。問題是觸發一種情況
subnet-recidive
,例如,recidive
同一子網有五個或更多命中…我雖然關於用另一個守護程序過濾fail2ban日誌文件並編寫第二個文件,其中最後一個字節每次都是0,並使用它觸發recive jail,但它看起來真的很笨拙……
Fail2ban 沒有自動阻止來自整個子網的攻擊的簡潔功能。不過,可以使用最新版本的 fail2ban(我使用 v0.11)、一些簡單的 fail2ban 腳本和一個小的純 python3 腳本。
注意:問題指的是“整個子網”(我將其稱為 CIDR 塊或 IP 範圍)。這是一件困難的事情,因為我們不知道攻擊者控制的地址塊有多大。甚至可能是攻擊者偶然控制了同一塊中的少數地址,中間地址是合法的。
Step 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 查詢,我們可以簡單地禁止我們發現的 CIDR 塊,基於我們認為合適的 maxretry 和 findtime。這是我使用的監獄:
[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\; \}
將這種魔法
/24
(有些人甚至可能喜歡/22
)添加到iptables
所有 jails 的命令中的最簡單方法是添加兩個文件:/etc/fail2ban/action.d/iptables-multiport.local /etc/fail2ban/action.d/iptables-allports.local
內容如下:
[Definition] actionban = <iptables> -I f2b-<name> 1 -s <ip>/24 -j <blocktype> actionunban = <iptables> -D f2b-<name> -s <ip>/24 -j <blocktype>
對於檢測部分,您需要修改過濾器……這有點棘手。但好的部分是 - 沒有必要!這個子網阻塞的東西工作得很好。