運用 iptables 限制同一IP單位時間連線數

開始是為了針對SSH連線都會有人使用字典攻擊來猜帳號及密碼,在sshd_config設定檔中的MaxAuthTries,只能限制每次連線時帳號密碼的重試測數,超過前數就會中斷連線,但是無法限制連線的次數。
所以如果設定MaxAuthTries=3,那每次連線時可以允許三次的密碼輸入錯誤,錯誤三次後就會中斷連線,但是可以再次連線進來再猜三次,所以還是擋不掉SSH字典攻擊。

除了可以利用 fail2ban 套件來阻擋,也可以利用 iptables 的 recent 模組來限制單位時間連線數,也可在 /etc/sysconfig/iptables 檔案中直接編寫:
-A INPUT -p tcp --dport 22 -m recent --rcheck --seconds 600 hitcount 5 --name SSH_LOG --rsource -j DROP
-A INPUT -p tcp --dport 22 -m recent --set --name SSH_LOG --rsource
-A INPUT -p tcp --dport 22 -j ACCEPT

使用 iptables 裡 recent 模組,先檢查該記錄,如果在600秒內有5次以上的連線,第6次開始就將連線 DROP 掉。如果沒有DROP掉而建立連線,則將連線的時間值記錄在 SSH_LOG 檔案中(位置依 CentOS 版本的不同在 /proc/net/xt_recent/ 或 /proc/net/ipt_recent/ 裡)。接下來就套用第3條規則 ACCEPT 接受連線。

其中
-m recent 是 套用 recnet 模組功能
--set 是指將符合設定條件的來源IP新增或更新於 recent 清單內
--name 指定 recent 清單的名稱(如 SSH_LOG),依 CentOS 版本的不同,會存在 /proc/net/xt_recent/ 或 /proc/net/ipt_recent/ 裡
--rsource 是指將「來源」IP的每個封包新增或更新於 recent 清單內(相反的則是 --rdest 記錄「目的地」IP的封包)
--rcheck 單純檢查 recent 資料表內是否有符合的IP封包記錄(--update 則是不但檢查還會更新封包的時間戳記timestamp,--remove 則是將符合的IP從清單中移除)
--seconds xxx 須與 --rcheck--update 配合使用,查詢在清單中指定秒數內的封包
--hitcount xxx 須與 --rcheck--update 配合使用,如果在清單中的封包數大於或等於指定的數量,則觸發規則

同理,
可以運用 iptables 這個 recent 模組來限制其他服務的同一來源單位時間連線數:
#限制同一IP每秒ping的次數
-A INPUT -p icmp --icmp-type any -m recent --rcheck --seconds 1 --hitcount 1 --name PING_LOG --rsource -j DROP
-A INPUT -p icmp --icmp-type any -m recent --set --name PING_LOG --rsource

-A INPUT -p icmp --icmp-type any -j ACCEPT
#限制DNS同一IP的每秒查詢次數
-A INPUT -p udp --dport 53 -m recent --rcheck --seconds 1 --hitcount 10 --name NAMED_LOG --rsource -j DROP
-A INPUT -p udp --dport 53 -m recent --set --name NAMED_LOG --rsource
-A INPUT -p udp --dport 53 -j ACCEPT

#限制網頁服務同一IP的每秒Session連線數,可讓DoS攻擊的影響降到最低
-A INPUT -p tcp --dport 80 -m recent --rcheck --seconds 1 --hitcount 5 --name HTTP_LOG --rsource -j DROP
-A INPUT -p tcp --dport 80 -m recent --set --name HTTP_LOG --rsource
-A INPUT -p tcp --dport 80 -j ACCEPT

#POP3也常常會受到字典攻擊
-A INPUT -p tcp --dport 110 -m recent --rcheck --seconds 600 --hitcount 5 --name POP3_LOG --rsource -j DROP
-A INPUT -p tcp --dport 110 -m recent --set --name POP3_LOG --rsource
-A INPUT -p tcp --dport 110 -j ACCEPT

在 /proc/net/xt_recent 或 /proc/net/ipt_recent 裡的清單,其記錄內容為:
src=41.203.18.183 ttl: 39 last_seen: 2264301821 oldest_pkt: 1 2264301821
src=114.42.196.217 ttl: 117 last_seen: 2264303237 oldest_pkt: 5 2264303214, 2264303214, 2264303215, 2264303216, 2264303237
src=173.184.240.144 ttl: 109 last_seen: 2264303673 oldest_pkt: 1 2264303673
src=209.172.128.8 ttl: 42 last_seen: 2264301974 oldest_pkt: 2 2264301783, 2264301974

其中src是來源IP,ttl是封包留存時間,last_seen是最近一個封包的時間戳記,oldest_pkt是記錄中所有連線封包的時間戳記。
這個時間戳記不是UNIX timestamp,而是jiffies時間,它記錄了自開機以來經過了幾個jiffy,一個jiffy是Linux系統時鐘(system timer)一個tick的時間,也就是所謂的時鐘中斷(interrupt),在Linux核心2.6.13之後的版本,intel x86 平台上,每個jiffy的預設長度是4ms(或1/250秒),在不同的平台上,jiffy時間長度也大約都落在 1ms ~ 10ms 之間。

它預設共可記錄100個IP,每個IP可記錄20個oldest_pkt封包,要修改它的數量的話:
先停止iptales服務:
#service iptables stop
移除ipt_recent的kernel module:
#rmmod ipt_recent
新增設定檔修改IP來源數
#vi /etc/modprobe.d/ipt_recent.conf

在ipt_recent.conf設定檔中輸入設定值:
options ipt_recent ip_list_tot=500
options ipt_recent ip_pkt_list_tot=50
其中 ip_list_tot 是記錄的IP數量,ip_pkt_list_tot則是每個IP記錄的封包數量

再將ipt_recent的module加入kernel中:
#modprobe ipt_recent

可到 /sys/modules/ipt_recent/parameters/ 中看各個參數的值:
#cat /sys/modules/ipt_recent/parameters/ip_list_tot

#cat /sys/modules/ipt_recent/parameters/ip_pkt_list_tot