Linux

IPTables 或替代解決方案阻止第一次連接嘗試,然後允許來自同一 IP 的進一步嘗試

  • March 23, 2019

我有一個獨特的情況,針對某個遊戲的 DDOS 攻擊正在發送逼真的玩家連接數據包,這些數據包似乎完美地模仿了真實玩家的連接數據包。

這些通常會循環通過看似數千個 IP,發送一個請求並填滿伺服器上的連接槽,從而阻止任何真正的玩家能夠連接。

連接後,真正的玩家開始以高頻率發送多個 UDP 數據包,但這些虛假數據包每個 IP 只發送一次。

有沒有辦法可以丟棄從 IP 接收到的第一個數據包,但隨後允許所有進一步的數據包?

伺服器在 Debian Linux 9.x 上執行。

iptables + ipset

您可以使用recent匹配擴展,但對於您的案例而言,它可能過於有限,至少有兩個原因:預設模組設置僅允許 100 個條目,這將限制為 < 100 個玩家而無需進行更多調整,並且過時條目的到期不會自動(它必須從數據包路徑完成,這在性能方面不好,或者從一些外部批處理作業)。

您還可以使用ipset,旨在與 iptables 一起使用,其功能是一個超集recent並且效果更好。它自動處理超時條目的刪除。

因此,例如,如果將其應用於埠 27960/udp…

ipset create firstseenpacket hash:ip timeout 60

iptables -N dropfirstseenpacket

iptables -I INPUT -p udp -m udp --dport 27960 -m set ! --match-set firstseenpacket src -j dropfirstseenpacket
iptables -I INPUT 2 -p udp -m udp --dport 27960 -j SET --exist --add-set firstseenpacket src
iptables -A dropfirstseenpacket -j SET --add-set firstseenpacket src
iptables -A dropfirstseenpacket -j DROP

第一次看到源時,測試將找不到條目,並且被反轉將跳轉到dropfirstseenpacket,這將添加條目並丟棄數據包。來自同一來源的更多事件將找到該條目,刷新其計時器 ( --exist),並且不會執行其他任何操作。如果第一個測試比僅檢查 27960/udp 更昂貴,則可以使用更多的鏈進一步分解規則。

您可以通過以下方式查看 ipset 內容:

ipset save firstseenpacket

請注意,我使用了-I(插入)語句來確保在不受干擾的情況下處理它。將它放在類似條目之後-A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT應該保持兼容,甚至可以提高性能和資源(通過將活動條目的責任從 ipset 移交給 conntrack),因為無論何時啟動 conntrack 都會維護自己的所有活動流集。這將給出類似於:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 27960 -m set ! --match-set firstseenpacket src -j dropfirstseenpacket
iptables -A dropfirstseenpacket -j SET --add-set firstseenpacket src
iptables -A dropfirstseenpacket -j DROP
iptables -A INPUT -p udp -m udp --dport 27960 -m conntrack --ctstate NEW -j ACCEPT

如果發送了回复,則第二個數據包之後的活動超時現在轉移到 conntrack 的處理,超時為 30 秒或 180 秒(或最新核心上的 120 秒),具體取決於各種條件。

您應該驗證它是否符合您的特定規則。

表格

因為它可能成為 iptables 的繼任者,所以這里相當於第一個帶有nft命令的版本(用於處理 IPv4 和 IPv6)。Nftables 原生處理集合。必須使用最近足夠的 nftables (>= 0.8.4 ?) 來設置允許從數據包路徑添加的語句功能,因此在 Debian 9 上它需要來自 stretch-backports 的 nftables

樣板:

nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0; }'

設置語句

set 語句用於從數據包路徑動態添加或更新集合中的元素。設置的 setname 必須已經存在於給定的表中。此外,任何將從 nftables 規則集中動態更新的集合都必須指定最大集合大小(以防止記憶體耗盡)和超時(以便集合中的條目數不會無限增長)。set 語句可用於例如創建動態黑名單。

與 ipset 等效的功能的命名集:

nft add set inet filter ipv4firstseenpacket '{ type ipv4_addr; timeout 60s; size 5000; }'
nft add set inet filter ipv6firstseenpacket '{ type ipv6_addr; timeout 60s; size 5000; }'

過濾規則:

nft add rule inet filter input udp dport 27960 ip  saddr != @ipv4firstseenpacket add @ipv4firstseenpacket '{ ip  saddr }' drop
nft add rule inet filter input udp dport 27960 update @ipv4firstseenpacket '{ ip  saddr }'

nft add rule inet filter input udp dport 27960 ip6 saddr != @ipv6firstseenpacket add @ipv6firstseenpacket '{ ip6 saddr }' drop
nft add rule inet filter input udp dport 27960 update @ipv6firstseenpacket '{ ip6 saddr }' drop

在這裡,一次檢查條目,如果未找到添加,則丟棄數據包。仍然必須有其他動作來刷新計時器。ipv6 版本的工作方式相同。

您可以查看設置的內容,例如:

nft list set inet filter ipv4firstseenpacket

為了徹底,使用 conntrack 將改為(並且也可以使用附加鏈分解):

nft add rule inet filter input ct state established accept
nft add rule inet filter input udp dport 27960 ip  saddr != @ipv4firstseenpacket add @ipv4firstseenpacket '{ ip  saddr }' drop
nft add rule inet filter input udp dport 27960 ip6 saddr != @ipv6firstseenpacket add @ipv6firstseenpacket '{ ip6 saddr }' drop
nft add rule inet filter input udp dport 27960 ct state new accept

引用自:https://serverfault.com/questions/959521